]>
Commit | Line | Data |
---|---|---|
19feb351 | 1 | /* |
274812b5 | 2 | * Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com> |
27b77df4 | 3 | * Copyright (C) 2003-2006 Kay Sievers <kay.sievers@vrfy.org> |
19feb351 | 4 | * |
19feb351 GKH |
5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation version 2 of the License. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along | |
15 | * with this program; if not, write to the Free Software Foundation, Inc., | |
27b77df4 | 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19feb351 GKH |
17 | * |
18 | */ | |
19 | ||
19feb351 GKH |
20 | #include <stddef.h> |
21 | #include <stdlib.h> | |
22 | #include <string.h> | |
23 | #include <stdio.h> | |
19feb351 GKH |
24 | #include <ctype.h> |
25 | #include <unistd.h> | |
e41245cb | 26 | #include <sys/stat.h> |
19feb351 GKH |
27 | #include <errno.h> |
28 | ||
29 | #include "udev.h" | |
e5e322bc | 30 | #include "udev_rules.h" |
19feb351 | 31 | |
e41016d3 | 32 | |
8bd41f36 | 33 | void udev_rules_iter_init(struct udev_rules *rules) |
6bf0ffe8 | 34 | { |
8bd41f36 KS |
35 | dbg("bufsize=%zi", rules->bufsize); |
36 | rules->current = 0; | |
6bf0ffe8 KS |
37 | } |
38 | ||
8bd41f36 | 39 | struct udev_rule *udev_rules_iter_next(struct udev_rules *rules) |
6bf0ffe8 KS |
40 | { |
41 | static struct udev_rule *rule; | |
42 | ||
8bd41f36 KS |
43 | if (!rules) |
44 | return NULL; | |
6bf0ffe8 | 45 | |
8bd41f36 | 46 | dbg("current=%zi", rules->current); |
594dd610 KS |
47 | if (rules->current >= rules->bufsize) { |
48 | dbg("no more rules"); | |
8bd41f36 | 49 | return NULL; |
594dd610 | 50 | } |
19feb351 | 51 | |
8bd41f36 KS |
52 | /* get next rule */ |
53 | rule = (struct udev_rule *) (rules->buf + rules->current); | |
54 | rules->current += sizeof(struct udev_rule) + rule->bufsize; | |
55 | ||
56 | return rule; | |
19feb351 | 57 | } |
61219c75 | 58 | |
594dd610 KS |
59 | struct udev_rule *udev_rules_iter_label(struct udev_rules *rules, const char *label) |
60 | { | |
61 | static struct udev_rule *rule; | |
62 | ||
63 | next: | |
64 | dbg("current=%zi", rules->current); | |
65 | if (rules->current >= rules->bufsize) { | |
66 | dbg("no more rules"); | |
67 | return NULL; | |
68 | } | |
69 | rule = (struct udev_rule *) (rules->buf + rules->current); | |
70 | ||
71 | if (strcmp(&rule->buf[rule->label.val_off], label) != 0) { | |
72 | dbg("moving forward, looking for label '%s'", label); | |
73 | rules->current += sizeof(struct udev_rule) + rule->bufsize; | |
74 | goto next; | |
75 | } | |
76 | ||
77 | dbg("found label '%s'", label); | |
78 | return rule; | |
79 | } | |
80 | ||
28ce66de KS |
81 | static int get_key(char **line, char **key, enum key_operation *operation, char **value) |
82 | { | |
83 | char *linepos; | |
84 | char *temp; | |
85 | ||
86 | linepos = *line; | |
13d11705 | 87 | if (linepos == NULL && linepos[0] == '\0') |
28ce66de KS |
88 | return -1; |
89 | ||
90 | /* skip whitespace */ | |
91 | while (isspace(linepos[0]) || linepos[0] == ',') | |
92 | linepos++; | |
93 | ||
94 | /* get the key */ | |
13d11705 KS |
95 | if (linepos[0] == '\0') |
96 | return -1; | |
28ce66de | 97 | *key = linepos; |
13d11705 | 98 | |
28ce66de KS |
99 | while (1) { |
100 | linepos++; | |
101 | if (linepos[0] == '\0') | |
102 | return -1; | |
103 | if (isspace(linepos[0])) | |
104 | break; | |
105 | if (linepos[0] == '=') | |
106 | break; | |
7db33ac1 TK |
107 | if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) |
108 | if (linepos[1] == '=') | |
109 | break; | |
28ce66de KS |
110 | } |
111 | ||
112 | /* remember end of key */ | |
113 | temp = linepos; | |
114 | ||
115 | /* skip whitespace after key */ | |
116 | while (isspace(linepos[0])) | |
117 | linepos++; | |
13d11705 KS |
118 | if (linepos[0] == '\0') |
119 | return -1; | |
28ce66de KS |
120 | |
121 | /* get operation type */ | |
122 | if (linepos[0] == '=' && linepos[1] == '=') { | |
123 | *operation = KEY_OP_MATCH; | |
124 | linepos += 2; | |
125 | dbg("operator=match"); | |
126 | } else if (linepos[0] == '!' && linepos[1] == '=') { | |
127 | *operation = KEY_OP_NOMATCH; | |
128 | linepos += 2; | |
129 | dbg("operator=nomatch"); | |
130 | } else if (linepos[0] == '+' && linepos[1] == '=') { | |
131 | *operation = KEY_OP_ADD; | |
132 | linepos += 2; | |
133 | dbg("operator=add"); | |
134 | } else if (linepos[0] == '=') { | |
135 | *operation = KEY_OP_ASSIGN; | |
136 | linepos++; | |
137 | dbg("operator=assign"); | |
c974742b KS |
138 | } else if (linepos[0] == ':' && linepos[1] == '=') { |
139 | *operation = KEY_OP_ASSIGN_FINAL; | |
140 | linepos += 2; | |
141 | dbg("operator=assign_final"); | |
28ce66de KS |
142 | } else |
143 | return -1; | |
144 | ||
145 | /* terminate key */ | |
146 | temp[0] = '\0'; | |
147 | dbg("key='%s'", *key); | |
148 | ||
149 | /* skip whitespace after operator */ | |
150 | while (isspace(linepos[0])) | |
151 | linepos++; | |
13d11705 KS |
152 | if (linepos[0] == '\0') |
153 | return -1; | |
28ce66de KS |
154 | |
155 | /* get the value*/ | |
156 | if (linepos[0] == '"') | |
157 | linepos++; | |
158 | else | |
159 | return -1; | |
160 | *value = linepos; | |
161 | ||
162 | temp = strchr(linepos, '"'); | |
163 | if (!temp) | |
164 | return -1; | |
165 | temp[0] = '\0'; | |
166 | temp++; | |
167 | dbg("value='%s'", *value); | |
168 | ||
169 | /* move line to next key */ | |
170 | *line = temp; | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
d4a32aa2 | 175 | /* extract possible KEY{attr} */ |
bb738647 KS |
176 | static char *get_key_attribute(char *str) |
177 | { | |
178 | char *pos; | |
179 | char *attr; | |
180 | ||
bb738647 KS |
181 | attr = strchr(str, '{'); |
182 | if (attr != NULL) { | |
183 | attr++; | |
184 | pos = strchr(attr, '}'); | |
185 | if (pos == NULL) { | |
6b493a20 | 186 | err("missing closing brace for format"); |
bb738647 KS |
187 | return NULL; |
188 | } | |
189 | pos[0] = '\0'; | |
190 | dbg("attribute='%s'", attr); | |
191 | return attr; | |
192 | } | |
193 | ||
194 | return NULL; | |
195 | } | |
196 | ||
8bd41f36 KS |
197 | static int add_rule_key(struct udev_rule *rule, struct key *key, |
198 | enum key_operation operation, const char *value) | |
19feb351 | 199 | { |
8bd41f36 KS |
200 | size_t val_len = strnlen(value, PATH_SIZE); |
201 | ||
202 | key->operation = operation; | |
203 | ||
204 | key->val_off = rule->bufsize; | |
205 | strlcpy(rule->buf + rule->bufsize, value, val_len+1); | |
206 | rule->bufsize += val_len+1; | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | static int add_rule_key_pair(struct udev_rule *rule, struct key_pairs *pairs, | |
212 | enum key_operation operation, const char *key, const char *value) | |
213 | { | |
214 | size_t key_len = strnlen(key, PATH_SIZE); | |
215 | ||
216 | if (pairs->count >= PAIRS_MAX) { | |
d0c8cb7d | 217 | err("skip, too many keys of the same type in a single rule"); |
8bd41f36 KS |
218 | return -1; |
219 | } | |
220 | ||
221 | add_rule_key(rule, &pairs->keys[pairs->count].key, operation, value); | |
222 | ||
223 | /* add the key-name of the pair */ | |
224 | pairs->keys[pairs->count].key_name_off = rule->bufsize; | |
225 | strlcpy(rule->buf + rule->bufsize, key, key_len+1); | |
226 | rule->bufsize += key_len+1; | |
227 | ||
228 | pairs->count++; | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
d59c84ef | 233 | static int add_to_rules(struct udev_rules *rules, char *line, const char *filename, unsigned int lineno) |
8bd41f36 | 234 | { |
3df3c93e | 235 | char buf[sizeof(struct udev_rule) + LINE_SIZE]; |
8bd41f36 KS |
236 | struct udev_rule *rule; |
237 | size_t rule_size; | |
238 | int valid; | |
28ce66de | 239 | char *linepos; |
bb738647 | 240 | char *attr; |
422d5bec | 241 | size_t padding; |
7b5206d6 | 242 | int physdev = 0; |
8bd41f36 | 243 | int retval; |
19feb351 | 244 | |
3df3c93e KS |
245 | memset(buf, 0x00, sizeof(buf)); |
246 | rule = (struct udev_rule *) buf; | |
8bd41f36 KS |
247 | linepos = line; |
248 | valid = 0; | |
19feb351 | 249 | |
3df3c93e | 250 | /* get all the keys */ |
8bd41f36 KS |
251 | while (1) { |
252 | char *key; | |
253 | char *value; | |
254 | enum key_operation operation = KEY_OP_UNSET; | |
9f8dfa19 | 255 | |
8bd41f36 KS |
256 | retval = get_key(&linepos, &key, &operation, &value); |
257 | if (retval) | |
258 | break; | |
19feb351 | 259 | |
95776dc6 KS |
260 | if (strcasecmp(key, "ACTION") == 0) { |
261 | if (operation != KEY_OP_MATCH && | |
262 | operation != KEY_OP_NOMATCH) { | |
263 | err("invalid ACTION operation"); | |
264 | goto invalid; | |
265 | } | |
266 | add_rule_key(rule, &rule->action, operation, value); | |
594dd610 KS |
267 | valid = 1; |
268 | continue; | |
269 | } | |
270 | ||
95776dc6 KS |
271 | if (strcasecmp(key, "DEVPATH") == 0) { |
272 | if (operation != KEY_OP_MATCH && | |
273 | operation != KEY_OP_NOMATCH) { | |
274 | err("invalid DEVPATH operation"); | |
275 | goto invalid; | |
276 | } | |
277 | add_rule_key(rule, &rule->devpath, operation, value); | |
594dd610 KS |
278 | valid = 1; |
279 | continue; | |
280 | } | |
281 | ||
8bd41f36 | 282 | if (strcasecmp(key, "KERNEL") == 0) { |
d59c84ef KS |
283 | if (operation != KEY_OP_MATCH && |
284 | operation != KEY_OP_NOMATCH) { | |
285 | err("invalid KERNEL operation"); | |
286 | goto invalid; | |
287 | } | |
95776dc6 | 288 | add_rule_key(rule, &rule->kernel, operation, value); |
8bd41f36 | 289 | valid = 1; |
3e441450 | 290 | continue; |
291 | } | |
093bf8f4 | 292 | |
8bd41f36 | 293 | if (strcasecmp(key, "SUBSYSTEM") == 0) { |
d59c84ef KS |
294 | if (operation != KEY_OP_MATCH && |
295 | operation != KEY_OP_NOMATCH) { | |
296 | err("invalid SUBSYSTEM operation"); | |
297 | goto invalid; | |
298 | } | |
f7e34eb3 KS |
299 | /* bus, class, subsystem events should all be the same */ |
300 | if (strcmp(value, "subsystem") == 0 || | |
301 | strcmp(value, "bus") == 0 || | |
302 | strcmp(value, "class") == 0) { | |
303 | if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) | |
304 | err("'%s' must be specified as 'subsystem' " | |
305 | "please fix it in %s:%u", value, filename, lineno); | |
306 | add_rule_key(rule, &rule->subsystem, operation, "subsystem|class|bus"); | |
307 | } else | |
308 | add_rule_key(rule, &rule->subsystem, operation, value); | |
8bd41f36 | 309 | valid = 1; |
3db7fa27 | 310 | continue; |
8bd41f36 | 311 | } |
3e441450 | 312 | |
95776dc6 | 313 | if (strcasecmp(key, "DRIVER") == 0) { |
915fde17 KS |
314 | if (operation != KEY_OP_MATCH && |
315 | operation != KEY_OP_NOMATCH) { | |
316 | err("invalid DRIVER operation"); | |
317 | goto invalid; | |
318 | } | |
273bebdb | 319 | add_rule_key(rule, &rule->driver, operation, value); |
8bd41f36 | 320 | valid = 1; |
19feb351 | 321 | continue; |
9f8dfa19 | 322 | } |
19feb351 | 323 | |
8bb39322 | 324 | if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { |
95776dc6 KS |
325 | attr = get_key_attribute(key + sizeof("ATTR")-1); |
326 | if (attr == NULL) { | |
38895e57 KS |
327 | err("error parsing ATTR attribute"); |
328 | goto invalid; | |
95776dc6 | 329 | } |
38895e57 KS |
330 | if (add_rule_key_pair(rule, &rule->attr, operation, attr, value) != 0) |
331 | goto invalid; | |
8bd41f36 KS |
332 | valid = 1; |
333 | continue; | |
334 | } | |
3e441450 | 335 | |
95776dc6 KS |
336 | if (strcasecmp(key, "KERNELS") == 0 || |
337 | strcasecmp(key, "ID") == 0) { | |
d59c84ef KS |
338 | if (operation != KEY_OP_MATCH && |
339 | operation != KEY_OP_NOMATCH) { | |
95776dc6 | 340 | err("invalid KERNELS operation"); |
d59c84ef KS |
341 | goto invalid; |
342 | } | |
95776dc6 | 343 | add_rule_key(rule, &rule->kernels, operation, value); |
8bd41f36 KS |
344 | valid = 1; |
345 | continue; | |
346 | } | |
28ce66de | 347 | |
c472055c | 348 | if (strcasecmp(key, "SUBSYSTEMS") == 0 || |
95776dc6 | 349 | strcasecmp(key, "BUS") == 0) { |
d59c84ef KS |
350 | if (operation != KEY_OP_MATCH && |
351 | operation != KEY_OP_NOMATCH) { | |
95776dc6 | 352 | err("invalid SUBSYSTEMS operation"); |
d59c84ef KS |
353 | goto invalid; |
354 | } | |
95776dc6 | 355 | add_rule_key(rule, &rule->subsystems, operation, value); |
8bd41f36 KS |
356 | valid = 1; |
357 | continue; | |
358 | } | |
3d150dfb | 359 | |
95776dc6 | 360 | if (strcasecmp(key, "DRIVERS") == 0) { |
b1c4f377 KS |
361 | if (operation != KEY_OP_MATCH && |
362 | operation != KEY_OP_NOMATCH) { | |
95776dc6 | 363 | err("invalid DRIVERS operation"); |
b1c4f377 KS |
364 | goto invalid; |
365 | } | |
95776dc6 | 366 | add_rule_key(rule, &rule->drivers, operation, value); |
8bd41f36 KS |
367 | valid = 1; |
368 | continue; | |
369 | } | |
5e39f90b | 370 | |
8bb39322 KS |
371 | if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 || |
372 | strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) { | |
273bebdb KS |
373 | if (operation != KEY_OP_MATCH && |
374 | operation != KEY_OP_NOMATCH) { | |
375 | err("invalid ATTRS operation"); | |
376 | goto invalid; | |
377 | } | |
95776dc6 KS |
378 | attr = get_key_attribute(key + sizeof("ATTRS")-1); |
379 | if (attr == NULL) { | |
38895e57 KS |
380 | err("error parsing ATTRS attribute"); |
381 | goto invalid; | |
95776dc6 | 382 | } |
091a660d KS |
383 | if (strncmp(attr, "device/", 7) == 0) |
384 | err("the 'device' link is deprecated and will be removed from a future kernel, " | |
385 | "please fix it in %s:%u", filename, lineno); | |
d419e962 | 386 | else if (strstr(attr, "../") != NULL) |
091a660d KS |
387 | err("do not reference parent sysfs directories directly, that may break with a future kernel, " |
388 | "please fix it in %s:%u", filename, lineno); | |
38895e57 KS |
389 | if (add_rule_key_pair(rule, &rule->attrs, operation, attr, value) != 0) |
390 | goto invalid; | |
b2fe4b9a KS |
391 | valid = 1; |
392 | continue; | |
393 | } | |
394 | ||
8bb39322 | 395 | if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) { |
8bd41f36 KS |
396 | attr = get_key_attribute(key + sizeof("ENV")-1); |
397 | if (attr == NULL) { | |
398 | err("error parsing ENV attribute"); | |
38895e57 | 399 | goto invalid; |
5e39f90b | 400 | } |
091a660d | 401 | if (strncmp(attr, "PHYSDEV", 7) == 0) |
7b5206d6 | 402 | physdev = 1; |
38895e57 KS |
403 | if (add_rule_key_pair(rule, &rule->env, operation, attr, value) != 0) |
404 | goto invalid; | |
8bd41f36 KS |
405 | valid = 1; |
406 | continue; | |
407 | } | |
5e39f90b | 408 | |
95776dc6 KS |
409 | if (strcasecmp(key, "PROGRAM") == 0) { |
410 | add_rule_key(rule, &rule->program, operation, value); | |
411 | valid = 1; | |
412 | continue; | |
413 | } | |
414 | ||
415 | if (strcasecmp(key, "RESULT") == 0) { | |
416 | if (operation != KEY_OP_MATCH && | |
417 | operation != KEY_OP_NOMATCH) { | |
418 | err("invalid RESULT operation"); | |
419 | goto invalid; | |
420 | } | |
421 | add_rule_key(rule, &rule->result, operation, value); | |
422 | valid = 1; | |
423 | continue; | |
424 | } | |
425 | ||
8bd41f36 KS |
426 | if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { |
427 | attr = get_key_attribute(key + sizeof("IMPORT")-1); | |
274da2b2 | 428 | if (attr != NULL && strstr(attr, "program")) { |
8bd41f36 | 429 | dbg("IMPORT will be executed"); |
0bfb84e1 | 430 | rule->import_type = IMPORT_PROGRAM; |
274da2b2 | 431 | } else if (attr != NULL && strstr(attr, "file")) { |
8bd41f36 | 432 | dbg("IMPORT will be included as file"); |
0bfb84e1 | 433 | rule->import_type = IMPORT_FILE; |
274da2b2 | 434 | } else if (attr != NULL && strstr(attr, "parent")) { |
0bfb84e1 KS |
435 | dbg("IMPORT will include the parent values"); |
436 | rule->import_type = IMPORT_PARENT; | |
8bd41f36 KS |
437 | } else { |
438 | /* figure it out if it is executable */ | |
439 | char file[PATH_SIZE]; | |
440 | char *pos; | |
254d6d3c | 441 | struct stat statbuf; |
8bd41f36 KS |
442 | |
443 | strlcpy(file, value, sizeof(file)); | |
444 | pos = strchr(file, ' '); | |
445 | if (pos) | |
446 | pos[0] = '\0'; | |
d2f605c8 KS |
447 | |
448 | /* allow programs in /lib/udev called without the path */ | |
449 | if (strchr(file, '/') == NULL) { | |
450 | strlcpy(file, "/lib/udev/", sizeof(file)); | |
451 | strlcat(file, value, sizeof(file)); | |
452 | pos = strchr(file, ' '); | |
453 | if (pos) | |
454 | pos[0] = '\0'; | |
455 | } | |
456 | ||
8bd41f36 | 457 | dbg("IMPORT auto mode for '%s'", file); |
254d6d3c | 458 | if (!lstat(file, &statbuf) && (statbuf.st_mode & S_IXUSR)) { |
0bfb84e1 KS |
459 | dbg("IMPORT is executable, will be executed (autotype)"); |
460 | rule->import_type = IMPORT_PROGRAM; | |
461 | } else { | |
462 | dbg("IMPORT is not executable, will be included as file (autotype)"); | |
463 | rule->import_type = IMPORT_FILE; | |
8bd41f36 | 464 | } |
bf5d2964 | 465 | } |
8bd41f36 KS |
466 | add_rule_key(rule, &rule->import, operation, value); |
467 | valid = 1; | |
468 | continue; | |
469 | } | |
bf5d2964 | 470 | |
953249a3 | 471 | if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) { |
a506e629 KS |
472 | if (operation != KEY_OP_MATCH && |
473 | operation != KEY_OP_NOMATCH) { | |
474 | err("invalid TEST operation"); | |
475 | goto invalid; | |
476 | } | |
953249a3 KS |
477 | attr = get_key_attribute(key + sizeof("TEST")-1); |
478 | if (attr != NULL) | |
479 | rule->test_mode_mask = strtol(attr, NULL, 8); | |
480 | add_rule_key(rule, &rule->test, operation, value); | |
481 | valid = 1; | |
482 | continue; | |
483 | } | |
484 | ||
4110664d KS |
485 | if (strncasecmp(key, "RUN", sizeof("RUN")-1) == 0) { |
486 | attr = get_key_attribute(key + sizeof("RUN")-1); | |
274da2b2 KS |
487 | if (attr != NULL) { |
488 | if (strstr(attr, "ignore_error")) | |
489 | rule->run_ignore_error = 1; | |
490 | } | |
95776dc6 | 491 | add_rule_key(rule, &rule->run, operation, value); |
8bd41f36 KS |
492 | valid = 1; |
493 | continue; | |
494 | } | |
d94df232 | 495 | |
95776dc6 KS |
496 | if (strcasecmp(key, "WAIT_FOR_SYSFS") == 0) { |
497 | add_rule_key(rule, &rule->wait_for_sysfs, operation, value); | |
8bd41f36 KS |
498 | valid = 1; |
499 | continue; | |
500 | } | |
d94df232 | 501 | |
95776dc6 KS |
502 | if (strcasecmp(key, "LABEL") == 0) { |
503 | add_rule_key(rule, &rule->label, operation, value); | |
504 | valid = 1; | |
505 | continue; | |
506 | } | |
507 | ||
508 | if (strcasecmp(key, "GOTO") == 0) { | |
509 | add_rule_key(rule, &rule->goto_label, operation, value); | |
8bd41f36 KS |
510 | valid = 1; |
511 | continue; | |
512 | } | |
79f651f4 | 513 | |
8bd41f36 KS |
514 | if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) { |
515 | attr = get_key_attribute(key + sizeof("NAME")-1); | |
516 | if (attr != NULL) { | |
517 | if (strstr(attr, "all_partitions") != NULL) { | |
518 | dbg("creation of partition nodes requested"); | |
519 | rule->partitions = DEFAULT_PARTITIONS_COUNT; | |
a8b01705 | 520 | } |
8bd41f36 KS |
521 | if (strstr(attr, "ignore_remove") != NULL) { |
522 | dbg("remove event should be ignored"); | |
523 | rule->ignore_remove = 1; | |
3e5958de | 524 | } |
3e5958de | 525 | } |
271c2d7d | 526 | if (value[0] == '\0') |
bf289c06 | 527 | dbg("name empty, node creation supressed"); |
271c2d7d | 528 | add_rule_key(rule, &rule->name, operation, value); |
8bd41f36 KS |
529 | continue; |
530 | } | |
3e5958de | 531 | |
8bd41f36 | 532 | if (strcasecmp(key, "SYMLINK") == 0) { |
1ff1aa42 KS |
533 | if (operation == KEY_OP_MATCH || |
534 | operation == KEY_OP_NOMATCH) | |
535 | add_rule_key(rule, &rule->symlink_match, operation, value); | |
536 | else | |
537 | add_rule_key(rule, &rule->symlink, operation, value); | |
8bd41f36 KS |
538 | valid = 1; |
539 | continue; | |
540 | } | |
3e5958de | 541 | |
8bd41f36 KS |
542 | if (strcasecmp(key, "OWNER") == 0) { |
543 | valid = 1; | |
2858b86c | 544 | if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { |
8bd41f36 KS |
545 | char *endptr; |
546 | strtoul(value, &endptr, 10); | |
547 | if (endptr[0] != '\0') { | |
548 | char owner[32]; | |
549 | uid_t uid = lookup_user(value); | |
550 | dbg("replacing username='%s' by id=%i", value, uid); | |
916c5e47 | 551 | sprintf(owner, "%u", (unsigned int) uid); |
8bd41f36 | 552 | add_rule_key(rule, &rule->owner, operation, owner); |
79f651f4 | 553 | continue; |
a8b01705 | 554 | } |
d94df232 KS |
555 | } |
556 | ||
8bd41f36 KS |
557 | add_rule_key(rule, &rule->owner, operation, value); |
558 | continue; | |
559 | } | |
bf5d2964 | 560 | |
8bd41f36 KS |
561 | if (strcasecmp(key, "GROUP") == 0) { |
562 | valid = 1; | |
2858b86c | 563 | if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { |
8bd41f36 KS |
564 | char *endptr; |
565 | strtoul(value, &endptr, 10); | |
566 | if (endptr[0] != '\0') { | |
567 | char group[32]; | |
568 | gid_t gid = lookup_group(value); | |
569 | dbg("replacing groupname='%s' by id=%i", value, gid); | |
916c5e47 | 570 | sprintf(group, "%u", (unsigned int) gid); |
a72e3f66 | 571 | add_rule_key(rule, &rule->group, operation, group); |
8bd41f36 | 572 | continue; |
319c6700 | 573 | } |
bd0ed2ff KS |
574 | } |
575 | ||
8bd41f36 KS |
576 | add_rule_key(rule, &rule->group, operation, value); |
577 | continue; | |
578 | } | |
2092fbcd | 579 | |
8bd41f36 KS |
580 | if (strcasecmp(key, "MODE") == 0) { |
581 | rule->mode = strtol(value, NULL, 8); | |
582 | rule->mode_operation = operation; | |
583 | valid = 1; | |
584 | continue; | |
585 | } | |
ac28b86d | 586 | |
8bd41f36 | 587 | if (strcasecmp(key, "OPTIONS") == 0) { |
2dbb47f8 KS |
588 | const char *pos; |
589 | ||
8bd41f36 KS |
590 | if (strstr(value, "last_rule") != NULL) { |
591 | dbg("last rule to be applied"); | |
592 | rule->last_rule = 1; | |
d94df232 | 593 | } |
8bd41f36 KS |
594 | if (strstr(value, "ignore_device") != NULL) { |
595 | dbg("device should be ignored"); | |
596 | rule->ignore_device = 1; | |
d94df232 | 597 | } |
8bd41f36 KS |
598 | if (strstr(value, "ignore_remove") != NULL) { |
599 | dbg("remove event should be ignored"); | |
600 | rule->ignore_remove = 1; | |
e41016d3 | 601 | } |
2dbb47f8 KS |
602 | pos = strstr(value, "link_priority="); |
603 | if (pos != NULL) { | |
604 | rule->link_priority = atoi(&pos[strlen("link_priority=")]); | |
254d6d3c | 605 | dbg("link priority=%i", rule->link_priority); |
2dbb47f8 | 606 | } |
3df3c93e KS |
607 | pos = strstr(value, "string_escape="); |
608 | if (pos != NULL) { | |
609 | pos = &pos[strlen("string_escape=")]; | |
610 | if (strncmp(pos, "none", strlen("none")) == 0) | |
611 | rule->string_escape = ESCAPE_NONE; | |
612 | else if (strncmp(pos, "replace", strlen("replace")) == 0) | |
613 | rule->string_escape = ESCAPE_REPLACE; | |
614 | } | |
8bd41f36 KS |
615 | if (strstr(value, "all_partitions") != NULL) { |
616 | dbg("creation of partition nodes requested"); | |
617 | rule->partitions = DEFAULT_PARTITIONS_COUNT; | |
e41016d3 | 618 | } |
8bd41f36 KS |
619 | valid = 1; |
620 | continue; | |
621 | } | |
e41016d3 | 622 | |
d4ae9925 | 623 | err("unknown key '%s' in %s:%u", key, filename, lineno); |
8bd41f36 | 624 | } |
821d0ec8 | 625 | |
7b5206d6 | 626 | if (physdev && rule->wait_for_sysfs.operation == KEY_OP_UNSET) |
b4e4b5a5 | 627 | err("PHYSDEV* values are deprecated and will be removed from a future kernel, " |
7b5206d6 KS |
628 | "please fix it in %s:%u", filename, lineno); |
629 | ||
8bd41f36 | 630 | /* skip line if not any valid key was found */ |
d59c84ef KS |
631 | if (!valid) |
632 | goto invalid; | |
e41016d3 | 633 | |
8bd41f36 KS |
634 | /* grow buffer and add rule */ |
635 | rule_size = sizeof(struct udev_rule) + rule->bufsize; | |
422d5bec KS |
636 | padding = (sizeof(size_t) - rule_size % sizeof(size_t)) % sizeof(size_t); |
637 | dbg("add %zi padding bytes", padding); | |
638 | rule_size += padding; | |
639 | rule->bufsize += padding; | |
640 | ||
8bd41f36 KS |
641 | rules->buf = realloc(rules->buf, rules->bufsize + rule_size); |
642 | if (!rules->buf) { | |
643 | err("realloc failed"); | |
644 | goto exit; | |
645 | } | |
594dd610 | 646 | dbg("adding rule to offset %zi", rules->bufsize); |
8bd41f36 KS |
647 | memcpy(rules->buf + rules->bufsize, rule, rule_size); |
648 | rules->bufsize += rule_size; | |
649 | exit: | |
8bd41f36 | 650 | return 0; |
d59c84ef KS |
651 | |
652 | invalid: | |
d59c84ef KS |
653 | err("invalid rule '%s:%u'", filename, lineno); |
654 | return -1; | |
8bd41f36 | 655 | } |
fd9efc00 | 656 | |
8bd41f36 KS |
657 | static int parse_file(struct udev_rules *rules, const char *filename) |
658 | { | |
659 | char line[LINE_SIZE]; | |
660 | char *bufline; | |
d59c84ef | 661 | unsigned int lineno; |
8bd41f36 KS |
662 | char *buf; |
663 | size_t bufsize; | |
664 | size_t cur; | |
665 | size_t count; | |
666 | int retval = 0; | |
667 | ||
668 | if (file_map(filename, &buf, &bufsize) != 0) { | |
ff3e4bed | 669 | err("can't open '%s' as rules file: %s", filename, strerror(errno)); |
8bd41f36 KS |
670 | return -1; |
671 | } | |
9dd0c257 | 672 | info("reading '%s' as rules file", filename); |
d94df232 | 673 | |
8bd41f36 KS |
674 | /* loop through the whole file */ |
675 | cur = 0; | |
676 | lineno = 0; | |
677 | while (cur < bufsize) { | |
678 | unsigned int i, j; | |
679 | ||
680 | count = buf_get_line(buf, bufsize, cur); | |
681 | bufline = &buf[cur]; | |
682 | cur += count+1; | |
683 | lineno++; | |
684 | ||
8bd41f36 KS |
685 | /* eat the whitespace */ |
686 | while ((count > 0) && isspace(bufline[0])) { | |
687 | bufline++; | |
688 | count--; | |
19feb351 | 689 | } |
8bd41f36 KS |
690 | if (count == 0) |
691 | continue; | |
19feb351 | 692 | |
8bd41f36 KS |
693 | /* see if this is a comment */ |
694 | if (bufline[0] == COMMENT_CHARACTER) | |
d94df232 | 695 | continue; |
8bd41f36 | 696 | |
d4ae9925 KS |
697 | if (count >= sizeof(line)) { |
698 | err("line too long, rule skipped '%s:%u'", filename, lineno); | |
699 | continue; | |
700 | } | |
701 | ||
702 | /* skip backslash and newline from multiline rules */ | |
8bd41f36 KS |
703 | for (i = j = 0; i < count; i++) { |
704 | if (bufline[i] == '\\' && bufline[i+1] == '\n') | |
705 | continue; | |
706 | ||
707 | line[j++] = bufline[i]; | |
19feb351 | 708 | } |
8bd41f36 KS |
709 | line[j] = '\0'; |
710 | ||
711 | dbg("read '%s'", line); | |
d59c84ef | 712 | add_to_rules(rules, line, filename, lineno); |
19feb351 | 713 | } |
c81b35c0 KS |
714 | |
715 | file_unmap(buf, bufsize); | |
19feb351 | 716 | return retval; |
d94df232 | 717 | } |
19feb351 | 718 | |
287814b2 | 719 | int udev_rules_init(struct udev_rules *rules, int resolve_names) |
6bf0ffe8 | 720 | { |
254d6d3c KS |
721 | struct stat statbuf; |
722 | char filename[PATH_MAX]; | |
723 | LIST_HEAD(name_list); | |
724 | LIST_HEAD(dyn_list); | |
725 | struct name_entry *name_loop, *name_tmp; | |
726 | struct name_entry *dyn_loop, *dyn_tmp; | |
727 | int retval = 0; | |
19feb351 | 728 | |
8bd41f36 KS |
729 | memset(rules, 0x00, sizeof(struct udev_rules)); |
730 | rules->resolve_names = resolve_names; | |
731 | ||
254d6d3c KS |
732 | /* read main config from single file or all files in a directory */ |
733 | if (stat(udev_rules_dir, &statbuf) != 0) | |
c81b35c0 | 734 | return -1; |
254d6d3c | 735 | if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { |
9dd0c257 | 736 | dbg("parse single rules file '%s'", udev_rules_dir); |
254d6d3c | 737 | name_list_add(&name_list, udev_rules_dir, 1); |
6bf0ffe8 | 738 | } else { |
9dd0c257 KS |
739 | dbg("parse rules directory '%s'", udev_rules_dir); |
740 | retval = add_matching_files(&name_list, udev_rules_dir, RULESFILE_SUFFIX); | |
254d6d3c KS |
741 | } |
742 | ||
743 | /* read dynamic rules directory */ | |
744 | strlcpy(filename, udev_root, sizeof(filename)); | |
745 | strlcat(filename, "/"RULES_DYN_DIR, sizeof(filename)); | |
746 | if (stat(filename, &statbuf) != 0) { | |
747 | create_path(filename); | |
748 | mkdir(filename, 0755); | |
749 | } | |
750 | add_matching_files(&dyn_list, filename, RULESFILE_SUFFIX); | |
751 | ||
752 | /* sort dynamic rules files by basename into list of files */ | |
753 | list_for_each_entry_safe(dyn_loop, dyn_tmp, &dyn_list, node) { | |
754 | const char *dyn_base = strrchr(dyn_loop->name, '/'); | |
755 | ||
756 | if (dyn_base == NULL) | |
757 | continue; | |
67747e1d KS |
758 | |
759 | list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { | |
254d6d3c KS |
760 | const char *name_base = strrchr(name_loop->name, '/'); |
761 | ||
762 | if (name_base == NULL) | |
763 | continue; | |
764 | ||
a7567d49 | 765 | if (strcmp(name_base, dyn_base) > 0) |
254d6d3c | 766 | break; |
67747e1d | 767 | } |
a7567d49 | 768 | list_move_tail(&dyn_loop->node, &name_loop->node); |
67747e1d | 769 | } |
e41245cb | 770 | |
254d6d3c KS |
771 | /* parse list of files */ |
772 | list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { | |
773 | if (stat(name_loop->name, &statbuf) == 0) { | |
774 | if (statbuf.st_size) | |
775 | parse_file(rules, name_loop->name); | |
776 | else | |
777 | dbg("empty rules file '%s'", name_loop->name); | |
778 | } else | |
779 | err("could not read '%s': %s", name_loop->name, strerror(errno)); | |
780 | list_del(&name_loop->node); | |
781 | free(name_loop); | |
782 | } | |
783 | ||
8b36cc0f | 784 | return retval; |
e41245cb | 785 | } |
6d564166 | 786 | |
1aa1e248 | 787 | void udev_rules_cleanup(struct udev_rules *rules) |
6d564166 | 788 | { |
63cc8f04 | 789 | if (rules->buf) { |
287814b2 | 790 | free(rules->buf); |
63cc8f04 KS |
791 | rules->buf = NULL; |
792 | } | |
6d564166 | 793 | } |
a7567d49 | 794 |