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