1/*
2 * Copyright 2004-2009, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <safemode.h>
8
9#include <ctype.h>
10#include <string.h>
11#include <strings.h>
12
13#include <algorithm>
14
15#include <KernelExport.h>
16
17#include <boot/kernel_args.h>
18#include <kernel.h>
19#include <syscalls.h>
20
21
22#ifndef _BOOT_MODE
23
24
25static status_t
26get_option_from_kernel_args(kernel_args* args, const char* settingsName,
27	const char* parameter, size_t parameterLength, char* buffer,
28	size_t* _bufferSize)
29{
30	// find the settings in the kernel args
31	const char* settings = NULL;
32	for (driver_settings_file* file = args->driver_settings;
33			file != NULL; file = file->next) {
34		if (strcmp(settingsName, file->name) == 0) {
35			settings = file->buffer;
36			break;
37		}
38	}
39
40	if (settings == NULL)
41		return B_ENTRY_NOT_FOUND;
42
43	// Unfortunately we can't just use parse_driver_settings_string(), since
44	// we might not have a working heap yet. So we do very limited parsing
45	// ourselves.
46	const char* settingsEnd = settings + strlen(settings);
47	int32 parameterLevel = 0;
48
49	while (*settings != '\0') {
50		// find end of line
51		const char* lineEnd = strchr(settings, '\n');
52		const char* nextLine;
53		if (lineEnd != NULL)
54			nextLine = lineEnd + 1;
55		else
56			nextLine = lineEnd = settingsEnd;
57
58		// ignore any trailing comments
59		lineEnd = std::find(settings, lineEnd, '#');
60
61		const char* nameStart = NULL;
62		const char* nameEnd = NULL;
63		const char* valueStart = NULL;
64		const char* valueEnd = NULL;
65		const char** elementEnd = NULL;
66		bool sawSeparator = true;
67
68		for (; settings < lineEnd; settings++) {
69			switch (*settings) {
70				case '{':
71					parameterLevel++;
72					sawSeparator = true;
73					break;
74
75				case '}':
76					parameterLevel--;
77					sawSeparator = true;
78					break;
79
80				case ';':
81					// TODO: That's not correct. There should be another loop.
82					sawSeparator = true;
83					break;
84
85				default:
86					if (parameterLevel != 0)
87						break;
88
89					if (isspace(*settings)) {
90						sawSeparator = true;
91						break;
92					}
93
94					if (!sawSeparator)
95						break;
96
97					sawSeparator = false;
98
99					if (nameStart == NULL) {
100						nameStart = settings;
101						elementEnd = &nameEnd;
102					} else if (valueStart == NULL) {
103						valueStart = settings;
104						elementEnd = &valueEnd;
105					}
106					break;
107			}
108
109			if (sawSeparator && elementEnd != NULL) {
110				*elementEnd = settings;
111				elementEnd = NULL;
112			}
113		}
114
115		if (elementEnd != NULL)
116			*elementEnd = settings;
117
118		if (nameStart != NULL && size_t(nameEnd - nameStart) == parameterLength
119			&& strncmp(parameter, nameStart, parameterLength) == 0) {
120			if (valueStart == NULL)
121				return B_NAME_NOT_FOUND;
122
123			size_t length = valueEnd - valueStart;
124			if (*_bufferSize > 0) {
125				size_t toCopy = std::min(length, *_bufferSize - 1);
126				memcpy(buffer, valueStart, toCopy);
127				buffer[toCopy] = '\0';
128			}
129
130			*_bufferSize = length;
131			return B_OK;
132		}
133
134		settings = nextLine;
135	}
136
137	return B_NAME_NOT_FOUND;
138}
139
140
141#endif	// !_BOOT_MODE
142
143
144static status_t
145get_option(kernel_args* args, const char* settingsName, const char* parameter,
146	size_t parameterLength, char* buffer, size_t* _bufferSize)
147{
148#ifndef _BOOT_MODE
149	if (args != NULL) {
150		return get_option_from_kernel_args(args, settingsName, parameter,
151			parameterLength, buffer, _bufferSize);
152	}
153#endif
154
155	void* handle = load_driver_settings(settingsName);
156	if (handle == NULL)
157		return B_ENTRY_NOT_FOUND;
158
159	status_t status = B_NAME_NOT_FOUND;
160
161	const char* value = get_driver_parameter(handle, parameter, NULL, NULL);
162	if (value != NULL) {
163		*_bufferSize = strlcpy(buffer, value, *_bufferSize);
164		status = B_OK;
165	}
166
167	unload_driver_settings(handle);
168	return status;
169}
170
171
172static status_t
173get_option(kernel_args* args, const char* parameter, char* buffer,
174	size_t* _bufferSize)
175{
176	size_t parameterLength = strlen(parameter);
177	status_t status = get_option(args, B_SAFEMODE_DRIVER_SETTINGS, parameter,
178		parameterLength, buffer, _bufferSize);
179	if (status != B_OK) {
180		// Try kernel settings file as a fall back
181		status = get_option(args, "kernel", parameter, parameterLength, buffer,
182			_bufferSize);
183	}
184
185	return status;
186}
187
188
189static bool
190get_boolean(kernel_args* args, const char* parameter, bool defaultValue)
191{
192	char value[16];
193	size_t length = sizeof(value);
194
195	if (get_option(args, parameter, value, &length) != B_OK)
196		return defaultValue;
197
198	return !strcasecmp(value, "on") || !strcasecmp(value, "true")
199		|| !strcmp(value, "1") || !strcasecmp(value, "yes")
200		|| !strcasecmp(value, "enabled");
201}
202
203
204// #pragma mark -
205
206
207status_t
208get_safemode_option(const char* parameter, char* buffer, size_t* _bufferSize)
209{
210	return get_option(NULL, parameter, buffer, _bufferSize);
211}
212
213
214bool
215get_safemode_boolean(const char* parameter, bool defaultValue)
216{
217	return get_boolean(NULL, parameter, defaultValue);
218}
219
220
221#ifndef _BOOT_MODE
222
223
224status_t
225get_safemode_option_early(kernel_args* args, const char* parameter,
226	char* buffer, size_t* _bufferSize)
227{
228	return get_option(args, parameter, buffer, _bufferSize);
229}
230
231
232bool
233get_safemode_boolean_early(kernel_args* args, const char* parameter,
234	bool defaultValue)
235{
236	return get_boolean(args, parameter, defaultValue);
237}
238
239
240#endif	// _BOOT_MODE
241
242
243//	#pragma mark - syscalls
244
245
246#ifndef _BOOT_MODE
247
248
249extern "C" status_t
250_user_get_safemode_option(const char* userParameter, char* userBuffer,
251	size_t* _userBufferSize)
252{
253	char parameter[B_FILE_NAME_LENGTH];
254	char buffer[B_PATH_NAME_LENGTH];
255	size_t bufferSize, originalBufferSize;
256
257	if (!IS_USER_ADDRESS(userParameter) || !IS_USER_ADDRESS(userBuffer)
258		|| !IS_USER_ADDRESS(_userBufferSize)
259		|| user_memcpy(&bufferSize, _userBufferSize, sizeof(size_t)) != B_OK
260		|| user_strlcpy(parameter, userParameter, B_FILE_NAME_LENGTH) < B_OK)
261		return B_BAD_ADDRESS;
262
263	if (bufferSize > B_PATH_NAME_LENGTH)
264		bufferSize = B_PATH_NAME_LENGTH;
265
266	originalBufferSize = bufferSize;
267	status_t status = get_safemode_option(parameter, buffer, &bufferSize);
268
269	if (status == B_OK
270		&& (user_strlcpy(userBuffer, buffer, originalBufferSize) < B_OK
271			|| user_memcpy(_userBufferSize, &bufferSize, sizeof(size_t))
272				!= B_OK))
273		return B_BAD_ADDRESS;
274
275	return status;
276}
277
278
279#endif	// !_BOOT_MODE
280