]>
Commit | Line | Data |
---|---|---|
2232cac8 | 1 | /* |
e5e322bc | 2 | * udev_rules.c |
2232cac8 GKH |
3 | * |
4 | * Userspace devfs | |
5 | * | |
6 | * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | |
fd9efc00 | 7 | * Copyright (C) 2003-2005 Kay Sievers <kay.sievers@vrfy.org> |
2232cac8 GKH |
8 | * |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation version 2 of the License. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along | |
20 | * with this program; if not, write to the Free Software Foundation, Inc., | |
21 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | * | |
23 | */ | |
24 | ||
25 | #include <stddef.h> | |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <stdio.h> | |
29 | #include <fcntl.h> | |
30 | #include <ctype.h> | |
31 | #include <unistd.h> | |
32 | #include <errno.h> | |
c27e6911 | 33 | #include <sys/wait.h> |
97c0448d | 34 | #include <sys/stat.h> |
2232cac8 | 35 | |
c80da508 | 36 | #include "libsysfs/sysfs/libsysfs.h" |
2232cac8 | 37 | #include "list.h" |
63f61c5c | 38 | #include "udev_libc_wrapper.h" |
2232cac8 | 39 | #include "udev.h" |
9af5bb2f | 40 | #include "udev_utils.h" |
2232cac8 | 41 | #include "udev_version.h" |
54988802 | 42 | #include "logging.h" |
e5e322bc | 43 | #include "udev_rules.h" |
02fa9ae5 | 44 | #include "udev_db.h" |
2232cac8 | 45 | |
a27cd06c | 46 | |
9f1da361 KS |
47 | /* compare string with pattern (supports * ? [0-9] [!A-Z]) */ |
48 | static int strcmp_pattern(const char *p, const char *s) | |
c124eafa | 49 | { |
8a08e4b1 KS |
50 | if (s[0] == '\0') { |
51 | while (p[0] == '*') | |
9f1da361 | 52 | p++; |
8a08e4b1 | 53 | return (p[0] != '\0'); |
9f1da361 | 54 | } |
8a08e4b1 | 55 | switch (p[0]) { |
9f1da361 KS |
56 | case '[': |
57 | { | |
58 | int not = 0; | |
59 | p++; | |
8a08e4b1 | 60 | if (p[0] == '!') { |
9f1da361 KS |
61 | not = 1; |
62 | p++; | |
63 | } | |
8a08e4b1 | 64 | while ((p[0] != '\0') && (p[0] != ']')) { |
9f1da361 KS |
65 | int match = 0; |
66 | if (p[1] == '-') { | |
8a08e4b1 | 67 | if ((s[0] >= p[0]) && (s[0] <= p[2])) |
9f1da361 KS |
68 | match = 1; |
69 | p += 3; | |
70 | } else { | |
8a08e4b1 | 71 | match = (p[0] == s[0]); |
9f1da361 KS |
72 | p++; |
73 | } | |
74 | if (match ^ not) { | |
8a08e4b1 | 75 | while ((p[0] != '\0') && (p[0] != ']')) |
9f1da361 | 76 | p++; |
8a08e4b1 KS |
77 | if (p[0] == ']') |
78 | return strcmp_pattern(p+1, s+1); | |
9f1da361 KS |
79 | } |
80 | } | |
81 | } | |
82 | break; | |
83 | case '*': | |
84 | if (strcmp_pattern(p, s+1)) | |
85 | return strcmp_pattern(p+1, s); | |
86 | return 0; | |
87 | case '\0': | |
8a08e4b1 | 88 | if (s[0] == '\0') { |
9f1da361 KS |
89 | return 0; |
90 | } | |
91 | break; | |
92 | default: | |
8a08e4b1 | 93 | if ((p[0] == s[0]) || (p[0] == '?')) |
9f1da361 KS |
94 | return strcmp_pattern(p+1, s+1); |
95 | break; | |
96 | } | |
97 | return 1; | |
c124eafa KS |
98 | } |
99 | ||
a27cd06c KS |
100 | /* extract possible {attr} and move str behind it */ |
101 | static char *get_format_attribute(char **str) | |
102 | { | |
103 | char *pos; | |
104 | char *attr = NULL; | |
105 | ||
106 | if (*str[0] == '{') { | |
107 | pos = strchr(*str, '}'); | |
108 | if (pos == NULL) { | |
6b493a20 | 109 | err("missing closing brace for format"); |
a27cd06c KS |
110 | return NULL; |
111 | } | |
112 | pos[0] = '\0'; | |
113 | attr = *str+1; | |
114 | *str = pos+1; | |
115 | dbg("attribute='%s', str='%s'", attr, *str); | |
116 | } | |
117 | return attr; | |
118 | } | |
119 | ||
120 | /* extract possible format length and move str behind it*/ | |
121 | static int get_format_len(char **str) | |
122 | { | |
123 | int num; | |
124 | char *tail; | |
125 | ||
126 | if (isdigit(*str[0])) { | |
127 | num = (int) strtoul(*str, &tail, 10); | |
aebef544 | 128 | if (num > 0) { |
a27cd06c KS |
129 | *str = tail; |
130 | dbg("format length=%i", num); | |
131 | return num; | |
132 | } else { | |
6b493a20 | 133 | err("format parsing error '%s'", *str); |
a27cd06c KS |
134 | } |
135 | } | |
136 | return -1; | |
137 | } | |
138 | ||
bd0ed2ff KS |
139 | static int get_key(char **line, char **key, char **value) |
140 | { | |
141 | char *linepos; | |
142 | char *temp; | |
143 | ||
144 | linepos = *line; | |
145 | if (!linepos) | |
146 | return -1; | |
147 | ||
148 | if (strchr(linepos, '\\')) { | |
149 | dbg("escaped characters are not supported, skip"); | |
150 | return -1; | |
151 | } | |
152 | ||
153 | /* skip whitespace */ | |
154 | while (isspace(linepos[0])) | |
155 | linepos++; | |
156 | ||
157 | /* get the key */ | |
158 | *key = linepos; | |
159 | while (1) { | |
160 | linepos++; | |
161 | if (linepos[0] == '\0') | |
162 | return -1; | |
163 | if (isspace(linepos[0])) | |
164 | break; | |
165 | if (linepos[0] == '=') | |
166 | break; | |
167 | } | |
168 | ||
169 | /* terminate key */ | |
170 | linepos[0] = '\0'; | |
171 | linepos++; | |
172 | ||
173 | /* skip whitespace */ | |
174 | while (isspace(linepos[0])) | |
175 | linepos++; | |
176 | ||
177 | /* get the value*/ | |
bd0ed2ff KS |
178 | if (linepos[0] == '"') { |
179 | linepos++; | |
180 | temp = strchr(linepos, '"'); | |
b8476286 KS |
181 | if (!temp) { |
182 | dbg("missing closing quote"); | |
bd0ed2ff | 183 | return -1; |
b8476286 KS |
184 | } |
185 | dbg("value is quoted"); | |
bd0ed2ff KS |
186 | temp[0] = '\0'; |
187 | } else if (linepos[0] == '\'') { | |
188 | linepos++; | |
189 | temp = strchr(linepos, '\''); | |
b8476286 KS |
190 | if (!temp) { |
191 | dbg("missing closing quote"); | |
bd0ed2ff | 192 | return -1; |
b8476286 KS |
193 | } |
194 | dbg("value is quoted"); | |
bd0ed2ff | 195 | temp[0] = '\0'; |
b8476286 KS |
196 | } else if (linepos[0] == '\0') { |
197 | dbg("value is empty"); | |
bd0ed2ff KS |
198 | } else { |
199 | temp = linepos; | |
200 | while (temp[0] && !isspace(temp[0])) | |
201 | temp++; | |
202 | temp[0] = '\0'; | |
203 | } | |
204 | *value = linepos; | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
b8476286 | 209 | static int import_keys_into_env(struct udevice *udev, const char *buf, size_t bufsize) |
bd0ed2ff KS |
210 | { |
211 | char line[LINE_SIZE]; | |
be4bedd1 | 212 | const char *bufline; |
bd0ed2ff KS |
213 | char *linepos; |
214 | char *variable; | |
215 | char *value; | |
bd0ed2ff KS |
216 | size_t cur; |
217 | size_t count; | |
218 | int lineno; | |
bd0ed2ff | 219 | |
b8476286 | 220 | /* loop through the whole buffer */ |
bd0ed2ff KS |
221 | lineno = 0; |
222 | cur = 0; | |
223 | while (cur < bufsize) { | |
224 | count = buf_get_line(buf, bufsize, cur); | |
225 | bufline = &buf[cur]; | |
226 | cur += count+1; | |
227 | lineno++; | |
228 | ||
229 | if (count >= sizeof(line)) { | |
230 | err("line too long, conf line skipped %s, line %d", udev_config_filename, lineno); | |
231 | continue; | |
232 | } | |
233 | ||
234 | /* eat the whitespace */ | |
235 | while ((count > 0) && isspace(bufline[0])) { | |
236 | bufline++; | |
237 | count--; | |
238 | } | |
239 | if (count == 0) | |
240 | continue; | |
241 | ||
242 | /* see if this is a comment */ | |
243 | if (bufline[0] == COMMENT_CHARACTER) | |
244 | continue; | |
245 | ||
246 | strlcpy(line, bufline, count+1); | |
247 | ||
248 | linepos = line; | |
249 | if (get_key(&linepos, &variable, &value) == 0) { | |
319c6700 | 250 | dbg("import '%s=%s'", variable, value); |
b8476286 | 251 | name_list_key_add(&udev->env_list, variable, value); |
319c6700 | 252 | setenv(variable, value, 1); |
bd0ed2ff KS |
253 | } |
254 | } | |
255 | ||
be4bedd1 KS |
256 | return 0; |
257 | } | |
258 | ||
b8476286 | 259 | static int import_file_into_env(struct udevice *udev, const char *filename) |
be4bedd1 KS |
260 | { |
261 | char *buf; | |
262 | size_t bufsize; | |
263 | ||
264 | if (file_map(filename, &buf, &bufsize) != 0) { | |
265 | err("can't open '%s'", filename); | |
266 | return -1; | |
267 | } | |
b8476286 | 268 | import_keys_into_env(udev, buf, bufsize); |
bd0ed2ff | 269 | file_unmap(buf, bufsize); |
be4bedd1 KS |
270 | |
271 | return 0; | |
bd0ed2ff KS |
272 | } |
273 | ||
319c6700 KS |
274 | static int import_program_into_env(struct udevice *udev, const char *program) |
275 | { | |
276 | char result[1024]; | |
277 | size_t reslen; | |
278 | ||
279 | if (execute_program(program, udev->subsystem, result, sizeof(result), &reslen) != 0) | |
280 | return -1; | |
b8476286 | 281 | return import_keys_into_env(udev, result, reslen); |
319c6700 KS |
282 | } |
283 | ||
0bfb84e1 KS |
284 | static int import_parent_into_env(struct udevice *udev, struct sysfs_class_device *class_dev, const char *filter) |
285 | { | |
286 | struct sysfs_class_device *parent = sysfs_get_classdev_parent(class_dev); | |
287 | int rc = -1; | |
288 | ||
289 | if (parent != NULL) { | |
290 | struct udevice udev_parent; | |
291 | struct name_entry *name_loop; | |
292 | ||
293 | dbg("found parent '%s', get the node name", parent->path); | |
294 | udev_init_device(&udev_parent, NULL, NULL, NULL); | |
295 | /* import the udev_db of the parent */ | |
296 | if (udev_db_get_device(&udev_parent, &parent->path[strlen(sysfs_path)]) == 0) { | |
297 | dbg("import stored parent env '%s'", udev_parent.name); | |
298 | list_for_each_entry(name_loop, &udev_parent.env_list, node) { | |
299 | char name[NAME_SIZE]; | |
300 | char *pos; | |
301 | ||
302 | strlcpy(name, name_loop->name, sizeof(name)); | |
303 | pos = strchr(name, '='); | |
304 | if (pos) { | |
305 | pos[0] = '\0'; | |
306 | pos++; | |
307 | if (strcmp_pattern(filter, name) == 0) { | |
308 | dbg("import key '%s'", name_loop->name); | |
309 | name_list_add(&udev->env_list, name_loop->name, 0); | |
310 | setenv(name, pos, 1); | |
311 | } else | |
312 | dbg("skip key '%s'", name_loop->name); | |
313 | } | |
314 | } | |
315 | rc = 0; | |
316 | } else | |
317 | dbg("parent not found in database"); | |
318 | udev_cleanup_device(&udev_parent); | |
319 | } | |
320 | ||
321 | return rc; | |
322 | } | |
323 | ||
319c6700 KS |
324 | /* finds the lowest positive N such that <name>N isn't present in the udevdb |
325 | * if <name> doesn't exist, 0 is returned, N otherwise | |
0a8dd7f3 | 326 | */ |
2b41e68a | 327 | static int find_free_number(struct udevice *udev, const char *name) |
0a8dd7f3 | 328 | { |
63f61c5c KS |
329 | char devpath[PATH_SIZE]; |
330 | char filename[PATH_SIZE]; | |
2b41e68a | 331 | int num = 0; |
0a8dd7f3 | 332 | |
63f61c5c | 333 | strlcpy(filename, name, sizeof(filename)); |
0a8dd7f3 | 334 | while (1) { |
2b41e68a | 335 | dbg("look for existing node '%s'", filename); |
63f61c5c | 336 | if (udev_db_search_name(devpath, sizeof(devpath), filename) != 0) { |
2b41e68a KS |
337 | dbg("free num=%d", num); |
338 | return num; | |
339 | } | |
0a8dd7f3 | 340 | |
2b41e68a KS |
341 | num++; |
342 | if (num > 1000) { | |
343 | info("find_free_number gone crazy (num=%d), aborted", num); | |
344 | return -1; | |
345 | } | |
63f61c5c KS |
346 | snprintf(filename, sizeof(filename), "%s%d", name, num); |
347 | filename[sizeof(filename)-1] = '\0'; | |
2b41e68a | 348 | } |
0a8dd7f3 DZ |
349 | } |
350 | ||
18614ab2 KS |
351 | static int find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, |
352 | const char *name, char *value, size_t len) | |
353 | { | |
354 | struct sysfs_attribute *tmpattr; | |
355 | ||
356 | dbg("look for device attribute '%s'", name); | |
357 | if (class_dev) { | |
bf5d2964 | 358 | dbg("look for class attribute '%s/%s'", class_dev->path, name); |
18614ab2 KS |
359 | tmpattr = sysfs_get_classdev_attr(class_dev, name); |
360 | if (tmpattr) | |
361 | goto attr_found; | |
362 | } | |
363 | if (sysfs_device) { | |
bf5d2964 | 364 | dbg("look for devices attribute '%s/%s'", sysfs_device->path, name); |
18614ab2 KS |
365 | tmpattr = sysfs_get_device_attr(sysfs_device, name); |
366 | if (tmpattr) | |
367 | goto attr_found; | |
368 | } | |
18614ab2 KS |
369 | return -1; |
370 | ||
371 | attr_found: | |
372 | strlcpy(value, tmpattr->value, len); | |
373 | remove_trailing_char(value, '\n'); | |
374 | ||
375 | dbg("found attribute '%s'", tmpattr->path); | |
376 | return 0; | |
377 | } | |
378 | ||
b2fe4b9a KS |
379 | #define WAIT_LOOP_PER_SECOND 20 |
380 | static int wait_for_sysfs(struct udevice *udev, const char *file, int timeout) | |
381 | { | |
382 | char filename[PATH_SIZE]; | |
383 | struct stat stats; | |
384 | int loop = timeout * WAIT_LOOP_PER_SECOND; | |
385 | ||
386 | snprintf(filename, sizeof(filename), "%s%s/%s", sysfs_path, udev->devpath, file); | |
387 | filename[sizeof(filename)-1] = '\0'; | |
388 | dbg("wait %i sec for '%s'", timeout, filename); | |
389 | ||
390 | while (--loop) { | |
391 | if (stat(filename, &stats) == 0) { | |
392 | dbg("file appeared after %i loops", (timeout * WAIT_LOOP_PER_SECOND) - loop-1); | |
393 | return 0; | |
394 | } | |
395 | usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); | |
396 | } | |
397 | dbg("waiting for '%s' failed", filename); | |
398 | return -1; | |
399 | } | |
400 | ||
831f800d | 401 | static void apply_format(struct udevice *udev, char *string, size_t maxsize, |
b821330f | 402 | struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) |
f3b04a2e | 403 | { |
63f61c5c KS |
404 | char temp[PATH_SIZE]; |
405 | char temp2[PATH_SIZE]; | |
bd0ed2ff | 406 | char *head, *tail, *pos, *cpos, *attr, *rest; |
63ead27c | 407 | int len; |
88f09368 | 408 | int i; |
0a8dd7f3 | 409 | unsigned int next_free_number; |
69aa6dfb | 410 | struct sysfs_class_device *class_dev_parent; |
bf5d2964 KS |
411 | enum subst_type { |
412 | SUBST_UNKNOWN, | |
413 | SUBST_DEVPATH, | |
414 | SUBST_ID, | |
415 | SUBST_KERNEL_NUMBER, | |
416 | SUBST_KERNEL_NAME, | |
417 | SUBST_MAJOR, | |
418 | SUBST_MINOR, | |
419 | SUBST_RESULT, | |
420 | SUBST_SYSFS, | |
421 | SUBST_ENUM, | |
422 | SUBST_PARENT, | |
423 | SUBST_TEMP_NODE, | |
424 | SUBST_ROOT, | |
425 | SUBST_MODALIAS, | |
bd0ed2ff | 426 | SUBST_ENV, |
bf5d2964 KS |
427 | }; |
428 | static const struct subst_map { | |
429 | char *name; | |
430 | char fmt; | |
431 | enum subst_type type; | |
432 | } map[] = { | |
433 | { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, | |
434 | { .name = "id", .fmt = 'b', .type = SUBST_ID }, | |
435 | { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, | |
436 | { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL_NAME }, | |
437 | { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, | |
438 | { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, | |
439 | { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, | |
440 | { .name = "sysfs", .fmt = 's', .type = SUBST_SYSFS }, | |
441 | { .name = "enum", .fmt = 'e', .type = SUBST_ENUM }, | |
442 | { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, | |
443 | { .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE }, | |
444 | { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, | |
445 | { .name = "modalias", .fmt = 'A', .type = SUBST_MODALIAS }, | |
bd0ed2ff | 446 | { .name = "env", .fmt = 'E', .type = SUBST_ENV }, |
bf5d2964 KS |
447 | {} |
448 | }; | |
449 | enum subst_type type; | |
450 | const struct subst_map *subst; | |
451 | ||
452 | head = string; | |
f3b04a2e | 453 | while (1) { |
bf5d2964 KS |
454 | len = -1; |
455 | while (head[0] != '\0') { | |
456 | if (head[0] == '$') { | |
457 | /* substitute named variable */ | |
458 | if (head[1] == '\0') | |
459 | break; | |
460 | if (head[1] == '$') { | |
461 | strlcpy(temp, head+2, sizeof(temp)); | |
462 | strlcpy(head+1, temp, maxsize); | |
463 | head++; | |
464 | continue; | |
465 | } | |
466 | head[0] = '\0'; | |
467 | for (subst = map; subst->name; subst++) { | |
468 | if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { | |
469 | type = subst->type; | |
470 | tail = head + strlen(subst->name)+1; | |
471 | dbg("will substitute format name '%s'", subst->name); | |
472 | goto found; | |
473 | } | |
474 | } | |
475 | } | |
476 | else if (head[0] == '%') { | |
477 | /* substitute format char */ | |
478 | if (head[1] == '\0') | |
479 | break; | |
480 | if (head[1] == '%') { | |
481 | strlcpy(temp, head+2, sizeof(temp)); | |
482 | strlcpy(head+1, temp, maxsize); | |
483 | head++; | |
484 | continue; | |
485 | } | |
486 | head[0] = '\0'; | |
487 | tail = head+1; | |
488 | len = get_format_len(&tail); | |
489 | for (subst = map; subst->name; subst++) { | |
490 | if (tail[0] == subst->fmt) { | |
491 | type = subst->type; | |
492 | tail++; | |
493 | dbg("will substitute format char '%c'", subst->fmt); | |
494 | goto found; | |
495 | } | |
496 | } | |
497 | } | |
498 | head++; | |
499 | } | |
500 | break; | |
501 | found: | |
a27cd06c | 502 | attr = get_format_attribute(&tail); |
bf5d2964 KS |
503 | strlcpy(temp, tail, sizeof(temp)); |
504 | dbg("format=%i, string='%s', tail='%s', class_dev=%p, sysfs_dev=%p", | |
505 | type ,string, tail, class_dev, sysfs_device); | |
a27cd06c | 506 | |
bf5d2964 KS |
507 | switch (type) { |
508 | case SUBST_DEVPATH: | |
63f61c5c | 509 | strlcat(string, udev->devpath, maxsize); |
bf5d2964 | 510 | dbg("substitute devpath '%s'", udev->devpath); |
c1ab0461 | 511 | break; |
bf5d2964 | 512 | case SUBST_ID: |
63f61c5c | 513 | strlcat(string, udev->bus_id, maxsize); |
a27cd06c KS |
514 | dbg("substitute bus_id '%s'", udev->bus_id); |
515 | break; | |
bf5d2964 | 516 | case SUBST_KERNEL_NAME: |
63f61c5c | 517 | strlcat(string, udev->kernel_name, maxsize); |
a27cd06c KS |
518 | dbg("substitute kernel name '%s'", udev->kernel_name); |
519 | break; | |
bf5d2964 | 520 | case SUBST_KERNEL_NUMBER: |
63f61c5c | 521 | strlcat(string, udev->kernel_number, maxsize); |
a27cd06c | 522 | dbg("substitute kernel number '%s'", udev->kernel_number); |
a27cd06c | 523 | break; |
bf5d2964 | 524 | case SUBST_MAJOR: |
03fd7a3a | 525 | sprintf(temp2, "%d", major(udev->devt)); |
63f61c5c | 526 | strlcat(string, temp2, maxsize); |
03fd7a3a | 527 | dbg("substitute major number '%s'", temp2); |
a27cd06c | 528 | break; |
bf5d2964 KS |
529 | case SUBST_MINOR: |
530 | sprintf(temp2, "%d", minor(udev->devt)); | |
531 | strlcat(string, temp2, maxsize); | |
532 | dbg("substitute minor number '%s'", temp2); | |
533 | break; | |
534 | case SUBST_RESULT: | |
b821330f | 535 | if (udev->program_result[0] == '\0') |
f3b04a2e | 536 | break; |
88f09368 | 537 | /* get part part of the result string */ |
63ead27c | 538 | i = 0; |
88f09368 | 539 | if (attr != NULL) |
558f80ba | 540 | i = strtoul(attr, &rest, 10); |
88f09368 | 541 | if (i > 0) { |
03fd7a3a KS |
542 | dbg("request part #%d of result string", i); |
543 | cpos = udev->program_result; | |
544 | while (--i) { | |
545 | while (cpos[0] != '\0' && !isspace(cpos[0])) | |
546 | cpos++; | |
547 | while (isspace(cpos[0])) | |
548 | cpos++; | |
b1c5e333 | 549 | } |
9fe3f9a9 | 550 | if (i > 0) { |
6b493a20 | 551 | err("requested part of result string not found"); |
9fe3f9a9 | 552 | break; |
a27cd06c | 553 | } |
63f61c5c | 554 | strlcpy(temp2, cpos, sizeof(temp2)); |
03fd7a3a KS |
555 | /* %{2+}c copies the whole string from the second part on */ |
556 | if (rest[0] != '+') { | |
557 | cpos = strchr(temp2, ' '); | |
558 | if (cpos) | |
559 | cpos[0] = '\0'; | |
560 | } | |
63f61c5c | 561 | strlcat(string, temp2, maxsize); |
27c3403d | 562 | dbg("substitute part of result string '%s'", temp2); |
a27cd06c | 563 | } else { |
63f61c5c | 564 | strlcat(string, udev->program_result, maxsize); |
a27cd06c | 565 | dbg("substitute result string '%s'", udev->program_result); |
f3b04a2e | 566 | } |
f3b04a2e | 567 | break; |
bf5d2964 | 568 | case SUBST_SYSFS: |
5e39f90b | 569 | if (attr == NULL) { |
a27cd06c | 570 | dbg("missing attribute"); |
5e39f90b KS |
571 | break; |
572 | } | |
18614ab2 | 573 | if (find_sysfs_attribute(class_dev, sysfs_device, attr, temp2, sizeof(temp2)) != 0) { |
98bbc835 KS |
574 | struct sysfs_device *parent_device; |
575 | ||
576 | dbg("sysfs attribute '%s' not found, walk up the physical devices", attr); | |
577 | parent_device = sysfs_get_device_parent(sysfs_device); | |
578 | while (parent_device) { | |
579 | dbg("looking at '%s'", parent_device->path); | |
580 | if (find_sysfs_attribute(NULL, parent_device, attr, temp2, sizeof(temp2)) == 0) | |
581 | break; | |
582 | parent_device = sysfs_get_device_parent(parent_device); | |
583 | } | |
584 | if (!parent_device) | |
585 | break; | |
5e39f90b | 586 | } |
18614ab2 KS |
587 | /* strip trailing whitespace of sysfs value */ |
588 | i = strlen(temp2); | |
589 | while (i > 0 && isspace(temp2[i-1])) | |
590 | temp2[--i] = '\0'; | |
591 | replace_untrusted_chars(temp2); | |
592 | strlcat(string, temp2, maxsize); | |
593 | dbg("substitute sysfs value '%s'", temp2); | |
a27cd06c | 594 | break; |
bf5d2964 | 595 | case SUBST_ENUM: |
0a8dd7f3 DZ |
596 | next_free_number = find_free_number(udev, string); |
597 | if (next_free_number > 0) { | |
2b41e68a | 598 | sprintf(temp2, "%d", next_free_number); |
63f61c5c | 599 | strlcat(string, temp2, maxsize); |
0a8dd7f3 DZ |
600 | } |
601 | break; | |
bf5d2964 | 602 | case SUBST_PARENT: |
8f2f6e42 KS |
603 | if (!class_dev) |
604 | break; | |
69aa6dfb KS |
605 | class_dev_parent = sysfs_get_classdev_parent(class_dev); |
606 | if (class_dev_parent != NULL) { | |
607 | struct udevice udev_parent; | |
608 | ||
609 | dbg("found parent '%s', get the node name", class_dev_parent->path); | |
fb39f056 | 610 | udev_init_device(&udev_parent, NULL, NULL, NULL); |
69aa6dfb | 611 | /* lookup the name in the udev_db with the DEVPATH of the parent */ |
e48fc108 | 612 | if (udev_db_get_device(&udev_parent, &class_dev_parent->path[strlen(sysfs_path)]) == 0) { |
63f61c5c | 613 | strlcat(string, udev_parent.name, maxsize); |
69aa6dfb KS |
614 | dbg("substitute parent node name'%s'", udev_parent.name); |
615 | } else | |
616 | dbg("parent not found in database"); | |
e48fc108 | 617 | udev_cleanup_device(&udev_parent); |
69aa6dfb KS |
618 | } |
619 | break; | |
bf5d2964 | 620 | case SUBST_TEMP_NODE: |
c1ab0461 KS |
621 | if (udev->tmp_node[0] == '\0') { |
622 | dbg("create temporary device node for callout"); | |
63f61c5c KS |
623 | snprintf(udev->tmp_node, sizeof(udev->tmp_node), "%s/.tmp-%u-%u", |
624 | udev_root, major(udev->devt), minor(udev->devt)); | |
625 | udev->tmp_node[sizeof(udev->tmp_node)-1] = '\0'; | |
7e720bd4 | 626 | udev_make_node(udev, udev->tmp_node, udev->devt, 0600, 0, 0); |
c1ab0461 | 627 | } |
63f61c5c | 628 | strlcat(string, udev->tmp_node, maxsize); |
c1ab0461 KS |
629 | dbg("substitute temporary device node name '%s'", udev->tmp_node); |
630 | break; | |
bf5d2964 | 631 | case SUBST_ROOT: |
63f61c5c | 632 | strlcat(string, udev_root, maxsize); |
69aa6dfb KS |
633 | dbg("substitute udev_root '%s'", udev_root); |
634 | break; | |
bf5d2964 KS |
635 | case SUBST_MODALIAS: |
636 | if (find_sysfs_attribute(NULL, sysfs_device, "modalias", temp2, sizeof(temp2)) != 0) | |
637 | break; | |
638 | strlcat(string, temp2, maxsize); | |
639 | dbg("substitute MODALIAS '%s'", temp2); | |
640 | break; | |
bd0ed2ff KS |
641 | case SUBST_ENV: |
642 | if (attr == NULL) { | |
643 | dbg("missing attribute"); | |
644 | break; | |
645 | } | |
646 | pos = getenv(attr); | |
0bfb84e1 KS |
647 | if (pos == NULL) { |
648 | dbg("env '%s' not avialable", attr); | |
bd0ed2ff | 649 | break; |
0bfb84e1 | 650 | } |
bd0ed2ff | 651 | dbg("substitute env '%s=%s'", attr, pos); |
ef520ba2 | 652 | strlcat(string, pos, maxsize); |
bd0ed2ff | 653 | break; |
a27cd06c | 654 | default: |
bf5d2964 | 655 | err("unknown substitution type=%i", type); |
a27cd06c KS |
656 | break; |
657 | } | |
bf5d2964 KS |
658 | /* possibly truncate to format-char specified length */ |
659 | if (len != -1) { | |
660 | head[len] = '\0'; | |
661 | dbg("truncate to %i chars, subtitution string becomes '%s'", len, head); | |
662 | } | |
bf5d2964 | 663 | strlcat(string, temp, maxsize); |
f3b04a2e GKH |
664 | } |
665 | } | |
666 | ||
8bd41f36 KS |
667 | static char *key_val(struct udev_rule *rule, struct key *key) |
668 | { | |
669 | return rule->buf + key->val_off; | |
670 | } | |
671 | ||
672 | static char *key_pair_name(struct udev_rule *rule, struct key_pair *pair) | |
673 | { | |
674 | return rule->buf + pair->key_name_off; | |
675 | } | |
676 | ||
677 | static int match_key(const char *key_name, struct udev_rule *rule, struct key *key, const char *val) | |
a0e5382d KS |
678 | { |
679 | int match; | |
0cd4ac47 | 680 | char value[PATH_SIZE]; |
8bd41f36 | 681 | char *key_value; |
0cd4ac47 | 682 | char *pos; |
a0e5382d | 683 | |
8bd41f36 | 684 | if (key->operation == KEY_OP_UNSET) |
a0e5382d KS |
685 | return 0; |
686 | ||
0cd4ac47 KS |
687 | strlcpy(value, rule->buf + key->val_off, sizeof(value)); |
688 | key_value = value; | |
8bd41f36 | 689 | |
0cd4ac47 KS |
690 | dbg("key %s value='%s'", key_name, key_value); |
691 | while (key_value) { | |
692 | pos = strchr(key_value, '|'); | |
693 | if (pos) { | |
694 | pos[0] = '\0'; | |
695 | pos++; | |
696 | } | |
697 | dbg("match %s '%s' <-> '%s'", key_name, key_value, val); | |
698 | match = (strcmp_pattern(key_value, val) == 0); | |
699 | if (match && (key->operation != KEY_OP_NOMATCH)) { | |
700 | dbg("%s is true (matching value)", key_name); | |
701 | return 0; | |
702 | } | |
703 | if (!match && (key->operation == KEY_OP_NOMATCH)) { | |
704 | dbg("%s is true (non-matching value)", key_name); | |
705 | return 0; | |
706 | } | |
707 | key_value = pos; | |
a0e5382d | 708 | } |
0cd4ac47 | 709 | dbg("%s is false", key_name); |
a0e5382d KS |
710 | return -1; |
711 | } | |
712 | ||
e5e322bc | 713 | static int match_rule(struct udevice *udev, struct udev_rule *rule, |
5f72c470 | 714 | struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) |
dac056aa | 715 | { |
aa341f21 KS |
716 | struct sysfs_device *parent_device = sysfs_device; |
717 | ||
8bd41f36 | 718 | if (match_key("ACTION", rule, &rule->action, udev->action)) |
a0e5382d | 719 | goto exit; |
821d0ec8 | 720 | |
8bd41f36 | 721 | if (match_key("KERNEL", rule, &rule->kernel_name, udev->kernel_name)) |
a0e5382d | 722 | goto exit; |
ca1cc0fe | 723 | |
8bd41f36 | 724 | if (match_key("SUBSYSTEM", rule, &rule->subsystem, udev->subsystem)) |
a0e5382d | 725 | goto exit; |
6818c51d | 726 | |
8bd41f36 | 727 | if (match_key("DEVPATH", rule, &rule->devpath, udev->devpath)) |
a0e5382d | 728 | goto exit; |
bf5d2964 | 729 | |
8bd41f36 | 730 | if (rule->modalias.operation != KEY_OP_UNSET) { |
bf5d2964 KS |
731 | char value[NAME_SIZE]; |
732 | ||
733 | if (find_sysfs_attribute(NULL, sysfs_device, "modalias", value, sizeof(value)) != 0) { | |
63698391 | 734 | dbg("MODALIAS value not found"); |
bf5d2964 KS |
735 | goto exit; |
736 | } | |
8bd41f36 | 737 | if (match_key("MODALIAS", rule, &rule->modalias, value)) |
a0e5382d | 738 | goto exit; |
bf5d2964 KS |
739 | } |
740 | ||
8bd41f36 | 741 | if (rule->env.count) { |
3e5958de KS |
742 | int i; |
743 | ||
8bd41f36 KS |
744 | dbg("check %i ENV keys", rule->env.count); |
745 | for (i = 0; i < rule->env.count; i++) { | |
746 | struct key_pair *pair = &rule->env.keys[i]; | |
747 | const char *key_name = key_pair_name(rule, pair); | |
748 | const char *value = getenv(key_name); | |
3e5958de | 749 | |
3e5958de | 750 | if (!value) { |
c609f627 KS |
751 | dbg("ENV{'%s'} is not set", key_name); |
752 | value = ""; | |
3e5958de | 753 | } |
8bd41f36 | 754 | if (match_key("ENV", rule, &pair->key, value)) |
a0e5382d | 755 | goto exit; |
3e5958de | 756 | } |
8bd41f36 | 757 | dbg("all %i ENV keys matched", rule->env.count); |
3e5958de KS |
758 | } |
759 | ||
b2fe4b9a KS |
760 | if (rule->wait_for_sysfs.operation != KEY_OP_UNSET) { |
761 | int match; | |
762 | ||
763 | match = (wait_for_sysfs(udev, key_val(rule, &rule->wait_for_sysfs), 3) == 0); | |
764 | if (match && (rule->wait_for_sysfs.operation != KEY_OP_NOMATCH)) { | |
0cd4ac47 | 765 | dbg("WAIT_FOR_SYSFS is true (matching value)"); |
b2fe4b9a KS |
766 | return 0; |
767 | } | |
768 | if (!match && (rule->wait_for_sysfs.operation == KEY_OP_NOMATCH)) { | |
0cd4ac47 | 769 | dbg("WAIT_FOR_SYSFS is true, (non matching value)"); |
b2fe4b9a KS |
770 | return 0; |
771 | } | |
0cd4ac47 | 772 | dbg("WAIT_FOR_SYSFS is false"); |
b2fe4b9a KS |
773 | return -1; |
774 | } | |
775 | ||
e57e7bc1 | 776 | /* walk up the chain of physical devices and find a match */ |
03a9875b | 777 | while (1) { |
2092fbcd | 778 | /* check for matching driver */ |
8bd41f36 | 779 | if (rule->driver.operation != KEY_OP_UNSET) { |
aa341f21 | 780 | if (parent_device == NULL) { |
2b40452a | 781 | dbg("device has no sysfs_device"); |
ca4c984c | 782 | goto exit; |
2b40452a | 783 | } |
8bd41f36 | 784 | if (match_key("DRIVER", rule, &rule->driver, parent_device->driver_name)) |
a0e5382d | 785 | goto try_parent; |
2092fbcd KS |
786 | } |
787 | ||
82b16983 | 788 | /* check for matching bus value */ |
8bd41f36 | 789 | if (rule->bus.operation != KEY_OP_UNSET) { |
aa341f21 | 790 | if (parent_device == NULL) { |
2b40452a | 791 | dbg("device has no sysfs_device"); |
ca4c984c | 792 | goto exit; |
82b16983 | 793 | } |
8bd41f36 | 794 | if (match_key("BUS", rule, &rule->bus, parent_device->bus)) |
a0e5382d | 795 | goto try_parent; |
82b16983 KS |
796 | } |
797 | ||
ac28b86d | 798 | /* check for matching bus id */ |
8bd41f36 | 799 | if (rule->id.operation != KEY_OP_UNSET) { |
aa341f21 | 800 | if (parent_device == NULL) { |
2b40452a | 801 | dbg("device has no sysfs_device"); |
ca4c984c | 802 | goto exit; |
2b40452a | 803 | } |
8bd41f36 | 804 | if (match_key("ID", rule, &rule->id, parent_device->bus_id)) |
a0e5382d | 805 | goto try_parent; |
ac28b86d | 806 | } |
ca1cc0fe | 807 | |
ac28b86d | 808 | /* check for matching sysfs pairs */ |
8bd41f36 | 809 | if (rule->sysfs.count) { |
3e5958de KS |
810 | int i; |
811 | ||
8bd41f36 KS |
812 | dbg("check %i SYSFS keys", rule->sysfs.count); |
813 | for (i = 0; i < rule->sysfs.count; i++) { | |
814 | struct key_pair *pair = &rule->sysfs.keys[i]; | |
815 | const char *key_name = key_pair_name(rule, pair); | |
816 | const char *key_value = key_val(rule, &pair->key); | |
db949b02 | 817 | char value[VALUE_SIZE]; |
bb1a77d3 | 818 | size_t len; |
3e5958de | 819 | |
8bd41f36 | 820 | if (find_sysfs_attribute(class_dev, parent_device, key_name, value, sizeof(value)) != 0) |
db949b02 KS |
821 | goto try_parent; |
822 | ||
823 | /* strip trailing whitespace of value, if not asked to match for it */ | |
8bd41f36 KS |
824 | len = strlen(key_value); |
825 | if (len && !isspace(key_value[len-1])) { | |
bb1a77d3 | 826 | len = strlen(value); |
db949b02 KS |
827 | while (len > 0 && isspace(value[len-1])) |
828 | value[--len] = '\0'; | |
aaff09a3 | 829 | dbg("removed %zi trailing whitespace chars from '%s'", strlen(value)-len, value); |
db949b02 KS |
830 | } |
831 | ||
8bd41f36 | 832 | if (match_key("SYSFS", rule, &pair->key, value)) |
a0e5382d | 833 | goto try_parent; |
ac28b86d | 834 | } |
8bd41f36 | 835 | dbg("all %i SYSFS keys matched", rule->sysfs.count); |
ac28b86d KS |
836 | } |
837 | ||
e57e7bc1 KS |
838 | /* found matching physical device */ |
839 | break; | |
c5118442 KS |
840 | try_parent: |
841 | dbg("try parent sysfs device"); | |
aa341f21 KS |
842 | parent_device = sysfs_get_device_parent(parent_device); |
843 | if (parent_device == NULL) | |
82b16983 | 844 | goto exit; |
aa341f21 KS |
845 | dbg("look at sysfs_device->path='%s'", parent_device->path); |
846 | dbg("look at sysfs_device->bus_id='%s'", parent_device->bus_id); | |
724257d9 | 847 | } |
82b16983 | 848 | |
be4bedd1 | 849 | /* import variables from file into environment */ |
8bd41f36 | 850 | if (rule->import.operation != KEY_OP_UNSET) { |
bd0ed2ff | 851 | char import[PATH_SIZE]; |
319c6700 | 852 | int rc = -1; |
bd0ed2ff | 853 | |
8bd41f36 | 854 | strlcpy(import, key_val(rule, &rule->import), sizeof(import)); |
bd0ed2ff | 855 | apply_format(udev, import, sizeof(import), class_dev, sysfs_device); |
63698391 | 856 | dbg("check for IMPORT import='%s'", import); |
0bfb84e1 | 857 | if (rule->import_type == IMPORT_PROGRAM) { |
319c6700 KS |
858 | dbg("run executable file import='%s'", import); |
859 | rc = import_program_into_env(udev, import); | |
0bfb84e1 | 860 | } else if (rule->import_type == IMPORT_FILE) { |
319c6700 | 861 | dbg("import file import='%s'", import); |
b8476286 | 862 | rc = import_file_into_env(udev, import); |
0bfb84e1 KS |
863 | } else if (rule->import_type == IMPORT_PARENT && class_dev) { |
864 | dbg("import parent import='%s'", import); | |
865 | rc = import_parent_into_env(udev, class_dev, import); | |
319c6700 KS |
866 | } |
867 | if (rc) { | |
63698391 | 868 | dbg("IMPORT failed"); |
8bd41f36 | 869 | if (rule->import.operation != KEY_OP_NOMATCH) |
bd0ed2ff KS |
870 | goto exit; |
871 | } else | |
8bd41f36 | 872 | dbg("IMPORT '%s' imported", key_val(rule, &rule->import)); |
63698391 | 873 | dbg("IMPORT key is true"); |
bd0ed2ff KS |
874 | } |
875 | ||
e57e7bc1 | 876 | /* execute external program */ |
8bd41f36 | 877 | if (rule->program.operation != KEY_OP_UNSET) { |
63f61c5c | 878 | char program[PATH_SIZE]; |
be4bedd1 | 879 | char result[PATH_SIZE]; |
e57e7bc1 | 880 | |
8bd41f36 | 881 | strlcpy(program, key_val(rule, &rule->program), sizeof(program)); |
e57e7bc1 | 882 | apply_format(udev, program, sizeof(program), class_dev, sysfs_device); |
63698391 | 883 | dbg("check for PROGRAM program='%s", program); |
be4bedd1 | 884 | if (execute_program(program, udev->subsystem, result, sizeof(result), NULL) != 0) { |
0cd4ac47 | 885 | dbg("PROGRAM is false"); |
8bd41f36 | 886 | if (rule->program.operation != KEY_OP_NOMATCH) |
28ce66de KS |
887 | goto exit; |
888 | } else { | |
63698391 | 889 | dbg("PROGRAM matches"); |
be4bedd1 KS |
890 | remove_trailing_char(result, '\n'); |
891 | replace_untrusted_chars(result); | |
892 | dbg("result is '%s'", result); | |
893 | strlcpy(udev->program_result, result, sizeof(udev->program_result)); | |
63698391 | 894 | dbg("PROGRAM returned successful"); |
8bd41f36 | 895 | if (rule->program.operation == KEY_OP_NOMATCH) |
28ce66de | 896 | goto exit; |
e57e7bc1 | 897 | } |
63698391 | 898 | dbg("PROGRAM key is true"); |
e57e7bc1 KS |
899 | } |
900 | ||
901 | /* check for matching result of external program */ | |
8bd41f36 | 902 | if (match_key("RESULT", rule, &rule->result, udev->program_result)) |
a0e5382d | 903 | goto exit; |
e57e7bc1 KS |
904 | |
905 | /* rule matches */ | |
906 | return 0; | |
907 | ||
82b16983 KS |
908 | exit: |
909 | return -1; | |
724257d9 GKH |
910 | } |
911 | ||
8bd41f36 | 912 | int udev_rules_get_name(struct udev_rules *rules, struct udevice *udev, struct sysfs_class_device *class_dev) |
724257d9 | 913 | { |
707680b1 | 914 | struct sysfs_class_device *class_dev_parent; |
724257d9 | 915 | struct sysfs_device *sysfs_device = NULL; |
e5e322bc | 916 | struct udev_rule *rule; |
613ffbeb | 917 | int name_set = 0; |
724257d9 | 918 | |
707680b1 KS |
919 | dbg("class_dev->name='%s'", class_dev->name); |
920 | ||
921 | /* Figure out where the "device"-symlink is at. For char devices this will | |
922 | * always be in the class_dev->path. On block devices, only the main block | |
923 | * device will have the device symlink in it's path. All partition devices | |
924 | * need to look at the symlink in its parent directory. | |
925 | */ | |
926 | class_dev_parent = sysfs_get_classdev_parent(class_dev); | |
927 | if (class_dev_parent != NULL) { | |
928 | dbg("given class device has a parent, use this instead"); | |
929 | sysfs_device = sysfs_get_classdev_device(class_dev_parent); | |
930 | } else { | |
931 | sysfs_device = sysfs_get_classdev_device(class_dev); | |
932 | } | |
724257d9 | 933 | |
724257d9 | 934 | if (sysfs_device) { |
7a947ce5 | 935 | dbg("found devices device: path='%s', bus_id='%s', bus='%s'", |
707680b1 | 936 | sysfs_device->path, sysfs_device->bus_id, sysfs_device->bus); |
63f61c5c | 937 | strlcpy(udev->bus_id, sysfs_device->bus_id, sizeof(udev->bus_id)); |
724257d9 GKH |
938 | } |
939 | ||
9ed47a9f | 940 | dbg("udev->kernel_name='%s'", udev->kernel_name); |
724257d9 GKH |
941 | |
942 | /* look for a matching rule to apply */ | |
8bd41f36 | 943 | udev_rules_iter_init(rules); |
6bf0ffe8 | 944 | while (1) { |
8bd41f36 | 945 | rule = udev_rules_iter_next(rules); |
6bf0ffe8 KS |
946 | if (rule == NULL) |
947 | break; | |
948 | ||
613ffbeb | 949 | if (name_set && rule->name.operation != KEY_OP_UNSET) { |
bf5d2964 KS |
950 | dbg("node name already set, rule ignored"); |
951 | continue; | |
952 | } | |
953 | ||
724257d9 | 954 | dbg("process rule"); |
e5e322bc | 955 | if (match_rule(udev, rule, class_dev, sysfs_device) == 0) { |
fd9efc00 | 956 | /* apply options */ |
e5e322bc | 957 | if (rule->ignore_device) { |
8bd41f36 | 958 | info("rule applied, '%s' is ignored", udev->kernel_name); |
821d0ec8 KS |
959 | udev->ignore_device = 1; |
960 | return 0; | |
fd9efc00 | 961 | } |
e5e322bc | 962 | if (rule->ignore_remove) { |
b821330f | 963 | udev->ignore_remove = 1; |
e5e322bc | 964 | dbg("remove event should be ignored"); |
fd9efc00 KS |
965 | } |
966 | /* apply all_partitions option only at a main block device */ | |
e5e322bc KS |
967 | if (rule->partitions && udev->type == DEV_BLOCK && udev->kernel_number[0] == '\0') { |
968 | udev->partitions = rule->partitions; | |
fd9efc00 KS |
969 | dbg("creation of partition nodes requested"); |
970 | } | |
971 | ||
38875577 | 972 | /* apply permissions */ |
c974742b KS |
973 | if (!udev->mode_final && rule->mode != 0000) { |
974 | if (rule->mode_operation == KEY_OP_ASSIGN_FINAL) | |
975 | udev->mode_final = 1; | |
e5e322bc | 976 | udev->mode = rule->mode; |
8bd41f36 | 977 | dbg("applied mode=%#o to '%s'", rule->mode, udev->kernel_name); |
38875577 | 978 | } |
8bd41f36 KS |
979 | if (!udev->owner_final && rule->owner.operation != KEY_OP_UNSET) { |
980 | if (rule->owner.operation == KEY_OP_ASSIGN_FINAL) | |
c974742b | 981 | udev->owner_final = 1; |
8bd41f36 | 982 | strlcpy(udev->owner, key_val(rule, &rule->owner), sizeof(udev->owner)); |
38875577 MB |
983 | apply_format(udev, udev->owner, sizeof(udev->owner), class_dev, sysfs_device); |
984 | dbg("applied owner='%s' to '%s'", udev->owner, udev->kernel_name); | |
985 | } | |
8bd41f36 KS |
986 | if (!udev->group_final && rule->group.operation != KEY_OP_UNSET) { |
987 | if (rule->group.operation == KEY_OP_ASSIGN_FINAL) | |
c974742b | 988 | udev->group_final = 1; |
8bd41f36 | 989 | strlcpy(udev->group, key_val(rule, &rule->group), sizeof(udev->group)); |
8cf97fb0 | 990 | apply_format(udev, udev->group, sizeof(udev->group), class_dev, sysfs_device); |
38875577 MB |
991 | dbg("applied group='%s' to '%s'", udev->group, udev->kernel_name); |
992 | } | |
993 | ||
9ed47a9f | 994 | /* collect symlinks */ |
8bd41f36 | 995 | if (!udev->symlink_final && rule->symlink.operation != KEY_OP_UNSET) { |
63f61c5c | 996 | char temp[PATH_SIZE]; |
e48fc108 | 997 | char *pos, *next; |
ddd5b5dc | 998 | |
8bd41f36 | 999 | if (rule->symlink.operation == KEY_OP_ASSIGN_FINAL) |
c974742b | 1000 | udev->symlink_final = 1; |
8bd41f36 | 1001 | if (rule->symlink.operation == KEY_OP_ASSIGN || rule->symlink.operation == KEY_OP_ASSIGN_FINAL) { |
995aec87 KS |
1002 | struct name_entry *name_loop; |
1003 | struct name_entry *temp_loop; | |
1004 | ||
c07669bd | 1005 | info("reset symlink list"); |
995aec87 KS |
1006 | list_for_each_entry_safe(name_loop, temp_loop, &udev->symlink_list, node) { |
1007 | list_del(&name_loop->node); | |
1008 | free(name_loop); | |
1009 | } | |
1010 | } | |
8bd41f36 KS |
1011 | strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp)); |
1012 | apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device); | |
1013 | info("rule applied, added symlink '%s'", temp); | |
1014 | ||
1015 | /* add multiple symlinks separated by spaces */ | |
1016 | pos = temp; | |
7b2bdb4b KS |
1017 | while (isspace(pos[0])) |
1018 | pos++; | |
1019 | next = strchr(pos, ' '); | |
8bd41f36 KS |
1020 | while (next) { |
1021 | next[0] = '\0'; | |
6b493a20 | 1022 | info("add symlink '%s'", pos); |
e48fc108 | 1023 | name_list_add(&udev->symlink_list, pos, 0); |
7b2bdb4b KS |
1024 | while (isspace(next[1])) |
1025 | next++; | |
8bd41f36 KS |
1026 | pos = &next[1]; |
1027 | next = strchr(pos, ' '); | |
e48fc108 | 1028 | } |
7b2bdb4b KS |
1029 | if (pos[0] != '\0') { |
1030 | info("add symlink '%s'", pos); | |
1031 | name_list_add(&udev->symlink_list, pos, 0); | |
1032 | } | |
97ed02ee | 1033 | } |
1034 | ||
821d0ec8 | 1035 | /* set name, later rules with name set will be ignored */ |
8bd41f36 | 1036 | if (rule->name.operation != KEY_OP_UNSET) { |
613ffbeb | 1037 | name_set = 1; |
8bd41f36 KS |
1038 | strlcpy(udev->name, key_val(rule, &rule->name), sizeof(udev->name)); |
1039 | apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); | |
1040 | ||
1041 | info("rule applied, '%s' becomes '%s'", udev->kernel_name, udev->name); | |
1042 | if (udev->type != DEV_NET) | |
1043 | dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i", | |
1044 | udev->name, udev->owner, udev->group, udev->mode, udev->partitions); | |
821d0ec8 | 1045 | } |
5f72c470 | 1046 | |
8bd41f36 | 1047 | if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) { |
821d0ec8 KS |
1048 | char program[PATH_SIZE]; |
1049 | ||
8bd41f36 | 1050 | if (rule->run.operation == KEY_OP_ASSIGN_FINAL) |
c974742b | 1051 | udev->run_final = 1; |
8bd41f36 | 1052 | if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) { |
995aec87 KS |
1053 | struct name_entry *name_loop; |
1054 | struct name_entry *temp_loop; | |
1055 | ||
c07669bd | 1056 | info("reset run list"); |
995aec87 KS |
1057 | list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) { |
1058 | list_del(&name_loop->node); | |
1059 | free(name_loop); | |
1060 | } | |
1061 | } | |
8bd41f36 KS |
1062 | strlcpy(program, key_val(rule, &rule->run), sizeof(program)); |
1063 | apply_format(udev, program, sizeof(program), class_dev, sysfs_device); | |
1064 | dbg("add run '%s'", program); | |
1065 | name_list_add(&udev->run_list, program, 0); | |
97ed02ee | 1066 | } |
3b6ed8bb KS |
1067 | |
1068 | if (rule->last_rule) { | |
1069 | dbg("last rule to be applied"); | |
1070 | break; | |
1071 | } | |
594dd610 KS |
1072 | |
1073 | if (rule->goto_label.operation != KEY_OP_UNSET) { | |
1074 | dbg("moving forward to label '%s'", key_val(rule, &rule->goto_label)); | |
1075 | udev_rules_iter_label(rules, key_val(rule, &rule->goto_label)); | |
1076 | } | |
724257d9 | 1077 | } |
ac28b86d | 1078 | } |
c2405f50 | 1079 | |
613ffbeb | 1080 | if (!name_set) { |
63f61c5c | 1081 | strlcpy(udev->name, udev->kernel_name, sizeof(udev->name)); |
8bd41f36 | 1082 | info("no rule found, will use kernel name '%s'", udev->name); |
b821330f | 1083 | } |
7bd22a78 | 1084 | |
c1ab0461 KS |
1085 | if (udev->tmp_node[0] != '\0') { |
1086 | dbg("removing temporary device node"); | |
1087 | unlink_secure(udev->tmp_node); | |
1088 | udev->tmp_node[0] = '\0'; | |
1089 | } | |
1090 | ||
120d45d0 | 1091 | return 0; |
185a35a4 | 1092 | } |
821d0ec8 | 1093 | |
8bd41f36 | 1094 | int udev_rules_get_run(struct udev_rules *rules, struct udevice *udev, struct sysfs_device *sysfs_device) |
821d0ec8 KS |
1095 | { |
1096 | struct udev_rule *rule; | |
821d0ec8 KS |
1097 | |
1098 | /* look for a matching rule to apply */ | |
8bd41f36 | 1099 | udev_rules_iter_init(rules); |
6bf0ffe8 | 1100 | while (1) { |
8bd41f36 | 1101 | rule = udev_rules_iter_next(rules); |
6bf0ffe8 KS |
1102 | if (rule == NULL) |
1103 | break; | |
821d0ec8 | 1104 | |
6bf0ffe8 | 1105 | dbg("process rule"); |
8bd41f36 KS |
1106 | if (rule->name.operation != KEY_OP_UNSET || rule->symlink.operation != KEY_OP_UNSET || |
1107 | rule->mode_operation != KEY_OP_UNSET || rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) { | |
821d0ec8 KS |
1108 | dbg("skip rule that names a device"); |
1109 | continue; | |
1110 | } | |
1111 | ||
c07669bd KS |
1112 | if (match_rule(udev, rule, NULL, sysfs_device) == 0) { |
1113 | if (rule->ignore_device) { | |
8bd41f36 | 1114 | info("rule applied, '%s' is ignored", udev->kernel_name); |
c07669bd KS |
1115 | udev->ignore_device = 1; |
1116 | return 0; | |
821d0ec8 | 1117 | } |
821d0ec8 | 1118 | |
8bd41f36 | 1119 | if (!udev->run_final && rule->run.operation != KEY_OP_UNSET) { |
c07669bd | 1120 | char program[PATH_SIZE]; |
821d0ec8 | 1121 | |
8bd41f36 | 1122 | if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) { |
c07669bd KS |
1123 | struct name_entry *name_loop; |
1124 | struct name_entry *temp_loop; | |
821d0ec8 | 1125 | |
c07669bd KS |
1126 | info("reset run list"); |
1127 | list_for_each_entry_safe(name_loop, temp_loop, &udev->run_list, node) { | |
1128 | list_del(&name_loop->node); | |
1129 | free(name_loop); | |
1130 | } | |
821d0ec8 | 1131 | } |
8bd41f36 KS |
1132 | strlcpy(program, key_val(rule, &rule->run), sizeof(program)); |
1133 | apply_format(udev, program, sizeof(program), NULL, sysfs_device); | |
1134 | dbg("add run '%s'", program); | |
1135 | name_list_add(&udev->run_list, program, 0); | |
1136 | if (rule->run.operation == KEY_OP_ASSIGN_FINAL) | |
c07669bd | 1137 | break; |
821d0ec8 | 1138 | } |
821d0ec8 | 1139 | |
c07669bd KS |
1140 | if (rule->last_rule) { |
1141 | dbg("last rule to be applied"); | |
1142 | break; | |
1143 | } | |
821d0ec8 KS |
1144 | } |
1145 | } | |
1146 | ||
1147 | return 0; | |
1148 | } |