]>
Commit | Line | Data |
---|---|---|
2232cac8 | 1 | /* |
fc206fbe | 2 | * Copyright (C) 2003-2009 Kay Sievers <kay.sievers@vrfy.org> |
023ed7b0 | 3 | * Copyright (C) 2008 Alan Jenkins <alan-jenkins@tuffmail.co.uk> |
2232cac8 | 4 | * |
55e9959b KS |
5 | * This program is free software: you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 2 of the License, or | |
8 | * (at your option) any later version. | |
3d6d12c6 | 9 | * |
55e9959b KS |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
2232cac8 | 14 | * |
55e9959b KS |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
2232cac8 GKH |
17 | */ |
18 | ||
19 | #include <stddef.h> | |
1449a55d | 20 | #include <limits.h> |
2232cac8 GKH |
21 | #include <stdlib.h> |
22 | #include <string.h> | |
23 | #include <stdio.h> | |
24 | #include <fcntl.h> | |
25 | #include <ctype.h> | |
26 | #include <unistd.h> | |
27 | #include <errno.h> | |
5ee7ecfb | 28 | #include <dirent.h> |
cea61f5c | 29 | #include <fnmatch.h> |
2232cac8 | 30 | |
2232cac8 | 31 | #include "udev.h" |
2232cac8 | 32 | |
6880b25d KS |
33 | #define PREALLOC_TOKEN 2048 |
34 | #define PREALLOC_STRBUF 32 * 1024 | |
1449a55d | 35 | #define PREALLOC_TRIE 256 |
c7521974 | 36 | |
4052400f KS |
37 | struct uid_gid { |
38 | unsigned int name_off; | |
39 | union { | |
40 | uid_t uid; | |
41 | gid_t gid; | |
42 | }; | |
43 | }; | |
44 | ||
1449a55d | 45 | struct trie_node { |
023ed7b0 | 46 | /* this node's first child */ |
bcf44d55 | 47 | unsigned int child_idx; |
023ed7b0 KS |
48 | /* the next child of our parent node's child list */ |
49 | unsigned int next_child_idx; | |
50 | /* this node's last child (shortcut for append) */ | |
bcf44d55 | 51 | unsigned int last_child_idx; |
1449a55d | 52 | unsigned int value_off; |
bcf44d55 | 53 | unsigned short value_len; |
023ed7b0 | 54 | unsigned char key; |
1449a55d AJ |
55 | }; |
56 | ||
4052400f KS |
57 | struct udev_rules { |
58 | struct udev *udev; | |
59 | int resolve_names; | |
60 | ||
61 | /* every key in the rules file becomes a token */ | |
62 | struct token *tokens; | |
63 | unsigned int token_cur; | |
64 | unsigned int token_max; | |
65 | ||
66 | /* all key strings are copied to a single string buffer */ | |
67 | char *buf; | |
68 | size_t buf_cur; | |
69 | size_t buf_max; | |
70 | unsigned int buf_count; | |
71 | ||
bcf44d55 | 72 | /* during rule parsing, strings are indexed to find duplicates */ |
bcf44d55 KS |
73 | struct trie_node *trie_nodes; |
74 | unsigned int trie_nodes_cur; | |
75 | unsigned int trie_nodes_max; | |
1449a55d | 76 | |
bcf44d55 | 77 | /* during rule parsing, uid/gid lookup results are cached */ |
4052400f KS |
78 | struct uid_gid *uids; |
79 | unsigned int uids_cur; | |
80 | unsigned int uids_max; | |
81 | struct uid_gid *gids; | |
82 | unsigned int gids_cur; | |
83 | unsigned int gids_max; | |
84 | }; | |
85 | ||
1449a55d | 86 | /* KEY=="", KEY!="", KEY+="", KEY="", KEY:="" */ |
b0f7409f KS |
87 | enum operation_type { |
88 | OP_UNSET, | |
89 | ||
90 | OP_MATCH, | |
91 | OP_NOMATCH, | |
92 | OP_MATCH_MAX, | |
93 | ||
94 | OP_ADD, | |
95 | OP_ASSIGN, | |
96 | OP_ASSIGN_FINAL, | |
c7521974 KS |
97 | }; |
98 | ||
ac218d9c KS |
99 | enum string_glob_type { |
100 | GL_UNSET, | |
db463fd3 KS |
101 | GL_PLAIN, /* no special chars */ |
102 | GL_GLOB, /* shell globs ?,*,[] */ | |
103 | GL_SPLIT, /* multi-value A|B */ | |
104 | GL_SPLIT_GLOB, /* multi-value with glob A*|B* */ | |
105 | GL_SOMETHING, /* commonly used "?*" */ | |
ac218d9c KS |
106 | }; |
107 | ||
32028733 KS |
108 | enum string_subst_type { |
109 | SB_UNSET, | |
110 | SB_NONE, | |
111 | SB_FORMAT, | |
112 | SB_SUBSYS, | |
113 | }; | |
114 | ||
154a7b84 | 115 | /* tokens of a rule are sorted/handled in this order */ |
6880b25d | 116 | enum token_type { |
ac218d9c | 117 | TK_UNSET, |
6880b25d KS |
118 | TK_RULE, |
119 | ||
6880b25d KS |
120 | TK_M_ACTION, /* val */ |
121 | TK_M_DEVPATH, /* val */ | |
122 | TK_M_KERNEL, /* val */ | |
123 | TK_M_DEVLINK, /* val */ | |
124 | TK_M_NAME, /* val */ | |
125 | TK_M_ENV, /* val, attr */ | |
126 | TK_M_SUBSYSTEM, /* val */ | |
127 | TK_M_DRIVER, /* val */ | |
7aeeaedc | 128 | TK_M_WAITFOR, /* val */ |
6880b25d KS |
129 | TK_M_ATTR, /* val, attr */ |
130 | ||
d15fcce4 | 131 | TK_M_PARENTS_MIN, |
6880b25d KS |
132 | TK_M_KERNELS, /* val */ |
133 | TK_M_SUBSYSTEMS, /* val */ | |
134 | TK_M_DRIVERS, /* val */ | |
135 | TK_M_ATTRS, /* val, attr */ | |
b0f7409f | 136 | TK_M_PARENTS_MAX, |
6880b25d KS |
137 | |
138 | TK_M_TEST, /* val, mode_t */ | |
139 | TK_M_PROGRAM, /* val */ | |
140 | TK_M_IMPORT_FILE, /* val */ | |
141 | TK_M_IMPORT_PROG, /* val */ | |
142 | TK_M_IMPORT_PARENT, /* val */ | |
143 | TK_M_RESULT, /* val */ | |
ac218d9c | 144 | TK_M_MAX, |
6880b25d KS |
145 | |
146 | TK_A_IGNORE_DEVICE, | |
147 | TK_A_STRING_ESCAPE_NONE, | |
148 | TK_A_STRING_ESCAPE_REPLACE, | |
46f194cb | 149 | TK_A_INOTIFY_WATCH, /* int */ |
6880b25d KS |
150 | TK_A_NUM_FAKE_PART, /* int */ |
151 | TK_A_DEVLINK_PRIO, /* int */ | |
152 | TK_A_OWNER, /* val */ | |
153 | TK_A_GROUP, /* val */ | |
154 | TK_A_MODE, /* val */ | |
155 | TK_A_OWNER_ID, /* uid_t */ | |
156 | TK_A_GROUP_ID, /* gid_t */ | |
157 | TK_A_MODE_ID, /* mode_t */ | |
158 | TK_A_ENV, /* val, attr */ | |
159 | TK_A_NAME, /* val */ | |
160 | TK_A_DEVLINK, /* val */ | |
161 | TK_A_EVENT_TIMEOUT, /* int */ | |
162 | TK_A_IGNORE_REMOVE, | |
163 | TK_A_ATTR, /* val, attr */ | |
164 | TK_A_RUN, /* val, bool */ | |
165 | TK_A_GOTO, /* size_t */ | |
166 | TK_A_LAST_RULE, | |
167 | ||
168 | TK_END, | |
c7521974 KS |
169 | }; |
170 | ||
3f3aa9f5 | 171 | /* we try to pack stuff in a way that we take only 12 bytes per token */ |
4052400f KS |
172 | struct token { |
173 | union { | |
3f3aa9f5 | 174 | unsigned char type; /* same as in rule and key */ |
4052400f | 175 | struct { |
a25d547d | 176 | enum token_type type:8; |
32028733 | 177 | unsigned int flags:8; |
3f3aa9f5 | 178 | unsigned short token_count; |
4052400f KS |
179 | unsigned int label_off; |
180 | unsigned short filename_off; | |
181 | unsigned short filename_line; | |
182 | } rule; | |
183 | struct { | |
a25d547d KS |
184 | enum token_type type:8; |
185 | enum operation_type op:8; | |
186 | enum string_glob_type glob:8; | |
32028733 KS |
187 | enum string_subst_type subst:4; |
188 | enum string_subst_type attrsubst:4; | |
4052400f KS |
189 | unsigned int value_off; |
190 | union { | |
191 | unsigned int attr_off; | |
192 | int ignore_error; | |
4052400f KS |
193 | unsigned int rule_goto; |
194 | mode_t mode; | |
195 | uid_t uid; | |
196 | gid_t gid; | |
197 | int num_fake_part; | |
198 | int devlink_prio; | |
199 | int event_timeout; | |
46f194cb | 200 | int watch; |
4052400f KS |
201 | }; |
202 | } key; | |
203 | }; | |
204 | }; | |
205 | ||
206 | #define MAX_TK 64 | |
207 | struct rule_tmp { | |
208 | struct udev_rules *rules; | |
209 | struct token rule; | |
210 | struct token token[MAX_TK]; | |
211 | unsigned int token_cur; | |
212 | }; | |
213 | ||
045a3bc8 | 214 | #ifdef ENABLE_DEBUG |
5d6a1fa6 KS |
215 | static const char *operation_str(enum operation_type type) |
216 | { | |
217 | static const char *operation_strs[] = { | |
218 | [OP_UNSET] = "UNSET", | |
219 | [OP_MATCH] = "match", | |
220 | [OP_NOMATCH] = "nomatch", | |
221 | [OP_MATCH_MAX] = "MATCH_MAX", | |
222 | ||
223 | [OP_ADD] = "add", | |
224 | [OP_ASSIGN] = "assign", | |
225 | [OP_ASSIGN_FINAL] = "assign-final", | |
226 | } ; | |
227 | ||
228 | return operation_strs[type]; | |
229 | } | |
4052400f | 230 | |
5d6a1fa6 KS |
231 | static const char *string_glob_str(enum string_glob_type type) |
232 | { | |
233 | static const char *string_glob_strs[] = { | |
234 | [GL_UNSET] = "UNSET", | |
235 | [GL_PLAIN] = "plain", | |
236 | [GL_GLOB] = "glob", | |
237 | [GL_SPLIT] = "split", | |
238 | [GL_SPLIT_GLOB] = "split-glob", | |
239 | [GL_SOMETHING] = "split-glob", | |
5d6a1fa6 | 240 | }; |
4052400f | 241 | |
5d6a1fa6 KS |
242 | return string_glob_strs[type]; |
243 | } | |
244 | ||
245 | static const char *token_str(enum token_type type) | |
246 | { | |
247 | static const char *token_strs[] = { | |
248 | [TK_UNSET] = "UNSET", | |
249 | [TK_RULE] = "RULE", | |
250 | ||
251 | [TK_M_ACTION] = "M ACTION", | |
252 | [TK_M_DEVPATH] = "M DEVPATH", | |
253 | [TK_M_KERNEL] = "M KERNEL", | |
254 | [TK_M_DEVLINK] = "M DEVLINK", | |
255 | [TK_M_NAME] = "M NAME", | |
256 | [TK_M_ENV] = "M ENV", | |
257 | [TK_M_SUBSYSTEM] = "M SUBSYSTEM", | |
258 | [TK_M_DRIVER] = "M DRIVER", | |
259 | [TK_M_WAITFOR] = "M WAITFOR", | |
260 | [TK_M_ATTR] = "M ATTR", | |
261 | ||
262 | [TK_M_PARENTS_MIN] = "M PARENTS_MIN", | |
263 | [TK_M_KERNELS] = "M KERNELS", | |
264 | [TK_M_SUBSYSTEMS] = "M SUBSYSTEMS", | |
265 | [TK_M_DRIVERS] = "M DRIVERS", | |
266 | [TK_M_ATTRS] = "M ATTRS", | |
267 | [TK_M_PARENTS_MAX] = "M PARENTS_MAX", | |
268 | ||
269 | [TK_M_TEST] = "M TEST", | |
270 | [TK_M_PROGRAM] = "M PROGRAM", | |
271 | [TK_M_IMPORT_FILE] = "M IMPORT_FILE", | |
272 | [TK_M_IMPORT_PROG] = "M IMPORT_PROG", | |
214a6c79 | 273 | [TK_M_IMPORT_PARENT] = "M IMPORT_PARENT", |
5d6a1fa6 KS |
274 | [TK_M_RESULT] = "M RESULT", |
275 | [TK_M_MAX] = "M MAX", | |
276 | ||
277 | [TK_A_IGNORE_DEVICE] = "A IGNORE_DEVICE", | |
278 | [TK_A_STRING_ESCAPE_NONE] = "A STRING_ESCAPE_NONE", | |
279 | [TK_A_STRING_ESCAPE_REPLACE] = "A STRING_ESCAPE_REPLACE", | |
bd284db1 | 280 | [TK_A_INOTIFY_WATCH] = "A INOTIFY_WATCH", |
5d6a1fa6 KS |
281 | [TK_A_NUM_FAKE_PART] = "A NUM_FAKE_PART", |
282 | [TK_A_DEVLINK_PRIO] = "A DEVLINK_PRIO", | |
283 | [TK_A_OWNER] = "A OWNER", | |
284 | [TK_A_GROUP] = "A GROUP", | |
285 | [TK_A_MODE] = "A MODE", | |
286 | [TK_A_OWNER_ID] = "A OWNER_ID", | |
287 | [TK_A_GROUP_ID] = "A GROUP_ID", | |
288 | [TK_A_MODE_ID] = "A MODE_ID", | |
289 | [TK_A_ENV] = "A ENV", | |
290 | [TK_A_NAME] = "A NAME", | |
291 | [TK_A_DEVLINK] = "A DEVLINK", | |
292 | [TK_A_EVENT_TIMEOUT] = "A EVENT_TIMEOUT", | |
293 | [TK_A_IGNORE_REMOVE] = "A IGNORE_REMOVE", | |
294 | [TK_A_ATTR] = "A ATTR", | |
295 | [TK_A_RUN] = "A RUN", | |
296 | [TK_A_GOTO] = "A GOTO", | |
297 | [TK_A_LAST_RULE] = "A LAST_RULE", | |
298 | ||
299 | [TK_END] = "END", | |
300 | }; | |
301 | ||
302 | return token_strs[type]; | |
303 | } | |
c7521974 | 304 | |
4052400f KS |
305 | static void dump_token(struct udev_rules *rules, struct token *token) |
306 | { | |
307 | enum token_type type = token->type; | |
308 | enum operation_type op = token->key.op; | |
309 | enum string_glob_type glob = token->key.glob; | |
310 | const char *value = &rules->buf[token->key.value_off]; | |
311 | const char *attr = &rules->buf[token->key.attr_off]; | |
154a7b84 | 312 | |
4052400f KS |
313 | switch (type) { |
314 | case TK_RULE: | |
315 | { | |
316 | const char *tks_ptr = (char *)rules->tokens; | |
317 | const char *tk_ptr = (char *)token; | |
5d6a1fa6 | 318 | unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); |
154a7b84 | 319 | |
5d6a1fa6 | 320 | dbg(rules->udev, "* RULE %s:%u, token: %u, count: %u, label: '%s', flags: 0x%02x\n", |
4052400f | 321 | &rules->buf[token->rule.filename_off], token->rule.filename_line, |
5d6a1fa6 | 322 | idx, token->rule.token_count, |
4052400f KS |
323 | &rules->buf[token->rule.label_off], |
324 | token->rule.flags); | |
325 | break; | |
326 | } | |
327 | case TK_M_ACTION: | |
328 | case TK_M_DEVPATH: | |
329 | case TK_M_KERNEL: | |
330 | case TK_M_SUBSYSTEM: | |
331 | case TK_M_DRIVER: | |
332 | case TK_M_WAITFOR: | |
333 | case TK_M_DEVLINK: | |
334 | case TK_M_NAME: | |
335 | case TK_M_KERNELS: | |
336 | case TK_M_SUBSYSTEMS: | |
337 | case TK_M_DRIVERS: | |
338 | case TK_M_PROGRAM: | |
339 | case TK_M_IMPORT_FILE: | |
340 | case TK_M_IMPORT_PROG: | |
341 | case TK_M_IMPORT_PARENT: | |
342 | case TK_M_RESULT: | |
343 | case TK_A_NAME: | |
344 | case TK_A_DEVLINK: | |
345 | case TK_A_OWNER: | |
346 | case TK_A_GROUP: | |
347 | case TK_A_MODE: | |
348 | case TK_A_RUN: | |
349 | dbg(rules->udev, "%s %s '%s'(%s)\n", | |
5d6a1fa6 | 350 | token_str(type), operation_str(op), value, string_glob_str(glob)); |
4052400f KS |
351 | break; |
352 | case TK_M_ATTR: | |
353 | case TK_M_ATTRS: | |
354 | case TK_M_ENV: | |
355 | case TK_A_ATTR: | |
356 | case TK_A_ENV: | |
357 | dbg(rules->udev, "%s %s '%s' '%s'(%s)\n", | |
5d6a1fa6 | 358 | token_str(type), operation_str(op), attr, value, string_glob_str(glob)); |
4052400f KS |
359 | break; |
360 | case TK_A_IGNORE_DEVICE: | |
361 | case TK_A_STRING_ESCAPE_NONE: | |
362 | case TK_A_STRING_ESCAPE_REPLACE: | |
363 | case TK_A_LAST_RULE: | |
364 | case TK_A_IGNORE_REMOVE: | |
5d6a1fa6 | 365 | dbg(rules->udev, "%s\n", token_str(type)); |
4052400f KS |
366 | break; |
367 | case TK_M_TEST: | |
368 | dbg(rules->udev, "%s %s '%s'(%s) %#o\n", | |
5d6a1fa6 | 369 | token_str(type), operation_str(op), value, string_glob_str(glob), token->key.mode); |
4052400f | 370 | break; |
46f194cb | 371 | case TK_A_INOTIFY_WATCH: |
2236ddae | 372 | dbg(rules->udev, "%s %u\n", token_str(type), token->key.watch); |
46f194cb | 373 | break; |
4052400f | 374 | case TK_A_NUM_FAKE_PART: |
5d6a1fa6 | 375 | dbg(rules->udev, "%s %u\n", token_str(type), token->key.num_fake_part); |
4052400f KS |
376 | break; |
377 | case TK_A_DEVLINK_PRIO: | |
5d6a1fa6 | 378 | dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.devlink_prio); |
4052400f KS |
379 | break; |
380 | case TK_A_OWNER_ID: | |
5d6a1fa6 | 381 | dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.uid); |
4052400f KS |
382 | break; |
383 | case TK_A_GROUP_ID: | |
5d6a1fa6 | 384 | dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.gid); |
4052400f KS |
385 | break; |
386 | case TK_A_MODE_ID: | |
5d6a1fa6 | 387 | dbg(rules->udev, "%s %s %#o\n", token_str(type), operation_str(op), token->key.mode); |
4052400f KS |
388 | break; |
389 | case TK_A_EVENT_TIMEOUT: | |
5d6a1fa6 | 390 | dbg(rules->udev, "%s %s %u\n", token_str(type), operation_str(op), token->key.event_timeout); |
4052400f KS |
391 | break; |
392 | case TK_A_GOTO: | |
5d6a1fa6 | 393 | dbg(rules->udev, "%s '%s' %u\n", token_str(type), value, token->key.rule_goto); |
4052400f KS |
394 | break; |
395 | case TK_END: | |
5d6a1fa6 | 396 | dbg(rules->udev, "* %s\n", token_str(type)); |
4052400f KS |
397 | break; |
398 | case TK_M_PARENTS_MIN: | |
399 | case TK_M_PARENTS_MAX: | |
400 | case TK_M_MAX: | |
401 | case TK_UNSET: | |
402 | dbg(rules->udev, "unknown type %u\n", type); | |
403 | break; | |
404 | } | |
405 | } | |
154a7b84 | 406 | |
4052400f KS |
407 | static void dump_rules(struct udev_rules *rules) |
408 | { | |
409 | unsigned int i; | |
154a7b84 | 410 | |
4052400f KS |
411 | dbg(rules->udev, "dumping %u (%zu bytes) tokens, %u (%zu bytes) strings\n", |
412 | rules->token_cur, | |
413 | rules->token_cur * sizeof(struct token), | |
414 | rules->buf_count, | |
415 | rules->buf_cur); | |
416 | for(i = 0; i < rules->token_cur; i++) | |
417 | dump_token(rules, &rules->tokens[i]); | |
418 | } | |
419 | #else | |
5d6a1fa6 KS |
420 | static inline const char *operation_str(enum operation_type type) { return NULL; } |
421 | static inline const char *token_str(enum token_type type) { return NULL; } | |
4052400f KS |
422 | static inline void dump_token(struct udev_rules *rules, struct token *token) {} |
423 | static inline void dump_rules(struct udev_rules *rules) {} | |
045a3bc8 | 424 | #endif /* ENABLE_DEBUG */ |
c7521974 | 425 | |
1449a55d | 426 | static int add_new_string(struct udev_rules *rules, const char *str, size_t bytes) |
c7521974 | 427 | { |
6880b25d | 428 | int off; |
c7521974 | 429 | |
34d6a259 | 430 | /* grow buffer if needed */ |
1449a55d | 431 | if (rules->buf_cur + bytes+1 >= rules->buf_max) { |
6880b25d KS |
432 | char *buf; |
433 | unsigned int add; | |
c7521974 | 434 | |
6880b25d KS |
435 | /* double the buffer size */ |
436 | add = rules->buf_max; | |
1449a55d AJ |
437 | if (add < bytes * 8) |
438 | add = bytes * 8; | |
c7521974 | 439 | |
6880b25d KS |
440 | buf = realloc(rules->buf, rules->buf_max + add); |
441 | if (buf == NULL) | |
442 | return -1; | |
86b57788 | 443 | dbg(rules->udev, "extend buffer from %zu to %zu\n", rules->buf_max, rules->buf_max + add); |
6880b25d KS |
444 | rules->buf = buf; |
445 | rules->buf_max += add; | |
c7521974 | 446 | } |
6880b25d | 447 | off = rules->buf_cur; |
1449a55d AJ |
448 | memcpy(&rules->buf[rules->buf_cur], str, bytes); |
449 | rules->buf_cur += bytes; | |
6880b25d KS |
450 | rules->buf_count++; |
451 | return off; | |
c7521974 KS |
452 | } |
453 | ||
1449a55d | 454 | static int add_string(struct udev_rules *rules, const char *str) |
c7521974 | 455 | { |
bcf44d55 KS |
456 | unsigned int node_idx; |
457 | struct trie_node *new_node; | |
458 | unsigned int new_node_idx; | |
1449a55d | 459 | unsigned char key; |
bcf44d55 | 460 | unsigned short len; |
1c8af93a | 461 | unsigned int depth; |
1449a55d | 462 | unsigned int off; |
023ed7b0 | 463 | struct trie_node *parent; |
1449a55d | 464 | |
023ed7b0 | 465 | /* walk trie, start from last character of str to find matching tails */ |
949075db | 466 | len = strlen(str); |
023ed7b0 | 467 | key = str[len-1]; |
949075db | 468 | node_idx = 0; |
023ed7b0 KS |
469 | for (depth = 0; depth <= len; depth++) { |
470 | struct trie_node *node; | |
471 | unsigned int child_idx; | |
472 | ||
473 | node = &rules->trie_nodes[node_idx]; | |
474 | off = node->value_off + node->value_len - len; | |
475 | ||
476 | /* match against current node */ | |
477 | if (depth == len || (node->value_len >= len && memcmp(&rules->buf[off], str, len) == 0)) | |
478 | return off; | |
479 | ||
480 | /* lookup child node */ | |
481 | key = str[len - 1 - depth]; | |
482 | child_idx = node->child_idx; | |
483 | while (child_idx > 0) { | |
484 | struct trie_node *child; | |
485 | ||
486 | child = &rules->trie_nodes[child_idx]; | |
487 | if (child->key == key) | |
1449a55d | 488 | break; |
023ed7b0 | 489 | child_idx = child->next_child_idx; |
1449a55d | 490 | } |
023ed7b0 KS |
491 | if (child_idx == 0) |
492 | break; | |
493 | node_idx = child_idx; | |
1449a55d AJ |
494 | } |
495 | ||
496 | /* string not found, add it */ | |
497 | off = add_new_string(rules, str, len + 1); | |
498 | ||
bcf44d55 KS |
499 | /* grow trie nodes if needed */ |
500 | if (rules->trie_nodes_cur >= rules->trie_nodes_max) { | |
501 | struct trie_node *nodes; | |
502 | unsigned int add; | |
1449a55d AJ |
503 | |
504 | /* double the buffer size */ | |
bcf44d55 | 505 | add = rules->trie_nodes_max; |
1449a55d AJ |
506 | if (add < 8) |
507 | add = 8; | |
508 | ||
bcf44d55 KS |
509 | nodes = realloc(rules->trie_nodes, (rules->trie_nodes_max + add) * sizeof(struct trie_node)); |
510 | if (nodes == NULL) | |
1449a55d | 511 | return -1; |
bcf44d55 KS |
512 | dbg(rules->udev, "extend trie nodes from %u to %u\n", |
513 | rules->trie_nodes_max, rules->trie_nodes_max + add); | |
514 | rules->trie_nodes = nodes; | |
515 | rules->trie_nodes_max += add; | |
1449a55d AJ |
516 | } |
517 | ||
023ed7b0 | 518 | /* get a new node */ |
bcf44d55 KS |
519 | new_node_idx = rules->trie_nodes_cur; |
520 | rules->trie_nodes_cur++; | |
521 | new_node = &rules->trie_nodes[new_node_idx]; | |
023ed7b0 | 522 | memset(new_node, 0x00, sizeof(struct trie_node)); |
bcf44d55 KS |
523 | new_node->value_off = off; |
524 | new_node->value_len = len; | |
023ed7b0 | 525 | new_node->key = key; |
bcf44d55 | 526 | |
023ed7b0 KS |
527 | /* join the parent's child list */ |
528 | parent = &rules->trie_nodes[node_idx]; | |
529 | if (parent->child_idx == 0) { | |
530 | parent->child_idx = new_node_idx; | |
bcf44d55 | 531 | } else { |
023ed7b0 | 532 | struct trie_node *last_child; |
1449a55d | 533 | |
023ed7b0 KS |
534 | last_child = &rules->trie_nodes[parent->last_child_idx]; |
535 | last_child->next_child_idx = new_node_idx; | |
bcf44d55 | 536 | } |
023ed7b0 | 537 | parent->last_child_idx = new_node_idx; |
1449a55d AJ |
538 | return off; |
539 | } | |
540 | ||
541 | static int add_token(struct udev_rules *rules, struct token *token) | |
542 | { | |
34d6a259 | 543 | /* grow buffer if needed */ |
6880b25d KS |
544 | if (rules->token_cur+1 >= rules->token_max) { |
545 | struct token *tokens; | |
546 | unsigned int add; | |
c7521974 | 547 | |
6880b25d KS |
548 | /* double the buffer size */ |
549 | add = rules->token_max; | |
154a7b84 KS |
550 | if (add < 8) |
551 | add = 8; | |
c7521974 | 552 | |
6880b25d KS |
553 | tokens = realloc(rules->tokens, (rules->token_max + add ) * sizeof(struct token)); |
554 | if (tokens == NULL) | |
555 | return -1; | |
86b57788 | 556 | dbg(rules->udev, "extend tokens from %u to %u\n", rules->token_max, rules->token_max + add); |
6880b25d KS |
557 | rules->tokens = tokens; |
558 | rules->token_max += add; | |
c7521974 | 559 | } |
6880b25d KS |
560 | memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); |
561 | rules->token_cur++; | |
562 | return 0; | |
c7521974 | 563 | } |
a27cd06c | 564 | |
154a7b84 KS |
565 | static uid_t add_uid(struct udev_rules *rules, const char *owner) |
566 | { | |
567 | unsigned int i; | |
568 | uid_t uid; | |
569 | unsigned int off; | |
570 | ||
571 | /* lookup, if we know it already */ | |
572 | for (i = 0; i < rules->uids_cur; i++) { | |
573 | off = rules->uids[i].name_off; | |
574 | if (strcmp(&rules->buf[off], owner) == 0) { | |
575 | uid = rules->uids[i].uid; | |
576 | dbg(rules->udev, "return existing %u for '%s'\n", uid, owner); | |
577 | return uid; | |
578 | } | |
579 | } | |
580 | uid = util_lookup_user(rules->udev, owner); | |
581 | ||
582 | /* grow buffer if needed */ | |
583 | if (rules->uids_cur+1 >= rules->uids_max) { | |
584 | struct uid_gid *uids; | |
585 | unsigned int add; | |
586 | ||
587 | /* double the buffer size */ | |
588 | add = rules->uids_max; | |
589 | if (add < 1) | |
590 | add = 8; | |
591 | ||
592 | uids = realloc(rules->uids, (rules->uids_max + add ) * sizeof(struct uid_gid)); | |
593 | if (uids == NULL) | |
594 | return uid; | |
86b57788 | 595 | dbg(rules->udev, "extend uids from %u to %u\n", rules->uids_max, rules->uids_max + add); |
154a7b84 KS |
596 | rules->uids = uids; |
597 | rules->uids_max += add; | |
598 | } | |
599 | rules->uids[rules->uids_cur].uid = uid; | |
600 | off = add_string(rules, owner); | |
601 | if (off <= 0) | |
602 | return uid; | |
603 | rules->uids[rules->uids_cur].name_off = off; | |
604 | rules->uids_cur++; | |
605 | return uid; | |
606 | } | |
607 | ||
608 | static gid_t add_gid(struct udev_rules *rules, const char *group) | |
609 | { | |
610 | unsigned int i; | |
611 | gid_t gid; | |
612 | unsigned int off; | |
613 | ||
614 | /* lookup, if we know it already */ | |
615 | for (i = 0; i < rules->gids_cur; i++) { | |
616 | off = rules->gids[i].name_off; | |
617 | if (strcmp(&rules->buf[off], group) == 0) { | |
618 | gid = rules->gids[i].gid; | |
ac218d9c | 619 | dbg(rules->udev, "return existing %u for '%s'\n", gid, group); |
154a7b84 KS |
620 | return gid; |
621 | } | |
622 | } | |
623 | gid = util_lookup_group(rules->udev, group); | |
624 | ||
625 | /* grow buffer if needed */ | |
626 | if (rules->gids_cur+1 >= rules->gids_max) { | |
627 | struct uid_gid *gids; | |
628 | unsigned int add; | |
629 | ||
630 | /* double the buffer size */ | |
631 | add = rules->gids_max; | |
632 | if (add < 1) | |
633 | add = 8; | |
634 | ||
635 | gids = realloc(rules->gids, (rules->gids_max + add ) * sizeof(struct uid_gid)); | |
636 | if (gids == NULL) | |
637 | return gid; | |
86b57788 | 638 | dbg(rules->udev, "extend gids from %u to %u\n", rules->gids_max, rules->gids_max + add); |
154a7b84 KS |
639 | rules->gids = gids; |
640 | rules->gids_max += add; | |
641 | } | |
642 | rules->gids[rules->gids_cur].gid = gid; | |
643 | off = add_string(rules, group); | |
644 | if (off <= 0) | |
645 | return gid; | |
646 | rules->gids[rules->gids_cur].name_off = off; | |
647 | rules->gids_cur++; | |
648 | return gid; | |
649 | } | |
650 | ||
bb144ed1 | 651 | static int import_property_from_string(struct udev_device *dev, char *line) |
bd0ed2ff | 652 | { |
bb144ed1 KS |
653 | struct udev *udev = udev_device_get_udev(dev); |
654 | char *key; | |
655 | char *val; | |
656 | size_t len; | |
bd0ed2ff | 657 | |
bb144ed1 KS |
658 | /* find key */ |
659 | key = line; | |
660 | while (isspace(key[0])) | |
661 | key++; | |
bd0ed2ff | 662 | |
bb144ed1 KS |
663 | /* comment or empty line */ |
664 | if (key[0] == '#' || key[0] == '\0') | |
665 | return -1; | |
16511863 | 666 | |
bb144ed1 KS |
667 | /* split key/value */ |
668 | val = strchr(key, '='); | |
669 | if (val == NULL) | |
670 | return -1; | |
671 | val[0] = '\0'; | |
672 | val++; | |
aa8734ff | 673 | |
bb144ed1 KS |
674 | /* find value */ |
675 | while (isspace(val[0])) | |
676 | val++; | |
677 | ||
678 | /* terminate key */ | |
679 | len = strlen(key); | |
680 | if (len == 0) | |
681 | return -1; | |
682 | while (isspace(key[len-1])) | |
683 | len--; | |
684 | key[len] = '\0'; | |
685 | ||
686 | /* terminate value */ | |
687 | len = strlen(val); | |
688 | if (len == 0) | |
689 | return -1; | |
690 | while (isspace(val[len-1])) | |
691 | len--; | |
692 | val[len] = '\0'; | |
693 | ||
694 | if (len == 0) | |
695 | return -1; | |
696 | ||
697 | /* unquote */ | |
698 | if (val[0] == '"' || val[0] == '\'') { | |
699 | if (val[len-1] != val[0]) { | |
700 | info(udev, "inconsistent quoting: '%s', skip\n", line); | |
701 | return -1; | |
bd0ed2ff | 702 | } |
bb144ed1 KS |
703 | val[len-1] = '\0'; |
704 | val++; | |
705 | } | |
706 | ||
86b57788 | 707 | dbg(udev, "adding '%s'='%s'\n", key, val); |
bb144ed1 KS |
708 | |
709 | /* handle device, renamed by external tool, returning new path */ | |
710 | if (strcmp(key, "DEVPATH") == 0) { | |
711 | char syspath[UTIL_PATH_SIZE]; | |
712 | ||
713 | info(udev, "updating devpath from '%s' to '%s'\n", | |
714 | udev_device_get_devpath(dev), val); | |
065db052 | 715 | util_strscpyl(syspath, sizeof(syspath), udev_get_sys_path(udev), val, NULL); |
bb144ed1 KS |
716 | udev_device_set_syspath(dev, syspath); |
717 | } else { | |
718 | struct udev_list_entry *entry; | |
719 | ||
720 | entry = udev_device_add_property(dev, key, val); | |
b25a9454 KS |
721 | /* store in db, skip private keys */ |
722 | if (key[0] != '.') | |
723 | udev_list_entry_set_flag(entry, 1); | |
bd0ed2ff | 724 | } |
be4bedd1 KS |
725 | return 0; |
726 | } | |
727 | ||
6880b25d | 728 | static int import_file_into_properties(struct udev_device *dev, const char *filename) |
be4bedd1 | 729 | { |
bb144ed1 KS |
730 | FILE *f; |
731 | char line[UTIL_LINE_SIZE]; | |
be4bedd1 | 732 | |
bb144ed1 KS |
733 | f = fopen(filename, "r"); |
734 | if (f == NULL) | |
be4bedd1 | 735 | return -1; |
fc42bd5d | 736 | while (fgets(line, sizeof(line), f) != NULL) |
bb144ed1 KS |
737 | import_property_from_string(dev, line); |
738 | fclose(f); | |
be4bedd1 | 739 | return 0; |
bd0ed2ff KS |
740 | } |
741 | ||
6880b25d | 742 | static int import_program_into_properties(struct udev_device *dev, const char *program) |
319c6700 | 743 | { |
d0db192f KS |
744 | struct udev *udev = udev_device_get_udev(dev); |
745 | char **envp; | |
52761bb0 | 746 | char result[4096]; |
319c6700 | 747 | size_t reslen; |
bb144ed1 | 748 | char *line; |
319c6700 | 749 | |
d0db192f | 750 | envp = udev_device_get_properties_envp(dev); |
54808d77 | 751 | if (util_run_program(udev, program, envp, result, sizeof(result), &reslen) != 0) |
319c6700 | 752 | return -1; |
bb144ed1 KS |
753 | |
754 | line = result; | |
755 | while (line != NULL) { | |
756 | char *pos; | |
757 | ||
758 | pos = strchr(line, '\n'); | |
759 | if (pos != NULL) { | |
760 | pos[0] = '\0'; | |
761 | pos = &pos[1]; | |
762 | } | |
763 | import_property_from_string(dev, line); | |
764 | line = pos; | |
765 | } | |
766 | return 0; | |
319c6700 KS |
767 | } |
768 | ||
6880b25d | 769 | static int import_parent_into_properties(struct udev_device *dev, const char *filter) |
0bfb84e1 | 770 | { |
bb144ed1 | 771 | struct udev *udev = udev_device_get_udev(dev); |
aa8734ff KS |
772 | struct udev_device *dev_parent; |
773 | struct udev_list_entry *list_entry; | |
0bfb84e1 | 774 | |
bb144ed1 | 775 | dev_parent = udev_device_get_parent(dev); |
aa8734ff KS |
776 | if (dev_parent == NULL) |
777 | return -1; | |
0bfb84e1 | 778 | |
bb144ed1 | 779 | dbg(udev, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent)); |
aa8734ff KS |
780 | udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { |
781 | const char *key = udev_list_entry_get_name(list_entry); | |
782 | const char *val = udev_list_entry_get_value(list_entry); | |
0bfb84e1 | 783 | |
aa8734ff KS |
784 | if (fnmatch(filter, key, 0) == 0) { |
785 | struct udev_list_entry *entry; | |
e2ecb34f | 786 | |
bb144ed1 KS |
787 | dbg(udev, "import key '%s=%s'\n", key, val); |
788 | entry = udev_device_add_property(dev, key, val); | |
b25a9454 KS |
789 | /* store in db, skip private keys */ |
790 | if (key[0] != '.') | |
791 | udev_list_entry_set_flag(entry, 1); | |
aa8734ff | 792 | } |
e2ecb34f | 793 | } |
aa8734ff | 794 | return 0; |
e2ecb34f KS |
795 | } |
796 | ||
c86be870 | 797 | #define WAIT_LOOP_PER_SECOND 50 |
6880b25d | 798 | static int wait_for_file(struct udev_device *dev, const char *file, int timeout) |
b2fe4b9a | 799 | { |
6880b25d | 800 | struct udev *udev = udev_device_get_udev(dev); |
17fcfb59 | 801 | char filepath[UTIL_PATH_SIZE]; |
1822e9b0 | 802 | char devicepath[UTIL_PATH_SIZE]; |
b2fe4b9a KS |
803 | struct stat stats; |
804 | int loop = timeout * WAIT_LOOP_PER_SECOND; | |
805 | ||
ea97dc37 | 806 | /* a relative path is a device attribute */ |
1822e9b0 | 807 | devicepath[0] = '\0'; |
ea97dc37 | 808 | if (file[0] != '/') { |
065db052 KS |
809 | util_strscpyl(devicepath, sizeof(devicepath), |
810 | udev_get_sys_path(udev), udev_device_get_devpath(dev), NULL); | |
811 | util_strscpyl(filepath, sizeof(filepath), devicepath, "/", file, NULL); | |
ea97dc37 KS |
812 | file = filepath; |
813 | } | |
814 | ||
6880b25d | 815 | dbg(udev, "will wait %i sec for '%s'\n", timeout, file); |
b2fe4b9a | 816 | while (--loop) { |
656ba91e | 817 | /* lookup file */ |
ea97dc37 | 818 | if (stat(file, &stats) == 0) { |
6880b25d | 819 | info(udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); |
b2fe4b9a KS |
820 | return 0; |
821 | } | |
eb5b8640 | 822 | /* make sure, the device did not disappear in the meantime */ |
ea97dc37 | 823 | if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { |
6880b25d | 824 | info(udev, "device disappeared while waiting for '%s'\n", file); |
656ba91e KS |
825 | return -2; |
826 | } | |
6880b25d | 827 | info(udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); |
b2fe4b9a KS |
828 | usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); |
829 | } | |
6880b25d | 830 | info(udev, "waiting for '%s' failed\n", file); |
b2fe4b9a KS |
831 | return -1; |
832 | } | |
833 | ||
0ea5e96e KS |
834 | static int attr_subst_subdir(char *attr, size_t len) |
835 | { | |
0ea5e96e KS |
836 | int found = 0; |
837 | ||
065db052 KS |
838 | if (strstr(attr, "/*/")) { |
839 | char *pos; | |
840 | char dirname[UTIL_PATH_SIZE]; | |
841 | const char *tail; | |
0ea5e96e KS |
842 | DIR *dir; |
843 | ||
065db052 KS |
844 | util_strscpy(dirname, sizeof(dirname), attr); |
845 | pos = strstr(dirname, "/*/"); | |
846 | if (pos == NULL) | |
847 | return -1; | |
848 | pos[0] = '\0'; | |
849 | tail = &pos[2]; | |
850 | dir = opendir(dirname); | |
0ea5e96e KS |
851 | if (dir != NULL) { |
852 | struct dirent *dent; | |
853 | ||
854 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
855 | struct stat stats; | |
856 | ||
857 | if (dent->d_name[0] == '.') | |
858 | continue; | |
065db052 | 859 | util_strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); |
0ea5e96e KS |
860 | if (stat(attr, &stats) == 0) { |
861 | found = 1; | |
862 | break; | |
863 | } | |
0ea5e96e KS |
864 | } |
865 | closedir(dir); | |
866 | } | |
0ea5e96e KS |
867 | } |
868 | ||
869 | return found; | |
870 | } | |
871 | ||
b0f7409f | 872 | static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) |
a0e5382d | 873 | { |
6880b25d KS |
874 | char *linepos; |
875 | char *temp; | |
a0e5382d | 876 | |
6880b25d KS |
877 | linepos = *line; |
878 | if (linepos == NULL && linepos[0] == '\0') | |
879 | return -1; | |
a0e5382d | 880 | |
6880b25d KS |
881 | /* skip whitespace */ |
882 | while (isspace(linepos[0]) || linepos[0] == ',') | |
883 | linepos++; | |
aa8734ff | 884 | |
6880b25d KS |
885 | /* get the key */ |
886 | if (linepos[0] == '\0') | |
887 | return -1; | |
888 | *key = linepos; | |
81313e1b | 889 | |
6880b25d KS |
890 | while (1) { |
891 | linepos++; | |
892 | if (linepos[0] == '\0') | |
893 | return -1; | |
894 | if (isspace(linepos[0])) | |
81313e1b | 895 | break; |
6880b25d KS |
896 | if (linepos[0] == '=') |
897 | break; | |
898 | if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) | |
899 | if (linepos[1] == '=') | |
1ff1aa42 | 900 | break; |
953249a3 KS |
901 | } |
902 | ||
6880b25d KS |
903 | /* remember end of key */ |
904 | temp = linepos; | |
b2fe4b9a | 905 | |
6880b25d KS |
906 | /* skip whitespace after key */ |
907 | while (isspace(linepos[0])) | |
908 | linepos++; | |
909 | if (linepos[0] == '\0') | |
910 | return -1; | |
b2fe4b9a | 911 | |
6880b25d KS |
912 | /* get operation type */ |
913 | if (linepos[0] == '=' && linepos[1] == '=') { | |
b0f7409f | 914 | *op = OP_MATCH; |
6880b25d KS |
915 | linepos += 2; |
916 | } else if (linepos[0] == '!' && linepos[1] == '=') { | |
b0f7409f | 917 | *op = OP_NOMATCH; |
6880b25d KS |
918 | linepos += 2; |
919 | } else if (linepos[0] == '+' && linepos[1] == '=') { | |
b0f7409f | 920 | *op = OP_ADD; |
6880b25d KS |
921 | linepos += 2; |
922 | } else if (linepos[0] == '=') { | |
b0f7409f | 923 | *op = OP_ASSIGN; |
6880b25d KS |
924 | linepos++; |
925 | } else if (linepos[0] == ':' && linepos[1] == '=') { | |
b0f7409f | 926 | *op = OP_ASSIGN_FINAL; |
6880b25d KS |
927 | linepos += 2; |
928 | } else | |
929 | return -1; | |
d0c8cb7d | 930 | |
6880b25d KS |
931 | /* terminate key */ |
932 | temp[0] = '\0'; | |
95776dc6 | 933 | |
6880b25d KS |
934 | /* skip whitespace after operator */ |
935 | while (isspace(linepos[0])) | |
936 | linepos++; | |
937 | if (linepos[0] == '\0') | |
938 | return -1; | |
aa8734ff | 939 | |
ac218d9c | 940 | /* get the value */ |
6880b25d KS |
941 | if (linepos[0] == '"') |
942 | linepos++; | |
943 | else | |
944 | return -1; | |
945 | *value = linepos; | |
aa8734ff | 946 | |
ac218d9c | 947 | /* terminate */ |
6880b25d KS |
948 | temp = strchr(linepos, '"'); |
949 | if (!temp) | |
950 | return -1; | |
951 | temp[0] = '\0'; | |
952 | temp++; | |
5d6a1fa6 | 953 | dbg(udev, "%s '%s'-'%s'\n", operation_str(*op), *key, *value); |
aa8734ff | 954 | |
6880b25d KS |
955 | /* move line to next key */ |
956 | *line = temp; | |
957 | return 0; | |
958 | } | |
95776dc6 | 959 | |
6880b25d KS |
960 | /* extract possible KEY{attr} */ |
961 | static char *get_key_attribute(struct udev *udev, char *str) | |
962 | { | |
963 | char *pos; | |
964 | char *attr; | |
95776dc6 | 965 | |
6880b25d KS |
966 | attr = strchr(str, '{'); |
967 | if (attr != NULL) { | |
968 | attr++; | |
969 | pos = strchr(attr, '}'); | |
970 | if (pos == NULL) { | |
971 | err(udev, "missing closing brace for format\n"); | |
972 | return NULL; | |
95776dc6 | 973 | } |
6880b25d KS |
974 | pos[0] = '\0'; |
975 | dbg(udev, "attribute='%s'\n", attr); | |
976 | return attr; | |
95776dc6 | 977 | } |
6880b25d KS |
978 | return NULL; |
979 | } | |
95776dc6 | 980 | |
0ef254d5 KS |
981 | static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, |
982 | enum operation_type op, | |
983 | const char *value, const void *data) | |
6880b25d KS |
984 | { |
985 | struct token *token = &rule_tmp->token[rule_tmp->token_cur]; | |
32028733 | 986 | const char *attr = NULL; |
ac218d9c KS |
987 | |
988 | memset(token, 0x00, sizeof(struct token)); | |
6880b25d KS |
989 | |
990 | switch (type) { | |
6880b25d KS |
991 | case TK_M_ACTION: |
992 | case TK_M_DEVPATH: | |
993 | case TK_M_KERNEL: | |
994 | case TK_M_SUBSYSTEM: | |
995 | case TK_M_DRIVER: | |
7aeeaedc | 996 | case TK_M_WAITFOR: |
6880b25d KS |
997 | case TK_M_DEVLINK: |
998 | case TK_M_NAME: | |
999 | case TK_M_KERNELS: | |
1000 | case TK_M_SUBSYSTEMS: | |
1001 | case TK_M_DRIVERS: | |
1002 | case TK_M_PROGRAM: | |
1003 | case TK_M_IMPORT_FILE: | |
1004 | case TK_M_IMPORT_PROG: | |
1005 | case TK_M_IMPORT_PARENT: | |
1006 | case TK_M_RESULT: | |
1007 | case TK_A_OWNER: | |
1008 | case TK_A_GROUP: | |
1009 | case TK_A_MODE: | |
1010 | case TK_A_NAME: | |
1011 | case TK_A_DEVLINK: | |
1012 | case TK_A_GOTO: | |
1013 | token->key.value_off = add_string(rule_tmp->rules, value); | |
e57e7bc1 | 1014 | break; |
6880b25d KS |
1015 | case TK_M_ENV: |
1016 | case TK_M_ATTR: | |
1017 | case TK_M_ATTRS: | |
1018 | case TK_A_ATTR: | |
1019 | case TK_A_ENV: | |
32028733 | 1020 | attr = data; |
6880b25d KS |
1021 | token->key.value_off = add_string(rule_tmp->rules, value); |
1022 | token->key.attr_off = add_string(rule_tmp->rules, attr); | |
1023 | break; | |
1024 | case TK_M_TEST: | |
6880b25d | 1025 | token->key.value_off = add_string(rule_tmp->rules, value); |
ac218d9c KS |
1026 | if (data != NULL) |
1027 | token->key.mode = *(mode_t *)data; | |
6880b25d KS |
1028 | break; |
1029 | case TK_A_IGNORE_DEVICE: | |
1030 | case TK_A_STRING_ESCAPE_NONE: | |
1031 | case TK_A_STRING_ESCAPE_REPLACE: | |
1032 | case TK_A_IGNORE_REMOVE: | |
1033 | case TK_A_LAST_RULE: | |
1034 | break; | |
1035 | case TK_A_RUN: | |
1036 | token->key.value_off = add_string(rule_tmp->rules, value); | |
1037 | token->key.ignore_error = *(int *)data; | |
1038 | break; | |
46f194cb | 1039 | case TK_A_INOTIFY_WATCH: |
6880b25d | 1040 | case TK_A_NUM_FAKE_PART: |
6880b25d KS |
1041 | case TK_A_DEVLINK_PRIO: |
1042 | token->key.devlink_prio = *(int *)data; | |
1043 | break; | |
1044 | case TK_A_OWNER_ID: | |
1045 | token->key.uid = *(uid_t *)data; | |
1046 | break; | |
1047 | case TK_A_GROUP_ID: | |
1048 | token->key.gid = *(gid_t *)data; | |
1049 | break; | |
1050 | case TK_A_MODE_ID: | |
1051 | token->key.mode = *(mode_t *)data; | |
1052 | break; | |
1053 | case TK_A_EVENT_TIMEOUT: | |
1054 | token->key.event_timeout = *(int *)data; | |
1055 | break; | |
1056 | case TK_RULE: | |
d15fcce4 | 1057 | case TK_M_PARENTS_MIN: |
b0f7409f | 1058 | case TK_M_PARENTS_MAX: |
ac218d9c | 1059 | case TK_M_MAX: |
6880b25d | 1060 | case TK_END: |
ac218d9c | 1061 | case TK_UNSET: |
6880b25d KS |
1062 | err(rule_tmp->rules->udev, "wrong type %u\n", type); |
1063 | return -1; | |
724257d9 | 1064 | } |
ac218d9c | 1065 | |
fb045134 KS |
1066 | if (value != NULL && type < TK_M_MAX) { |
1067 | /* check if we need to split or call fnmatch() while matching rules */ | |
32028733 | 1068 | enum string_glob_type glob; |
fb045134 KS |
1069 | int has_split; |
1070 | int has_glob; | |
1071 | ||
1072 | has_split = (strchr(value, '|') != NULL); | |
32028733 | 1073 | has_glob = (strchr(value, '*') != NULL || strchr(value, '?') != NULL || strchr(value, '[') != NULL); |
fb045134 KS |
1074 | if (has_split && has_glob) { |
1075 | glob = GL_SPLIT_GLOB; | |
1076 | } else if (has_split) { | |
1077 | glob = GL_SPLIT; | |
1078 | } else if (has_glob) { | |
1079 | if (strcmp(value, "?*") == 0) | |
1080 | glob = GL_SOMETHING; | |
1081 | else | |
1082 | glob = GL_GLOB; | |
32028733 KS |
1083 | } else { |
1084 | glob = GL_PLAIN; | |
ac218d9c | 1085 | } |
32028733 KS |
1086 | token->key.glob = glob; |
1087 | } | |
1088 | ||
1089 | if (value != NULL && type > TK_M_MAX) { | |
1090 | /* check if assigned value has substitution chars */ | |
1091 | if (value[0] == '[') | |
1092 | token->key.subst = SB_SUBSYS; | |
1093 | else if (strchr(value, '%') != NULL || strchr(value, '$') != NULL) | |
1094 | token->key.subst = SB_FORMAT; | |
1095 | else | |
1096 | token->key.subst = SB_NONE; | |
1097 | } | |
1098 | ||
1099 | if (attr != NULL) { | |
1100 | /* check if property/attribut name has substitution chars */ | |
1101 | if (attr[0] == '[') | |
1102 | token->key.attrsubst = SB_SUBSYS; | |
1103 | else if (strchr(attr, '%') != NULL || strchr(attr, '$') != NULL) | |
1104 | token->key.attrsubst = SB_FORMAT; | |
1105 | else | |
1106 | token->key.attrsubst = SB_NONE; | |
ac218d9c KS |
1107 | } |
1108 | ||
0ef254d5 | 1109 | token->key.type = type; |
6880b25d KS |
1110 | token->key.op = op; |
1111 | rule_tmp->token_cur++; | |
1112 | if (rule_tmp->token_cur >= ARRAY_SIZE(rule_tmp->token)) { | |
1113 | err(rule_tmp->rules->udev, "temporary rule array too small\n"); | |
1114 | return -1; | |
e57e7bc1 | 1115 | } |
6880b25d KS |
1116 | return 0; |
1117 | } | |
e57e7bc1 | 1118 | |
6880b25d KS |
1119 | static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) |
1120 | { | |
1121 | unsigned int i; | |
1122 | unsigned int start = 0; | |
1123 | unsigned int end = rule_tmp->token_cur; | |
1124 | ||
1125 | for (i = 0; i < rule_tmp->token_cur; i++) { | |
ac218d9c | 1126 | enum token_type next_val = TK_UNSET; |
6880b25d KS |
1127 | unsigned int next_idx; |
1128 | unsigned int j; | |
1129 | ||
1130 | /* find smallest value */ | |
1131 | for (j = start; j < end; j++) { | |
ac218d9c | 1132 | if (rule_tmp->token[j].type == TK_UNSET) |
6880b25d | 1133 | continue; |
ac218d9c | 1134 | if (next_val == TK_UNSET || rule_tmp->token[j].type < next_val) { |
6880b25d KS |
1135 | next_val = rule_tmp->token[j].type; |
1136 | next_idx = j; | |
db6e59df | 1137 | } |
5618b561 | 1138 | } |
5618b561 | 1139 | |
6880b25d KS |
1140 | /* add token and mark done */ |
1141 | if (add_token(rules, &rule_tmp->token[next_idx]) != 0) | |
1142 | return -1; | |
ac218d9c | 1143 | rule_tmp->token[next_idx].type = TK_UNSET; |
956cf793 | 1144 | |
6880b25d KS |
1145 | /* shrink range */ |
1146 | if (next_idx == start) | |
1147 | start++; | |
1148 | if (next_idx+1 == end) | |
1149 | end--; | |
d0c8cb7d | 1150 | } |
e57e7bc1 | 1151 | return 0; |
724257d9 GKH |
1152 | } |
1153 | ||
6880b25d KS |
1154 | static int add_rule(struct udev_rules *rules, char *line, |
1155 | const char *filename, unsigned int filename_off, unsigned int lineno) | |
724257d9 | 1156 | { |
6880b25d KS |
1157 | char *linepos; |
1158 | char *attr; | |
6880b25d | 1159 | struct rule_tmp rule_tmp; |
724257d9 | 1160 | |
6880b25d KS |
1161 | memset(&rule_tmp, 0x00, sizeof(struct rule_tmp)); |
1162 | rule_tmp.rules = rules; | |
1163 | rule_tmp.rule.type = TK_RULE; | |
1164 | rule_tmp.rule.rule.filename_off = filename_off; | |
0ef254d5 | 1165 | rule_tmp.rule.rule.filename_line = lineno; |
724257d9 | 1166 | |
6880b25d | 1167 | linepos = line; |
6bf0ffe8 | 1168 | while (1) { |
6880b25d KS |
1169 | char *key; |
1170 | char *value; | |
ac218d9c | 1171 | enum operation_type op; |
6880b25d KS |
1172 | |
1173 | if (get_key(rules->udev, &linepos, &key, &op, &value) != 0) | |
6bf0ffe8 KS |
1174 | break; |
1175 | ||
bd75fddb | 1176 | if (strcmp(key, "ACTION") == 0) { |
b0f7409f | 1177 | if (op > OP_MATCH_MAX) { |
6880b25d KS |
1178 | err(rules->udev, "invalid ACTION operation\n"); |
1179 | goto invalid; | |
1180 | } | |
0ef254d5 | 1181 | rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); |
bf5d2964 KS |
1182 | continue; |
1183 | } | |
1184 | ||
bd75fddb | 1185 | if (strcmp(key, "DEVPATH") == 0) { |
b0f7409f | 1186 | if (op > OP_MATCH_MAX) { |
6880b25d KS |
1187 | err(rules->udev, "invalid DEVPATH operation\n"); |
1188 | goto invalid; | |
fd9efc00 | 1189 | } |
0ef254d5 | 1190 | rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); |
6880b25d KS |
1191 | continue; |
1192 | } | |
c7521974 | 1193 | |
bd75fddb | 1194 | if (strcmp(key, "KERNEL") == 0) { |
b0f7409f | 1195 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1196 | err(rules->udev, "invalid KERNEL operation\n"); |
1197 | goto invalid; | |
1198 | } | |
0ef254d5 | 1199 | rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); |
c7521974 KS |
1200 | continue; |
1201 | } | |
1202 | ||
bd75fddb | 1203 | if (strcmp(key, "SUBSYSTEM") == 0) { |
b0f7409f | 1204 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1205 | err(rules->udev, "invalid SUBSYSTEM operation\n"); |
1206 | goto invalid; | |
1207 | } | |
1208 | /* bus, class, subsystem events should all be the same */ | |
1209 | if (strcmp(value, "subsystem") == 0 || | |
1210 | strcmp(value, "bus") == 0 || | |
1211 | strcmp(value, "class") == 0) { | |
1212 | if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) | |
1213 | err(rules->udev, "'%s' must be specified as 'subsystem' \n" | |
1214 | "please fix it in %s:%u", value, filename, lineno); | |
0ef254d5 | 1215 | rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); |
c7521974 | 1216 | } else |
0ef254d5 | 1217 | rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); |
c7521974 KS |
1218 | continue; |
1219 | } | |
1220 | ||
bd75fddb | 1221 | if (strcmp(key, "DRIVER") == 0) { |
b0f7409f | 1222 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1223 | err(rules->udev, "invalid DRIVER operation\n"); |
1224 | goto invalid; | |
1225 | } | |
0ef254d5 | 1226 | rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); |
c7521974 KS |
1227 | continue; |
1228 | } | |
1229 | ||
bd75fddb | 1230 | if (strncmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { |
6880b25d | 1231 | attr = get_key_attribute(rules->udev, key + sizeof("ATTR")-1); |
c7521974 KS |
1232 | if (attr == NULL) { |
1233 | err(rules->udev, "error parsing ATTR attribute\n"); | |
1234 | goto invalid; | |
1235 | } | |
b0f7409f | 1236 | if (op < OP_MATCH_MAX) { |
0ef254d5 | 1237 | rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); |
6880b25d | 1238 | } else { |
0ef254d5 | 1239 | rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); |
6880b25d | 1240 | } |
c7521974 KS |
1241 | continue; |
1242 | } | |
1243 | ||
bd75fddb KS |
1244 | if (strcmp(key, "KERNELS") == 0 || |
1245 | strcmp(key, "ID") == 0) { | |
b0f7409f | 1246 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1247 | err(rules->udev, "invalid KERNELS operation\n"); |
1248 | goto invalid; | |
1249 | } | |
0ef254d5 | 1250 | rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); |
c7521974 KS |
1251 | continue; |
1252 | } | |
1253 | ||
bd75fddb KS |
1254 | if (strcmp(key, "SUBSYSTEMS") == 0 || |
1255 | strcmp(key, "BUS") == 0) { | |
b0f7409f | 1256 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1257 | err(rules->udev, "invalid SUBSYSTEMS operation\n"); |
1258 | goto invalid; | |
1259 | } | |
0ef254d5 | 1260 | rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); |
c7521974 KS |
1261 | continue; |
1262 | } | |
1263 | ||
bd75fddb | 1264 | if (strcmp(key, "DRIVERS") == 0) { |
b0f7409f | 1265 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1266 | err(rules->udev, "invalid DRIVERS operation\n"); |
1267 | goto invalid; | |
1268 | } | |
0ef254d5 | 1269 | rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); |
c7521974 KS |
1270 | continue; |
1271 | } | |
1272 | ||
bd75fddb KS |
1273 | if (strncmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 || |
1274 | strncmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) { | |
b0f7409f | 1275 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1276 | err(rules->udev, "invalid ATTRS operation\n"); |
1277 | goto invalid; | |
1278 | } | |
6880b25d | 1279 | attr = get_key_attribute(rules->udev, key + sizeof("ATTRS")-1); |
c7521974 KS |
1280 | if (attr == NULL) { |
1281 | err(rules->udev, "error parsing ATTRS attribute\n"); | |
1282 | goto invalid; | |
1283 | } | |
1284 | if (strncmp(attr, "device/", 7) == 0) | |
1285 | err(rules->udev, "the 'device' link may not be available in a future kernel, " | |
1286 | "please fix it in %s:%u", filename, lineno); | |
1287 | else if (strstr(attr, "../") != NULL) | |
1288 | err(rules->udev, "do not reference parent sysfs directories directly, " | |
1289 | "it may break with a future kernel, please fix it in %s:%u", filename, lineno); | |
0ef254d5 | 1290 | rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); |
c7521974 KS |
1291 | continue; |
1292 | } | |
1293 | ||
bd75fddb | 1294 | if (strncmp(key, "ENV{", sizeof("ENV{")-1) == 0) { |
6880b25d | 1295 | attr = get_key_attribute(rules->udev, key + sizeof("ENV")-1); |
c7521974 KS |
1296 | if (attr == NULL) { |
1297 | err(rules->udev, "error parsing ENV attribute\n"); | |
1298 | goto invalid; | |
1299 | } | |
b0f7409f | 1300 | if (op < OP_MATCH_MAX) { |
0ef254d5 | 1301 | if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) |
6880b25d KS |
1302 | goto invalid; |
1303 | } else { | |
0ef254d5 | 1304 | if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) |
6880b25d KS |
1305 | goto invalid; |
1306 | } | |
c7521974 KS |
1307 | continue; |
1308 | } | |
1309 | ||
bd75fddb | 1310 | if (strcmp(key, "PROGRAM") == 0) { |
0ef254d5 | 1311 | rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); |
c7521974 KS |
1312 | continue; |
1313 | } | |
1314 | ||
bd75fddb | 1315 | if (strcmp(key, "RESULT") == 0) { |
b0f7409f | 1316 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1317 | err(rules->udev, "invalid RESULT operation\n"); |
1318 | goto invalid; | |
1319 | } | |
0ef254d5 | 1320 | rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); |
c7521974 KS |
1321 | continue; |
1322 | } | |
1323 | ||
bd75fddb | 1324 | if (strncmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { |
6880b25d | 1325 | attr = get_key_attribute(rules->udev, key + sizeof("IMPORT")-1); |
c7521974 KS |
1326 | if (attr != NULL && strstr(attr, "program")) { |
1327 | dbg(rules->udev, "IMPORT will be executed\n"); | |
0ef254d5 | 1328 | rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); |
c7521974 KS |
1329 | } else if (attr != NULL && strstr(attr, "file")) { |
1330 | dbg(rules->udev, "IMPORT will be included as file\n"); | |
0ef254d5 | 1331 | rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); |
c7521974 KS |
1332 | } else if (attr != NULL && strstr(attr, "parent")) { |
1333 | dbg(rules->udev, "IMPORT will include the parent values\n"); | |
0ef254d5 | 1334 | rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); |
c7521974 KS |
1335 | } else { |
1336 | /* figure it out if it is executable */ | |
1337 | char file[UTIL_PATH_SIZE]; | |
1338 | char *pos; | |
1339 | struct stat statbuf; | |
1340 | ||
065db052 KS |
1341 | /* allow programs in /lib/udev called without the path */ |
1342 | if (value[0] != '/') | |
6133f343 | 1343 | util_strscpyl(file, sizeof(file), LIBEXECDIR "/", value, NULL); |
065db052 KS |
1344 | else |
1345 | util_strscpy(file, sizeof(file), value); | |
c7521974 KS |
1346 | pos = strchr(file, ' '); |
1347 | if (pos) | |
1348 | pos[0] = '\0'; | |
c7521974 | 1349 | dbg(rules->udev, "IMPORT auto mode for '%s'\n", file); |
065db052 | 1350 | if (stat(file, &statbuf) == 0 && (statbuf.st_mode & S_IXUSR)) { |
6880b25d | 1351 | dbg(rules->udev, "IMPORT will be executed (autotype)\n"); |
0ef254d5 | 1352 | rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); |
c7521974 | 1353 | } else { |
6880b25d | 1354 | dbg(rules->udev, "IMPORT will be included as file (autotype)\n"); |
0ef254d5 | 1355 | rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); |
c7521974 KS |
1356 | } |
1357 | } | |
c7521974 KS |
1358 | continue; |
1359 | } | |
1360 | ||
bd75fddb | 1361 | if (strncmp(key, "TEST", sizeof("TEST")-1) == 0) { |
6880b25d KS |
1362 | mode_t mode = 0; |
1363 | ||
b0f7409f | 1364 | if (op > OP_MATCH_MAX) { |
c7521974 KS |
1365 | err(rules->udev, "invalid TEST operation\n"); |
1366 | goto invalid; | |
1367 | } | |
6880b25d KS |
1368 | attr = get_key_attribute(rules->udev, key + sizeof("TEST")-1); |
1369 | if (attr != NULL) { | |
1370 | mode = strtol(attr, NULL, 8); | |
0ef254d5 | 1371 | rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); |
6880b25d | 1372 | } else { |
0ef254d5 | 1373 | rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); |
6880b25d | 1374 | } |
c7521974 KS |
1375 | continue; |
1376 | } | |
1377 | ||
bd75fddb | 1378 | if (strncmp(key, "RUN", sizeof("RUN")-1) == 0) { |
6880b25d KS |
1379 | int flag = 0; |
1380 | ||
1381 | attr = get_key_attribute(rules->udev, key + sizeof("RUN")-1); | |
16dd0aa9 | 1382 | if (attr != NULL && strstr(attr, "fail_event_on_error")) |
6880b25d | 1383 | flag = 1; |
0ef254d5 | 1384 | rule_add_key(&rule_tmp, TK_A_RUN, op, value, &flag); |
c7521974 KS |
1385 | continue; |
1386 | } | |
1387 | ||
bd75fddb | 1388 | if (strcmp(key, "WAIT_FOR") == 0 || strcmp(key, "WAIT_FOR_SYSFS") == 0) { |
0ef254d5 | 1389 | rule_add_key(&rule_tmp, TK_M_WAITFOR, 0, value, NULL); |
c7521974 KS |
1390 | continue; |
1391 | } | |
1392 | ||
bd75fddb | 1393 | if (strcmp(key, "LABEL") == 0) { |
6880b25d | 1394 | rule_tmp.rule.rule.label_off = add_string(rules, value); |
c7521974 KS |
1395 | continue; |
1396 | } | |
1397 | ||
bd75fddb | 1398 | if (strcmp(key, "GOTO") == 0) { |
0ef254d5 | 1399 | rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); |
c7521974 KS |
1400 | continue; |
1401 | } | |
1402 | ||
bd75fddb | 1403 | if (strncmp(key, "NAME", sizeof("NAME")-1) == 0) { |
b0f7409f | 1404 | if (op < OP_MATCH_MAX) { |
0ef254d5 | 1405 | rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); |
6880b25d KS |
1406 | } else { |
1407 | if (value[0] == '\0') | |
647f7c49 | 1408 | info(rules->udev, "name empty, node creation suppressed\n"); |
8e2470d6 KS |
1409 | else if (strcmp(value, "%k") == 0) |
1410 | err(rules->udev, "NAME=\"%%k\" is superfluous and breaks " | |
1411 | "kernel supplied names, please remove it from %s:%u\n", filename, lineno); | |
0ef254d5 | 1412 | rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); |
6880b25d KS |
1413 | attr = get_key_attribute(rules->udev, key + sizeof("NAME")-1); |
1414 | if (attr != NULL) { | |
1415 | if (strstr(attr, "all_partitions") != NULL) { | |
1416 | int num = DEFAULT_FAKE_PARTITIONS_COUNT; | |
1417 | ||
1418 | dbg(rules->udev, "creation of partition nodes requested\n"); | |
0ef254d5 | 1419 | rule_add_key(&rule_tmp, TK_A_NUM_FAKE_PART, 0, NULL, &num); |
6880b25d KS |
1420 | } |
1421 | if (strstr(attr, "ignore_remove") != NULL) { | |
1422 | dbg(rules->udev, "remove event should be ignored\n"); | |
0ef254d5 | 1423 | rule_add_key(&rule_tmp, TK_A_IGNORE_REMOVE, 0, NULL, NULL); |
6880b25d | 1424 | } |
c7521974 KS |
1425 | } |
1426 | } | |
00f98bd2 | 1427 | rule_tmp.rule.rule.flags = 1; |
c7521974 KS |
1428 | continue; |
1429 | } | |
1430 | ||
bd75fddb | 1431 | if (strcmp(key, "SYMLINK") == 0) { |
b0f7409f | 1432 | if (op < OP_MATCH_MAX) |
0ef254d5 | 1433 | rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); |
b0f7409f | 1434 | else |
0ef254d5 | 1435 | rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL); |
00f98bd2 | 1436 | rule_tmp.rule.rule.flags = 1; |
b0f7409f KS |
1437 | continue; |
1438 | } | |
c7521974 | 1439 | |
bd75fddb | 1440 | if (strcmp(key, "OWNER") == 0) { |
6880b25d KS |
1441 | uid_t uid; |
1442 | char *endptr; | |
1443 | ||
1444 | uid = strtoul(value, &endptr, 10); | |
1445 | if (endptr[0] == '\0') { | |
0ef254d5 | 1446 | rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); |
5f03ed8a | 1447 | } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { |
154a7b84 | 1448 | uid = add_uid(rules, value); |
0ef254d5 | 1449 | rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); |
055e40ed | 1450 | } else if (rules->resolve_names >= 0) { |
0ef254d5 | 1451 | rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); |
c7521974 | 1452 | } |
00f98bd2 | 1453 | rule_tmp.rule.rule.flags = 1; |
c7521974 KS |
1454 | continue; |
1455 | } | |
1456 | ||
bd75fddb | 1457 | if (strcmp(key, "GROUP") == 0) { |
6880b25d KS |
1458 | gid_t gid; |
1459 | char *endptr; | |
1460 | ||
1461 | gid = strtoul(value, &endptr, 10); | |
1462 | if (endptr[0] == '\0') { | |
0ef254d5 | 1463 | rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); |
5f03ed8a | 1464 | } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { |
154a7b84 | 1465 | gid = add_gid(rules, value); |
0ef254d5 | 1466 | rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); |
055e40ed | 1467 | } else if (rules->resolve_names >= 0) { |
0ef254d5 | 1468 | rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); |
c7521974 | 1469 | } |
00f98bd2 | 1470 | rule_tmp.rule.rule.flags = 1; |
c7521974 KS |
1471 | continue; |
1472 | } | |
1473 | ||
bd75fddb | 1474 | if (strcmp(key, "MODE") == 0) { |
6880b25d KS |
1475 | mode_t mode; |
1476 | char *endptr; | |
1477 | ||
1478 | mode = strtol(value, &endptr, 8); | |
1479 | if (endptr[0] == '\0') | |
0ef254d5 | 1480 | rule_add_key(&rule_tmp, TK_A_MODE_ID, op, NULL, &mode); |
6880b25d | 1481 | else |
0ef254d5 | 1482 | rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); |
00f98bd2 | 1483 | rule_tmp.rule.rule.flags = 1; |
c7521974 KS |
1484 | continue; |
1485 | } | |
1486 | ||
bd75fddb | 1487 | if (strcmp(key, "OPTIONS") == 0) { |
c7521974 KS |
1488 | const char *pos; |
1489 | ||
1490 | if (strstr(value, "last_rule") != NULL) { | |
1491 | dbg(rules->udev, "last rule to be applied\n"); | |
0ef254d5 | 1492 | rule_add_key(&rule_tmp, TK_A_LAST_RULE, 0, NULL, NULL); |
c7521974 KS |
1493 | } |
1494 | if (strstr(value, "ignore_device") != NULL) { | |
1495 | dbg(rules->udev, "device should be ignored\n"); | |
0ef254d5 | 1496 | rule_add_key(&rule_tmp, TK_A_IGNORE_DEVICE, 0, NULL, NULL); |
c7521974 KS |
1497 | } |
1498 | if (strstr(value, "ignore_remove") != NULL) { | |
1499 | dbg(rules->udev, "remove event should be ignored\n"); | |
0ef254d5 | 1500 | rule_add_key(&rule_tmp, TK_A_IGNORE_REMOVE, 0, NULL, NULL); |
c7521974 KS |
1501 | } |
1502 | pos = strstr(value, "link_priority="); | |
1503 | if (pos != NULL) { | |
6880b25d KS |
1504 | int prio = atoi(&pos[strlen("link_priority=")]); |
1505 | ||
0ef254d5 | 1506 | rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, 0, NULL, &prio); |
6880b25d | 1507 | dbg(rules->udev, "link priority=%i\n", prio); |
c7521974 KS |
1508 | } |
1509 | pos = strstr(value, "event_timeout="); | |
1510 | if (pos != NULL) { | |
6880b25d KS |
1511 | int tout = atoi(&pos[strlen("event_timeout=")]); |
1512 | ||
0ef254d5 | 1513 | rule_add_key(&rule_tmp, TK_A_EVENT_TIMEOUT, 0, NULL, &tout); |
214a6c79 | 1514 | dbg(rules->udev, "event timeout=%i\n", tout); |
c7521974 KS |
1515 | } |
1516 | pos = strstr(value, "string_escape="); | |
1517 | if (pos != NULL) { | |
1518 | pos = &pos[strlen("string_escape=")]; | |
1519 | if (strncmp(pos, "none", strlen("none")) == 0) | |
0ef254d5 | 1520 | rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, 0, NULL, NULL); |
c7521974 | 1521 | else if (strncmp(pos, "replace", strlen("replace")) == 0) |
0ef254d5 | 1522 | rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_REPLACE, 0, NULL, NULL); |
c7521974 KS |
1523 | } |
1524 | if (strstr(value, "all_partitions") != NULL) { | |
6880b25d KS |
1525 | int num = DEFAULT_FAKE_PARTITIONS_COUNT; |
1526 | ||
0ef254d5 | 1527 | rule_add_key(&rule_tmp, TK_A_NUM_FAKE_PART, 0, NULL, &num); |
c7521974 | 1528 | dbg(rules->udev, "creation of partition nodes requested\n"); |
c7521974 | 1529 | } |
46f194cb | 1530 | pos = strstr(value, "nowatch"); |
bd284db1 | 1531 | if (pos != NULL) { |
46f194cb KS |
1532 | const int off = 0; |
1533 | ||
1534 | rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, 0, NULL, &off); | |
1535 | dbg(rules->udev, "inotify watch of device disabled\n"); | |
1536 | } else { | |
1537 | pos = strstr(value, "watch"); | |
1538 | if (pos != NULL) { | |
1539 | const int on = 1; | |
1540 | ||
1541 | rule_add_key(&rule_tmp, TK_A_INOTIFY_WATCH, 0, NULL, &on); | |
1542 | dbg(rules->udev, "inotify watch of device requested\n"); | |
1543 | } | |
bd284db1 | 1544 | } |
c7521974 KS |
1545 | continue; |
1546 | } | |
c7521974 KS |
1547 | err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); |
1548 | } | |
1549 | ||
6880b25d | 1550 | /* add rule token */ |
3f3aa9f5 | 1551 | rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; |
6880b25d KS |
1552 | if (add_token(rules, &rule_tmp.rule) != 0) |
1553 | goto invalid; | |
c7521974 | 1554 | |
6880b25d KS |
1555 | /* add tokens to list, sorted by type */ |
1556 | if (sort_token(rules, &rule_tmp) != 0) | |
1557 | goto invalid; | |
3f3aa9f5 | 1558 | |
6880b25d | 1559 | return 0; |
c7521974 KS |
1560 | invalid: |
1561 | err(rules->udev, "invalid rule '%s:%u'\n", filename, lineno); | |
1562 | return -1; | |
1563 | } | |
1564 | ||
0ef254d5 | 1565 | static int parse_file(struct udev_rules *rules, const char *filename, unsigned short filename_off) |
c7521974 KS |
1566 | { |
1567 | FILE *f; | |
6880b25d | 1568 | unsigned int first_token; |
c7521974 | 1569 | char line[UTIL_LINE_SIZE]; |
6880b25d KS |
1570 | int line_nr = 0; |
1571 | unsigned int i; | |
c7521974 | 1572 | |
c7521974 KS |
1573 | info(rules->udev, "reading '%s' as rules file\n", filename); |
1574 | ||
1575 | f = fopen(filename, "r"); | |
1576 | if (f == NULL) | |
1577 | return -1; | |
1578 | ||
6880b25d KS |
1579 | first_token = rules->token_cur; |
1580 | ||
c7521974 | 1581 | while(fgets(line, sizeof(line), f) != NULL) { |
c7521974 KS |
1582 | char *key; |
1583 | size_t len; | |
1584 | ||
1585 | /* skip whitespace */ | |
1586 | line_nr++; | |
1587 | key = line; | |
1588 | while (isspace(key[0])) | |
1589 | key++; | |
1590 | ||
1591 | /* comment */ | |
1592 | if (key[0] == '#') | |
1593 | continue; | |
1594 | ||
1595 | len = strlen(line); | |
1596 | if (len < 3) | |
1597 | continue; | |
1598 | ||
1599 | /* continue reading if backslash+newline is found */ | |
1600 | while (line[len-2] == '\\') { | |
1601 | if (fgets(&line[len-2], (sizeof(line)-len)+2, f) == NULL) | |
1602 | break; | |
1603 | line_nr++; | |
1604 | len = strlen(line); | |
1605 | } | |
1606 | ||
1607 | if (len+1 >= sizeof(line)) { | |
1608 | err(rules->udev, "line too long '%s':%u, ignored\n", filename, line_nr); | |
1609 | continue; | |
1610 | } | |
6880b25d | 1611 | add_rule(rules, key, filename, filename_off, line_nr); |
c7521974 KS |
1612 | } |
1613 | fclose(f); | |
1614 | ||
6880b25d KS |
1615 | /* link GOTOs to LABEL rules in this file to be able to fast-forward */ |
1616 | for (i = first_token+1; i < rules->token_cur; i++) { | |
1617 | if (rules->tokens[i].type == TK_A_GOTO) { | |
1618 | char *label = &rules->buf[rules->tokens[i].key.value_off]; | |
1619 | unsigned int j; | |
1620 | ||
1621 | for (j = i+1; j < rules->token_cur; j++) { | |
1622 | if (rules->tokens[j].type != TK_RULE) | |
1623 | continue; | |
1624 | if (rules->tokens[j].rule.label_off == 0) | |
1625 | continue; | |
1626 | if (strcmp(label, &rules->buf[rules->tokens[j].rule.label_off]) != 0) | |
1627 | continue; | |
1628 | rules->tokens[i].key.rule_goto = j; | |
0c377989 | 1629 | break; |
c7521974 | 1630 | } |
6880b25d KS |
1631 | if (rules->tokens[i].key.rule_goto == 0) |
1632 | err(rules->udev, "GOTO '%s' has no matching label in: '%s'\n", label, filename); | |
c7521974 KS |
1633 | } |
1634 | } | |
1635 | return 0; | |
1636 | } | |
1637 | ||
1638 | static int add_matching_files(struct udev *udev, struct udev_list_node *file_list, const char *dirname, const char *suffix) | |
1639 | { | |
c7521974 KS |
1640 | DIR *dir; |
1641 | char filename[UTIL_PATH_SIZE]; | |
1642 | ||
1643 | dbg(udev, "open directory '%s'\n", dirname); | |
1644 | dir = opendir(dirname); | |
1645 | if (dir == NULL) { | |
92e52e2f | 1646 | info(udev, "unable to open '%s': %m\n", dirname); |
c7521974 KS |
1647 | return -1; |
1648 | } | |
1649 | ||
1650 | while (1) { | |
78230c0d KS |
1651 | struct dirent *dent; |
1652 | ||
1653 | dent = readdir(dir); | |
1654 | if (dent == NULL || dent->d_name[0] == '\0') | |
c7521974 KS |
1655 | break; |
1656 | ||
78230c0d | 1657 | if (dent->d_name[0] == '.') |
c7521974 KS |
1658 | continue; |
1659 | ||
1660 | /* look for file matching with specified suffix */ | |
1661 | if (suffix != NULL) { | |
1662 | const char *ext; | |
1663 | ||
78230c0d | 1664 | ext = strrchr(dent->d_name, '.'); |
c7521974 KS |
1665 | if (ext == NULL) |
1666 | continue; | |
1667 | if (strcmp(ext, suffix) != 0) | |
1668 | continue; | |
1669 | } | |
e6c1a2bd KS |
1670 | util_strscpyl(filename, sizeof(filename), dirname, "/", dent->d_name, NULL); |
1671 | dbg(udev, "put file '%s' into list\n", filename); | |
c7521974 KS |
1672 | udev_list_entry_add(udev, file_list, filename, NULL, 1, 1); |
1673 | } | |
1674 | ||
1675 | closedir(dir); | |
1676 | return 0; | |
1677 | } | |
1678 | ||
d7ddce18 | 1679 | struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) |
c7521974 | 1680 | { |
d7ddce18 | 1681 | struct udev_rules *rules; |
c7521974 | 1682 | struct stat statbuf; |
c7521974 KS |
1683 | struct udev_list_node file_list; |
1684 | struct udev_list_entry *file_loop, *file_tmp; | |
6880b25d | 1685 | struct token end_token; |
c7521974 | 1686 | |
6880b25d | 1687 | rules = malloc(sizeof(struct udev_rules)); |
d7ddce18 | 1688 | if (rules == NULL) |
6880b25d KS |
1689 | return NULL; |
1690 | memset(rules, 0x00, sizeof(struct udev_rules)); | |
c7521974 KS |
1691 | rules->udev = udev; |
1692 | rules->resolve_names = resolve_names; | |
1693 | udev_list_init(&file_list); | |
1694 | ||
6880b25d KS |
1695 | /* init token array and string buffer */ |
1696 | rules->tokens = malloc(PREALLOC_TOKEN * sizeof(struct token)); | |
34d6a259 KS |
1697 | if (rules->tokens == NULL) |
1698 | return NULL; | |
1699 | rules->token_max = PREALLOC_TOKEN; | |
bcf44d55 | 1700 | |
6880b25d | 1701 | rules->buf = malloc(PREALLOC_STRBUF); |
34d6a259 KS |
1702 | if (rules->buf == NULL) |
1703 | return NULL; | |
1704 | rules->buf_max = PREALLOC_STRBUF; | |
1705 | /* offset 0 is always '\0' */ | |
1706 | rules->buf[0] = '\0'; | |
1707 | rules->buf_cur = 1; | |
86b57788 KS |
1708 | dbg(udev, "prealloc %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", |
1709 | rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); | |
6880b25d | 1710 | |
bcf44d55 KS |
1711 | rules->trie_nodes = malloc(PREALLOC_TRIE * sizeof(struct trie_node)); |
1712 | if (rules->trie_nodes == NULL) | |
1713 | return NULL; | |
1714 | rules->trie_nodes_max = PREALLOC_TRIE; | |
949075db | 1715 | /* offset 0 is the trie root, with an empty string */ |
023ed7b0 | 1716 | memset(rules->trie_nodes, 0x00, sizeof(struct trie_node)); |
bcf44d55 KS |
1717 | rules->trie_nodes_cur = 1; |
1718 | ||
c7521974 KS |
1719 | if (udev_get_rules_path(udev) != NULL) { |
1720 | /* custom rules location for testing */ | |
1721 | add_matching_files(udev, &file_list, udev_get_rules_path(udev), ".rules"); | |
1722 | } else { | |
0ef254d5 | 1723 | char filename[PATH_MAX]; |
c7521974 KS |
1724 | struct udev_list_node sort_list; |
1725 | struct udev_list_entry *sort_loop, *sort_tmp; | |
1726 | ||
1727 | /* read user/custom rules */ | |
1728 | add_matching_files(udev, &file_list, SYSCONFDIR "/udev/rules.d", ".rules"); | |
1729 | ||
1730 | /* read dynamic/temporary rules */ | |
065db052 | 1731 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL); |
c7521974 | 1732 | if (stat(filename, &statbuf) != 0) { |
54808d77 | 1733 | util_create_path(udev, filename); |
c7521974 KS |
1734 | udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755); |
1735 | mkdir(filename, 0755); | |
1736 | udev_selinux_resetfscreatecon(udev); | |
1737 | } | |
1738 | udev_list_init(&sort_list); | |
1739 | add_matching_files(udev, &sort_list, filename, ".rules"); | |
1740 | ||
1741 | /* read default rules */ | |
6133f343 | 1742 | add_matching_files(udev, &sort_list, LIBEXECDIR "/rules.d", ".rules"); |
c7521974 KS |
1743 | |
1744 | /* sort all rules files by basename into list of files */ | |
1745 | udev_list_entry_foreach_safe(sort_loop, sort_tmp, udev_list_get_entry(&sort_list)) { | |
1746 | const char *sort_name = udev_list_entry_get_name(sort_loop); | |
1747 | const char *sort_base = strrchr(sort_name, '/'); | |
1748 | ||
1749 | if (sort_base == NULL) | |
1750 | continue; | |
1e78dcbe | 1751 | /* sort entry into existing list */ |
c7521974 KS |
1752 | udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) { |
1753 | const char *file_name = udev_list_entry_get_name(file_loop); | |
1754 | const char *file_base = strrchr(file_name, '/'); | |
1755 | ||
1756 | if (file_base == NULL) | |
1757 | continue; | |
1758 | if (strcmp(file_base, sort_base) == 0) { | |
1759 | info(udev, "rule file basename '%s' already added, ignoring '%s'\n", | |
1760 | file_name, sort_name); | |
1e78dcbe | 1761 | udev_list_entry_delete(sort_loop); |
c7521974 KS |
1762 | sort_loop = NULL; |
1763 | break; | |
1764 | } | |
1e78dcbe KS |
1765 | if (strcmp(file_base, sort_base) > 0) { |
1766 | /* found later file, insert before */ | |
1767 | udev_list_entry_remove(sort_loop); | |
1768 | udev_list_entry_insert_before(sort_loop, file_loop); | |
1769 | sort_loop = NULL; | |
c7521974 | 1770 | break; |
1e78dcbe | 1771 | } |
c7521974 | 1772 | } |
1e78dcbe KS |
1773 | /* current file already handled */ |
1774 | if (sort_loop == NULL) | |
1775 | continue; | |
1776 | /* no later file, append to end of list */ | |
1777 | udev_list_entry_remove(sort_loop); | |
1778 | udev_list_entry_append(sort_loop, &file_list); | |
c7521974 KS |
1779 | } |
1780 | } | |
1781 | ||
0ef254d5 KS |
1782 | /* add all filenames to the string buffer */ |
1783 | udev_list_entry_foreach(file_loop, udev_list_get_entry(&file_list)) { | |
1784 | const char *filename = udev_list_entry_get_name(file_loop); | |
1785 | unsigned int filename_off; | |
1786 | ||
1787 | filename_off = add_string(rules, filename); | |
1788 | /* the offset in the rule is limited to unsigned short */ | |
1789 | if (filename_off < USHRT_MAX) | |
1790 | udev_list_entry_set_flag(file_loop, filename_off); | |
1791 | } | |
1792 | ||
c7521974 KS |
1793 | /* parse list of files */ |
1794 | udev_list_entry_foreach_safe(file_loop, file_tmp, udev_list_get_entry(&file_list)) { | |
0ef254d5 KS |
1795 | const char *filename = udev_list_entry_get_name(file_loop); |
1796 | unsigned int filename_off = udev_list_entry_get_flag(file_loop); | |
c7521974 | 1797 | |
0ef254d5 KS |
1798 | if (stat(filename, &statbuf) == 0 && statbuf.st_size > 0) |
1799 | parse_file(rules, filename, filename_off); | |
c7521974 | 1800 | else |
24d1fea8 | 1801 | err(udev, "can not read '%s'\n", filename); |
1e78dcbe | 1802 | udev_list_entry_delete(file_loop); |
c7521974 | 1803 | } |
6880b25d KS |
1804 | |
1805 | memset(&end_token, 0x00, sizeof(struct token)); | |
1806 | end_token.type = TK_END; | |
1807 | add_token(rules, &end_token); | |
1808 | ||
154a7b84 | 1809 | /* shrink allocated token and string buffer */ |
6880b25d KS |
1810 | if (rules->token_cur < rules->token_max) { |
1811 | struct token *tokens; | |
1812 | ||
1813 | tokens = realloc(rules->tokens, rules->token_cur * sizeof(struct token)); | |
1814 | if (tokens != NULL || rules->token_cur == 0) { | |
1815 | rules->tokens = tokens; | |
1816 | rules->token_max = rules->token_cur; | |
1817 | } | |
1818 | } | |
1819 | if (rules->buf_cur < rules->buf_max) { | |
1820 | char *buf; | |
1821 | ||
1822 | buf = realloc(rules->buf, rules->buf_cur); | |
1823 | if (buf != NULL || rules->buf_cur == 0) { | |
1824 | rules->buf = buf; | |
1825 | rules->buf_max = rules->buf_cur; | |
1826 | } | |
1827 | } | |
0dd9f015 | 1828 | info(udev, "rules use %zu bytes tokens (%u * %zu bytes), %zu bytes buffer\n", |
6880b25d | 1829 | rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->buf_max); |
023ed7b0 KS |
1830 | info(udev, "temporary index used %zu bytes (%u * %zu bytes)\n", |
1831 | rules->trie_nodes_cur * sizeof(struct trie_node), | |
1832 | rules->trie_nodes_cur, sizeof(struct trie_node)); | |
1449a55d AJ |
1833 | |
1834 | /* cleanup trie */ | |
bcf44d55 KS |
1835 | free(rules->trie_nodes); |
1836 | rules->trie_nodes = NULL; | |
1837 | rules->trie_nodes_cur = 0; | |
1838 | rules->trie_nodes_max = 0; | |
6880b25d | 1839 | |
154a7b84 KS |
1840 | /* cleanup uid/gid cache */ |
1841 | free(rules->uids); | |
1842 | rules->uids = NULL; | |
1843 | rules->uids_cur = 0; | |
1844 | rules->uids_max = 0; | |
1845 | free(rules->gids); | |
1846 | rules->gids = NULL; | |
1847 | rules->gids_cur = 0; | |
1848 | rules->gids_max = 0; | |
1849 | ||
6880b25d | 1850 | dump_rules(rules); |
d7ddce18 | 1851 | return rules; |
c7521974 KS |
1852 | } |
1853 | ||
d7ddce18 | 1854 | void udev_rules_unref(struct udev_rules *rules) |
c7521974 | 1855 | { |
d7ddce18 KS |
1856 | if (rules == NULL) |
1857 | return; | |
6880b25d KS |
1858 | free(rules->tokens); |
1859 | free(rules->buf); | |
bcf44d55 | 1860 | free(rules->trie_nodes); |
154a7b84 KS |
1861 | free(rules->uids); |
1862 | free(rules->gids); | |
d7ddce18 | 1863 | free(rules); |
c7521974 | 1864 | } |
6880b25d KS |
1865 | |
1866 | static int match_key(struct udev_rules *rules, struct token *token, const char *val) | |
1867 | { | |
6880b25d KS |
1868 | char *key_value = &rules->buf[token->key.value_off]; |
1869 | char *pos; | |
1870 | int match = 0; | |
1871 | ||
1872 | if (val == NULL) | |
1873 | val = ""; | |
1874 | ||
ac218d9c KS |
1875 | switch (token->key.glob) { |
1876 | case GL_PLAIN: | |
1877 | match = (strcmp(key_value, val) == 0); | |
1878 | break; | |
1879 | case GL_GLOB: | |
1880 | match = (fnmatch(key_value, val, 0) == 0); | |
1881 | break; | |
1882 | case GL_SPLIT: | |
1883 | { | |
91a75e4a KS |
1884 | const char *split; |
1885 | size_t len; | |
6880b25d | 1886 | |
91a75e4a KS |
1887 | split = &rules->buf[token->key.value_off]; |
1888 | len = strlen(val); | |
1889 | while (1) { | |
1890 | const char *next; | |
1891 | ||
1892 | next = strchr(split, '|'); | |
1893 | if (next != NULL) { | |
1894 | size_t matchlen = (size_t)(next - split); | |
1895 | ||
1896 | match = (matchlen == len && strncmp(split, val, matchlen) == 0); | |
1897 | if (match) | |
1898 | break; | |
1899 | } else { | |
1900 | match = (strcmp(split, val) == 0); | |
ac218d9c | 1901 | break; |
91a75e4a KS |
1902 | } |
1903 | split = &next[1]; | |
6880b25d | 1904 | } |
ac218d9c | 1905 | break; |
6880b25d | 1906 | } |
ac218d9c KS |
1907 | case GL_SPLIT_GLOB: |
1908 | { | |
1909 | char value[UTIL_PATH_SIZE]; | |
1910 | ||
065db052 | 1911 | util_strscpy(value, sizeof(value), &rules->buf[token->key.value_off]); |
ac218d9c KS |
1912 | key_value = value; |
1913 | while (key_value != NULL) { | |
1914 | pos = strchr(key_value, '|'); | |
1915 | if (pos != NULL) { | |
1916 | pos[0] = '\0'; | |
1917 | pos = &pos[1]; | |
1918 | } | |
5d6a1fa6 | 1919 | dbg(rules->udev, "match %s '%s' <-> '%s'\n", token_str(token->type), key_value, val); |
ac218d9c KS |
1920 | match = (fnmatch(key_value, val, 0) == 0); |
1921 | if (match) | |
1922 | break; | |
1923 | key_value = pos; | |
1924 | } | |
1925 | break; | |
1926 | } | |
db463fd3 KS |
1927 | case GL_SOMETHING: |
1928 | match = (val[0] != '\0'); | |
1929 | break; | |
ac218d9c KS |
1930 | case GL_UNSET: |
1931 | return -1; | |
6880b25d KS |
1932 | } |
1933 | ||
b0f7409f | 1934 | if (match && (token->key.op == OP_MATCH)) { |
5d6a1fa6 | 1935 | dbg(rules->udev, "%s is true (matching value)\n", token_str(token->type)); |
6880b25d KS |
1936 | return 0; |
1937 | } | |
b0f7409f | 1938 | if (!match && (token->key.op == OP_NOMATCH)) { |
5d6a1fa6 | 1939 | dbg(rules->udev, "%s is true (non-matching value)\n", token_str(token->type)); |
6880b25d KS |
1940 | return 0; |
1941 | } | |
5d6a1fa6 | 1942 | dbg(rules->udev, "%s is not true\n", token_str(token->type)); |
6880b25d KS |
1943 | return -1; |
1944 | } | |
1945 | ||
1946 | static int match_attr(struct udev_rules *rules, struct udev_device *dev, struct udev_event *event, struct token *cur) | |
1947 | { | |
32028733 KS |
1948 | const char *name; |
1949 | char nbuf[UTIL_NAME_SIZE]; | |
1950 | const char *value; | |
1951 | char vbuf[UTIL_NAME_SIZE]; | |
6880b25d KS |
1952 | size_t len; |
1953 | ||
32028733 KS |
1954 | name = &rules->buf[cur->key.attr_off]; |
1955 | switch (cur->key.attrsubst) { | |
1956 | case SB_FORMAT: | |
1957 | udev_event_apply_format(event, name, nbuf, sizeof(nbuf)); | |
1958 | name = nbuf; | |
1959 | /* fall through */ | |
1960 | case SB_NONE: | |
1961 | value = udev_device_get_sysattr_value(dev, name); | |
1962 | if (value == NULL) | |
aeb53ca3 | 1963 | return -1; |
32028733 KS |
1964 | break; |
1965 | case SB_SUBSYS: | |
1966 | if (util_resolve_subsys_kernel(event->udev, name, vbuf, sizeof(vbuf), 1) != 0) | |
1967 | return -1; | |
1968 | value = vbuf; | |
1969 | break; | |
1970 | default: | |
1971 | return -1; | |
6880b25d | 1972 | } |
6880b25d | 1973 | |
32028733 KS |
1974 | /* remove trailing whitespace, if not asked to match for it */ |
1975 | len = strlen(value); | |
1976 | if (len > 0 && isspace(value[len-1])) { | |
1977 | const char *key_value; | |
1978 | size_t klen; | |
1979 | ||
1980 | key_value = &rules->buf[cur->key.value_off]; | |
1981 | klen = strlen(key_value); | |
1982 | if (klen > 0 && !isspace(key_value[klen-1])) { | |
1983 | if (value != vbuf) { | |
1984 | util_strscpy(vbuf, sizeof(vbuf), value); | |
1985 | value = vbuf; | |
1986 | } | |
1987 | while (len > 0 && isspace(vbuf[--len])) | |
1988 | vbuf[len] = '\0'; | |
1989 | dbg(rules->udev, "removed trailing whitespace from '%s'\n", value); | |
1990 | } | |
6880b25d | 1991 | } |
32028733 | 1992 | |
6880b25d KS |
1993 | return match_key(rules, cur, value); |
1994 | } | |
1995 | ||
1996 | enum escape_type { | |
1997 | ESCAPE_UNSET, | |
1998 | ESCAPE_NONE, | |
1999 | ESCAPE_REPLACE, | |
2000 | }; | |
2001 | ||
2002 | int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event) | |
2003 | { | |
6880b25d | 2004 | struct token *cur; |
34d6a259 | 2005 | struct token *rule; |
3e5c7595 | 2006 | enum escape_type esc = ESCAPE_UNSET; |
00f98bd2 | 2007 | int can_set_name; |
6880b25d KS |
2008 | |
2009 | if (rules->tokens == NULL) | |
2010 | return -1; | |
2011 | ||
00f98bd2 KS |
2012 | can_set_name = ((strcmp(udev_device_get_action(event->dev), "add") == 0 || |
2013 | strcmp(udev_device_get_action(event->dev), "change") == 0) && | |
2014 | (major(udev_device_get_devnum(event->dev)) > 0 || | |
2015 | strcmp(udev_device_get_subsystem(event->dev), "net") == 0)); | |
2016 | ||
6880b25d KS |
2017 | /* loop through token list, match, run actions or forward to next rule */ |
2018 | cur = &rules->tokens[0]; | |
34d6a259 | 2019 | rule = cur; |
6270756c | 2020 | while (1) { |
6880b25d KS |
2021 | dump_token(rules, cur); |
2022 | switch (cur->type) { | |
2023 | case TK_RULE: | |
2024 | /* current rule */ | |
2025 | rule = cur; | |
00f98bd2 KS |
2026 | /* possibly skip rules which want to set NAME, SYMLINK, OWNER, GROUP, MODE */ |
2027 | if (!can_set_name && rule->rule.flags) | |
0c057e9c | 2028 | goto nomatch; |
6880b25d KS |
2029 | esc = ESCAPE_UNSET; |
2030 | break; | |
6880b25d KS |
2031 | case TK_M_ACTION: |
2032 | if (match_key(rules, cur, udev_device_get_action(event->dev)) != 0) | |
2033 | goto nomatch; | |
2034 | break; | |
2035 | case TK_M_DEVPATH: | |
2036 | if (match_key(rules, cur, udev_device_get_devpath(event->dev)) != 0) | |
2037 | goto nomatch; | |
2038 | break; | |
2039 | case TK_M_KERNEL: | |
2040 | if (match_key(rules, cur, udev_device_get_sysname(event->dev)) != 0) | |
2041 | goto nomatch; | |
2042 | break; | |
2043 | case TK_M_DEVLINK: | |
2044 | { | |
2045 | size_t devlen = strlen(udev_get_dev_path(event->udev))+1; | |
2046 | struct udev_list_entry *list_entry; | |
2047 | int match = 0; | |
2048 | ||
2049 | udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(event->dev)) { | |
2050 | const char *devlink; | |
2051 | ||
2052 | devlink = &udev_list_entry_get_name(list_entry)[devlen]; | |
2053 | if (match_key(rules, cur, devlink) == 0) { | |
2054 | match = 1; | |
2055 | break; | |
2056 | } | |
2057 | } | |
2058 | if (!match) | |
2059 | goto nomatch; | |
2060 | break; | |
2061 | } | |
2062 | case TK_M_NAME: | |
2063 | if (match_key(rules, cur, event->name) != 0) | |
2064 | goto nomatch; | |
2065 | break; | |
2066 | case TK_M_ENV: | |
2067 | { | |
6880b25d KS |
2068 | const char *key_name = &rules->buf[cur->key.attr_off]; |
2069 | const char *value; | |
2070 | ||
3d7b2831 | 2071 | value = udev_device_get_property_value(event->dev, key_name); |
6880b25d KS |
2072 | if (value == NULL) { |
2073 | dbg(event->udev, "ENV{%s} is not set, treat as empty\n", key_name); | |
2074 | value = ""; | |
2075 | } | |
2076 | if (match_key(rules, cur, value)) | |
2077 | goto nomatch; | |
2078 | break; | |
2079 | } | |
2080 | case TK_M_SUBSYSTEM: | |
2081 | if (match_key(rules, cur, udev_device_get_subsystem(event->dev)) != 0) | |
2082 | goto nomatch; | |
2083 | break; | |
2084 | case TK_M_DRIVER: | |
2085 | if (match_key(rules, cur, udev_device_get_driver(event->dev)) != 0) | |
2086 | goto nomatch; | |
2087 | break; | |
7aeeaedc AJ |
2088 | case TK_M_WAITFOR: |
2089 | { | |
2090 | char filename[UTIL_PATH_SIZE]; | |
2091 | int found; | |
2092 | ||
065db052 | 2093 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); |
7aeeaedc | 2094 | found = (wait_for_file(event->dev, filename, 10) == 0); |
b0f7409f | 2095 | if (!found && (cur->key.op != OP_NOMATCH)) |
7aeeaedc AJ |
2096 | goto nomatch; |
2097 | break; | |
2098 | } | |
6880b25d KS |
2099 | case TK_M_ATTR: |
2100 | if (match_attr(rules, event->dev, event, cur) != 0) | |
2101 | goto nomatch; | |
2102 | break; | |
2103 | case TK_M_KERNELS: | |
2104 | case TK_M_SUBSYSTEMS: | |
2105 | case TK_M_DRIVERS: | |
2106 | case TK_M_ATTRS: | |
2107 | { | |
2108 | struct token *next; | |
2109 | ||
2110 | /* get whole sequence of parent matches */ | |
2111 | next = cur; | |
d15fcce4 | 2112 | while (next->type > TK_M_PARENTS_MIN && next->type < TK_M_PARENTS_MAX) |
6880b25d KS |
2113 | next++; |
2114 | ||
2115 | /* loop over parents */ | |
2116 | event->dev_parent = event->dev; | |
2117 | while (1) { | |
2118 | struct token *key; | |
2119 | ||
2120 | dbg(event->udev, "parent: '%s'\n", udev_device_get_syspath(event->dev_parent)); | |
2121 | /* loop over sequence of parent match keys */ | |
2122 | for (key = cur; key < next; key++ ) { | |
2123 | dump_token(rules, key); | |
2124 | switch(key->type) { | |
2125 | case TK_M_KERNELS: | |
2126 | if (match_key(rules, key, udev_device_get_sysname(event->dev_parent)) != 0) | |
2127 | goto try_parent; | |
2128 | break; | |
2129 | case TK_M_SUBSYSTEMS: | |
2130 | if (match_key(rules, key, udev_device_get_subsystem(event->dev_parent)) != 0) | |
2131 | goto try_parent; | |
2132 | break; | |
2133 | case TK_M_DRIVERS: | |
2134 | if (match_key(rules, key, udev_device_get_driver(event->dev_parent)) != 0) | |
2135 | goto try_parent; | |
2136 | break; | |
2137 | case TK_M_ATTRS: | |
2138 | if (match_attr(rules, event->dev_parent, event, key) != 0) | |
2139 | goto try_parent; | |
2140 | break; | |
2141 | default: | |
2142 | goto nomatch; | |
2143 | } | |
2144 | dbg(event->udev, "parent key matched\n"); | |
2145 | } | |
2146 | dbg(event->udev, "all parent keys matched\n"); | |
2147 | /* all keys matched */ | |
2148 | break; | |
2149 | ||
2150 | try_parent: | |
2151 | event->dev_parent = udev_device_get_parent(event->dev_parent); | |
2152 | if (event->dev_parent == NULL) | |
2153 | goto nomatch; | |
2154 | } | |
2155 | /* move behind our sequence of parent match keys */ | |
2156 | cur = next; | |
2157 | continue; | |
2158 | } | |
2159 | case TK_M_TEST: | |
2160 | { | |
2161 | char filename[UTIL_PATH_SIZE]; | |
2162 | struct stat statbuf; | |
2163 | int match; | |
2164 | ||
065db052 KS |
2165 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], filename, sizeof(filename)); |
2166 | if (util_resolve_subsys_kernel(event->udev, filename, filename, sizeof(filename), 0) != 0) { | |
6880b25d KS |
2167 | if (filename[0] != '/') { |
2168 | char tmp[UTIL_PATH_SIZE]; | |
2169 | ||
065db052 KS |
2170 | util_strscpy(tmp, sizeof(tmp), filename); |
2171 | util_strscpyl(filename, sizeof(filename), | |
2172 | udev_device_get_syspath(event->dev), "/", tmp, NULL); | |
6880b25d | 2173 | } |
065db052 | 2174 | } |
6880b25d KS |
2175 | attr_subst_subdir(filename, sizeof(filename)); |
2176 | ||
2177 | match = (stat(filename, &statbuf) == 0); | |
86b57788 | 2178 | dbg(event->udev, "'%s' %s", filename, match ? "exists\n" : "does not exist\n"); |
6880b25d KS |
2179 | if (match && cur->key.mode > 0) { |
2180 | match = ((statbuf.st_mode & cur->key.mode) > 0); | |
86b57788 KS |
2181 | dbg(event->udev, "'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, |
2182 | match ? "matches" : "does not match", cur->key.mode); | |
6880b25d | 2183 | } |
b0f7409f | 2184 | if (match && cur->key.op == OP_NOMATCH) |
6880b25d | 2185 | goto nomatch; |
b0f7409f | 2186 | if (!match && cur->key.op == OP_MATCH) |
6880b25d KS |
2187 | goto nomatch; |
2188 | break; | |
2189 | } | |
2190 | case TK_M_PROGRAM: | |
2191 | { | |
2192 | char program[UTIL_PATH_SIZE]; | |
2193 | char **envp; | |
2194 | char result[UTIL_PATH_SIZE]; | |
2195 | ||
40fd3bc8 KS |
2196 | free(event->program_result); |
2197 | event->program_result = NULL; | |
065db052 | 2198 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], program, sizeof(program)); |
6880b25d | 2199 | envp = udev_device_get_properties_envp(event->dev); |
08e7f229 KS |
2200 | info(event->udev, "PROGRAM '%s' %s:%u\n", |
2201 | program, | |
2202 | &rules->buf[rule->rule.filename_off], | |
2203 | rule->rule.filename_line); | |
6880b25d | 2204 | if (util_run_program(event->udev, program, envp, result, sizeof(result), NULL) != 0) { |
b0f7409f | 2205 | if (cur->key.op != OP_NOMATCH) |
6880b25d KS |
2206 | goto nomatch; |
2207 | } else { | |
2208 | int count; | |
2209 | ||
2210 | util_remove_trailing_chars(result, '\n'); | |
2211 | if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { | |
92f43136 | 2212 | count = udev_util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT); |
6880b25d KS |
2213 | if (count > 0) |
2214 | info(event->udev, "%i character(s) replaced\n" , count); | |
2215 | } | |
40fd3bc8 KS |
2216 | event->program_result = strdup(result); |
2217 | dbg(event->udev, "storing result '%s'\n", event->program_result); | |
b0f7409f | 2218 | if (cur->key.op == OP_NOMATCH) |
6880b25d KS |
2219 | goto nomatch; |
2220 | } | |
2221 | break; | |
2222 | } | |
2223 | case TK_M_IMPORT_FILE: | |
2224 | { | |
2225 | char import[UTIL_PATH_SIZE]; | |
2226 | ||
065db052 | 2227 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); |
6880b25d | 2228 | if (import_file_into_properties(event->dev, import) != 0) |
b0f7409f | 2229 | if (cur->key.op != OP_NOMATCH) |
6880b25d KS |
2230 | goto nomatch; |
2231 | break; | |
2232 | } | |
2233 | case TK_M_IMPORT_PROG: | |
2234 | { | |
2235 | char import[UTIL_PATH_SIZE]; | |
2236 | ||
065db052 | 2237 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); |
08e7f229 KS |
2238 | info(event->udev, "IMPORT '%s' %s:%u\n", |
2239 | import, | |
2240 | &rules->buf[rule->rule.filename_off], | |
2241 | rule->rule.filename_line); | |
6880b25d | 2242 | if (import_program_into_properties(event->dev, import) != 0) |
b0f7409f | 2243 | if (cur->key.op != OP_NOMATCH) |
6880b25d KS |
2244 | goto nomatch; |
2245 | break; | |
2246 | } | |
2247 | case TK_M_IMPORT_PARENT: | |
2248 | { | |
2249 | char import[UTIL_PATH_SIZE]; | |
2250 | ||
065db052 | 2251 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], import, sizeof(import)); |
6880b25d | 2252 | if (import_parent_into_properties(event->dev, import) != 0) |
b0f7409f | 2253 | if (cur->key.op != OP_NOMATCH) |
6880b25d KS |
2254 | goto nomatch; |
2255 | break; | |
2256 | } | |
2257 | case TK_M_RESULT: | |
2258 | if (match_key(rules, cur, event->program_result) != 0) | |
2259 | goto nomatch; | |
2260 | break; | |
2261 | ||
2262 | case TK_A_IGNORE_DEVICE: | |
2263 | event->ignore_device = 1; | |
2264 | return 0; | |
2265 | break; | |
2266 | case TK_A_STRING_ESCAPE_NONE: | |
2267 | esc = ESCAPE_NONE; | |
2268 | break; | |
2269 | case TK_A_STRING_ESCAPE_REPLACE: | |
2270 | esc = ESCAPE_REPLACE; | |
2271 | break; | |
2272 | case TK_A_NUM_FAKE_PART: | |
2273 | if (strcmp(udev_device_get_subsystem(event->dev), "block") != 0) | |
2274 | break; | |
2275 | if (udev_device_get_sysnum(event->dev) != NULL) | |
2276 | break; | |
2277 | udev_device_set_num_fake_partitions(event->dev, cur->key.num_fake_part); | |
2278 | break; | |
bd284db1 | 2279 | case TK_A_INOTIFY_WATCH: |
46f194cb | 2280 | event->inotify_watch = cur->key.watch; |
bd284db1 | 2281 | break; |
6880b25d KS |
2282 | case TK_A_DEVLINK_PRIO: |
2283 | udev_device_set_devlink_priority(event->dev, cur->key.devlink_prio); | |
2284 | break; | |
2285 | case TK_A_OWNER: | |
2286 | { | |
2287 | char owner[UTIL_NAME_SIZE]; | |
2288 | ||
2289 | if (event->owner_final) | |
2290 | break; | |
b0f7409f | 2291 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d | 2292 | event->owner_final = 1; |
065db052 | 2293 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], owner, sizeof(owner)); |
6880b25d | 2294 | event->uid = util_lookup_user(event->udev, owner); |
0ef254d5 KS |
2295 | info(event->udev, "OWNER %u %s:%u\n", |
2296 | event->uid, | |
2297 | &rules->buf[rule->rule.filename_off], | |
2298 | rule->rule.filename_line); | |
6880b25d KS |
2299 | break; |
2300 | } | |
2301 | case TK_A_GROUP: | |
2302 | { | |
2303 | char group[UTIL_NAME_SIZE]; | |
2304 | ||
2305 | if (event->group_final) | |
2306 | break; | |
b0f7409f | 2307 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d | 2308 | event->group_final = 1; |
065db052 | 2309 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], group, sizeof(group)); |
6880b25d | 2310 | event->gid = util_lookup_group(event->udev, group); |
0ef254d5 KS |
2311 | info(event->udev, "GROUP %u %s:%u\n", |
2312 | event->gid, | |
2313 | &rules->buf[rule->rule.filename_off], | |
2314 | rule->rule.filename_line); | |
6880b25d KS |
2315 | break; |
2316 | } | |
2317 | case TK_A_MODE: | |
2318 | { | |
2319 | char mode[UTIL_NAME_SIZE]; | |
2320 | char *endptr; | |
2321 | ||
2322 | if (event->mode_final) | |
2323 | break; | |
b0f7409f | 2324 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d | 2325 | event->mode_final = 1; |
065db052 | 2326 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], mode, sizeof(mode)); |
6880b25d KS |
2327 | event->mode = strtol(mode, &endptr, 8); |
2328 | if (endptr[0] != '\0') { | |
2329 | err(event->udev, "invalide mode '%s' set default mode 0660\n", mode); | |
2330 | event->mode = 0660; | |
2331 | } | |
0ef254d5 KS |
2332 | info(event->udev, "MODE %#o %s:%u\n", |
2333 | event->mode, | |
2334 | &rules->buf[rule->rule.filename_off], | |
2335 | rule->rule.filename_line); | |
6880b25d KS |
2336 | break; |
2337 | } | |
2338 | case TK_A_OWNER_ID: | |
2339 | if (event->owner_final) | |
2340 | break; | |
b0f7409f | 2341 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d KS |
2342 | event->owner_final = 1; |
2343 | event->uid = cur->key.uid; | |
0ef254d5 KS |
2344 | info(event->udev, "OWNER %u %s:%u\n", |
2345 | event->uid, | |
2346 | &rules->buf[rule->rule.filename_off], | |
2347 | rule->rule.filename_line); | |
6880b25d KS |
2348 | break; |
2349 | case TK_A_GROUP_ID: | |
2350 | if (event->group_final) | |
2351 | break; | |
b0f7409f | 2352 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d KS |
2353 | event->group_final = 1; |
2354 | event->gid = cur->key.gid; | |
0ef254d5 KS |
2355 | info(event->udev, "GROUP %u %s:%u\n", |
2356 | event->gid, | |
2357 | &rules->buf[rule->rule.filename_off], | |
2358 | rule->rule.filename_line); | |
6880b25d KS |
2359 | break; |
2360 | case TK_A_MODE_ID: | |
2361 | if (event->mode_final) | |
2362 | break; | |
b0f7409f | 2363 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d KS |
2364 | event->mode_final = 1; |
2365 | event->mode = cur->key.mode; | |
0ef254d5 KS |
2366 | info(event->udev, "MODE %#o %s:%u\n", |
2367 | event->mode, | |
2368 | &rules->buf[rule->rule.filename_off], | |
2369 | rule->rule.filename_line); | |
6880b25d KS |
2370 | break; |
2371 | case TK_A_ENV: | |
2372 | { | |
2373 | const char *name = &rules->buf[cur->key.attr_off]; | |
2374 | char *value = &rules->buf[cur->key.value_off]; | |
2375 | ||
2376 | if (value[0] != '\0') { | |
2377 | char temp_value[UTIL_NAME_SIZE]; | |
2378 | struct udev_list_entry *entry; | |
2379 | ||
065db052 | 2380 | udev_event_apply_format(event, value, temp_value, sizeof(temp_value)); |
6880b25d | 2381 | entry = udev_device_add_property(event->dev, name, temp_value); |
b25a9454 KS |
2382 | /* store in db, skip private keys */ |
2383 | if (name[0] != '.') | |
2384 | udev_list_entry_set_flag(entry, 1); | |
6880b25d KS |
2385 | } else { |
2386 | udev_device_add_property(event->dev, name, NULL); | |
2387 | } | |
2388 | break; | |
2389 | } | |
2390 | case TK_A_NAME: | |
2391 | { | |
2392 | const char *name = &rules->buf[cur->key.value_off]; | |
b99028c9 | 2393 | char name_str[UTIL_PATH_SIZE]; |
6880b25d KS |
2394 | int count; |
2395 | ||
2396 | if (event->name_final) | |
2397 | break; | |
b0f7409f | 2398 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d | 2399 | event->name_final = 1; |
065db052 | 2400 | udev_event_apply_format(event, name, name_str, sizeof(name_str)); |
6880b25d | 2401 | if (esc == ESCAPE_UNSET || esc == ESCAPE_REPLACE) { |
92f43136 | 2402 | count = udev_util_replace_chars(name_str, "/"); |
6880b25d KS |
2403 | if (count > 0) |
2404 | info(event->udev, "%i character(s) replaced\n", count); | |
2405 | } | |
10b2d011 KS |
2406 | free(event->name); |
2407 | event->name = strdup(name_str); | |
2408 | info(event->udev, "NAME '%s' %s:%u\n", | |
2409 | event->name, | |
2410 | &rules->buf[rule->rule.filename_off], | |
2411 | rule->rule.filename_line); | |
6880b25d KS |
2412 | break; |
2413 | } | |
2414 | case TK_A_DEVLINK: | |
2415 | { | |
2416 | char temp[UTIL_PATH_SIZE]; | |
2417 | char filename[UTIL_PATH_SIZE]; | |
2418 | char *pos, *next; | |
2419 | int count = 0; | |
2420 | ||
2421 | if (event->devlink_final) | |
2422 | break; | |
548459e9 KS |
2423 | if (major(udev_device_get_devnum(event->dev)) == 0) |
2424 | break; | |
b0f7409f | 2425 | if (cur->key.op == OP_ASSIGN_FINAL) |
6880b25d | 2426 | event->devlink_final = 1; |
b0f7409f | 2427 | if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) |
6880b25d KS |
2428 | udev_device_cleanup_devlinks_list(event->dev); |
2429 | ||
2430 | /* allow multiple symlinks separated by spaces */ | |
065db052 | 2431 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], temp, sizeof(temp)); |
6880b25d | 2432 | if (esc == ESCAPE_UNSET) |
92f43136 | 2433 | count = udev_util_replace_chars(temp, "/ "); |
6880b25d | 2434 | else if (esc == ESCAPE_REPLACE) |
92f43136 | 2435 | count = udev_util_replace_chars(temp, "/"); |
6880b25d KS |
2436 | if (count > 0) |
2437 | info(event->udev, "%i character(s) replaced\n" , count); | |
2438 | dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp); | |
2439 | pos = temp; | |
2440 | while (isspace(pos[0])) | |
2441 | pos++; | |
2442 | next = strchr(pos, ' '); | |
2443 | while (next) { | |
2444 | next[0] = '\0'; | |
0ef254d5 KS |
2445 | info(event->udev, "LINK '%s' %s:%u\n", |
2446 | pos, | |
2447 | &rules->buf[rule->rule.filename_off], | |
2448 | rule->rule.filename_line); | |
065db052 | 2449 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); |
6880b25d KS |
2450 | udev_device_add_devlink(event->dev, filename); |
2451 | while (isspace(next[1])) | |
2452 | next++; | |
2453 | pos = &next[1]; | |
2454 | next = strchr(pos, ' '); | |
2455 | } | |
2456 | if (pos[0] != '\0') { | |
0ef254d5 KS |
2457 | info(event->udev, "LINK '%s' %s:%u\n", |
2458 | pos, | |
2459 | &rules->buf[rule->rule.filename_off], | |
2460 | rule->rule.filename_line); | |
065db052 | 2461 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(event->udev), "/", pos, NULL); |
6880b25d KS |
2462 | udev_device_add_devlink(event->dev, filename); |
2463 | } | |
2464 | } | |
2465 | break; | |
2466 | case TK_A_EVENT_TIMEOUT: | |
2467 | udev_device_set_event_timeout(event->dev, cur->key.event_timeout); | |
2468 | break; | |
2469 | case TK_A_IGNORE_REMOVE: | |
2470 | udev_device_set_ignore_remove(event->dev, 1); | |
2471 | break; | |
2472 | case TK_A_ATTR: | |
2473 | { | |
2474 | const char *key_name = &rules->buf[cur->key.attr_off]; | |
2475 | char attr[UTIL_PATH_SIZE]; | |
2476 | char value[UTIL_NAME_SIZE]; | |
2477 | FILE *f; | |
2478 | ||
065db052 KS |
2479 | if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) |
2480 | util_strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); | |
6880b25d KS |
2481 | attr_subst_subdir(attr, sizeof(attr)); |
2482 | ||
065db052 | 2483 | udev_event_apply_format(event, &rules->buf[cur->key.value_off], value, sizeof(value)); |
86b57788 KS |
2484 | info(event->udev, "ATTR '%s' writing '%s' %s:%u\n", attr, value, |
2485 | &rules->buf[rule->rule.filename_off], | |
2486 | rule->rule.filename_line); | |
6880b25d KS |
2487 | f = fopen(attr, "w"); |
2488 | if (f != NULL) { | |
ec2dd02e KS |
2489 | if (fprintf(f, "%s", value) <= 0) |
2490 | err(event->udev, "error writing ATTR{%s}: %m\n", attr); | |
6880b25d KS |
2491 | fclose(f); |
2492 | } else { | |
2493 | err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr); | |
2494 | } | |
2495 | break; | |
2496 | } | |
2497 | case TK_A_RUN: | |
2498 | { | |
2499 | struct udev_list_entry *list_entry; | |
2500 | ||
b0f7409f | 2501 | if (cur->key.op == OP_ASSIGN || cur->key.op == OP_ASSIGN_FINAL) |
6880b25d | 2502 | udev_list_cleanup_entries(event->udev, &event->run_list); |
0ef254d5 KS |
2503 | info(event->udev, "RUN '%s' %s:%u\n", |
2504 | &rules->buf[cur->key.value_off], | |
2505 | &rules->buf[rule->rule.filename_off], | |
2506 | rule->rule.filename_line); | |
6880b25d KS |
2507 | list_entry = udev_list_entry_add(event->udev, &event->run_list, |
2508 | &rules->buf[cur->key.value_off], NULL, 1, 0); | |
2509 | if (cur->key.ignore_error) | |
2510 | udev_list_entry_set_flag(list_entry, 1); | |
2511 | break; | |
2512 | } | |
2513 | case TK_A_GOTO: | |
0c377989 KS |
2514 | if (cur->key.rule_goto == 0) |
2515 | break; | |
6880b25d KS |
2516 | cur = &rules->tokens[cur->key.rule_goto]; |
2517 | continue; | |
2518 | case TK_A_LAST_RULE: | |
6270756c | 2519 | case TK_END: |
11ae7578 | 2520 | return 0; |
6880b25d | 2521 | |
d15fcce4 | 2522 | case TK_M_PARENTS_MIN: |
b0f7409f | 2523 | case TK_M_PARENTS_MAX: |
ac218d9c | 2524 | case TK_M_MAX: |
ac218d9c | 2525 | case TK_UNSET: |
6880b25d KS |
2526 | err(rules->udev, "wrong type %u\n", cur->type); |
2527 | goto nomatch; | |
2528 | } | |
2529 | ||
2530 | cur++; | |
2531 | continue; | |
2532 | nomatch: | |
2533 | /* fast-forward to next rule */ | |
3f3aa9f5 AJ |
2534 | cur = rule + rule->rule.token_count; |
2535 | dbg(rules->udev, "forward to rule: %u\n", | |
2536 | (unsigned int) (cur - rules->tokens)); | |
6880b25d | 2537 | } |
6880b25d | 2538 | } |