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