]>
git.ipfire.org Git - thirdparty/systemd.git/blob - udev_rules_parse.c
6 * Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com>
7 * Copyright (C) 2003-2005 Kay Sievers <kay.sievers@vrfy.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation version 2 of the License.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 675 Mass Ave, Cambridge, MA 02139, USA.
34 #include "udev_libc_wrapper.h"
36 #include "udev_utils.h"
38 #include "udev_rules.h"
40 LIST_HEAD(udev_rule_list
);
42 static int add_config_dev(struct udev_rule
*rule
)
44 struct udev_rule
*tmp_rule
;
46 tmp_rule
= malloc(sizeof(*tmp_rule
));
49 memcpy(tmp_rule
, rule
, sizeof(struct udev_rule
));
50 list_add_tail(&tmp_rule
->node
, &udev_rule_list
);
52 dbg("name='%s', symlink='%s', bus='%s', id='%s', "
53 "sysfs_file[0]='%s', sysfs_value[0]='%s', "
54 "kernel='%s', program='%s', result='%s', "
55 "owner='%s', group='%s', mode=%#o, "
56 "all_partions=%u, ignore_remove=%u, ignore_device=%u, last_rule=%u",
57 rule
->name
, rule
->symlink
, rule
->bus
, rule
->id
,
58 rule
->sysfs_pair
[0].name
, rule
->sysfs_pair
[0].value
,
59 rule
->kernel
, rule
->program
, rule
->result
, rule
->owner
, rule
->group
, rule
->mode
,
60 rule
->partitions
, rule
->ignore_remove
, rule
->ignore_device
, rule
->last_rule
);
65 static int get_key(char **line
, char **key
, enum key_operation
*operation
, char **value
)
75 while (isspace(linepos
[0]) || linepos
[0] == ',')
82 if (linepos
[0] == '\0')
84 if (isspace(linepos
[0]))
86 if (linepos
[0] == '=')
88 if (linepos
[0] == '+')
90 if (linepos
[0] == '!')
94 /* remember end of key */
97 /* skip whitespace after key */
98 while (isspace(linepos
[0]))
101 /* get operation type */
102 if (linepos
[0] == '=' && linepos
[1] == '=') {
103 *operation
= KEY_OP_MATCH
;
105 dbg("operator=match");
106 } else if (linepos
[0] == '!' && linepos
[1] == '=') {
107 *operation
= KEY_OP_NOMATCH
;
109 dbg("operator=nomatch");
110 } else if (linepos
[0] == '+' && linepos
[1] == '=') {
111 *operation
= KEY_OP_ADD
;
114 } else if (linepos
[0] == '=') {
115 *operation
= KEY_OP_ASSIGN
;
117 dbg("operator=assign");
123 dbg("key='%s'", *key
);
125 /* skip whitespace after operator */
126 while (isspace(linepos
[0]))
130 if (linepos
[0] == '"')
136 temp
= strchr(linepos
, '"');
141 dbg("value='%s'", *value
);
143 /* move line to next key */
149 /* extract possible KEY{attr} */
150 static char *get_key_attribute(char *str
)
155 attr
= strchr(str
, '{');
158 pos
= strchr(attr
, '}');
160 err("missing closing brace for format");
164 dbg("attribute='%s'", attr
);
171 static int rules_parse(const char *filename
)
173 char line
[LINE_SIZE
];
182 int program_given
= 0;
185 struct udev_rule rule
;
187 if (file_map(filename
, &buf
, &bufsize
) != 0) {
188 err("can't open '%s' as rules file", filename
);
191 dbg("reading '%s' as rules file", filename
);
193 /* loop through the whole file */
196 while (cur
< bufsize
) {
199 count
= buf_get_line(buf
, bufsize
, cur
);
204 if (count
>= sizeof(line
)) {
205 info("line too long, rule skipped %s, line %d", filename
, lineno
);
209 /* eat the whitespace */
210 while ((count
> 0) && isspace(bufline
[0])) {
217 /* see if this is a comment */
218 if (bufline
[0] == COMMENT_CHARACTER
)
221 /* skip backslash and newline from multi line rules */
222 for (i
= j
= 0; i
< count
; i
++) {
223 if (bufline
[i
] == '\\' && bufline
[i
+1] == '\n')
226 line
[j
++] = bufline
[i
];
229 dbg("read '%s'", line
);
231 /* get all known keys */
232 memset(&rule
, 0x00, sizeof(struct udev_rule
));
239 enum key_operation operation
= KEY_OP_UNSET
;
241 retval
= get_key(&linepos
, &key
, &operation
, &value
);
245 if (strcasecmp(key
, KEY_KERNEL
) == 0) {
246 strlcpy(rule
.kernel
, value
, sizeof(rule
.kernel
));
247 rule
.kernel_operation
= operation
;
252 if (strcasecmp(key
, KEY_SUBSYSTEM
) == 0) {
253 strlcpy(rule
.subsystem
, value
, sizeof(rule
.subsystem
));
254 rule
.subsystem_operation
= operation
;
259 if (strcasecmp(key
, KEY_ACTION
) == 0) {
260 strlcpy(rule
.action
, value
, sizeof(rule
.action
));
261 rule
.action_operation
= operation
;
266 if (strcasecmp(key
, KEY_BUS
) == 0) {
267 strlcpy(rule
.bus
, value
, sizeof(rule
.bus
));
268 rule
.bus_operation
= operation
;
273 if (strcasecmp(key
, KEY_ID
) == 0) {
274 strlcpy(rule
.id
, value
, sizeof(rule
.id
));
275 rule
.id_operation
= operation
;
280 if (strncasecmp(key
, KEY_SYSFS
, sizeof(KEY_SYSFS
)-1) == 0) {
281 struct key_pair
*pair
;
283 if (rule
.sysfs_pair_count
>= KEY_SYSFS_PAIRS_MAX
) {
284 err("skip rule, to many " KEY_SYSFS
" keys in a single rule");
287 pair
= &rule
.sysfs_pair
[rule
.sysfs_pair_count
];
288 attr
= get_key_attribute(key
+ sizeof(KEY_SYSFS
)-1);
290 err("error parsing " KEY_SYSFS
" attribute");
293 strlcpy(pair
->name
, attr
, sizeof(pair
->name
));
294 strlcpy(pair
->value
, value
, sizeof(pair
->value
));
295 pair
->operation
= operation
;
296 rule
.sysfs_pair_count
++;
301 if (strncasecmp(key
, KEY_ENV
, sizeof(KEY_ENV
)-1) == 0) {
302 struct key_pair
*pair
;
304 if (rule
.env_pair_count
>= KEY_ENV_PAIRS_MAX
) {
305 err("skip rule, to many " KEY_ENV
" keys in a single rule");
308 pair
= &rule
.env_pair
[rule
.env_pair_count
];
309 attr
= get_key_attribute(key
+ sizeof(KEY_ENV
)-1);
311 err("error parsing " KEY_ENV
" attribute");
314 strlcpy(pair
->name
, attr
, sizeof(pair
->name
));
315 strlcpy(pair
->value
, value
, sizeof(pair
->value
));
316 pair
->operation
= operation
;
317 rule
.env_pair_count
++;
322 if (strcasecmp(key
, KEY_DRIVER
) == 0) {
323 strlcpy(rule
.driver
, value
, sizeof(rule
.driver
));
324 rule
.driver_operation
= operation
;
329 if (strcasecmp(key
, KEY_RESULT
) == 0) {
330 strlcpy(rule
.result
, value
, sizeof(rule
.result
));
331 rule
.result_operation
= operation
;
336 if (strcasecmp(key
, KEY_PROGRAM
) == 0) {
337 strlcpy(rule
.program
, value
, sizeof(rule
.program
));
338 rule
.program_operation
= operation
;
344 if (strncasecmp(key
, KEY_NAME
, sizeof(KEY_NAME
)-1) == 0) {
345 attr
= get_key_attribute(key
+ sizeof(KEY_NAME
)-1);
346 /* FIXME: remove old style options and make OPTIONS= mandatory */
348 if (strstr(attr
, OPTION_PARTITIONS
) != NULL
) {
349 dbg("creation of partition nodes requested");
350 rule
.partitions
= DEFAULT_PARTITIONS_COUNT
;
352 if (strstr(attr
, OPTION_IGNORE_REMOVE
) != NULL
) {
353 dbg("remove event should be ignored");
354 rule
.ignore_remove
= 1;
357 if (value
[0] != '\0')
358 strlcpy(rule
.name
, value
, sizeof(rule
.name
));
360 rule
.ignore_device
= 1;
365 if (strcasecmp(key
, KEY_SYMLINK
) == 0) {
366 strlcpy(rule
.symlink
, value
, sizeof(rule
.symlink
));
371 if (strcasecmp(key
, KEY_OWNER
) == 0) {
372 strlcpy(rule
.owner
, value
, sizeof(rule
.owner
));
377 if (strcasecmp(key
, KEY_GROUP
) == 0) {
378 strlcpy(rule
.group
, value
, sizeof(rule
.group
));
383 if (strcasecmp(key
, KEY_MODE
) == 0) {
384 rule
.mode
= strtol(value
, NULL
, 8);
389 if (strcasecmp(key
, KEY_RUN
) == 0) {
390 strlcpy(rule
.run
, value
, sizeof(rule
.run
));
395 if (strcasecmp(key
, KEY_OPTIONS
) == 0) {
396 if (strstr(value
, OPTION_LAST_RULE
) != NULL
) {
397 dbg("last rule to be applied");
400 if (strstr(value
, OPTION_IGNORE_DEVICE
) != NULL
) {
401 dbg("device should be ignored");
402 rule
.ignore_device
= 1;
404 if (strstr(value
, OPTION_IGNORE_REMOVE
) != NULL
) {
405 dbg("remove event should be ignored");
406 rule
.ignore_remove
= 1;
408 if (strstr(value
, OPTION_PARTITIONS
) != NULL
) {
409 dbg("creation of partition nodes requested");
410 rule
.partitions
= DEFAULT_PARTITIONS_COUNT
;
416 err("unknown key '%s'", key
);
420 /* skip line if not any valid key was found */
424 if ((rule
.result
[0] != '\0') && (program_given
== 0)) {
425 info(KEY_RESULT
" is only useful when " KEY_PROGRAM
" is called in any rule before");
429 rule
.config_line
= lineno
;
430 strlcpy(rule
.config_file
, filename
, sizeof(rule
.config_file
));
431 retval
= add_config_dev(&rule
);
433 dbg("add_config_dev returned with error %d", retval
);
436 err("parse error %s, line %d:%d, rule skipped",
437 filename
, lineno
, (int) (linepos
- line
));
441 file_unmap(buf
, bufsize
);
445 int udev_rules_init(void)
450 if (stat(udev_rules_filename
, &stats
) != 0)
453 if ((stats
.st_mode
& S_IFMT
) != S_IFDIR
)
454 retval
= rules_parse(udev_rules_filename
);
456 struct name_entry
*name_loop
, *name_tmp
;
457 LIST_HEAD(name_list
);
459 retval
= add_matching_files(&name_list
, udev_rules_filename
, RULEFILE_SUFFIX
);
461 list_for_each_entry_safe(name_loop
, name_tmp
, &name_list
, node
) {
462 rules_parse(name_loop
->name
);
463 list_del(&name_loop
->node
);
470 void udev_rules_close(void)
472 struct udev_rule
*rule
;
473 struct udev_rule
*temp_rule
;
475 list_for_each_entry_safe(rule
, temp_rule
, &udev_rule_list
, node
) {
476 list_del(&rule
->node
);