]>
Commit | Line | Data |
---|---|---|
2232cac8 | 1 | /* |
e5e322bc | 2 | * udev_rules.c |
2232cac8 GKH |
3 | * |
4 | * Userspace devfs | |
5 | * | |
6 | * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | |
fd9efc00 | 7 | * Copyright (C) 2003-2005 Kay Sievers <kay.sievers@vrfy.org> |
2232cac8 GKH |
8 | * |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation version 2 of the License. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along | |
20 | * with this program; if not, write to the Free Software Foundation, Inc., | |
21 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | * | |
23 | */ | |
24 | ||
25 | #include <stddef.h> | |
26 | #include <stdlib.h> | |
27 | #include <string.h> | |
28 | #include <stdio.h> | |
29 | #include <fcntl.h> | |
30 | #include <ctype.h> | |
31 | #include <unistd.h> | |
32 | #include <errno.h> | |
c27e6911 | 33 | #include <sys/wait.h> |
dac056aa | 34 | #include <sys/stat.h> |
2232cac8 | 35 | |
c80da508 | 36 | #include "libsysfs/sysfs/libsysfs.h" |
2232cac8 | 37 | #include "list.h" |
63f61c5c | 38 | #include "udev_libc_wrapper.h" |
2232cac8 | 39 | #include "udev.h" |
9af5bb2f | 40 | #include "udev_utils.h" |
2232cac8 | 41 | #include "udev_version.h" |
54988802 | 42 | #include "logging.h" |
e5e322bc | 43 | #include "udev_rules.h" |
02fa9ae5 | 44 | #include "udev_db.h" |
2232cac8 | 45 | |
a27cd06c | 46 | |
9f1da361 KS |
47 | /* compare string with pattern (supports * ? [0-9] [!A-Z]) */ |
48 | static int strcmp_pattern(const char *p, const char *s) | |
c124eafa | 49 | { |
8a08e4b1 KS |
50 | if (s[0] == '\0') { |
51 | while (p[0] == '*') | |
9f1da361 | 52 | p++; |
8a08e4b1 | 53 | return (p[0] != '\0'); |
9f1da361 | 54 | } |
8a08e4b1 | 55 | switch (p[0]) { |
9f1da361 KS |
56 | case '[': |
57 | { | |
58 | int not = 0; | |
59 | p++; | |
8a08e4b1 | 60 | if (p[0] == '!') { |
9f1da361 KS |
61 | not = 1; |
62 | p++; | |
63 | } | |
8a08e4b1 | 64 | while ((p[0] != '\0') && (p[0] != ']')) { |
9f1da361 KS |
65 | int match = 0; |
66 | if (p[1] == '-') { | |
8a08e4b1 | 67 | if ((s[0] >= p[0]) && (s[0] <= p[2])) |
9f1da361 KS |
68 | match = 1; |
69 | p += 3; | |
70 | } else { | |
8a08e4b1 | 71 | match = (p[0] == s[0]); |
9f1da361 KS |
72 | p++; |
73 | } | |
74 | if (match ^ not) { | |
8a08e4b1 | 75 | while ((p[0] != '\0') && (p[0] != ']')) |
9f1da361 | 76 | p++; |
8a08e4b1 KS |
77 | if (p[0] == ']') |
78 | return strcmp_pattern(p+1, s+1); | |
9f1da361 KS |
79 | } |
80 | } | |
81 | } | |
82 | break; | |
83 | case '*': | |
84 | if (strcmp_pattern(p, s+1)) | |
85 | return strcmp_pattern(p+1, s); | |
86 | return 0; | |
87 | case '\0': | |
8a08e4b1 | 88 | if (s[0] == '\0') { |
9f1da361 KS |
89 | return 0; |
90 | } | |
91 | break; | |
92 | default: | |
8a08e4b1 | 93 | if ((p[0] == s[0]) || (p[0] == '?')) |
9f1da361 KS |
94 | return strcmp_pattern(p+1, s+1); |
95 | break; | |
96 | } | |
97 | return 1; | |
c124eafa KS |
98 | } |
99 | ||
a27cd06c KS |
100 | /* extract possible {attr} and move str behind it */ |
101 | static char *get_format_attribute(char **str) | |
102 | { | |
103 | char *pos; | |
104 | char *attr = NULL; | |
105 | ||
106 | if (*str[0] == '{') { | |
107 | pos = strchr(*str, '}'); | |
108 | if (pos == NULL) { | |
6b493a20 | 109 | err("missing closing brace for format"); |
a27cd06c KS |
110 | return NULL; |
111 | } | |
112 | pos[0] = '\0'; | |
113 | attr = *str+1; | |
114 | *str = pos+1; | |
115 | dbg("attribute='%s', str='%s'", attr, *str); | |
116 | } | |
117 | return attr; | |
118 | } | |
119 | ||
120 | /* extract possible format length and move str behind it*/ | |
121 | static int get_format_len(char **str) | |
122 | { | |
123 | int num; | |
124 | char *tail; | |
125 | ||
126 | if (isdigit(*str[0])) { | |
127 | num = (int) strtoul(*str, &tail, 10); | |
aebef544 | 128 | if (num > 0) { |
a27cd06c KS |
129 | *str = tail; |
130 | dbg("format length=%i", num); | |
131 | return num; | |
132 | } else { | |
6b493a20 | 133 | err("format parsing error '%s'", *str); |
a27cd06c KS |
134 | } |
135 | } | |
136 | return -1; | |
137 | } | |
138 | ||
0a8dd7f3 DZ |
139 | /** Finds the lowest positive N such that <name>N isn't present in |
140 | * $(udevroot) either as a file or a symlink. | |
141 | * | |
142 | * @param name Name to check for | |
143 | * @return 0 if <name> didn't exist and N otherwise. | |
144 | */ | |
2b41e68a | 145 | static int find_free_number(struct udevice *udev, const char *name) |
0a8dd7f3 | 146 | { |
63f61c5c KS |
147 | char devpath[PATH_SIZE]; |
148 | char filename[PATH_SIZE]; | |
2b41e68a | 149 | int num = 0; |
0a8dd7f3 | 150 | |
63f61c5c | 151 | strlcpy(filename, name, sizeof(filename)); |
0a8dd7f3 | 152 | while (1) { |
2b41e68a | 153 | dbg("look for existing node '%s'", filename); |
63f61c5c | 154 | if (udev_db_search_name(devpath, sizeof(devpath), filename) != 0) { |
2b41e68a KS |
155 | dbg("free num=%d", num); |
156 | return num; | |
157 | } | |
0a8dd7f3 | 158 | |
2b41e68a KS |
159 | num++; |
160 | if (num > 1000) { | |
161 | info("find_free_number gone crazy (num=%d), aborted", num); | |
162 | return -1; | |
163 | } | |
63f61c5c KS |
164 | snprintf(filename, sizeof(filename), "%s%d", name, num); |
165 | filename[sizeof(filename)-1] = '\0'; | |
2b41e68a | 166 | } |
0a8dd7f3 DZ |
167 | } |
168 | ||
18614ab2 KS |
169 | static int find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, |
170 | const char *name, char *value, size_t len) | |
171 | { | |
172 | struct sysfs_attribute *tmpattr; | |
173 | ||
174 | dbg("look for device attribute '%s'", name); | |
175 | if (class_dev) { | |
176 | tmpattr = sysfs_get_classdev_attr(class_dev, name); | |
177 | if (tmpattr) | |
178 | goto attr_found; | |
179 | } | |
180 | if (sysfs_device) { | |
181 | tmpattr = sysfs_get_device_attr(sysfs_device, name); | |
182 | if (tmpattr) | |
183 | goto attr_found; | |
184 | } | |
185 | ||
186 | return -1; | |
187 | ||
188 | attr_found: | |
189 | strlcpy(value, tmpattr->value, len); | |
190 | remove_trailing_char(value, '\n'); | |
191 | ||
192 | dbg("found attribute '%s'", tmpattr->path); | |
193 | return 0; | |
194 | } | |
195 | ||
831f800d | 196 | static void apply_format(struct udevice *udev, char *string, size_t maxsize, |
b821330f | 197 | struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) |
f3b04a2e | 198 | { |
63f61c5c KS |
199 | char temp[PATH_SIZE]; |
200 | char temp2[PATH_SIZE]; | |
03fd7a3a | 201 | char *tail, *pos, *cpos, *attr, *rest; |
63ead27c | 202 | int len; |
88f09368 | 203 | int i; |
a27cd06c | 204 | char c; |
0a8dd7f3 | 205 | unsigned int next_free_number; |
69aa6dfb | 206 | struct sysfs_class_device *class_dev_parent; |
f3b04a2e | 207 | |
8ffb636f | 208 | pos = string; |
f3b04a2e | 209 | while (1) { |
a36a3c3a KS |
210 | pos = strchr(pos, '%'); |
211 | if (pos == NULL) | |
a27cd06c | 212 | break; |
b1c5e333 | 213 | |
a36a3c3a KS |
214 | pos[0] = '\0'; |
215 | tail = pos+1; | |
216 | len = get_format_len(&tail); | |
217 | c = tail[0]; | |
63f61c5c | 218 | strlcpy(temp, tail+1, sizeof(temp)); |
a36a3c3a KS |
219 | tail = temp; |
220 | dbg("format=%c, string='%s', tail='%s'",c , string, tail); | |
a27cd06c KS |
221 | attr = get_format_attribute(&tail); |
222 | ||
223 | switch (c) { | |
c1ab0461 | 224 | case 'p': |
63f61c5c | 225 | strlcat(string, udev->devpath, maxsize); |
c1ab0461 KS |
226 | dbg("substitute kernel name '%s'", udev->kernel_name); |
227 | break; | |
a27cd06c | 228 | case 'b': |
63f61c5c | 229 | strlcat(string, udev->bus_id, maxsize); |
a27cd06c KS |
230 | dbg("substitute bus_id '%s'", udev->bus_id); |
231 | break; | |
232 | case 'k': | |
63f61c5c | 233 | strlcat(string, udev->kernel_name, maxsize); |
a27cd06c KS |
234 | dbg("substitute kernel name '%s'", udev->kernel_name); |
235 | break; | |
236 | case 'n': | |
63f61c5c | 237 | strlcat(string, udev->kernel_number, maxsize); |
a27cd06c | 238 | dbg("substitute kernel number '%s'", udev->kernel_number); |
f3b04a2e | 239 | break; |
a27cd06c | 240 | case 'm': |
03fd7a3a | 241 | sprintf(temp2, "%d", minor(udev->devt)); |
63f61c5c | 242 | strlcat(string, temp2, maxsize); |
03fd7a3a | 243 | dbg("substitute minor number '%s'", temp2); |
a27cd06c | 244 | break; |
c58e36c0 | 245 | case 'M': |
03fd7a3a | 246 | sprintf(temp2, "%d", major(udev->devt)); |
63f61c5c | 247 | strlcat(string, temp2, maxsize); |
03fd7a3a | 248 | dbg("substitute major number '%s'", temp2); |
a27cd06c KS |
249 | break; |
250 | case 'c': | |
b821330f | 251 | if (udev->program_result[0] == '\0') |
f3b04a2e | 252 | break; |
88f09368 | 253 | /* get part part of the result string */ |
63ead27c | 254 | i = 0; |
88f09368 | 255 | if (attr != NULL) |
558f80ba | 256 | i = strtoul(attr, &rest, 10); |
88f09368 | 257 | if (i > 0) { |
03fd7a3a KS |
258 | dbg("request part #%d of result string", i); |
259 | cpos = udev->program_result; | |
260 | while (--i) { | |
261 | while (cpos[0] != '\0' && !isspace(cpos[0])) | |
262 | cpos++; | |
263 | while (isspace(cpos[0])) | |
264 | cpos++; | |
b1c5e333 | 265 | } |
9fe3f9a9 | 266 | if (i > 0) { |
6b493a20 | 267 | err("requested part of result string not found"); |
9fe3f9a9 | 268 | break; |
a27cd06c | 269 | } |
63f61c5c | 270 | strlcpy(temp2, cpos, sizeof(temp2)); |
03fd7a3a KS |
271 | /* %{2+}c copies the whole string from the second part on */ |
272 | if (rest[0] != '+') { | |
273 | cpos = strchr(temp2, ' '); | |
274 | if (cpos) | |
275 | cpos[0] = '\0'; | |
276 | } | |
63f61c5c | 277 | strlcat(string, temp2, maxsize); |
27c3403d | 278 | dbg("substitute part of result string '%s'", temp2); |
a27cd06c | 279 | } else { |
63f61c5c | 280 | strlcat(string, udev->program_result, maxsize); |
a27cd06c | 281 | dbg("substitute result string '%s'", udev->program_result); |
f3b04a2e | 282 | } |
f3b04a2e | 283 | break; |
a27cd06c | 284 | case 's': |
5e39f90b | 285 | if (attr == NULL) { |
a27cd06c | 286 | dbg("missing attribute"); |
5e39f90b KS |
287 | break; |
288 | } | |
18614ab2 | 289 | if (find_sysfs_attribute(class_dev, sysfs_device, attr, temp2, sizeof(temp2)) != 0) { |
6b493a20 | 290 | dbg("sysfs attribute '%s' not found", attr); |
5e39f90b KS |
291 | break; |
292 | } | |
18614ab2 KS |
293 | /* strip trailing whitespace of sysfs value */ |
294 | i = strlen(temp2); | |
295 | while (i > 0 && isspace(temp2[i-1])) | |
296 | temp2[--i] = '\0'; | |
297 | replace_untrusted_chars(temp2); | |
298 | strlcat(string, temp2, maxsize); | |
299 | dbg("substitute sysfs value '%s'", temp2); | |
a27cd06c KS |
300 | break; |
301 | case '%': | |
63f61c5c | 302 | strlcat(string, "%", maxsize); |
a36a3c3a | 303 | pos++; |
a27cd06c | 304 | break; |
0a8dd7f3 DZ |
305 | case 'e': |
306 | next_free_number = find_free_number(udev, string); | |
307 | if (next_free_number > 0) { | |
2b41e68a | 308 | sprintf(temp2, "%d", next_free_number); |
63f61c5c | 309 | strlcat(string, temp2, maxsize); |
0a8dd7f3 DZ |
310 | } |
311 | break; | |
69aa6dfb | 312 | case 'P': |
8f2f6e42 KS |
313 | if (!class_dev) |
314 | break; | |
69aa6dfb KS |
315 | class_dev_parent = sysfs_get_classdev_parent(class_dev); |
316 | if (class_dev_parent != NULL) { | |
317 | struct udevice udev_parent; | |
318 | ||
319 | dbg("found parent '%s', get the node name", class_dev_parent->path); | |
e48fc108 | 320 | udev_init_device(&udev_parent, NULL, NULL); |
69aa6dfb | 321 | /* lookup the name in the udev_db with the DEVPATH of the parent */ |
e48fc108 | 322 | if (udev_db_get_device(&udev_parent, &class_dev_parent->path[strlen(sysfs_path)]) == 0) { |
63f61c5c | 323 | strlcat(string, udev_parent.name, maxsize); |
69aa6dfb KS |
324 | dbg("substitute parent node name'%s'", udev_parent.name); |
325 | } else | |
326 | dbg("parent not found in database"); | |
e48fc108 | 327 | udev_cleanup_device(&udev_parent); |
69aa6dfb KS |
328 | } |
329 | break; | |
c1ab0461 KS |
330 | case 'N': |
331 | if (udev->tmp_node[0] == '\0') { | |
332 | dbg("create temporary device node for callout"); | |
63f61c5c KS |
333 | snprintf(udev->tmp_node, sizeof(udev->tmp_node), "%s/.tmp-%u-%u", |
334 | udev_root, major(udev->devt), minor(udev->devt)); | |
335 | udev->tmp_node[sizeof(udev->tmp_node)-1] = '\0'; | |
7e720bd4 | 336 | udev_make_node(udev, udev->tmp_node, udev->devt, 0600, 0, 0); |
c1ab0461 | 337 | } |
63f61c5c | 338 | strlcat(string, udev->tmp_node, maxsize); |
c1ab0461 KS |
339 | dbg("substitute temporary device node name '%s'", udev->tmp_node); |
340 | break; | |
69aa6dfb | 341 | case 'r': |
63f61c5c | 342 | strlcat(string, udev_root, maxsize); |
69aa6dfb KS |
343 | dbg("substitute udev_root '%s'", udev_root); |
344 | break; | |
a27cd06c | 345 | default: |
6b493a20 | 346 | err("unknown substitution type '%%%c'", c); |
a27cd06c KS |
347 | break; |
348 | } | |
63ead27c KS |
349 | /* truncate to specified length */ |
350 | if (len > 0) | |
351 | pos[len] = '\0'; | |
352 | ||
63f61c5c | 353 | strlcat(string, tail, maxsize); |
f3b04a2e GKH |
354 | } |
355 | } | |
356 | ||
af4b05d4 | 357 | static int execute_program(struct udevice *udev, const char *path, char *value, int len) |
c27e6911 PM |
358 | { |
359 | int retval; | |
35b38379 | 360 | int count; |
c27e6911 PM |
361 | int status; |
362 | int fds[2]; | |
363 | pid_t pid; | |
20524642 | 364 | char *pos; |
63f61c5c KS |
365 | char arg[PATH_SIZE]; |
366 | char *argv[(sizeof(arg) / 2) + 1]; | |
bc434511 | 367 | int i; |
c27e6911 | 368 | |
63f61c5c | 369 | strlcpy(arg, path, sizeof(arg)); |
35b38379 KS |
370 | i = 0; |
371 | if (strchr(path, ' ')) { | |
35b38379 KS |
372 | pos = arg; |
373 | while (pos != NULL) { | |
374 | if (pos[0] == '\'') { | |
375 | /* don't separate if in apostrophes */ | |
376 | pos++; | |
377 | argv[i] = strsep(&pos, "\'"); | |
a75e2c14 | 378 | while (pos && pos[0] == ' ') |
35b38379 | 379 | pos++; |
a75e2c14 | 380 | } else { |
35b38379 KS |
381 | argv[i] = strsep(&pos, " "); |
382 | } | |
383 | dbg("arg[%i] '%s'", i, argv[i]); | |
384 | i++; | |
385 | } | |
f608f8ac KS |
386 | argv[i] = NULL; |
387 | dbg("execute '%s' with parsed arguments", arg); | |
388 | } else { | |
389 | argv[0] = arg; | |
af4b05d4 | 390 | argv[1] = udev->subsystem; |
f608f8ac KS |
391 | argv[2] = NULL; |
392 | dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]); | |
35b38379 | 393 | } |
f608f8ac | 394 | |
c27e6911 PM |
395 | retval = pipe(fds); |
396 | if (retval != 0) { | |
6b493a20 | 397 | err("pipe failed"); |
c27e6911 PM |
398 | return -1; |
399 | } | |
35b38379 | 400 | |
c27e6911 | 401 | pid = fork(); |
2a25816f KS |
402 | switch(pid) { |
403 | case 0: | |
18614ab2 | 404 | /* child dup2 write side of pipe to STDOUT */ |
6e3e3c34 | 405 | dup2(fds[1], STDOUT_FILENO); |
f608f8ac | 406 | retval = execv(arg, argv); |
35b38379 | 407 | |
28ce66de | 408 | info(KEY_PROGRAM " execution of '%s' failed", path); |
2a25816f KS |
409 | exit(1); |
410 | case -1: | |
6b493a20 | 411 | err("fork of '%s' failed", path); |
2a25816f KS |
412 | return -1; |
413 | default: | |
8f43a65e | 414 | /* parent reads from fds[0] */ |
c27e6911 PM |
415 | close(fds[1]); |
416 | retval = 0; | |
35b38379 | 417 | i = 0; |
c27e6911 | 418 | while (1) { |
35b38379 | 419 | count = read(fds[0], value + i, len - i-1); |
18614ab2 KS |
420 | if (count < 0) { |
421 | err("read failed with '%s'", strerror(errno)); | |
422 | retval = -1; | |
423 | } | |
424 | ||
425 | if (count == 0) | |
c27e6911 | 426 | break; |
35b38379 KS |
427 | |
428 | i += count; | |
429 | if (i >= len-1) { | |
6b493a20 | 430 | err("result len %d too short", len); |
c27e6911 | 431 | retval = -1; |
35b38379 | 432 | break; |
c27e6911 PM |
433 | } |
434 | } | |
35b38379 | 435 | value[i] = '\0'; |
35b38379 KS |
436 | |
437 | close(fds[0]); | |
e920fed3 | 438 | waitpid(pid, &status, 0); |
35b38379 | 439 | |
c27e6911 | 440 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { |
ac28b86d | 441 | dbg("exec program status 0x%x", status); |
c27e6911 PM |
442 | retval = -1; |
443 | } | |
444 | } | |
a8b01705 | 445 | |
18614ab2 KS |
446 | if (!retval) { |
447 | remove_trailing_char(value, '\n'); | |
448 | dbg("result is '%s'", value); | |
449 | replace_untrusted_chars(value); | |
450 | } else | |
451 | value[0] = '\0'; | |
a27cd06c | 452 | |
18614ab2 | 453 | return retval; |
a27cd06c KS |
454 | } |
455 | ||
79f651f4 | 456 | static int compare_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, struct key_pair *pair) |
a27cd06c | 457 | { |
18614ab2 | 458 | char value[VALUE_SIZE]; |
d5f91372 | 459 | int i; |
a27cd06c | 460 | |
18614ab2 KS |
461 | if (find_sysfs_attribute(class_dev, sysfs_device, pair->name, value, sizeof(value)) != 0) |
462 | return -1; | |
a27cd06c | 463 | |
d5f91372 | 464 | /* strip trailing whitespace of value, if not asked to match for it */ |
18614ab2 KS |
465 | if (!isspace(pair->value[strlen(pair->value)-1])) { |
466 | i = strlen(value); | |
467 | while (i > 0 && isspace(value[i-1])) | |
468 | value[--i] = '\0'; | |
469 | dbg("removed %i trailing whitespace chars from '%s'", strlen(value)-i, value); | |
d5f91372 KS |
470 | } |
471 | ||
18614ab2 KS |
472 | dbg("compare attribute '%s' value '%s' with '%s'", pair->name, value, pair->value); |
473 | if (strcmp_pattern(pair->value, value) != 0) | |
474 | return -1; | |
a8b01705 | 475 | |
18614ab2 | 476 | dbg("found matching attribute '%s' with value '%s'", pair->name, pair->value); |
a8b01705 GKH |
477 | return 0; |
478 | } | |
479 | ||
e5e322bc | 480 | static int match_rule(struct udevice *udev, struct udev_rule *rule, |
5f72c470 | 481 | struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) |
dac056aa | 482 | { |
e5e322bc | 483 | if (rule->kernel[0] != '\0') { |
28ce66de | 484 | dbg("check for " KEY_KERNEL " rule->kernel='%s' class_dev->name='%s'", |
e5e322bc KS |
485 | rule->kernel, class_dev->name); |
486 | if (strcmp_pattern(rule->kernel, class_dev->name) != 0) { | |
28ce66de KS |
487 | dbg(KEY_KERNEL " is not matching"); |
488 | if (rule->kernel_operation != KEY_OP_NOMATCH) | |
489 | goto exit; | |
490 | } else { | |
491 | dbg(KEY_KERNEL " matches"); | |
492 | if (rule->kernel_operation == KEY_OP_NOMATCH) | |
493 | goto exit; | |
ac28b86d | 494 | } |
28ce66de | 495 | dbg(KEY_KERNEL " key is true"); |
03a9875b | 496 | } |
ca1cc0fe | 497 | |
e5e322bc | 498 | if (rule->subsystem[0] != '\0') { |
28ce66de | 499 | dbg("check for " KEY_SUBSYSTEM " rule->subsystem='%s' class_dev->name='%s'", |
e5e322bc KS |
500 | rule->subsystem, class_dev->name); |
501 | if (strcmp_pattern(rule->subsystem, udev->subsystem) != 0) { | |
28ce66de KS |
502 | dbg(KEY_SUBSYSTEM " is not matching"); |
503 | if (rule->subsystem_operation != KEY_OP_NOMATCH) | |
504 | goto exit; | |
505 | } else { | |
506 | dbg(KEY_SUBSYSTEM " matches"); | |
507 | if (rule->subsystem_operation == KEY_OP_NOMATCH) | |
508 | goto exit; | |
6818c51d | 509 | } |
28ce66de | 510 | dbg(KEY_SUBSYSTEM " key is true"); |
03a9875b | 511 | } |
6818c51d | 512 | |
3e5958de KS |
513 | if (rule->env_pair_count) { |
514 | int i; | |
515 | ||
516 | dbg("check for " KEY_ENV " pairs"); | |
517 | for (i = 0; i < rule->env_pair_count; i++) { | |
518 | struct key_pair *pair; | |
519 | const char *value; | |
520 | ||
521 | pair = &rule->env_pair[i]; | |
522 | value = getenv(pair->name); | |
523 | if (!value) { | |
524 | dbg(KEY_ENV "{'%s'} is not found", pair->name); | |
525 | goto exit; | |
526 | } | |
527 | if (strcmp_pattern(pair->value, value) != 0) { | |
528 | dbg(KEY_ENV "{'%s'} is not matching", pair->name); | |
529 | if (pair->operation != KEY_OP_NOMATCH) | |
530 | goto exit; | |
531 | } else { | |
532 | dbg(KEY_ENV "{'%s'} matches", pair->name); | |
533 | if (pair->operation == KEY_OP_NOMATCH) | |
534 | goto exit; | |
535 | } | |
536 | } | |
537 | dbg(KEY_ENV " key is true"); | |
538 | } | |
539 | ||
e57e7bc1 | 540 | /* walk up the chain of physical devices and find a match */ |
03a9875b | 541 | while (1) { |
2092fbcd | 542 | /* check for matching driver */ |
e5e322bc | 543 | if (rule->driver[0] != '\0') { |
2b40452a KS |
544 | if (sysfs_device == NULL) { |
545 | dbg("device has no sysfs_device"); | |
ca4c984c | 546 | goto exit; |
2b40452a | 547 | } |
28ce66de | 548 | dbg("check for " KEY_DRIVER " rule->driver='%s' sysfs_device->driver_name='%s'", |
e5e322bc KS |
549 | rule->driver, sysfs_device->driver_name); |
550 | if (strcmp_pattern(rule->driver, sysfs_device->driver_name) != 0) { | |
28ce66de KS |
551 | dbg(KEY_DRIVER " is not matching"); |
552 | if (rule->driver_operation != KEY_OP_NOMATCH) | |
553 | goto try_parent; | |
554 | } else { | |
555 | dbg(KEY_DRIVER " matches"); | |
556 | if (rule->driver_operation == KEY_OP_NOMATCH) | |
557 | goto try_parent; | |
2092fbcd | 558 | } |
28ce66de | 559 | dbg(KEY_DRIVER " key is true"); |
2092fbcd KS |
560 | } |
561 | ||
82b16983 | 562 | /* check for matching bus value */ |
e5e322bc | 563 | if (rule->bus[0] != '\0') { |
82b16983 | 564 | if (sysfs_device == NULL) { |
2b40452a | 565 | dbg("device has no sysfs_device"); |
ca4c984c | 566 | goto exit; |
82b16983 | 567 | } |
28ce66de | 568 | dbg("check for " KEY_BUS " rule->bus='%s' sysfs_device->bus='%s'", |
e5e322bc KS |
569 | rule->bus, sysfs_device->bus); |
570 | if (strcmp_pattern(rule->bus, sysfs_device->bus) != 0) { | |
28ce66de KS |
571 | dbg(KEY_BUS " is not matching"); |
572 | if (rule->bus_operation != KEY_OP_NOMATCH) | |
573 | goto try_parent; | |
574 | } else { | |
575 | dbg(KEY_BUS " matches"); | |
576 | if (rule->bus_operation == KEY_OP_NOMATCH) | |
577 | goto try_parent; | |
82b16983 | 578 | } |
28ce66de | 579 | dbg(KEY_BUS " key is true"); |
82b16983 KS |
580 | } |
581 | ||
ac28b86d | 582 | /* check for matching bus id */ |
e5e322bc | 583 | if (rule->id[0] != '\0') { |
2b40452a KS |
584 | if (sysfs_device == NULL) { |
585 | dbg("device has no sysfs_device"); | |
ca4c984c | 586 | goto exit; |
2b40452a | 587 | } |
28ce66de | 588 | dbg("check " KEY_ID); |
d6d1a18d | 589 | if (strcmp_pattern(rule->id, sysfs_device->bus_id) != 0) { |
28ce66de KS |
590 | dbg(KEY_ID " is not matching"); |
591 | if (rule->id_operation != KEY_OP_NOMATCH) | |
592 | goto try_parent; | |
593 | } else { | |
594 | dbg(KEY_ID " matches"); | |
595 | if (rule->id_operation == KEY_OP_NOMATCH) | |
596 | goto try_parent; | |
ac28b86d | 597 | } |
28ce66de | 598 | dbg(KEY_ID " key is true"); |
ac28b86d | 599 | } |
ca1cc0fe | 600 | |
ac28b86d | 601 | /* check for matching sysfs pairs */ |
3e5958de KS |
602 | if (rule->sysfs_pair_count) { |
603 | int i; | |
604 | ||
28ce66de | 605 | dbg("check " KEY_SYSFS " pairs"); |
3e5958de KS |
606 | for (i = 0; i < rule->sysfs_pair_count; i++) { |
607 | struct key_pair *pair; | |
608 | ||
609 | pair = &rule->sysfs_pair[i]; | |
610 | if (compare_sysfs_attribute(class_dev, sysfs_device, pair) != 0) { | |
611 | dbg(KEY_SYSFS "{'%s'} is not matching", pair->name); | |
612 | if (pair->operation != KEY_OP_NOMATCH) | |
613 | goto try_parent; | |
614 | } else { | |
615 | dbg(KEY_SYSFS "{'%s'} matches", pair->name); | |
616 | if (pair->operation == KEY_OP_NOMATCH) | |
617 | goto try_parent; | |
618 | } | |
ac28b86d | 619 | } |
28ce66de | 620 | dbg(KEY_SYSFS " keys are true"); |
ac28b86d KS |
621 | } |
622 | ||
e57e7bc1 KS |
623 | /* found matching physical device */ |
624 | break; | |
c5118442 KS |
625 | try_parent: |
626 | dbg("try parent sysfs device"); | |
724257d9 GKH |
627 | sysfs_device = sysfs_get_device_parent(sysfs_device); |
628 | if (sysfs_device == NULL) | |
82b16983 | 629 | goto exit; |
ca4c984c KS |
630 | dbg("look at sysfs_device->path='%s'", sysfs_device->path); |
631 | dbg("look at sysfs_device->bus_id='%s'", sysfs_device->bus_id); | |
724257d9 | 632 | } |
82b16983 | 633 | |
e57e7bc1 | 634 | /* execute external program */ |
e5e322bc | 635 | if (rule->program[0] != '\0') { |
63f61c5c | 636 | char program[PATH_SIZE]; |
e57e7bc1 | 637 | |
28ce66de | 638 | dbg("check " KEY_PROGRAM); |
e5e322bc | 639 | strlcpy(program, rule->program, sizeof(program)); |
e57e7bc1 | 640 | apply_format(udev, program, sizeof(program), class_dev, sysfs_device); |
63f61c5c | 641 | if (execute_program(udev, program, udev->program_result, sizeof(udev->program_result)) != 0) { |
28ce66de KS |
642 | dbg(KEY_PROGRAM " returned nonzero"); |
643 | if (rule->program_operation != KEY_OP_NOMATCH) | |
644 | goto exit; | |
645 | } else { | |
646 | dbg(KEY_PROGRAM " returned successful"); | |
647 | if (rule->program_operation == KEY_OP_NOMATCH) | |
648 | goto exit; | |
e57e7bc1 | 649 | } |
28ce66de | 650 | dbg(KEY_PROGRAM " key is true"); |
e57e7bc1 KS |
651 | } |
652 | ||
653 | /* check for matching result of external program */ | |
e5e322bc | 654 | if (rule->result[0] != '\0') { |
28ce66de | 655 | dbg("check for " KEY_RESULT " rule->result='%s', udev->program_result='%s'", |
e5e322bc KS |
656 | rule->result, udev->program_result); |
657 | if (strcmp_pattern(rule->result, udev->program_result) != 0) { | |
28ce66de KS |
658 | dbg(KEY_RESULT " is not matching"); |
659 | if (rule->result_operation != KEY_OP_NOMATCH) | |
660 | goto exit; | |
661 | } else { | |
662 | dbg(KEY_RESULT " matches"); | |
663 | if (rule->result_operation == KEY_OP_NOMATCH) | |
664 | goto exit; | |
e57e7bc1 | 665 | } |
28ce66de | 666 | dbg(KEY_RESULT " key is true"); |
e57e7bc1 KS |
667 | } |
668 | ||
669 | /* rule matches */ | |
670 | return 0; | |
671 | ||
82b16983 KS |
672 | exit: |
673 | return -1; | |
724257d9 GKH |
674 | } |
675 | ||
e5e322bc | 676 | int udev_rules_get_name(struct udevice *udev, struct sysfs_class_device *class_dev) |
724257d9 | 677 | { |
707680b1 | 678 | struct sysfs_class_device *class_dev_parent; |
724257d9 | 679 | struct sysfs_device *sysfs_device = NULL; |
e5e322bc | 680 | struct udev_rule *rule; |
724257d9 | 681 | |
707680b1 KS |
682 | dbg("class_dev->name='%s'", class_dev->name); |
683 | ||
684 | /* Figure out where the "device"-symlink is at. For char devices this will | |
685 | * always be in the class_dev->path. On block devices, only the main block | |
686 | * device will have the device symlink in it's path. All partition devices | |
687 | * need to look at the symlink in its parent directory. | |
688 | */ | |
689 | class_dev_parent = sysfs_get_classdev_parent(class_dev); | |
690 | if (class_dev_parent != NULL) { | |
691 | dbg("given class device has a parent, use this instead"); | |
692 | sysfs_device = sysfs_get_classdev_device(class_dev_parent); | |
693 | } else { | |
694 | sysfs_device = sysfs_get_classdev_device(class_dev); | |
695 | } | |
724257d9 | 696 | |
724257d9 | 697 | if (sysfs_device) { |
7a947ce5 | 698 | dbg("found devices device: path='%s', bus_id='%s', bus='%s'", |
707680b1 | 699 | sysfs_device->path, sysfs_device->bus_id, sysfs_device->bus); |
63f61c5c | 700 | strlcpy(udev->bus_id, sysfs_device->bus_id, sizeof(udev->bus_id)); |
724257d9 GKH |
701 | } |
702 | ||
9ed47a9f | 703 | dbg("udev->kernel_name='%s'", udev->kernel_name); |
724257d9 GKH |
704 | |
705 | /* look for a matching rule to apply */ | |
e5e322bc | 706 | list_for_each_entry(rule, &udev_rule_list, node) { |
724257d9 | 707 | dbg("process rule"); |
e5e322bc | 708 | if (match_rule(udev, rule, class_dev, sysfs_device) == 0) { |
e9390146 | 709 | |
fd9efc00 | 710 | /* apply options */ |
e5e322bc | 711 | if (rule->ignore_device) { |
fd9efc00 | 712 | info("configured rule in '%s[%i]' applied, '%s' is ignored", |
e5e322bc | 713 | rule->config_file, rule->config_line, udev->kernel_name); |
fd9efc00 KS |
714 | return -1; |
715 | } | |
e5e322bc | 716 | if (rule->ignore_remove) { |
b821330f | 717 | udev->ignore_remove = 1; |
e5e322bc | 718 | dbg("remove event should be ignored"); |
fd9efc00 KS |
719 | } |
720 | /* apply all_partitions option only at a main block device */ | |
e5e322bc KS |
721 | if (rule->partitions && udev->type == DEV_BLOCK && udev->kernel_number[0] == '\0') { |
722 | udev->partitions = rule->partitions; | |
fd9efc00 KS |
723 | dbg("creation of partition nodes requested"); |
724 | } | |
725 | ||
38875577 | 726 | /* apply permissions */ |
e5e322bc KS |
727 | if (rule->mode != 0000) { |
728 | udev->mode = rule->mode; | |
38875577 MB |
729 | dbg("applied mode=%#o to '%s'", udev->mode, udev->kernel_name); |
730 | } | |
e5e322bc KS |
731 | if (rule->owner[0] != '\0') { |
732 | strlcpy(udev->owner, rule->owner, sizeof(udev->owner)); | |
38875577 MB |
733 | apply_format(udev, udev->owner, sizeof(udev->owner), class_dev, sysfs_device); |
734 | dbg("applied owner='%s' to '%s'", udev->owner, udev->kernel_name); | |
735 | } | |
e5e322bc KS |
736 | if (rule->group[0] != '\0') { |
737 | strlcpy(udev->group, rule->group, sizeof(udev->group)); | |
38875577 MB |
738 | apply_format(udev, udev->group, sizeof(udev->group), class_dev, sysfs_device); |
739 | dbg("applied group='%s' to '%s'", udev->group, udev->kernel_name); | |
740 | } | |
741 | ||
9ed47a9f | 742 | /* collect symlinks */ |
e5e322bc | 743 | if (rule->symlink[0] != '\0') { |
63f61c5c | 744 | char temp[PATH_SIZE]; |
e48fc108 | 745 | char *pos, *next; |
ddd5b5dc | 746 | |
3908058c | 747 | info("configured rule in '%s[%i]' applied, added symlink '%s'", |
e5e322bc KS |
748 | rule->config_file, rule->config_line, rule->symlink); |
749 | strlcpy(temp, rule->symlink, sizeof(temp)); | |
ddd5b5dc | 750 | apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device); |
e48fc108 KS |
751 | |
752 | /* add multiple symlinks separated by spaces */ | |
753 | pos = temp; | |
754 | next = strchr(temp, ' '); | |
755 | while (next) { | |
756 | next[0] = '\0'; | |
6b493a20 | 757 | info("add symlink '%s'", pos); |
e48fc108 KS |
758 | name_list_add(&udev->symlink_list, pos, 0); |
759 | pos = &next[1]; | |
760 | next = strchr(pos, ' '); | |
761 | } | |
6b493a20 | 762 | info("add symlink '%s'", pos); |
e48fc108 | 763 | name_list_add(&udev->symlink_list, pos, 0); |
97ed02ee | 764 | } |
765 | ||
5f72c470 | 766 | /* rule matches */ |
e5e322bc | 767 | if (rule->name[0] != '\0') { |
e9390146 | 768 | info("configured rule in '%s[%i]' applied, '%s' becomes '%s'", |
e5e322bc | 769 | rule->config_file, rule->config_line, udev->kernel_name, rule->name); |
5f72c470 | 770 | |
e5e322bc | 771 | strlcpy(udev->name, rule->name, sizeof(udev->name)); |
5f72c470 | 772 | apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); |
e5e322bc KS |
773 | strlcpy(udev->config_file, rule->config_file, sizeof(udev->config_file)); |
774 | udev->config_line = rule->config_line; | |
65ab1334 | 775 | |
e6764498 | 776 | if (udev->type != DEV_NET) |
fd9efc00 KS |
777 | dbg("name, '%s' is going to have owner='%s', group='%s', mode=%#o partitions=%i", |
778 | udev->name, udev->owner, udev->group, udev->mode, udev->partitions); | |
5f72c470 | 779 | |
b821330f | 780 | break; |
97ed02ee | 781 | } |
3b6ed8bb KS |
782 | |
783 | if (rule->last_rule) { | |
784 | dbg("last rule to be applied"); | |
785 | break; | |
786 | } | |
787 | ||
724257d9 | 788 | } |
ac28b86d | 789 | } |
c2405f50 | 790 | |
b821330f KS |
791 | if (udev->name[0] == '\0') { |
792 | /* no rule matched, so we use the kernel name */ | |
63f61c5c | 793 | strlcpy(udev->name, udev->kernel_name, sizeof(udev->name)); |
6b493a20 | 794 | info("no rule found, use kernel name '%s'", udev->name); |
b821330f | 795 | } |
7bd22a78 | 796 | |
c1ab0461 KS |
797 | if (udev->tmp_node[0] != '\0') { |
798 | dbg("removing temporary device node"); | |
799 | unlink_secure(udev->tmp_node); | |
800 | udev->tmp_node[0] = '\0'; | |
801 | } | |
802 | ||
120d45d0 | 803 | return 0; |
185a35a4 | 804 | } |