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