2 * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
6 /*! \brief Implements the driver settings API
7 This file is used by three different components with different needs:
9 Buffers a list of settings files to move over to the kernel - the
10 actual buffering is located in the boot loader directly, though.
11 Creates driver_settings structures out of those on demand only.
13 Maintains a list of settings so that no disk access is required
14 for known settings (such as those passed over from the boot
17 Exports the parser to userland applications, so that they can
18 easily make use of driver_settings styled files.
20 The file has to be recompiled for every component separately, so that
21 it properly exports the required functionality (which is specified by
22 _BOOT_MODE for the boot loader, and _KERNEL_MODE for the kernel).
25 // The boot loader is compiled with kernel rules, but we want to explicitely
26 // differentiate between the two here.
31 #include <directories.h>
32 #include <driver_settings.h>
33 #include <FindDirectory.h>
37 # include <KernelExport.h>
38 # include <util/list.h>
40 # include <kdriver_settings.h>
42 # include <boot/kernel_args.h>
43 # include <boot_device.h>
46 # include <boot/kernel_args.h>
47 # include <boot/stage2.h>
49 # include <find_directory_private.h>
59 #ifndef B_BUFFER_OVERFLOW
60 # define B_BUFFER_OVERFLOW B_ERROR
63 #define SETTINGS_DIRECTORY "/kernel/drivers/"
64 #define SETTINGS_MAGIC 'DrvS'
66 // Those maximum values are independent from the implementation - they
67 // have been chosen to make the code more robust against bad files
68 #define MAX_SETTINGS_SIZE 32768
69 #define MAX_SETTINGS_LEVEL 8
71 #define CONTINUE_PARAMETER 1
72 #define NO_PARAMETER 2
75 typedef struct settings_handle
{
78 char name
[B_OS_NAME_LENGTH
];
80 // A negative ref_count means the node is not reference counted and not
81 // stored in the list.
84 struct driver_settings settings
;
89 enum assignment_mode
{
97 static struct list sHandles
;
98 static mutex sLock
= MUTEX_INITIALIZER("driver settings");
102 // #pragma mark - private functions
106 \brief Returns true for any characters that separate parameters
108 Those characters are ignored in the input stream and won't be added
112 is_parameter_separator(char c
)
114 return c
== '\n' || c
== ';';
118 /*! Indicates if "c" begins a new word or not.
121 is_word_break(char c
)
123 return isspace(c
) || is_parameter_separator(c
);
128 check_handle(settings_handle
*handle
)
131 || handle
->magic
!= SETTINGS_MAGIC
)
138 static driver_parameter
*
139 get_parameter(settings_handle
*handle
, const char *name
)
142 for (i
= handle
->settings
.parameter_count
; i
-- > 0;) {
143 if (!strcmp(handle
->settings
.parameters
[i
].name
, name
))
144 return &handle
->settings
.parameters
[i
];
151 Returns the next word in the input buffer passed in via "_pos" - if
152 this function returns, it will bump the input position after the word.
153 It automatically cares about quoted strings and escaped characters.
154 If "allowNewLine" is true, it reads over comments to get to the next
156 Depending on the "assignmentMode" parameter, the '=' sign is either
157 used as a work break, or not.
158 The input buffer will be changed to contain the word without quotes
159 or escaped characters and adds a terminating NULL byte. The "_word"
160 parameter will be set to the beginning of the word.
161 If the word is followed by a newline it will return B_OK, if white
162 spaces follows, it will return CONTINUE_PARAMETER.
165 get_word(char **_pos
, char **_word
, int32 assignmentMode
, bool allowNewLine
)
169 bool newLine
= false, end
= false;
171 bool charEscaped
= false;
173 // Skip any white space and comments
175 && ((allowNewLine
&& (isspace(pos
[0]) || is_parameter_separator(pos
[0])
177 || (!allowNewLine
&& (pos
[0] == '\t' || pos
[0] == ' '))
178 || (assignmentMode
== ALLOW_ASSIGNMENT
&& pos
[0] == '='))) {
179 // skip any comment lines
181 while (pos
[0] && pos
[0] != '\n')
187 if (pos
[0] == '}' || pos
[0] == '\0') {
188 // if we just read some white space before an end of a
189 // parameter, this is just no parameter at all
194 // Read in a word - might contain escaped (\) spaces, or it
195 // might also be quoted (" or ').
197 if (pos
[0] == '"' || pos
[0] == '\'') {
206 else if (pos
[0] == '\\') {
209 } else if ((!quoted
&& (is_word_break(pos
[0])
210 || (assignmentMode
!= IGNORE_ASSIGNMENT
&& pos
[0] == '=')))
211 || (quoted
&& pos
[0] == quoted
))
217 // "String exceeds line" - missing end quote
218 if (quoted
&& pos
[0] != quoted
)
221 // last character is a backslash
225 end
= pos
[0] == '\0';
226 newLine
= is_parameter_separator(pos
[0]) || end
;
229 // Correct name if there were any escaped characters
233 while (word
<= pos
) {
234 if (word
[0] == '\\') {
238 word
[offset
] = word
[0];
248 // Scan for next beginning word, open brackets, or comment start
256 if (is_parameter_separator(pos
[0])) {
257 // an open bracket '{' could follow after the first
258 // newline, but not later
263 } else if (pos
[0] == '{' || pos
[0] == '}' || pos
[0] == '#')
265 else if (!isspace(pos
[0]))
266 return newLine
? B_NO_ERROR
: CONTINUE_PARAMETER
;
274 parse_parameter(struct driver_parameter
*parameter
, char **_pos
, int32 level
)
279 // initialize parameter first
280 memset(parameter
, 0, sizeof(struct driver_parameter
));
282 status
= get_word(&pos
, ¶meter
->name
, NO_ASSIGNMENT
, true);
283 if (status
== CONTINUE_PARAMETER
) {
284 while (status
== CONTINUE_PARAMETER
) {
285 char **newArray
, *value
= NULL
;
286 status
= get_word(&pos
, &value
, parameter
->value_count
== 0
287 ? ALLOW_ASSIGNMENT
: IGNORE_ASSIGNMENT
, false);
291 // enlarge value array and save the value
293 newArray
= (char**)realloc(parameter
->values
,
294 (parameter
->value_count
+ 1) * sizeof(char *));
295 if (newArray
== NULL
)
298 parameter
->values
= newArray
;
299 parameter
->values
[parameter
->value_count
++] = value
;
309 parse_parameters(struct driver_parameter
**_parameters
, int *_count
,
310 char **_pos
, int32 level
)
312 if (level
> MAX_SETTINGS_LEVEL
)
316 struct driver_parameter parameter
;
317 struct driver_parameter
*newArray
;
320 status
= parse_parameter(¶meter
, _pos
, level
);
324 if (status
!= NO_PARAMETER
) {
325 driver_parameter
*newParameter
;
327 newArray
= (driver_parameter
*)realloc(*_parameters
, (*_count
+ 1)
328 * sizeof(struct driver_parameter
));
329 if (newArray
== NULL
)
332 memcpy(&newArray
[*_count
], ¶meter
, sizeof(struct driver_parameter
));
333 newParameter
= &newArray
[*_count
];
335 *_parameters
= newArray
;
338 // check for level beginning and end
340 // if we go a level deeper, just start all over again...
342 status
= parse_parameters(&newParameter
->parameters
,
343 &newParameter
->parameter_count
, _pos
, level
+ 1);
349 if ((**_pos
== '}' && level
> 0)
350 || (**_pos
== '\0' && level
== 0)) {
351 // take the closing bracket from the stack
356 // obviously, something has gone wrong
357 if (**_pos
== '}' || **_pos
== '\0')
364 parse_settings(settings_handle
*handle
)
366 char *text
= handle
->text
;
368 memset(&handle
->settings
, 0, sizeof(struct driver_settings
));
370 // empty settings are allowed
374 return parse_parameters(&handle
->settings
.parameters
,
375 &handle
->settings
.parameter_count
, &text
, 0);
380 free_parameter(struct driver_parameter
*parameter
)
383 for (i
= parameter
->parameter_count
; i
-- > 0;)
384 free_parameter(¶meter
->parameters
[i
]);
386 free(parameter
->parameters
);
387 free(parameter
->values
);
392 free_settings(settings_handle
*handle
)
395 for (i
= handle
->settings
.parameter_count
; i
-- > 0;)
396 free_parameter(&handle
->settings
.parameters
[i
]);
398 free(handle
->settings
.parameters
);
404 static settings_handle
*
405 new_settings(char *buffer
, const char *driverName
)
407 settings_handle
*handle
= (settings_handle
*)malloc(sizeof(settings_handle
));
411 handle
->magic
= SETTINGS_MAGIC
;
412 handle
->text
= buffer
;
415 if (driverName
!= NULL
) {
416 handle
->ref_count
= 1;
417 strlcpy(handle
->name
, driverName
, sizeof(handle
->name
));
419 handle
->ref_count
= -1;
424 if (parse_settings(handle
) == B_OK
)
432 static settings_handle
*
433 load_driver_settings_from_file(int file
, const char *driverName
)
437 // Allocate a buffer and read the whole file into it.
438 // We will keep this buffer in memory, until the settings
440 // The driver_parameter::name field will point directly
443 if (fstat(file
, &stat
) < B_OK
)
446 if (stat
.st_size
> B_OK
&& stat
.st_size
< MAX_SETTINGS_SIZE
) {
447 char *text
= (char *)malloc(stat
.st_size
+ 1);
448 if (text
!= NULL
&& read(file
, text
, stat
.st_size
) == stat
.st_size
) {
449 settings_handle
*handle
;
451 text
[stat
.st_size
] = '\0';
452 // make sure the string is null terminated
453 // to avoid misbehaviour
455 handle
= new_settings(text
, driverName
);
456 if (handle
!= NULL
) {
457 // everything went fine!
463 // "text" might be NULL here, but that's allowed
472 put_string(char **_buffer
, ssize_t
*_bufferSize
, char *string
)
474 size_t length
, reserved
, quotes
;
475 char *buffer
= *_buffer
, c
;
481 for (length
= reserved
= quotes
= 0; (c
= string
[length
]) != '\0'; length
++) {
484 else if (is_word_break(c
))
487 quoted
= reserved
|| quotes
;
489 // update _bufferSize in any way, so that we can chain several
490 // of these calls without having to check the return value
492 *_bufferSize
-= length
+ (quoted
? 2 + quotes
: 0);
494 if (*_bufferSize
<= 0)
500 for (;(c
= string
[0]) != '\0'; string
++) {
512 // update the buffer position
520 put_chars(char **_buffer
, ssize_t
*_bufferSize
, const char *chars
)
522 char *buffer
= *_buffer
;
528 length
= strlen(chars
);
529 *_bufferSize
-= length
;
531 if (*_bufferSize
<= 0)
534 memcpy(buffer
, chars
, length
);
538 // update the buffer position
546 put_char(char **_buffer
, ssize_t
*_bufferSize
, char c
)
548 char *buffer
= *_buffer
;
552 if (*_bufferSize
<= 0)
558 // update the buffer position
559 *_buffer
= buffer
+ 1;
566 put_level_space(char **_buffer
, ssize_t
*_bufferSize
, int32 level
)
569 put_char(_buffer
, _bufferSize
, '\t');
574 put_parameter(char **_buffer
, ssize_t
*_bufferSize
,
575 struct driver_parameter
*parameter
, int32 level
, bool flat
)
580 put_level_space(_buffer
, _bufferSize
, level
);
582 put_string(_buffer
, _bufferSize
, parameter
->name
);
583 if (flat
&& parameter
->value_count
> 0)
584 put_chars(_buffer
, _bufferSize
, " =");
586 for (i
= 0; i
< parameter
->value_count
; i
++) {
587 put_char(_buffer
, _bufferSize
, ' ');
588 put_string(_buffer
, _bufferSize
, parameter
->values
[i
]);
591 if (parameter
->parameter_count
> 0) {
592 put_chars(_buffer
, _bufferSize
, " {");
594 put_char(_buffer
, _bufferSize
, '\n');
596 for (i
= 0; i
< parameter
->parameter_count
; i
++) {
597 put_parameter(_buffer
, _bufferSize
, ¶meter
->parameters
[i
],
600 if (parameter
->parameters
[i
].parameter_count
== 0)
601 put_chars(_buffer
, _bufferSize
, flat
? "; " : "\n");
605 put_level_space(_buffer
, _bufferSize
, level
);
606 put_chars(_buffer
, _bufferSize
, flat
? "}" : "}\n");
611 // #pragma mark - Kernel only functions
615 static settings_handle
*
616 find_driver_settings(const char *name
)
618 settings_handle
*handle
= NULL
;
620 ASSERT_LOCKED_MUTEX(&sLock
);
622 while ((handle
= (settings_handle
*)list_get_next_item(&sHandles
, handle
))
624 if (!strcmp(handle
->name
, name
))
633 driver_settings_init(kernel_args
*args
)
635 struct driver_settings_file
*settings
= args
->driver_settings
;
637 // Move the preloaded driver settings over to the kernel
639 list_init(&sHandles
);
641 while (settings
!= NULL
) {
642 settings_handle
*handle
643 = (settings_handle
*)malloc(sizeof(settings_handle
));
647 if (settings
->size
!= 0) {
648 handle
->text
= (char*)malloc(settings
->size
+ 1);
649 if (handle
->text
== NULL
) {
654 memcpy(handle
->text
, settings
->buffer
, settings
->size
);
655 handle
->text
[settings
->size
] = '\0';
656 // null terminate the buffer
660 strlcpy(handle
->name
, settings
->name
, sizeof(handle
->name
));
661 handle
->settings
.parameters
= NULL
;
662 handle
->settings
.parameter_count
= 0;
664 // this triggers parsing the settings when they are actually used
666 if (!strcmp(handle
->name
, B_SAFEMODE_DRIVER_SETTINGS
)) {
667 // These settings cannot be reloaded, so we better don't throw
669 handle
->ref_count
= 1;
671 handle
->ref_count
= 0;
673 list_add_item(&sHandles
, handle
);
675 settings
= settings
->next
;
683 // #pragma mark - public API
687 unload_driver_settings(void *_handle
)
689 settings_handle
*handle
= (settings_handle
*)_handle
;
690 if (!check_handle(handle
))
696 if (handle
->ref_count
> 0) {
697 if (--handle
->ref_count
== 0 && gBootDevice
> 0) {
698 // don't unload an handle when /boot is not available
699 list_remove_link(&handle
->link
);
703 mutex_unlock(&sLock
);
707 free_settings(handle
);
714 load_driver_settings(const char *driverName
)
716 settings_handle
*handle
;
719 if (driverName
== NULL
)
723 // see if we already have these settings loaded
725 handle
= find_driver_settings(driverName
);
726 if (handle
!= NULL
&& handle
->ref_count
== 0 && gBootDevice
> 0) {
727 // A handle with a zero ref_count should be unloaded if /boot is
729 list_remove_link(&handle
->link
);
730 free_settings(handle
);
731 } else if (handle
!= NULL
) {
734 // we got it, now let's see if it already has been parsed
735 if (handle
->magic
!= SETTINGS_MAGIC
) {
736 handle
->magic
= SETTINGS_MAGIC
;
738 if (parse_settings(handle
) != B_OK
) {
739 // no valid settings, let's cut down its memory requirements
745 mutex_unlock(&sLock
);
749 // we are allowed to call the driver settings pretty early in the boot process
750 if (gKernelStartup
) {
751 mutex_unlock(&sLock
);
754 #endif // _KERNEL_MODE
756 // see if we already have these settings loaded
758 struct driver_settings_file
*settings
= gKernelArgs
.driver_settings
;
759 while (settings
!= NULL
) {
760 if (!strcmp(settings
->name
, driverName
)) {
761 // we have it - since the buffer is clobbered, we have to
762 // copy its contents, though
763 char *text
= (char*)malloc(settings
->size
+ 1);
767 memcpy(text
, settings
->buffer
, settings
->size
+ 1);
768 return new_settings(text
, driverName
);
770 settings
= settings
->next
;
775 // open the settings from the standardized location
776 if (driverName
[0] != '/') {
777 char path
[B_FILE_NAME_LENGTH
+ 64];
780 strcpy(path
, kUserSettingsDirectory
);
782 // TODO: use B_SYSTEM_SETTINGS_DIRECTORY instead!
783 if (__find_directory(B_USER_SETTINGS_DIRECTORY
, -1, false, path
,
784 sizeof(path
)) == B_OK
)
787 strlcat(path
, SETTINGS_DIRECTORY
, sizeof(path
));
788 strlcat(path
, driverName
, sizeof(path
));
791 file
= open(path
, O_RDONLY
);
793 file
= open(driverName
, O_RDONLY
);
797 mutex_unlock(&sLock
);
802 handle
= load_driver_settings_from_file(file
, driverName
);
806 list_add_item(&sHandles
, handle
);
807 mutex_unlock(&sLock
);
811 return (void *)handle
;
816 load_driver_settings_file(int fd
)
818 return load_driver_settings_from_file(fd
, NULL
);
823 Returns a new driver_settings handle that has the parsed contents
824 of the passed string.
825 You can get an empty driver_settings object when you pass NULL as
826 the "settingsString" parameter.
829 parse_driver_settings_string(const char *settingsString
)
832 if (settingsString
!= NULL
) {
833 // we simply copy the whole string to use it as our internal buffer
834 text
= strdup(settingsString
);
839 settings_handle
*handle
= new_settings(text
, NULL
);
847 This function prints out a driver settings structure to a human
849 It's either in standard style or the single line style speficied
850 by the "flat" parameter.
851 If the buffer is too small to hold the string, B_BUFFER_OVERFLOW
852 is returned, and the needed amount of bytes if placed in the
853 "_bufferSize" parameter.
854 If the "handle" parameter is not a valid driver settings handle, or
855 the "buffer" parameter is NULL, B_BAD_VALUE is returned.
858 get_driver_settings_string(void *_handle
, char *buffer
, ssize_t
*_bufferSize
,
861 settings_handle
*handle
= (settings_handle
*)_handle
;
862 ssize_t bufferSize
= *_bufferSize
;
865 if (!check_handle(handle
) || !buffer
|| *_bufferSize
== 0)
868 for (i
= 0; i
< handle
->settings
.parameter_count
; i
++) {
869 put_parameter(&buffer
, &bufferSize
, &handle
->settings
.parameters
[i
],
873 *_bufferSize
-= bufferSize
;
874 return bufferSize
>= 0 ? B_OK
: B_BUFFER_OVERFLOW
;
879 Matches the first value of the parameter matching "keyName" with a set
880 of boolean values like 1/true/yes/on/enabled/...
881 Returns "unknownValue" if the parameter could not be found or doesn't
882 have any valid boolean setting, and "noArgValue" if the parameter
883 doesn't have any values.
884 Also returns "unknownValue" if the handle passed in was not valid.
887 get_driver_boolean_parameter(void *_handle
, const char *keyName
,
888 bool unknownValue
, bool noArgValue
)
890 settings_handle
*handle
= (settings_handle
*)_handle
;
891 driver_parameter
*parameter
;
894 if (!check_handle(handle
))
897 // check for the parameter
898 if ((parameter
= get_parameter(handle
, keyName
)) == NULL
)
901 // check for the argument
902 if (parameter
->value_count
<= 0)
905 boolean
= parameter
->values
[0];
906 if (!strcmp(boolean
, "1")
907 || !strcasecmp(boolean
, "true")
908 || !strcasecmp(boolean
, "yes")
909 || !strcasecmp(boolean
, "on")
910 || !strcasecmp(boolean
, "enable")
911 || !strcasecmp(boolean
, "enabled"))
914 if (!strcmp(boolean
, "0")
915 || !strcasecmp(boolean
, "false")
916 || !strcasecmp(boolean
, "no")
917 || !strcasecmp(boolean
, "off")
918 || !strcasecmp(boolean
, "disable")
919 || !strcasecmp(boolean
, "disabled"))
922 // if no known keyword is found, "unknownValue" is returned
928 get_driver_parameter(void *_handle
, const char *keyName
,
929 const char *unknownValue
, const char *noArgValue
)
931 settings_handle
* handle
= (settings_handle
*)_handle
;
932 struct driver_parameter
*parameter
;
934 if (!check_handle(handle
))
937 // check for the parameter
938 if ((parameter
= get_parameter(handle
, keyName
)) == NULL
)
941 // check for the argument
942 if (parameter
->value_count
<= 0)
945 return parameter
->values
[0];
949 const driver_settings
*
950 get_driver_settings(void *handle
)
952 if (!check_handle((settings_handle
*)handle
))
955 return &((settings_handle
*)handle
)->settings
;
960 delete_driver_settings(void *_handle
)
962 return unload_driver_settings(_handle
);