2 * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 #define PREALLOC_TOKEN 2048
32 #define PREALLOC_STRBUF 32 * 1024
43 static const char *operation_str
[] = {
44 [KEY_OP_MATCH
] = "match",
45 [KEY_OP_NOMATCH
] = "nomatch",
47 [KEY_OP_ASSIGN
] = "assign",
48 [KEY_OP_ASSIGN_FINAL
] = "assign-final",
55 TK_M_WAITFOR
, /* val */
56 TK_M_ACTION
, /* val */
57 TK_M_DEVPATH
, /* val */
58 TK_M_KERNEL
, /* val */
59 TK_M_DEVLINK
, /* val */
61 TK_M_ENV
, /* val, attr */
62 TK_M_SUBSYSTEM
, /* val */
63 TK_M_DRIVER
, /* val */
64 TK_M_ATTR
, /* val, attr */
66 TK_M_KERNELS
, /* val */
67 TK_M_SUBSYSTEMS
, /* val */
68 TK_M_DRIVERS
, /* val */
69 TK_M_ATTRS
, /* val, attr */
72 TK_M_TEST
, /* val, mode_t */
73 TK_M_PROGRAM
, /* val */
74 TK_M_IMPORT_FILE
, /* val */
75 TK_M_IMPORT_PROG
, /* val */
76 TK_M_IMPORT_PARENT
, /* val */
77 TK_M_RESULT
, /* val */
80 TK_A_STRING_ESCAPE_NONE
,
81 TK_A_STRING_ESCAPE_REPLACE
,
82 TK_A_NUM_FAKE_PART
, /* int */
83 TK_A_DEVLINK_PRIO
, /* int */
87 TK_A_OWNER_ID
, /* uid_t */
88 TK_A_GROUP_ID
, /* gid_t */
89 TK_A_MODE_ID
, /* mode_t */
90 TK_A_ENV
, /* val, attr */
92 TK_A_DEVLINK
, /* val */
93 TK_A_EVENT_TIMEOUT
, /* int */
95 TK_A_ATTR
, /* val, attr */
96 TK_A_RUN
, /* val, bool */
97 TK_A_GOTO
, /* size_t */
103 static const char *token_str
[] = {
104 [TK_UNDEF
] = "UNDEF",
107 [TK_M_WAITFOR
] = "M WAITFOR",
108 [TK_M_ACTION
] = "M ACTION",
109 [TK_M_DEVPATH
] = "M DEVPATH",
110 [TK_M_KERNEL
] = "M KERNEL",
111 [TK_M_DEVLINK
] = "M DEVLINK",
112 [TK_M_NAME
] = "M NAME",
113 [TK_M_ENV
] = "M ENV",
114 [TK_M_SUBSYSTEM
] = "M SUBSYSTEM",
115 [TK_M_DRIVER
] = "M DRIVER",
116 [TK_M_ATTR
] = "M ATTR",
118 [TK_M_KERNELS
] = "M KERNELS",
119 [TK_M_SUBSYSTEMS
] = "M SUBSYSTEMS",
120 [TK_M_DRIVERS
] = "M DRIVERS",
121 [TK_M_ATTRS
] = "M ATTRS",
122 [TK_PARENTS_MAX
] = "PARENTS_MAX",
124 [TK_M_TEST
] = "M TEST",
125 [TK_M_PROGRAM
] = "M PROGRAM",
126 [TK_M_IMPORT_FILE
] = "M IMPORT_FILE",
127 [TK_M_IMPORT_PROG
] = "M IMPORT_PROG",
128 [TK_M_IMPORT_PARENT
] = "M MPORT_PARENT",
129 [TK_M_RESULT
] = "M RESULT",
131 [TK_A_IGNORE_DEVICE
] = "A IGNORE_DEVICE",
132 [TK_A_STRING_ESCAPE_NONE
] = "A STRING_ESCAPE_NONE",
133 [TK_A_STRING_ESCAPE_REPLACE
] = "A STRING_ESCAPE_REPLACE",
134 [TK_A_NUM_FAKE_PART
] = "A NUM_FAKE_PART",
135 [TK_A_DEVLINK_PRIO
] = "A DEVLINK_PRIO",
136 [TK_A_OWNER
] = "A OWNER",
137 [TK_A_GROUP
] = "A GROUP",
138 [TK_A_MODE
] = "A MODE",
139 [TK_A_OWNER_ID
] = "A OWNER_ID",
140 [TK_A_GROUP_ID
] = "A GROUP_ID",
141 [TK_A_MODE_ID
] = "A MODE_ID",
142 [TK_A_ENV
] = "A ENV",
143 [TK_A_NAME
] = "A NAME",
144 [TK_A_DEVLINK
] = "A DEVLINK",
145 [TK_A_EVENT_TIMEOUT
] = "A EVENT_TIMEOUT",
146 [TK_A_IGNORE_REMOVE
] = "A IGNORE_REMOVE",
147 [TK_A_ATTR
] = "A ATTR",
148 [TK_A_RUN
] = "A RUN",
149 [TK_A_GOTO
] = "A GOTO",
150 [TK_A_LAST_RULE
] = "A LAST_RULE",
156 enum token_type type
;
159 unsigned int next_rule
;
160 unsigned int label_off
;
161 unsigned int filename_off
;
164 enum key_operation op
;
165 unsigned int value_off
;
167 unsigned int attr_off
;
170 unsigned int rule_goto
;
184 struct udev_rules
*rules
;
186 struct token token
[MAX_TK
];
187 unsigned int token_cur
;
193 struct token
*tokens
;
194 unsigned int token_cur
;
195 unsigned int token_max
;
199 unsigned int buf_count
;
202 /* we could lookup and return existing strings, or tails of strings */
203 static int add_string(struct udev_rules
*rules
, const char *str
)
205 size_t len
= strlen(str
)+1;
208 if (rules
->buf_cur
+ len
+1 >= rules
->buf_max
) {
212 /* double the buffer size */
213 add
= rules
->buf_max
;
217 buf
= realloc(rules
->buf
, rules
->buf_max
+ add
);
220 info(rules
->udev
, "extend buffer from %zu to %zu\n", rules
->buf_max
, rules
->buf_max
+ add
);
222 rules
->buf_max
+= add
;
224 off
= rules
->buf_cur
;
225 memcpy(&rules
->buf
[rules
->buf_cur
], str
, len
);
226 rules
->buf_cur
+= len
;
231 static int add_token(struct udev_rules
*rules
, struct token
*token
)
234 if (rules
->token_cur
+1 >= rules
->token_max
) {
235 struct token
*tokens
;
238 /* double the buffer size */
239 add
= rules
->token_max
;
243 tokens
= realloc(rules
->tokens
, (rules
->token_max
+ add
) * sizeof(struct token
));
246 info(rules
->udev
, "extend tokens from %u to %u\n", rules
->token_max
, rules
->token_max
+ add
);
247 rules
->tokens
= tokens
;
248 rules
->token_max
+= add
;
250 memcpy(&rules
->tokens
[rules
->token_cur
], token
, sizeof(struct token
));
255 static int import_property_from_string(struct udev_device
*dev
, char *line
)
257 struct udev
*udev
= udev_device_get_udev(dev
);
264 while (isspace(key
[0]))
267 /* comment or empty line */
268 if (key
[0] == '#' || key
[0] == '\0')
271 /* split key/value */
272 val
= strchr(key
, '=');
279 while (isspace(val
[0]))
286 while (isspace(key
[len
-1]))
290 /* terminate value */
294 while (isspace(val
[len
-1]))
302 if (val
[0] == '"' || val
[0] == '\'') {
303 if (val
[len
-1] != val
[0]) {
304 info(udev
, "inconsistent quoting: '%s', skip\n", line
);
311 info(udev
, "adding '%s'='%s'\n", key
, val
);
313 /* handle device, renamed by external tool, returning new path */
314 if (strcmp(key
, "DEVPATH") == 0) {
315 char syspath
[UTIL_PATH_SIZE
];
317 info(udev
, "updating devpath from '%s' to '%s'\n",
318 udev_device_get_devpath(dev
), val
);
319 util_strlcpy(syspath
, udev_get_sys_path(udev
), sizeof(syspath
));
320 util_strlcat(syspath
, val
, sizeof(syspath
));
321 udev_device_set_syspath(dev
, syspath
);
323 struct udev_list_entry
*entry
;
325 entry
= udev_device_add_property(dev
, key
, val
);
327 udev_list_entry_set_flag(entry
, 1);
332 static int import_file_into_properties(struct udev_device
*dev
, const char *filename
)
335 char line
[UTIL_LINE_SIZE
];
337 f
= fopen(filename
, "r");
340 while (fgets(line
, sizeof(line
), f
) != NULL
)
341 import_property_from_string(dev
, line
);
346 static int import_program_into_properties(struct udev_device
*dev
, const char *program
)
348 struct udev
*udev
= udev_device_get_udev(dev
);
354 envp
= udev_device_get_properties_envp(dev
);
355 if (util_run_program(udev
, program
, envp
, result
, sizeof(result
), &reslen
) != 0)
359 while (line
!= NULL
) {
362 pos
= strchr(line
, '\n');
367 import_property_from_string(dev
, line
);
373 static int import_parent_into_properties(struct udev_device
*dev
, const char *filter
)
375 struct udev
*udev
= udev_device_get_udev(dev
);
376 struct udev_device
*dev_parent
;
377 struct udev_list_entry
*list_entry
;
379 dev_parent
= udev_device_get_parent(dev
);
380 if (dev_parent
== NULL
)
383 dbg(udev
, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent
));
384 udev_list_entry_foreach(list_entry
, udev_device_get_properties_list_entry(dev_parent
)) {
385 const char *key
= udev_list_entry_get_name(list_entry
);
386 const char *val
= udev_list_entry_get_value(list_entry
);
388 if (fnmatch(filter
, key
, 0) == 0) {
389 struct udev_list_entry
*entry
;
391 dbg(udev
, "import key '%s=%s'\n", key
, val
);
392 entry
= udev_device_add_property(dev
, key
, val
);
394 udev_list_entry_set_flag(entry
, 1);
400 #define WAIT_LOOP_PER_SECOND 50
401 static int wait_for_file(struct udev_device
*dev
, const char *file
, int timeout
)
403 struct udev
*udev
= udev_device_get_udev(dev
);
404 char filepath
[UTIL_PATH_SIZE
];
405 char devicepath
[UTIL_PATH_SIZE
] = "";
407 int loop
= timeout
* WAIT_LOOP_PER_SECOND
;
409 /* a relative path is a device attribute */
410 if (file
[0] != '/') {
411 util_strlcpy(devicepath
, udev_get_sys_path(udev
), sizeof(devicepath
));
412 util_strlcat(devicepath
, udev_device_get_devpath(dev
), sizeof(devicepath
));
414 util_strlcpy(filepath
, devicepath
, sizeof(filepath
));
415 util_strlcat(filepath
, "/", sizeof(filepath
));
416 util_strlcat(filepath
, file
, sizeof(filepath
));
420 dbg(udev
, "will wait %i sec for '%s'\n", timeout
, file
);
423 if (stat(file
, &stats
) == 0) {
424 info(udev
, "file '%s' appeared after %i loops\n", file
, (timeout
* WAIT_LOOP_PER_SECOND
) - loop
-1);
427 /* make sure, the device did not disappear in the meantime */
428 if (devicepath
[0] != '\0' && stat(devicepath
, &stats
) != 0) {
429 info(udev
, "device disappeared while waiting for '%s'\n", file
);
432 info(udev
, "wait for '%s' for %i mseconds\n", file
, 1000 / WAIT_LOOP_PER_SECOND
);
433 usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND
);
435 info(udev
, "waiting for '%s' failed\n", file
);
439 static int attr_subst_subdir(char *attr
, size_t len
)
444 pos
= strstr(attr
, "/*/");
446 char str
[UTIL_PATH_SIZE
];
450 util_strlcpy(str
, &pos
[2], sizeof(str
));
455 for (dent
= readdir(dir
); dent
!= NULL
; dent
= readdir(dir
)) {
458 if (dent
->d_name
[0] == '.')
460 util_strlcat(attr
, dent
->d_name
, len
);
461 util_strlcat(attr
, str
, len
);
462 if (stat(attr
, &stats
) == 0) {
471 util_strlcat(attr
, str
, len
);
477 static int get_key(struct udev
*udev
, char **line
, char **key
, enum key_operation
*op
, char **value
)
483 if (linepos
== NULL
&& linepos
[0] == '\0')
486 /* skip whitespace */
487 while (isspace(linepos
[0]) || linepos
[0] == ',')
491 if (linepos
[0] == '\0')
497 if (linepos
[0] == '\0')
499 if (isspace(linepos
[0]))
501 if (linepos
[0] == '=')
503 if ((linepos
[0] == '+') || (linepos
[0] == '!') || (linepos
[0] == ':'))
504 if (linepos
[1] == '=')
508 /* remember end of key */
511 /* skip whitespace after key */
512 while (isspace(linepos
[0]))
514 if (linepos
[0] == '\0')
517 /* get operation type */
518 if (linepos
[0] == '=' && linepos
[1] == '=') {
521 } else if (linepos
[0] == '!' && linepos
[1] == '=') {
522 *op
= KEY_OP_NOMATCH
;
524 } else if (linepos
[0] == '+' && linepos
[1] == '=') {
527 } else if (linepos
[0] == '=') {
530 } else if (linepos
[0] == ':' && linepos
[1] == '=') {
531 *op
= KEY_OP_ASSIGN_FINAL
;
539 /* skip whitespace after operator */
540 while (isspace(linepos
[0]))
542 if (linepos
[0] == '\0')
546 if (linepos
[0] == '"')
552 temp
= strchr(linepos
, '"');
557 dbg(udev
, "%s '%s'-'%s'\n", operation_str
[*op
], *key
, *value
);
559 /* move line to next key */
564 /* extract possible KEY{attr} */
565 static char *get_key_attribute(struct udev
*udev
, char *str
)
570 attr
= strchr(str
, '{');
573 pos
= strchr(attr
, '}');
575 err(udev
, "missing closing brace for format\n");
579 dbg(udev
, "attribute='%s'\n", attr
);
585 static int rule_add_token(struct rule_tmp
*rule_tmp
, enum token_type type
,
586 enum key_operation op
,
587 const char *value
, const void *data
)
589 struct token
*token
= &rule_tmp
->token
[rule_tmp
->token_cur
];
590 const char *attr
= data
;
603 case TK_M_SUBSYSTEMS
:
606 case TK_M_IMPORT_FILE
:
607 case TK_M_IMPORT_PROG
:
608 case TK_M_IMPORT_PARENT
:
616 token
->key
.value_off
= add_string(rule_tmp
->rules
, value
);
623 token
->key
.value_off
= add_string(rule_tmp
->rules
, value
);
624 token
->key
.attr_off
= add_string(rule_tmp
->rules
, attr
);
628 mode
= *(mode_t
*)data
;
629 token
->key
.value_off
= add_string(rule_tmp
->rules
, value
);
630 token
->key
.mode
= mode
;
632 case TK_A_IGNORE_DEVICE
:
633 case TK_A_STRING_ESCAPE_NONE
:
634 case TK_A_STRING_ESCAPE_REPLACE
:
635 case TK_A_IGNORE_REMOVE
:
639 token
->key
.value_off
= add_string(rule_tmp
->rules
, value
);
640 token
->key
.ignore_error
= *(int *)data
;
642 case TK_A_NUM_FAKE_PART
:
643 token
->key
.num_fake_part
= *(int *)data
;
645 case TK_A_DEVLINK_PRIO
:
646 token
->key
.devlink_prio
= *(int *)data
;
649 token
->key
.uid
= *(uid_t
*)data
;
652 token
->key
.gid
= *(gid_t
*)data
;
655 token
->key
.mode
= *(mode_t
*)data
;
657 case TK_A_EVENT_TIMEOUT
:
658 token
->key
.event_timeout
= *(int *)data
;
664 err(rule_tmp
->rules
->udev
, "wrong type %u\n", type
);
669 rule_tmp
->token_cur
++;
670 if (rule_tmp
->token_cur
>= ARRAY_SIZE(rule_tmp
->token
)) {
671 err(rule_tmp
->rules
->udev
, "temporary rule array too small\n");
678 static void dump_token(struct udev_rules
*rules
, struct token
*token
)
680 enum token_type type
= token
->type
;
681 enum key_operation op
= token
->key
.op
;
682 const char *value
= &rules
->buf
[token
->key
.value_off
];
683 const char *attr
= &rules
->buf
[token
->key
.attr_off
];
688 const char *tks_ptr
= (char *)rules
->tokens
;
689 const char *tk_ptr
= (char *)token
;
690 unsigned int off
= tk_ptr
- tks_ptr
;
692 dbg(rules
->udev
, "* RULE '%s', off: %u(%u), next: %u, label: '%s'\n",
693 &rules
->buf
[token
->rule
.filename_off
],
694 off
/ (unsigned int) sizeof(struct token
), off
,
695 token
->rule
.next_rule
,
696 &rules
->buf
[token
->rule
.label_off
]);
708 case TK_M_SUBSYSTEMS
:
711 case TK_M_IMPORT_FILE
:
712 case TK_M_IMPORT_PROG
:
713 case TK_M_IMPORT_PARENT
:
721 dbg(rules
->udev
, "%s %s '%s'\n", token_str
[type
], operation_str
[op
], value
);
728 dbg(rules
->udev
, "%s %s '%s' '%s'\n", token_str
[type
], operation_str
[op
], attr
, value
);
730 case TK_A_IGNORE_DEVICE
:
731 case TK_A_STRING_ESCAPE_NONE
:
732 case TK_A_STRING_ESCAPE_REPLACE
:
734 case TK_A_IGNORE_REMOVE
:
735 dbg(rules
->udev
, "%s\n", token_str
[type
]);
738 dbg(rules
->udev
, "%s %s '%s' %#o\n", token_str
[type
], operation_str
[op
], value
, token
->key
.mode
);
740 case TK_A_NUM_FAKE_PART
:
741 dbg(rules
->udev
, "%s %u\n", token_str
[type
], token
->key
.num_fake_part
);
743 case TK_A_DEVLINK_PRIO
:
744 dbg(rules
->udev
, "%s %s %u\n", token_str
[type
], operation_str
[op
], token
->key
.devlink_prio
);
747 dbg(rules
->udev
, "%s %s %u\n", token_str
[type
], operation_str
[op
], token
->key
.uid
);
750 dbg(rules
->udev
, "%s %s %u\n", token_str
[type
], operation_str
[op
], token
->key
.gid
);
753 dbg(rules
->udev
, "%s %s %#o\n", token_str
[type
], operation_str
[op
], token
->key
.mode
);
755 case TK_A_EVENT_TIMEOUT
:
756 dbg(rules
->udev
, "%s %s %u\n", token_str
[type
], operation_str
[op
], token
->key
.event_timeout
);
759 dbg(rules
->udev
, "%s '%s' %u\n", token_str
[type
], value
, token
->key
.rule_goto
);
762 dbg(rules
->udev
, "* %s\n", token_str
[type
]);
766 dbg(rules
->udev
, "unknown type %u\n", type
);
771 static void dump_rules(struct udev_rules
*rules
)
775 dbg(rules
->udev
, "dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n",
777 rules
->token_cur
* sizeof(struct token
),
780 for(i
= 0; i
< rules
->token_cur
; i
++)
781 dump_token(rules
, &rules
->tokens
[i
]);
784 static inline void dump_token(struct udev_rules
*rules
, struct token
*token
) {}
785 static inline void dump_rules(struct udev_rules
*rules
) {}
788 static int sort_token(struct udev_rules
*rules
, struct rule_tmp
*rule_tmp
)
791 unsigned int start
= 0;
792 unsigned int end
= rule_tmp
->token_cur
;
794 for (i
= 0; i
< rule_tmp
->token_cur
; i
++) {
795 enum token_type next_val
= TK_UNDEF
;
796 unsigned int next_idx
;
799 /* find smallest value */
800 for (j
= start
; j
< end
; j
++) {
801 if (rule_tmp
->token
[j
].type
== TK_UNDEF
)
803 if (next_val
== TK_UNDEF
|| rule_tmp
->token
[j
].type
< next_val
) {
804 next_val
= rule_tmp
->token
[j
].type
;
809 /* add token and mark done */
810 if (add_token(rules
, &rule_tmp
->token
[next_idx
]) != 0)
812 rule_tmp
->token
[next_idx
].type
= TK_UNDEF
;
815 if (next_idx
== start
)
817 if (next_idx
+1 == end
)
823 static int add_rule(struct udev_rules
*rules
, char *line
,
824 const char *filename
, unsigned int filename_off
, unsigned int lineno
)
830 struct rule_tmp rule_tmp
;
832 memset(&rule_tmp
, 0x00, sizeof(struct rule_tmp
));
833 rule_tmp
.rules
= rules
;
834 rule_tmp
.rule
.type
= TK_RULE
;
835 rule_tmp
.rule
.rule
.filename_off
= filename_off
;
841 enum key_operation op
= KEY_OP_UNSET
;
843 if (get_key(rules
->udev
, &linepos
, &key
, &op
, &value
) != 0)
846 if (strcasecmp(key
, "ACTION") == 0) {
847 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
848 err(rules
->udev
, "invalid ACTION operation\n");
851 rule_add_token(&rule_tmp
, TK_M_ACTION
, op
, value
, NULL
);
856 if (strcasecmp(key
, "DEVPATH") == 0) {
857 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
858 err(rules
->udev
, "invalid DEVPATH operation\n");
861 rule_add_token(&rule_tmp
, TK_M_DEVPATH
, op
, value
, NULL
);
866 if (strcasecmp(key
, "KERNEL") == 0) {
867 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
868 err(rules
->udev
, "invalid KERNEL operation\n");
871 rule_add_token(&rule_tmp
, TK_M_KERNEL
, op
, value
, NULL
);
876 if (strcasecmp(key
, "SUBSYSTEM") == 0) {
877 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
878 err(rules
->udev
, "invalid SUBSYSTEM operation\n");
881 /* bus, class, subsystem events should all be the same */
882 if (strcmp(value
, "subsystem") == 0 ||
883 strcmp(value
, "bus") == 0 ||
884 strcmp(value
, "class") == 0) {
885 if (strcmp(value
, "bus") == 0 || strcmp(value
, "class") == 0)
886 err(rules
->udev
, "'%s' must be specified as 'subsystem' \n"
887 "please fix it in %s:%u", value
, filename
, lineno
);
888 rule_add_token(&rule_tmp
, TK_M_SUBSYSTEM
, op
, "subsystem|class|bus", NULL
);
890 rule_add_token(&rule_tmp
, TK_M_SUBSYSTEM
, op
, value
, NULL
);
895 if (strcasecmp(key
, "DRIVER") == 0) {
896 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
897 err(rules
->udev
, "invalid DRIVER operation\n");
900 rule_add_token(&rule_tmp
, TK_M_DRIVER
, op
, value
, NULL
);
905 if (strncasecmp(key
, "ATTR{", sizeof("ATTR{")-1) == 0) {
906 attr
= get_key_attribute(rules
->udev
, key
+ sizeof("ATTR")-1);
908 err(rules
->udev
, "error parsing ATTR attribute\n");
911 if (op
== KEY_OP_MATCH
|| op
== KEY_OP_NOMATCH
) {
912 rule_add_token(&rule_tmp
, TK_M_ATTR
, op
, value
, attr
);
914 rule_add_token(&rule_tmp
, TK_A_ATTR
, op
, value
, attr
);
920 if (strcasecmp(key
, "KERNELS") == 0 ||
921 strcasecmp(key
, "ID") == 0) {
922 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
923 err(rules
->udev
, "invalid KERNELS operation\n");
926 rule_add_token(&rule_tmp
, TK_M_KERNELS
, op
, value
, NULL
);
931 if (strcasecmp(key
, "SUBSYSTEMS") == 0 ||
932 strcasecmp(key
, "BUS") == 0) {
933 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
934 err(rules
->udev
, "invalid SUBSYSTEMS operation\n");
937 rule_add_token(&rule_tmp
, TK_M_SUBSYSTEMS
, op
, value
, NULL
);
942 if (strcasecmp(key
, "DRIVERS") == 0) {
943 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
944 err(rules
->udev
, "invalid DRIVERS operation\n");
947 rule_add_token(&rule_tmp
, TK_M_DRIVERS
, op
, value
, NULL
);
952 if (strncasecmp(key
, "ATTRS{", sizeof("ATTRS{")-1) == 0 ||
953 strncasecmp(key
, "SYSFS{", sizeof("SYSFS{")-1) == 0) {
954 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
955 err(rules
->udev
, "invalid ATTRS operation\n");
958 attr
= get_key_attribute(rules
->udev
, key
+ sizeof("ATTRS")-1);
960 err(rules
->udev
, "error parsing ATTRS attribute\n");
963 if (strncmp(attr
, "device/", 7) == 0)
964 err(rules
->udev
, "the 'device' link may not be available in a future kernel, "
965 "please fix it in %s:%u", filename
, lineno
);
966 else if (strstr(attr
, "../") != NULL
)
967 err(rules
->udev
, "do not reference parent sysfs directories directly, "
968 "it may break with a future kernel, please fix it in %s:%u", filename
, lineno
);
969 rule_add_token(&rule_tmp
, TK_M_ATTRS
, op
, value
, attr
);
974 if (strncasecmp(key
, "ENV{", sizeof("ENV{")-1) == 0) {
975 attr
= get_key_attribute(rules
->udev
, key
+ sizeof("ENV")-1);
977 err(rules
->udev
, "error parsing ENV attribute\n");
980 if (strncmp(attr
, "PHYSDEV", 7) == 0)
982 if (op
== KEY_OP_MATCH
|| op
== KEY_OP_NOMATCH
) {
983 if (rule_add_token(&rule_tmp
, TK_M_ENV
, op
, value
, attr
) != 0)
986 if (rule_add_token(&rule_tmp
, TK_A_ENV
, op
, value
, attr
) != 0)
993 if (strcasecmp(key
, "PROGRAM") == 0) {
994 rule_add_token(&rule_tmp
, TK_M_PROGRAM
, op
, value
, NULL
);
999 if (strcasecmp(key
, "RESULT") == 0) {
1000 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
1001 err(rules
->udev
, "invalid RESULT operation\n");
1004 rule_add_token(&rule_tmp
, TK_M_RESULT
, op
, value
, NULL
);
1009 if (strncasecmp(key
, "IMPORT", sizeof("IMPORT")-1) == 0) {
1010 attr
= get_key_attribute(rules
->udev
, key
+ sizeof("IMPORT")-1);
1011 if (attr
!= NULL
&& strstr(attr
, "program")) {
1012 dbg(rules
->udev
, "IMPORT will be executed\n");
1013 rule_add_token(&rule_tmp
, TK_M_IMPORT_PROG
, op
, value
, NULL
);
1015 } else if (attr
!= NULL
&& strstr(attr
, "file")) {
1016 dbg(rules
->udev
, "IMPORT will be included as file\n");
1017 rule_add_token(&rule_tmp
, TK_M_IMPORT_FILE
, op
, value
, NULL
);
1019 } else if (attr
!= NULL
&& strstr(attr
, "parent")) {
1020 dbg(rules
->udev
, "IMPORT will include the parent values\n");
1021 rule_add_token(&rule_tmp
, TK_M_IMPORT_PARENT
, op
, value
, NULL
);
1024 /* figure it out if it is executable */
1025 char file
[UTIL_PATH_SIZE
];
1027 struct stat statbuf
;
1029 util_strlcpy(file
, value
, sizeof(file
));
1030 pos
= strchr(file
, ' ');
1034 /* allow programs in /lib/udev called without the path */
1035 if (strchr(file
, '/') == NULL
) {
1036 util_strlcpy(file
, UDEV_PREFIX
"/lib/udev/", sizeof(file
));
1037 util_strlcat(file
, value
, sizeof(file
));
1038 pos
= strchr(file
, ' ');
1043 dbg(rules
->udev
, "IMPORT auto mode for '%s'\n", file
);
1044 if (!lstat(file
, &statbuf
) && (statbuf
.st_mode
& S_IXUSR
)) {
1045 dbg(rules
->udev
, "IMPORT will be executed (autotype)\n");
1046 rule_add_token(&rule_tmp
, TK_M_IMPORT_PROG
, op
, value
, NULL
);
1049 dbg(rules
->udev
, "IMPORT will be included as file (autotype)\n");
1050 rule_add_token(&rule_tmp
, TK_M_IMPORT_FILE
, op
, value
, NULL
);
1057 if (strncasecmp(key
, "TEST", sizeof("TEST")-1) == 0) {
1060 if (op
!= KEY_OP_MATCH
&& op
!= KEY_OP_NOMATCH
) {
1061 err(rules
->udev
, "invalid TEST operation\n");
1064 attr
= get_key_attribute(rules
->udev
, key
+ sizeof("TEST")-1);
1066 mode
= strtol(attr
, NULL
, 8);
1067 rule_add_token(&rule_tmp
, TK_M_TEST
, op
, value
, &mode
);
1069 rule_add_token(&rule_tmp
, TK_M_TEST
, op
, value
, NULL
);
1075 if (strncasecmp(key
, "RUN", sizeof("RUN")-1) == 0) {
1078 attr
= get_key_attribute(rules
->udev
, key
+ sizeof("RUN")-1);
1079 if (attr
!= NULL
&& strstr(attr
, "ignore_error"))
1081 rule_add_token(&rule_tmp
, TK_A_RUN
, op
, value
, &flag
);
1086 if (strcasecmp(key
, "WAIT_FOR") == 0 || strcasecmp(key
, "WAIT_FOR_SYSFS") == 0) {
1087 rule_add_token(&rule_tmp
, TK_M_WAITFOR
, 0, value
, NULL
);
1092 if (strcasecmp(key
, "LABEL") == 0) {
1093 rule_tmp
.rule
.rule
.label_off
= add_string(rules
, value
);
1098 if (strcasecmp(key
, "GOTO") == 0) {
1099 rule_add_token(&rule_tmp
, TK_A_GOTO
, 0, value
, NULL
);
1104 if (strncasecmp(key
, "NAME", sizeof("NAME")-1) == 0) {
1105 if (op
== KEY_OP_MATCH
|| op
== KEY_OP_NOMATCH
) {
1106 rule_add_token(&rule_tmp
, TK_M_NAME
, op
, value
, NULL
);
1108 if (value
[0] == '\0')
1109 dbg(rules
->udev
, "name empty, node creation suppressed\n");
1110 rule_add_token(&rule_tmp
, TK_A_NAME
, op
, value
, NULL
);
1111 attr
= get_key_attribute(rules
->udev
, key
+ sizeof("NAME")-1);
1113 if (strstr(attr
, "all_partitions") != NULL
) {
1114 int num
= DEFAULT_FAKE_PARTITIONS_COUNT
;
1116 dbg(rules
->udev
, "creation of partition nodes requested\n");
1117 rule_add_token(&rule_tmp
, TK_A_NUM_FAKE_PART
, 0, NULL
, &num
);
1119 if (strstr(attr
, "ignore_remove") != NULL
) {
1120 dbg(rules
->udev
, "remove event should be ignored\n");
1121 rule_add_token(&rule_tmp
, TK_A_IGNORE_REMOVE
, 0, NULL
, NULL
);
1128 if (strcasecmp(key
, "SYMLINK") == 0) {
1129 if (op
== KEY_OP_MATCH
|| op
== KEY_OP_NOMATCH
)
1130 rule_add_token(&rule_tmp
, TK_M_DEVLINK
, op
, value
, NULL
);
1132 rule_add_token(&rule_tmp
, TK_A_DEVLINK
, op
, value
, NULL
);
1137 if (strcasecmp(key
, "OWNER") == 0) {
1141 uid
= strtoul(value
, &endptr
, 10);
1142 if (endptr
[0] == '\0') {
1143 rule_add_token(&rule_tmp
, TK_A_OWNER_ID
, op
, NULL
, &uid
);
1144 } else if (rules
->resolve_names
&& strchr("$%", value
[0]) == NULL
) {
1145 uid
= util_lookup_user(rules
->udev
, value
);
1146 rule_add_token(&rule_tmp
, TK_A_OWNER_ID
, op
, NULL
, &uid
);
1148 rule_add_token(&rule_tmp
, TK_A_OWNER
, op
, value
, NULL
);
1154 if (strcasecmp(key
, "GROUP") == 0) {
1158 gid
= strtoul(value
, &endptr
, 10);
1159 if (endptr
[0] == '\0') {
1160 rule_add_token(&rule_tmp
, TK_A_GROUP_ID
, op
, NULL
, &gid
);
1161 } else if (rules
->resolve_names
&& strchr("$%", value
[0]) == NULL
) {
1162 gid
= util_lookup_group(rules
->udev
, value
);
1163 rule_add_token(&rule_tmp
, TK_A_GROUP_ID
, op
, NULL
, &gid
);
1165 rule_add_token(&rule_tmp
, TK_A_GROUP
, op
, value
, NULL
);
1171 if (strcasecmp(key
, "MODE") == 0) {
1175 mode
= strtol(value
, &endptr
, 8);
1176 if (endptr
[0] == '\0')
1177 rule_add_token(&rule_tmp
, TK_A_MODE_ID
, op
, NULL
, &mode
);
1179 rule_add_token(&rule_tmp
, TK_A_MODE
, op
, value
, NULL
);
1184 if (strcasecmp(key
, "OPTIONS") == 0) {
1187 if (strstr(value
, "last_rule") != NULL
) {
1188 dbg(rules
->udev
, "last rule to be applied\n");
1189 rule_add_token(&rule_tmp
, TK_A_LAST_RULE
, 0, NULL
, NULL
);
1191 if (strstr(value
, "ignore_device") != NULL
) {
1192 dbg(rules
->udev
, "device should be ignored\n");
1193 rule_add_token(&rule_tmp
, TK_A_IGNORE_DEVICE
, 0, NULL
, NULL
);
1195 if (strstr(value
, "ignore_remove") != NULL
) {
1196 dbg(rules
->udev
, "remove event should be ignored\n");
1197 rule_add_token(&rule_tmp
, TK_A_IGNORE_REMOVE
, 0, NULL
, NULL
);
1199 pos
= strstr(value
, "link_priority=");
1201 int prio
= atoi(&pos
[strlen("link_priority=")]);
1203 rule_add_token(&rule_tmp
, TK_A_DEVLINK_PRIO
, 0, NULL
, &prio
);
1204 dbg(rules
->udev
, "link priority=%i\n", prio
);
1206 pos
= strstr(value
, "event_timeout=");
1208 int tout
= atoi(&pos
[strlen("event_timeout=")]);
1210 rule_add_token(&rule_tmp
, TK_A_EVENT_TIMEOUT
, 0, NULL
, &tout
);
1211 dbg(rules
->udev
, "event timout=%i\n", tout
);
1213 pos
= strstr(value
, "string_escape=");
1215 pos
= &pos
[strlen("string_escape=")];
1216 if (strncmp(pos
, "none", strlen("none")) == 0)
1217 rule_add_token(&rule_tmp
, TK_A_STRING_ESCAPE_NONE
, 0, NULL
, NULL
);
1218 else if (strncmp(pos
, "replace", strlen("replace")) == 0)
1219 rule_add_token(&rule_tmp
, TK_A_STRING_ESCAPE_REPLACE
, 0, NULL
, NULL
);
1221 if (strstr(value
, "all_partitions") != NULL
) {
1222 int num
= DEFAULT_FAKE_PARTITIONS_COUNT
;
1224 rule_add_token(&rule_tmp
, TK_A_NUM_FAKE_PART
, 0, NULL
, &num
);
1225 dbg(rules
->udev
, "creation of partition nodes requested\n");
1230 err(rules
->udev
, "unknown key '%s' in %s:%u\n", key
, filename
, lineno
);
1234 err(rules
->udev
, "PHYSDEV* values are deprecated and not available on recent kernels, \n"
1235 "please fix it in %s:%u", filename
, lineno
);
1237 /* skip line if not any valid key was found */
1241 /* add rule token */
1242 if (add_token(rules
, &rule_tmp
.rule
) != 0)
1245 /* add tokens to list, sorted by type */
1246 if (sort_token(rules
, &rule_tmp
) != 0)
1250 err(rules
->udev
, "invalid rule '%s:%u'\n", filename
, lineno
);
1254 static int parse_file(struct udev_rules
*rules
, const char *filename
)
1257 unsigned int filename_off
;
1258 unsigned int first_token
;
1259 char line
[UTIL_LINE_SIZE
];
1263 info(rules
->udev
, "reading '%s' as rules file\n", filename
);
1265 f
= fopen(filename
, "r");
1269 filename_off
= add_string(rules
, filename
);
1270 first_token
= rules
->token_cur
;
1272 while(fgets(line
, sizeof(line
), f
) != NULL
) {
1276 /* skip whitespace */
1279 while (isspace(key
[0]))
1290 /* continue reading if backslash+newline is found */
1291 while (line
[len
-2] == '\\') {
1292 if (fgets(&line
[len
-2], (sizeof(line
)-len
)+2, f
) == NULL
)
1298 if (len
+1 >= sizeof(line
)) {
1299 err(rules
->udev
, "line too long '%s':%u, ignored\n", filename
, line_nr
);
1302 add_rule(rules
, key
, filename
, filename_off
, line_nr
);
1306 /* link GOTOs to LABEL rules in this file to be able to fast-forward */
1307 for (i
= first_token
+1; i
< rules
->token_cur
; i
++) {
1308 if (rules
->tokens
[i
].type
== TK_A_GOTO
) {
1309 char *label
= &rules
->buf
[rules
->tokens
[i
].key
.value_off
];
1312 for (j
= i
+1; j
< rules
->token_cur
; j
++) {
1313 if (rules
->tokens
[j
].type
!= TK_RULE
)
1315 if (rules
->tokens
[j
].rule
.label_off
== 0)
1317 if (strcmp(label
, &rules
->buf
[rules
->tokens
[j
].rule
.label_off
]) != 0)
1319 rules
->tokens
[i
].key
.rule_goto
= j
;
1321 if (rules
->tokens
[i
].key
.rule_goto
== 0)
1322 err(rules
->udev
, "GOTO '%s' has no matching label in: '%s'\n", label
, filename
);
1328 static int add_matching_files(struct udev
*udev
, struct udev_list_node
*file_list
, const char *dirname
, const char *suffix
)
1332 char filename
[UTIL_PATH_SIZE
];
1334 dbg(udev
, "open directory '%s'\n", dirname
);
1335 dir
= opendir(dirname
);
1337 err(udev
, "unable to open '%s': %m\n", dirname
);
1343 if (ent
== NULL
|| ent
->d_name
[0] == '\0')
1346 if ((ent
->d_name
[0] == '.') || (ent
->d_name
[0] == '#'))
1349 /* look for file matching with specified suffix */
1350 if (suffix
!= NULL
) {
1353 ext
= strrchr(ent
->d_name
, '.');
1356 if (strcmp(ext
, suffix
) != 0)
1359 dbg(udev
, "put file '%s/%s' into list\n", dirname
, ent
->d_name
);
1361 snprintf(filename
, sizeof(filename
), "%s/%s", dirname
, ent
->d_name
);
1362 filename
[sizeof(filename
)-1] = '\0';
1363 udev_list_entry_add(udev
, file_list
, filename
, NULL
, 1, 1);
1370 struct udev_rules
*udev_rules_new(struct udev
*udev
, int resolve_names
)
1372 struct udev_rules
*rules
;
1373 struct stat statbuf
;
1374 char filename
[PATH_MAX
];
1375 struct udev_list_node file_list
;
1376 struct udev_list_entry
*file_loop
, *file_tmp
;
1377 unsigned int prev_rule
;
1378 struct token end_token
;
1381 rules
= malloc(sizeof(struct udev_rules
));
1384 memset(rules
, 0x00, sizeof(struct udev_rules
));
1386 rules
->resolve_names
= resolve_names
;
1387 udev_list_init(&file_list
);
1389 /* init token array and string buffer */
1390 rules
->tokens
= malloc(PREALLOC_TOKEN
* sizeof(struct token
));
1391 if (rules
->tokens
!= NULL
)
1392 rules
->token_max
= PREALLOC_TOKEN
;
1393 rules
->buf
= malloc(PREALLOC_STRBUF
);
1394 if (rules
->buf
!= NULL
)
1395 rules
->buf_max
= PREALLOC_STRBUF
;
1396 info(udev
, "prealloc %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n",
1397 rules
->token_max
* sizeof(struct token
), rules
->token_max
, sizeof(struct token
), rules
->buf_max
);
1398 /* offset 0 in the string buffer is always empty */
1399 add_string(rules
, "");
1401 if (udev_get_rules_path(udev
) != NULL
) {
1402 /* custom rules location for testing */
1403 add_matching_files(udev
, &file_list
, udev_get_rules_path(udev
), ".rules");
1405 struct udev_list_node sort_list
;
1406 struct udev_list_entry
*sort_loop
, *sort_tmp
;
1408 /* read user/custom rules */
1409 add_matching_files(udev
, &file_list
, SYSCONFDIR
"/udev/rules.d", ".rules");
1411 /* read dynamic/temporary rules */
1412 util_strlcpy(filename
, udev_get_dev_path(udev
), sizeof(filename
));
1413 util_strlcat(filename
, "/.udev/rules.d", sizeof(filename
));
1414 if (stat(filename
, &statbuf
) != 0) {
1415 util_create_path(udev
, filename
);
1416 udev_selinux_setfscreatecon(udev
, filename
, S_IFDIR
|0755);
1417 mkdir(filename
, 0755);
1418 udev_selinux_resetfscreatecon(udev
);
1420 udev_list_init(&sort_list
);
1421 add_matching_files(udev
, &sort_list
, filename
, ".rules");
1423 /* read default rules */
1424 add_matching_files(udev
, &sort_list
, UDEV_PREFIX
"/lib/udev/rules.d", ".rules");
1426 /* sort all rules files by basename into list of files */
1427 udev_list_entry_foreach_safe(sort_loop
, sort_tmp
, udev_list_get_entry(&sort_list
)) {
1428 const char *sort_name
= udev_list_entry_get_name(sort_loop
);
1429 const char *sort_base
= strrchr(sort_name
, '/');
1431 if (sort_base
== NULL
)
1434 udev_list_entry_foreach_safe(file_loop
, file_tmp
, udev_list_get_entry(&file_list
)) {
1435 const char *file_name
= udev_list_entry_get_name(file_loop
);
1436 const char *file_base
= strrchr(file_name
, '/');
1438 if (file_base
== NULL
)
1440 if (strcmp(file_base
, sort_base
) == 0) {
1441 info(udev
, "rule file basename '%s' already added, ignoring '%s'\n",
1442 file_name
, sort_name
);
1443 udev_list_entry_remove(sort_loop
);
1447 if (strcmp(file_base
, sort_base
) > 0)
1450 if (sort_loop
!= NULL
)
1451 udev_list_entry_move_before(sort_loop
, file_loop
);
1455 /* parse list of files */
1456 udev_list_entry_foreach_safe(file_loop
, file_tmp
, udev_list_get_entry(&file_list
)) {
1457 const char *file_name
= udev_list_entry_get_name(file_loop
);
1459 if (stat(file_name
, &statbuf
) == 0 && statbuf
.st_size
> 0)
1460 parse_file(rules
, file_name
);
1462 info(udev
, "can not read '%s'\n", file_name
);
1463 udev_list_entry_remove(file_loop
);
1466 memset(&end_token
, 0x00, sizeof(struct token
));
1467 end_token
.type
= TK_END
;
1468 add_token(rules
, &end_token
);
1470 /* shrink allocate buffers */
1471 if (rules
->token_cur
< rules
->token_max
) {
1472 struct token
*tokens
;
1474 tokens
= realloc(rules
->tokens
, rules
->token_cur
* sizeof(struct token
));
1475 if (tokens
!= NULL
|| rules
->token_cur
== 0) {
1476 rules
->tokens
= tokens
;
1477 rules
->token_max
= rules
->token_cur
;
1480 if (rules
->buf_cur
< rules
->buf_max
) {
1483 buf
= realloc(rules
->buf
, rules
->buf_cur
);
1484 if (buf
!= NULL
|| rules
->buf_cur
== 0) {
1486 rules
->buf_max
= rules
->buf_cur
;
1489 info(udev
, "shrunk to %lu bytes tokens (%u * %zu bytes), %zu bytes buffer\n",
1490 rules
->token_max
* sizeof(struct token
), rules
->token_max
, sizeof(struct token
), rules
->buf_max
);
1492 /* link all TK_RULE tokens to be able to fast-forward to next TK_RULE */
1494 for (i
= 1; i
< rules
->token_cur
; i
++) {
1495 if (rules
->tokens
[i
].type
== TK_RULE
) {
1496 rules
->tokens
[prev_rule
].rule
.next_rule
= i
;
1504 void udev_rules_unref(struct udev_rules
*rules
)
1508 free(rules
->tokens
);
1513 static int match_key(struct udev_rules
*rules
, struct token
*token
, const char *val
)
1515 const char *key_name
= token_str
[token
->type
];
1516 char *key_value
= &rules
->buf
[token
->key
.value_off
];
1523 /* look for a matching string, parts are separated by '|' */
1524 if (strchr(key_value
, '|') != NULL
) {
1525 char value
[UTIL_PATH_SIZE
];
1527 util_strlcpy(value
, &rules
->buf
[token
->key
.value_off
], sizeof(value
));
1529 while (key_value
!= NULL
) {
1530 pos
= strchr(key_value
, '|');
1535 dbg(rules
->udev
, "match %s '%s' <-> '%s'\n", key_name
, key_value
, val
);
1536 match
= (fnmatch(key_value
, val
, 0) == 0);
1542 match
= (fnmatch(key_value
, val
, 0) == 0);
1545 if (match
&& (token
->key
.op
== KEY_OP_MATCH
)) {
1546 dbg(rules
->udev
, "%s is true (matching value)\n", key_name
);
1549 if (!match
&& (token
->key
.op
== KEY_OP_NOMATCH
)) {
1550 dbg(rules
->udev
, "%s is true (non-matching value)\n", key_name
);
1553 dbg(rules
->udev
, "%s is not true\n", key_name
);
1557 static int match_attr(struct udev_rules
*rules
, struct udev_device
*dev
, struct udev_event
*event
, struct token
*cur
)
1559 char attr
[UTIL_PATH_SIZE
];
1560 const char *key_name
= &rules
->buf
[cur
->key
.attr_off
];
1561 const char *key_value
= &rules
->buf
[cur
->key
.value_off
];
1562 char value
[UTIL_NAME_SIZE
] = "";
1565 util_strlcpy(attr
, key_name
, sizeof(attr
));
1566 util_resolve_subsys_kernel(event
->udev
, attr
, value
, sizeof(value
), 1);
1567 if (value
[0] == '\0') {
1570 val
= udev_device_get_sysattr_value(dev
, key_name
);
1572 util_strlcpy(value
, val
, sizeof(value
));
1577 /* strip trailing whitespace of value, if not asked to match for it */
1578 len
= strlen(key_value
);
1579 if (len
> 0 && !isspace(key_value
[len
-1])) {
1580 len
= strlen(value
);
1581 while (len
> 0 && isspace(value
[--len
]))
1583 dbg(rules
->udev
, "removed trailing whitespace from '%s'\n", value
);
1585 return match_key(rules
, cur
, value
);
1594 int udev_rules_apply_to_event(struct udev_rules
*rules
, struct udev_event
*event
)
1599 if (rules
->tokens
== NULL
)
1602 /* loop through token list, match, run actions or forward to next rule */
1603 cur
= &rules
->tokens
[0];
1604 while (cur
!= NULL
&& cur
->type
!= TK_END
) {
1605 enum escape_type esc
= ESCAPE_UNSET
;
1608 dump_token(rules
, cur
);
1609 switch (cur
->type
) {
1617 char filename
[UTIL_PATH_SIZE
];
1620 util_strlcpy(filename
, &rules
->buf
[cur
->key
.value_off
], sizeof(filename
));
1621 udev_event_apply_format(event
, filename
, sizeof(filename
));
1622 found
= (wait_for_file(event
->dev
, filename
, 10) == 0);
1623 if (!found
&& (cur
->key
.op
!= KEY_OP_NOMATCH
))
1628 if (match_key(rules
, cur
, udev_device_get_action(event
->dev
)) != 0)
1632 if (match_key(rules
, cur
, udev_device_get_devpath(event
->dev
)) != 0)
1636 if (match_key(rules
, cur
, udev_device_get_sysname(event
->dev
)) != 0)
1641 size_t devlen
= strlen(udev_get_dev_path(event
->udev
))+1;
1642 struct udev_list_entry
*list_entry
;
1645 udev_list_entry_foreach(list_entry
, udev_device_get_devlinks_list_entry(event
->dev
)) {
1646 const char *devlink
;
1648 devlink
= &udev_list_entry_get_name(list_entry
)[devlen
];
1649 if (match_key(rules
, cur
, devlink
) == 0) {
1659 if (match_key(rules
, cur
, event
->name
) != 0)
1664 struct udev_list_entry
*list_entry
;
1665 const char *key_name
= &rules
->buf
[cur
->key
.attr_off
];
1668 list_entry
= udev_device_get_properties_list_entry(event
->dev
);
1669 list_entry
= udev_list_entry_get_by_name(list_entry
, key_name
);
1670 value
= udev_list_entry_get_value(list_entry
);
1671 if (value
== NULL
) {
1672 dbg(event
->udev
, "ENV{%s} is not set, treat as empty\n", key_name
);
1675 if (match_key(rules
, cur
, value
))
1679 case TK_M_SUBSYSTEM
:
1680 if (match_key(rules
, cur
, udev_device_get_subsystem(event
->dev
)) != 0)
1684 if (match_key(rules
, cur
, udev_device_get_driver(event
->dev
)) != 0)
1688 if (match_attr(rules
, event
->dev
, event
, cur
) != 0)
1692 case TK_M_SUBSYSTEMS
:
1698 /* get whole sequence of parent matches */
1700 while (next
->type
< TK_PARENTS_MAX
)
1703 /* loop over parents */
1704 event
->dev_parent
= event
->dev
;
1708 dbg(event
->udev
, "parent: '%s'\n", udev_device_get_syspath(event
->dev_parent
));
1709 /* loop over sequence of parent match keys */
1710 for (key
= cur
; key
< next
; key
++ ) {
1711 dump_token(rules
, key
);
1714 if (match_key(rules
, key
, udev_device_get_sysname(event
->dev_parent
)) != 0)
1717 case TK_M_SUBSYSTEMS
:
1718 if (match_key(rules
, key
, udev_device_get_subsystem(event
->dev_parent
)) != 0)
1722 if (match_key(rules
, key
, udev_device_get_driver(event
->dev_parent
)) != 0)
1726 if (match_attr(rules
, event
->dev_parent
, event
, key
) != 0)
1732 dbg(event
->udev
, "parent key matched\n");
1734 dbg(event
->udev
, "all parent keys matched\n");
1735 /* all keys matched */
1739 event
->dev_parent
= udev_device_get_parent(event
->dev_parent
);
1740 if (event
->dev_parent
== NULL
)
1743 /* move behind our sequence of parent match keys */
1749 char filename
[UTIL_PATH_SIZE
];
1750 struct stat statbuf
;
1753 util_strlcpy(filename
, &rules
->buf
[cur
->key
.value_off
], sizeof(filename
));
1754 udev_event_apply_format(event
, filename
, sizeof(filename
));
1755 if (util_resolve_subsys_kernel(event
->udev
, NULL
, filename
, sizeof(filename
), 0) != 0)
1756 if (filename
[0] != '/') {
1757 char tmp
[UTIL_PATH_SIZE
];
1759 util_strlcpy(tmp
, udev_device_get_syspath(event
->dev
), sizeof(tmp
));
1760 util_strlcat(tmp
, "/", sizeof(tmp
));
1761 util_strlcat(tmp
, filename
, sizeof(tmp
));
1762 util_strlcpy(filename
, tmp
, sizeof(filename
));
1765 attr_subst_subdir(filename
, sizeof(filename
));
1767 match
= (stat(filename
, &statbuf
) == 0);
1768 info(event
->udev
, "'%s' %s", filename
, match
? "exists\n" : "does not exist\n");
1769 if (match
&& cur
->key
.mode
> 0) {
1770 match
= ((statbuf
.st_mode
& cur
->key
.mode
) > 0);
1771 info(event
->udev
, "'%s' has mode=%#o and %s %#o\n", filename
, statbuf
.st_mode
,
1772 match
? "matches" : "does not match", cur
->key
.mode
);
1774 if (match
&& cur
->key
.op
== KEY_OP_NOMATCH
)
1776 if (!match
&& cur
->key
.op
== KEY_OP_MATCH
)
1782 char program
[UTIL_PATH_SIZE
];
1784 char result
[UTIL_PATH_SIZE
];
1786 free(event
->program_result
);
1787 event
->program_result
= NULL
;
1788 util_strlcpy(program
, &rules
->buf
[cur
->key
.value_off
], sizeof(program
));
1789 udev_event_apply_format(event
, program
, sizeof(program
));
1790 envp
= udev_device_get_properties_envp(event
->dev
);
1791 if (util_run_program(event
->udev
, program
, envp
, result
, sizeof(result
), NULL
) != 0) {
1792 if (cur
->key
.op
!= KEY_OP_NOMATCH
)
1797 util_remove_trailing_chars(result
, '\n');
1798 if (esc
== ESCAPE_UNSET
|| esc
== ESCAPE_REPLACE
) {
1799 count
= util_replace_chars(result
, ALLOWED_CHARS_INPUT
);
1801 info(event
->udev
, "%i character(s) replaced\n" , count
);
1803 event
->program_result
= strdup(result
);
1804 dbg(event
->udev
, "storing result '%s'\n", event
->program_result
);
1805 if (cur
->key
.op
== KEY_OP_NOMATCH
)
1810 case TK_M_IMPORT_FILE
:
1812 char import
[UTIL_PATH_SIZE
];
1814 util_strlcpy(import
, &rules
->buf
[cur
->key
.value_off
], sizeof(import
));
1815 udev_event_apply_format(event
, import
, sizeof(import
));
1816 if (import_file_into_properties(event
->dev
, import
) != 0)
1817 if (cur
->key
.op
!= KEY_OP_NOMATCH
)
1821 case TK_M_IMPORT_PROG
:
1823 char import
[UTIL_PATH_SIZE
];
1825 util_strlcpy(import
, &rules
->buf
[cur
->key
.value_off
], sizeof(import
));
1826 udev_event_apply_format(event
, import
, sizeof(import
));
1827 if (import_program_into_properties(event
->dev
, import
) != 0)
1828 if (cur
->key
.op
!= KEY_OP_NOMATCH
)
1832 case TK_M_IMPORT_PARENT
:
1834 char import
[UTIL_PATH_SIZE
];
1836 util_strlcpy(import
, &rules
->buf
[cur
->key
.value_off
], sizeof(import
));
1837 udev_event_apply_format(event
, import
, sizeof(import
));
1838 if (import_parent_into_properties(event
->dev
, import
) != 0)
1839 if (cur
->key
.op
!= KEY_OP_NOMATCH
)
1844 if (match_key(rules
, cur
, event
->program_result
) != 0)
1848 case TK_A_IGNORE_DEVICE
:
1849 event
->ignore_device
= 1;
1852 case TK_A_STRING_ESCAPE_NONE
:
1855 case TK_A_STRING_ESCAPE_REPLACE
:
1856 esc
= ESCAPE_REPLACE
;
1858 case TK_A_NUM_FAKE_PART
:
1859 if (strcmp(udev_device_get_subsystem(event
->dev
), "block") != 0)
1861 if (udev_device_get_sysnum(event
->dev
) != NULL
)
1863 udev_device_set_num_fake_partitions(event
->dev
, cur
->key
.num_fake_part
);
1865 case TK_A_DEVLINK_PRIO
:
1866 udev_device_set_devlink_priority(event
->dev
, cur
->key
.devlink_prio
);
1870 char owner
[UTIL_NAME_SIZE
];
1872 if (event
->owner_final
)
1874 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1875 event
->owner_final
= 1;
1876 util_strlcpy(owner
, &rules
->buf
[cur
->key
.value_off
], sizeof(owner
));
1877 udev_event_apply_format(event
, owner
, sizeof(owner
));
1878 event
->uid
= util_lookup_user(event
->udev
, owner
);
1883 char group
[UTIL_NAME_SIZE
];
1885 if (event
->group_final
)
1887 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1888 event
->group_final
= 1;
1889 util_strlcpy(group
, &rules
->buf
[cur
->key
.value_off
], sizeof(group
));
1890 udev_event_apply_format(event
, group
, sizeof(group
));
1891 event
->gid
= util_lookup_group(event
->udev
, group
);
1896 char mode
[UTIL_NAME_SIZE
];
1899 if (event
->mode_final
)
1901 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1902 event
->mode_final
= 1;
1903 util_strlcpy(mode
, &rules
->buf
[cur
->key
.value_off
], sizeof(mode
));
1904 udev_event_apply_format(event
, mode
, sizeof(mode
));
1905 event
->mode
= strtol(mode
, &endptr
, 8);
1906 if (endptr
[0] != '\0') {
1907 err(event
->udev
, "invalide mode '%s' set default mode 0660\n", mode
);
1913 if (event
->owner_final
)
1915 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1916 event
->owner_final
= 1;
1917 event
->uid
= cur
->key
.uid
;
1920 if (event
->group_final
)
1922 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1923 event
->group_final
= 1;
1924 event
->gid
= cur
->key
.gid
;
1927 if (event
->mode_final
)
1929 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1930 event
->mode_final
= 1;
1931 event
->mode
= cur
->key
.mode
;
1935 const char *name
= &rules
->buf
[cur
->key
.attr_off
];
1936 char *value
= &rules
->buf
[cur
->key
.value_off
];
1938 if (value
[0] != '\0') {
1939 char temp_value
[UTIL_NAME_SIZE
];
1940 struct udev_list_entry
*entry
;
1942 util_strlcpy(temp_value
, value
, sizeof(temp_value
));
1943 udev_event_apply_format(event
, temp_value
, sizeof(temp_value
));
1944 entry
= udev_device_add_property(event
->dev
, name
, temp_value
);
1946 udev_list_entry_set_flag(entry
, 1);
1948 udev_device_add_property(event
->dev
, name
, NULL
);
1954 const char *name
= &rules
->buf
[cur
->key
.value_off
];
1955 char name_str
[UTIL_PATH_SIZE
];
1958 if (event
->name_final
)
1960 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1961 event
->name_final
= 1;
1962 if (name
[0] == '\0') {
1967 util_strlcpy(name_str
, name
, sizeof(name_str
));
1968 udev_event_apply_format(event
, name_str
, sizeof(name_str
));
1969 if (esc
== ESCAPE_UNSET
|| esc
== ESCAPE_REPLACE
) {
1970 count
= util_replace_chars(name_str
, ALLOWED_CHARS_FILE
);
1972 info(event
->udev
, "%i character(s) replaced\n", count
);
1973 event
->name
= strdup(name_str
);
1979 char temp
[UTIL_PATH_SIZE
];
1980 char filename
[UTIL_PATH_SIZE
];
1984 if (event
->devlink_final
)
1986 if (cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1987 event
->devlink_final
= 1;
1988 if (cur
->key
.op
== KEY_OP_ASSIGN
|| cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
1989 udev_device_cleanup_devlinks_list(event
->dev
);
1991 /* allow multiple symlinks separated by spaces */
1992 util_strlcpy(temp
, &rules
->buf
[cur
->key
.value_off
], sizeof(temp
));
1993 udev_event_apply_format(event
, temp
, sizeof(temp
));
1994 if (esc
== ESCAPE_UNSET
)
1995 count
= util_replace_chars(temp
, ALLOWED_CHARS_FILE
" ");
1996 else if (esc
== ESCAPE_REPLACE
)
1997 count
= util_replace_chars(temp
, ALLOWED_CHARS_FILE
);
1999 info(event
->udev
, "%i character(s) replaced\n" , count
);
2000 dbg(event
->udev
, "rule applied, added symlink(s) '%s'\n", temp
);
2002 while (isspace(pos
[0]))
2004 next
= strchr(pos
, ' ');
2007 info(event
->udev
, "add symlink '%s'\n", pos
);
2008 util_strlcpy(filename
, udev_get_dev_path(event
->udev
), sizeof(filename
));
2009 util_strlcat(filename
, "/", sizeof(filename
));
2010 util_strlcat(filename
, pos
, sizeof(filename
));
2011 udev_device_add_devlink(event
->dev
, filename
);
2012 while (isspace(next
[1]))
2015 next
= strchr(pos
, ' ');
2017 if (pos
[0] != '\0') {
2018 info(event
->udev
, "add symlink '%s'\n", pos
);
2019 util_strlcpy(filename
, udev_get_dev_path(event
->udev
), sizeof(filename
));
2020 util_strlcat(filename
, "/", sizeof(filename
));
2021 util_strlcat(filename
, pos
, sizeof(filename
));
2022 udev_device_add_devlink(event
->dev
, filename
);
2026 case TK_A_EVENT_TIMEOUT
:
2027 udev_device_set_event_timeout(event
->dev
, cur
->key
.event_timeout
);
2029 case TK_A_IGNORE_REMOVE
:
2030 udev_device_set_ignore_remove(event
->dev
, 1);
2034 const char *key_name
= &rules
->buf
[cur
->key
.attr_off
];
2035 char attr
[UTIL_PATH_SIZE
];
2036 char value
[UTIL_NAME_SIZE
];
2039 util_strlcpy(attr
, key_name
, sizeof(attr
));
2040 if (util_resolve_subsys_kernel(event
->udev
, key_name
, attr
, sizeof(attr
), 0) != 0) {
2041 util_strlcpy(attr
, udev_device_get_syspath(event
->dev
), sizeof(attr
));
2042 util_strlcat(attr
, "/", sizeof(attr
));
2043 util_strlcat(attr
, key_name
, sizeof(attr
));
2046 attr_subst_subdir(attr
, sizeof(attr
));
2048 util_strlcpy(value
, &rules
->buf
[cur
->key
.value_off
], sizeof(value
));
2049 udev_event_apply_format(event
, value
, sizeof(value
));
2050 info(event
->udev
, "writing '%s' to sysfs file '%s'\n", value
, attr
);
2051 f
= fopen(attr
, "w");
2054 if (fprintf(f
, "%s", value
) <= 0)
2055 err(event
->udev
, "error writing ATTR{%s}: %m\n", attr
);
2058 err(event
->udev
, "error opening ATTR{%s} for writing: %m\n", attr
);
2064 struct udev_list_entry
*list_entry
;
2066 if (cur
->key
.op
== KEY_OP_ASSIGN
|| cur
->key
.op
== KEY_OP_ASSIGN_FINAL
)
2067 udev_list_cleanup_entries(event
->udev
, &event
->run_list
);
2068 list_entry
= udev_list_entry_add(event
->udev
, &event
->run_list
,
2069 &rules
->buf
[cur
->key
.value_off
], NULL
, 1, 0);
2070 if (cur
->key
.ignore_error
)
2071 udev_list_entry_set_flag(list_entry
, 1);
2075 cur
= &rules
->tokens
[cur
->key
.rule_goto
];
2077 case TK_A_LAST_RULE
:
2080 case TK_PARENTS_MAX
:
2083 err(rules
->udev
, "wrong type %u\n", cur
->type
);
2090 /* fast-forward to next rule */
2091 idx
= rule
->rule
.next_rule
;
2094 dbg(rules
->udev
, "forward to rule: %u\n", idx
);
2095 cur
= &rules
->tokens
[idx
];