]>
Commit | Line | Data |
---|---|---|
2232cac8 GKH |
1 | /* |
2 | * namedev.c | |
3 | * | |
4 | * Userspace devfs | |
5 | * | |
6 | * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | |
7 | * | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation version 2 of the License. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along | |
19 | * with this program; if not, write to the Free Software Foundation, Inc., | |
20 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <stddef.h> | |
25 | #include <stdlib.h> | |
26 | #include <string.h> | |
27 | #include <stdio.h> | |
28 | #include <fcntl.h> | |
29 | #include <ctype.h> | |
30 | #include <unistd.h> | |
31 | #include <errno.h> | |
c27e6911 | 32 | #include <sys/wait.h> |
dac056aa | 33 | #include <sys/stat.h> |
bbbe503e | 34 | #include <sys/sysinfo.h> |
2232cac8 | 35 | |
c80da508 | 36 | #include "libsysfs/sysfs/libsysfs.h" |
2232cac8 GKH |
37 | #include "list.h" |
38 | #include "udev.h" | |
c81b35c0 | 39 | #include "udev_lib.h" |
2232cac8 | 40 | #include "udev_version.h" |
54988802 | 41 | #include "logging.h" |
2232cac8 | 42 | #include "namedev.h" |
2023350e | 43 | #include "klibc_fixups.h" |
0a8dd7f3 | 44 | #include "udevdb.h" |
2232cac8 | 45 | |
a27cd06c KS |
46 | static struct sysfs_attribute *find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, char *attr); |
47 | ||
19feb351 | 48 | LIST_HEAD(config_device_list); |
61219c75 | 49 | LIST_HEAD(perm_device_list); |
2232cac8 | 50 | |
e41016d3 | 51 | |
9f1da361 KS |
52 | /* compare string with pattern (supports * ? [0-9] [!A-Z]) */ |
53 | static int strcmp_pattern(const char *p, const char *s) | |
c124eafa | 54 | { |
8a08e4b1 KS |
55 | if (s[0] == '\0') { |
56 | while (p[0] == '*') | |
9f1da361 | 57 | p++; |
8a08e4b1 | 58 | return (p[0] != '\0'); |
9f1da361 | 59 | } |
8a08e4b1 | 60 | switch (p[0]) { |
9f1da361 KS |
61 | case '[': |
62 | { | |
63 | int not = 0; | |
64 | p++; | |
8a08e4b1 | 65 | if (p[0] == '!') { |
9f1da361 KS |
66 | not = 1; |
67 | p++; | |
68 | } | |
8a08e4b1 | 69 | while ((p[0] != '\0') && (p[0] != ']')) { |
9f1da361 KS |
70 | int match = 0; |
71 | if (p[1] == '-') { | |
8a08e4b1 | 72 | if ((s[0] >= p[0]) && (s[0] <= p[2])) |
9f1da361 KS |
73 | match = 1; |
74 | p += 3; | |
75 | } else { | |
8a08e4b1 | 76 | match = (p[0] == s[0]); |
9f1da361 KS |
77 | p++; |
78 | } | |
79 | if (match ^ not) { | |
8a08e4b1 | 80 | while ((p[0] != '\0') && (p[0] != ']')) |
9f1da361 | 81 | p++; |
8a08e4b1 KS |
82 | if (p[0] == ']') |
83 | return strcmp_pattern(p+1, s+1); | |
9f1da361 KS |
84 | } |
85 | } | |
86 | } | |
87 | break; | |
88 | case '*': | |
89 | if (strcmp_pattern(p, s+1)) | |
90 | return strcmp_pattern(p+1, s); | |
91 | return 0; | |
92 | case '\0': | |
8a08e4b1 | 93 | if (s[0] == '\0') { |
9f1da361 KS |
94 | return 0; |
95 | } | |
96 | break; | |
97 | default: | |
8a08e4b1 | 98 | if ((p[0] == s[0]) || (p[0] == '?')) |
9f1da361 KS |
99 | return strcmp_pattern(p+1, s+1); |
100 | break; | |
101 | } | |
102 | return 1; | |
c124eafa KS |
103 | } |
104 | ||
61219c75 GKH |
105 | static struct perm_device *find_perm(char *name) |
106 | { | |
843d1a84 | 107 | struct perm_device *perm; |
61219c75 | 108 | |
843d1a84 | 109 | list_for_each_entry(perm, &perm_device_list, node) { |
61219c75 GKH |
110 | if (strcmp_pattern(perm->name, name)) |
111 | continue; | |
112 | return perm; | |
113 | } | |
114 | return NULL; | |
115 | } | |
116 | ||
765cbd97 | 117 | static mode_t get_default_mode(void) |
185a35a4 | 118 | { |
89571022 GKH |
119 | mode_t mode = 0600; /* default to owner rw only */ |
120 | ||
267f534d | 121 | if (strlen(default_mode_str) != 0) |
89571022 | 122 | mode = strtol(default_mode_str, NULL, 8); |
267f534d | 123 | |
89571022 | 124 | return mode; |
185a35a4 GKH |
125 | } |
126 | ||
267f534d | 127 | static char *get_default_owner(void) |
74c73ef9 | 128 | { |
267f534d | 129 | if (strlen(default_owner_str) == 0) |
c472e3c8 | 130 | strfieldcpy(default_owner_str, "root"); |
267f534d | 131 | |
74c73ef9 | 132 | return default_owner_str; |
133 | } | |
134 | ||
267f534d | 135 | static char *get_default_group(void) |
74c73ef9 | 136 | { |
267f534d | 137 | if (strlen(default_group_str) == 0) |
c472e3c8 | 138 | strfieldcpy(default_group_str, "root"); |
267f534d | 139 | |
74c73ef9 | 140 | return default_group_str; |
141 | } | |
142 | ||
a27cd06c KS |
143 | /* extract possible {attr} and move str behind it */ |
144 | static char *get_format_attribute(char **str) | |
145 | { | |
146 | char *pos; | |
147 | char *attr = NULL; | |
148 | ||
149 | if (*str[0] == '{') { | |
150 | pos = strchr(*str, '}'); | |
151 | if (pos == NULL) { | |
152 | dbg("missing closing brace for format"); | |
153 | return NULL; | |
154 | } | |
155 | pos[0] = '\0'; | |
156 | attr = *str+1; | |
157 | *str = pos+1; | |
158 | dbg("attribute='%s', str='%s'", attr, *str); | |
159 | } | |
160 | return attr; | |
161 | } | |
162 | ||
163 | /* extract possible format length and move str behind it*/ | |
164 | static int get_format_len(char **str) | |
165 | { | |
166 | int num; | |
167 | char *tail; | |
168 | ||
169 | if (isdigit(*str[0])) { | |
170 | num = (int) strtoul(*str, &tail, 10); | |
aebef544 | 171 | if (num > 0) { |
a27cd06c KS |
172 | *str = tail; |
173 | dbg("format length=%i", num); | |
174 | return num; | |
175 | } else { | |
176 | dbg("format parsing error '%s'", *str); | |
177 | } | |
178 | } | |
179 | return -1; | |
180 | } | |
181 | ||
0a8dd7f3 DZ |
182 | /** Finds the lowest positive N such that <name>N isn't present in |
183 | * $(udevroot) either as a file or a symlink. | |
184 | * | |
185 | * @param name Name to check for | |
186 | * @return 0 if <name> didn't exist and N otherwise. | |
187 | */ | |
188 | static unsigned int find_free_number (struct udevice *udev, char *name) | |
189 | { | |
190 | char temp[NAME_SIZE]; | |
191 | char path[NAME_SIZE]; | |
192 | struct udevice dev; | |
193 | int result; | |
194 | ||
195 | /* have to sweep the database for each lookup */ | |
196 | result = 0; | |
197 | strncpy(temp, name, sizeof (temp)); | |
198 | while (1) { | |
199 | if (udevdb_get_dev_byname(temp, path, &dev) != 0) | |
200 | goto found; | |
201 | /* symlink might be stale if $(udevroot) isn't cleaned; check | |
202 | * on major/minor to see if it's the same device | |
203 | */ | |
204 | if (dev.major == udev->major && dev.minor == udev->minor) | |
205 | goto found; | |
206 | snprintf (temp, sizeof(temp), "%s%d", name, ++result); | |
207 | } | |
208 | ||
209 | found: | |
210 | return result; | |
211 | } | |
212 | ||
831f800d KS |
213 | static void apply_format(struct udevice *udev, char *string, size_t maxsize, |
214 | struct sysfs_class_device *class_dev, | |
215 | struct sysfs_device *sysfs_device) | |
f3b04a2e | 216 | { |
b1c5e333 | 217 | char temp[NAME_SIZE]; |
27c3403d | 218 | char temp2[NAME_SIZE]; |
b1c5e333 | 219 | char *tail; |
f3b04a2e | 220 | char *pos; |
a27cd06c | 221 | char *attr; |
63ead27c | 222 | int len; |
88f09368 | 223 | int i; |
a27cd06c | 224 | char c; |
ef672b3d | 225 | char *spos; |
558f80ba | 226 | char *rest; |
ef672b3d | 227 | int slen; |
a27cd06c | 228 | struct sysfs_attribute *tmpattr; |
0a8dd7f3 | 229 | unsigned int next_free_number; |
f3b04a2e | 230 | |
8ffb636f | 231 | pos = string; |
f3b04a2e | 232 | while (1) { |
a36a3c3a KS |
233 | pos = strchr(pos, '%'); |
234 | if (pos == NULL) | |
a27cd06c | 235 | break; |
b1c5e333 | 236 | |
a36a3c3a KS |
237 | pos[0] = '\0'; |
238 | tail = pos+1; | |
239 | len = get_format_len(&tail); | |
240 | c = tail[0]; | |
241 | strfieldcpy(temp, tail+1); | |
242 | tail = temp; | |
243 | dbg("format=%c, string='%s', tail='%s'",c , string, tail); | |
a27cd06c KS |
244 | attr = get_format_attribute(&tail); |
245 | ||
a36a3c3a | 246 | |
a27cd06c KS |
247 | switch (c) { |
248 | case 'b': | |
249 | if (strlen(udev->bus_id) == 0) | |
f3b04a2e | 250 | break; |
17794d77 | 251 | strfieldcatmax(string, udev->bus_id, maxsize); |
a27cd06c KS |
252 | dbg("substitute bus_id '%s'", udev->bus_id); |
253 | break; | |
254 | case 'k': | |
255 | if (strlen(udev->kernel_name) == 0) | |
3e540368 | 256 | break; |
17794d77 | 257 | strfieldcatmax(string, udev->kernel_name, maxsize); |
a27cd06c KS |
258 | dbg("substitute kernel name '%s'", udev->kernel_name); |
259 | break; | |
260 | case 'n': | |
261 | if (strlen(udev->kernel_number) == 0) | |
f3b04a2e | 262 | break; |
17794d77 | 263 | strfieldcatmax(string, udev->kernel_number, maxsize); |
a27cd06c | 264 | dbg("substitute kernel number '%s'", udev->kernel_number); |
f3b04a2e | 265 | break; |
a27cd06c | 266 | case 'm': |
17794d77 | 267 | strintcatmax(string, udev->minor, maxsize); |
a27cd06c KS |
268 | dbg("substitute minor number '%u'", udev->minor); |
269 | break; | |
c58e36c0 | 270 | case 'M': |
17794d77 | 271 | strintcatmax(string, udev->major, maxsize); |
a27cd06c KS |
272 | dbg("substitute major number '%u'", udev->major); |
273 | break; | |
274 | case 'c': | |
275 | if (strlen(udev->program_result) == 0) | |
f3b04a2e | 276 | break; |
88f09368 | 277 | /* get part part of the result string */ |
63ead27c | 278 | i = 0; |
88f09368 | 279 | if (attr != NULL) |
558f80ba | 280 | i = strtoul(attr, &rest, 10); |
88f09368 | 281 | if (i > 0) { |
9fe3f9a9 | 282 | foreach_strpart(udev->program_result, " \n\r", spos, slen) { |
88f09368 | 283 | i--; |
9fe3f9a9 | 284 | if (i == 0) |
a27cd06c | 285 | break; |
b1c5e333 | 286 | } |
9fe3f9a9 KS |
287 | if (i > 0) { |
288 | dbg("requested part of result string not found"); | |
289 | break; | |
a27cd06c | 290 | } |
558f80ba KS |
291 | if (rest[0] == '+') |
292 | strfieldcpy(temp2, spos); | |
293 | else | |
294 | strfieldcpymax(temp2, spos, slen+1); | |
17794d77 | 295 | strfieldcatmax(string, temp2, maxsize); |
27c3403d | 296 | dbg("substitute part of result string '%s'", temp2); |
a27cd06c | 297 | } else { |
17794d77 | 298 | strfieldcatmax(string, udev->program_result, maxsize); |
a27cd06c | 299 | dbg("substitute result string '%s'", udev->program_result); |
f3b04a2e | 300 | } |
f3b04a2e | 301 | break; |
a27cd06c KS |
302 | case 's': |
303 | if (attr != NULL) { | |
304 | tmpattr = find_sysfs_attribute(class_dev, sysfs_device, attr); | |
305 | if (tmpattr == NULL) { | |
306 | dbg("sysfa attribute '%s' not found", attr); | |
307 | break; | |
308 | } | |
1fa26490 | 309 | /* strip trailing whitespace of matching value */ |
310 | if (isspace(tmpattr->value[strlen(tmpattr->value)-1])) { | |
311 | i = len = strlen(tmpattr->value); | |
312 | while (i > 0 && isspace(tmpattr->value[i-1])) | |
313 | i--; | |
314 | if (i < len) { | |
315 | tmpattr->value[i] = '\0'; | |
316 | dbg("remove %i trailing whitespace chars from '%s'", | |
317 | len - i, tmpattr->value); | |
318 | } | |
319 | } | |
17794d77 | 320 | strfieldcatmax(string, tmpattr->value, maxsize); |
a27cd06c KS |
321 | dbg("substitute sysfs value '%s'", tmpattr->value); |
322 | } else { | |
323 | dbg("missing attribute"); | |
324 | } | |
325 | break; | |
326 | case '%': | |
17794d77 | 327 | strfieldcatmax(string, "%", maxsize); |
a36a3c3a | 328 | pos++; |
a27cd06c | 329 | break; |
0a8dd7f3 DZ |
330 | case 'e': |
331 | next_free_number = find_free_number(udev, string); | |
332 | if (next_free_number > 0) { | |
333 | snprintf(temp2, sizeof(temp2), "%d", next_free_number); | |
334 | strfieldcatmax(string, temp2, maxsize); | |
335 | } | |
336 | break; | |
a27cd06c KS |
337 | default: |
338 | dbg("unknown substitution type '%%%c'", c); | |
339 | break; | |
340 | } | |
63ead27c KS |
341 | /* truncate to specified length */ |
342 | if (len > 0) | |
343 | pos[len] = '\0'; | |
344 | ||
17794d77 | 345 | strfieldcatmax(string, tail, maxsize); |
f3b04a2e GKH |
346 | } |
347 | } | |
348 | ||
1b941027 GKH |
349 | static void fix_kernel_name(struct udevice *udev) |
350 | { | |
351 | char *temp = udev->kernel_name; | |
352 | ||
353 | while (*temp != 0x00) { | |
354 | /* Some block devices have a ! in their name, | |
355 | * we need to change that to / */ | |
356 | if (*temp == '!') | |
357 | *temp = '/'; | |
358 | ++temp; | |
359 | } | |
360 | } | |
361 | ||
f608f8ac | 362 | static int execute_program(const char *path, char *value, int len) |
c27e6911 PM |
363 | { |
364 | int retval; | |
35b38379 | 365 | int count; |
c27e6911 PM |
366 | int status; |
367 | int fds[2]; | |
368 | pid_t pid; | |
20524642 | 369 | char *pos; |
35b38379 | 370 | char arg[PROGRAM_SIZE]; |
f608f8ac | 371 | char *argv[(PROGRAM_SIZE / 2) + 1]; |
bc434511 | 372 | int i; |
c27e6911 | 373 | |
f608f8ac | 374 | strfieldcpy(arg, path); |
35b38379 KS |
375 | i = 0; |
376 | if (strchr(path, ' ')) { | |
35b38379 KS |
377 | pos = arg; |
378 | while (pos != NULL) { | |
379 | if (pos[0] == '\'') { | |
380 | /* don't separate if in apostrophes */ | |
381 | pos++; | |
382 | argv[i] = strsep(&pos, "\'"); | |
a75e2c14 | 383 | while (pos && pos[0] == ' ') |
35b38379 | 384 | pos++; |
a75e2c14 | 385 | } else { |
35b38379 KS |
386 | argv[i] = strsep(&pos, " "); |
387 | } | |
388 | dbg("arg[%i] '%s'", i, argv[i]); | |
389 | i++; | |
390 | } | |
f608f8ac KS |
391 | argv[i] = NULL; |
392 | dbg("execute '%s' with parsed arguments", arg); | |
393 | } else { | |
394 | argv[0] = arg; | |
395 | argv[1] = main_argv[1]; | |
396 | argv[2] = NULL; | |
397 | dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]); | |
35b38379 | 398 | } |
f608f8ac | 399 | |
c27e6911 PM |
400 | retval = pipe(fds); |
401 | if (retval != 0) { | |
402 | dbg("pipe failed"); | |
403 | return -1; | |
404 | } | |
35b38379 | 405 | |
c27e6911 | 406 | pid = fork(); |
2a25816f KS |
407 | switch(pid) { |
408 | case 0: | |
8f43a65e | 409 | /* child */ |
6e3e3c34 HH |
410 | /* dup2 write side of pipe to STDOUT */ |
411 | dup2(fds[1], STDOUT_FILENO); | |
f608f8ac | 412 | retval = execv(arg, argv); |
35b38379 | 413 | |
dde05ccb | 414 | info(FIELD_PROGRAM " execution of '%s' failed", path); |
2a25816f KS |
415 | exit(1); |
416 | case -1: | |
417 | dbg("fork failed"); | |
418 | return -1; | |
419 | default: | |
8f43a65e | 420 | /* parent reads from fds[0] */ |
c27e6911 PM |
421 | close(fds[1]); |
422 | retval = 0; | |
35b38379 | 423 | i = 0; |
c27e6911 | 424 | while (1) { |
35b38379 KS |
425 | count = read(fds[0], value + i, len - i-1); |
426 | if (count <= 0) | |
c27e6911 | 427 | break; |
35b38379 KS |
428 | |
429 | i += count; | |
430 | if (i >= len-1) { | |
ac28b86d | 431 | dbg("result len %d too short", len); |
c27e6911 | 432 | retval = -1; |
35b38379 | 433 | break; |
c27e6911 PM |
434 | } |
435 | } | |
35b38379 KS |
436 | |
437 | if (count < 0) { | |
438 | dbg("read failed with '%s'", strerror(errno)); | |
c27e6911 PM |
439 | retval = -1; |
440 | } | |
441 | ||
bbbe503e | 442 | if (i > 0 && value[i-1] == '\n') |
35b38379 KS |
443 | i--; |
444 | value[i] = '\0'; | |
445 | dbg("result is '%s'", value); | |
446 | ||
447 | close(fds[0]); | |
448 | wait(&status); | |
449 | ||
c27e6911 | 450 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { |
ac28b86d | 451 | dbg("exec program status 0x%x", status); |
c27e6911 PM |
452 | retval = -1; |
453 | } | |
454 | } | |
455 | return retval; | |
456 | } | |
457 | ||
a27cd06c | 458 | static struct sysfs_attribute *find_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, char *attr) |
ca1cc0fe GKH |
459 | { |
460 | struct sysfs_attribute *tmpattr = NULL; | |
a8b01705 GKH |
461 | char *c; |
462 | ||
a27cd06c | 463 | dbg("look for device attribute '%s'", attr); |
a8b01705 | 464 | /* try to find the attribute in the class device directory */ |
a27cd06c | 465 | tmpattr = sysfs_get_classdev_attr(class_dev, attr); |
a8b01705 | 466 | if (tmpattr) |
a27cd06c | 467 | goto attr_found; |
a8b01705 GKH |
468 | |
469 | /* look in the class device directory if present */ | |
470 | if (sysfs_device) { | |
a27cd06c | 471 | tmpattr = sysfs_get_device_attr(sysfs_device, attr); |
a8b01705 | 472 | if (tmpattr) |
a27cd06c | 473 | goto attr_found; |
a8b01705 | 474 | } |
a8b01705 | 475 | |
a27cd06c KS |
476 | return NULL; |
477 | ||
478 | attr_found: | |
479 | c = strchr(tmpattr->value, '\n'); | |
480 | if (c != NULL) | |
481 | c[0] = '\0'; | |
482 | ||
483 | dbg("found attribute '%s'", tmpattr->path); | |
484 | return tmpattr; | |
485 | } | |
486 | ||
487 | static int compare_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, struct sysfs_pair *pair) | |
488 | { | |
489 | struct sysfs_attribute *tmpattr; | |
d5f91372 KS |
490 | int i; |
491 | int len; | |
a27cd06c KS |
492 | |
493 | if ((pair == NULL) || (pair->file[0] == '\0') || (pair->value == '\0')) | |
494 | return -ENODEV; | |
495 | ||
496 | tmpattr = find_sysfs_attribute(class_dev, sysfs_device, pair->file); | |
497 | if (tmpattr == NULL) | |
498 | return -ENODEV; | |
499 | ||
d5f91372 KS |
500 | /* strip trailing whitespace of value, if not asked to match for it */ |
501 | if (! isspace(pair->value[strlen(pair->value)-1])) { | |
502 | i = len = strlen(tmpattr->value); | |
503 | while (i > 0 && isspace(tmpattr->value[i-1])) | |
504 | i--; | |
505 | if (i < len) { | |
506 | tmpattr->value[i] = '\0'; | |
507 | dbg("remove %i trailing whitespace chars from '%s'", | |
508 | len - i, tmpattr->value); | |
509 | } | |
510 | } | |
511 | ||
a8b01705 GKH |
512 | dbg("compare attribute '%s' value '%s' with '%s'", |
513 | pair->file, tmpattr->value, pair->value); | |
514 | if (strcmp_pattern(pair->value, tmpattr->value) != 0) | |
515 | return -ENODEV; | |
516 | ||
517 | dbg("found matching attribute '%s' with value '%s'", | |
518 | pair->file, pair->value); | |
519 | return 0; | |
520 | } | |
521 | ||
ac28b86d | 522 | static int match_sysfs_pairs(struct config_device *dev, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) |
a8b01705 GKH |
523 | { |
524 | struct sysfs_pair *pair; | |
a8b01705 | 525 | int i; |
137af0cc | 526 | |
ac28b86d KS |
527 | for (i = 0; i < MAX_SYSFS_PAIRS; ++i) { |
528 | pair = &dev->sysfs_pair[i]; | |
529 | if ((pair->file[0] == '\0') || (pair->value[0] == '\0')) | |
530 | break; | |
531 | if (compare_sysfs_attribute(class_dev, sysfs_device, pair) != 0) { | |
532 | dbg("sysfs attribute doesn't match"); | |
533 | return -ENODEV; | |
ca1cc0fe | 534 | } |
ca1cc0fe | 535 | } |
ac28b86d KS |
536 | |
537 | return 0; | |
ca1cc0fe GKH |
538 | } |
539 | ||
ac28b86d | 540 | static int match_id(struct config_device *dev, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) |
ca1cc0fe | 541 | { |
ca1cc0fe | 542 | char path[SYSFS_PATH_MAX]; |
ca1cc0fe GKH |
543 | char *temp = NULL; |
544 | ||
1f7148c7 | 545 | /* we have to have a sysfs device for ID to work */ |
ca1cc0fe GKH |
546 | if (!sysfs_device) |
547 | return -ENODEV; | |
548 | ||
ac28b86d KS |
549 | strfieldcpy(path, sysfs_device->path); |
550 | temp = strrchr(path, '/'); | |
3a8c51a7 | 551 | temp++; |
ac28b86d | 552 | dbg("search '%s' in '%s', path='%s'", dev->id, temp, path); |
3a8c51a7 | 553 | if (strcmp_pattern(dev->id, temp) != 0) |
ac28b86d | 554 | return -ENODEV; |
5a42932b PM |
555 | else |
556 | return 0; | |
ca1cc0fe GKH |
557 | } |
558 | ||
ac28b86d | 559 | static int match_place(struct config_device *dev, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device) |
8c51bbfe | 560 | { |
8c51bbfe GKH |
561 | char path[SYSFS_PATH_MAX]; |
562 | int found; | |
563 | char *temp = NULL; | |
564 | ||
1f7148c7 | 565 | /* we have to have a sysfs device for PLACE to work */ |
8c51bbfe GKH |
566 | if (!sysfs_device) |
567 | return -ENODEV; | |
568 | ||
ac28b86d KS |
569 | found = 0; |
570 | strfieldcpy(path, sysfs_device->path); | |
571 | temp = strrchr(path, '/'); | |
572 | dbg("search '%s' in '%s', path='%s'", dev->place, temp, path); | |
573 | if (strstr(temp, dev->place) != NULL) { | |
574 | found = 1; | |
575 | } else { | |
576 | *temp = 0x00; | |
8c51bbfe | 577 | temp = strrchr(path, '/'); |
19feb351 | 578 | dbg("search '%s' in '%s', path='%s'", dev->place, temp, path); |
ac28b86d | 579 | if (strstr(temp, dev->place) != NULL) |
8c51bbfe | 580 | found = 1; |
8c51bbfe | 581 | } |
ac28b86d KS |
582 | if (!found) { |
583 | dbg("place doesn't match"); | |
584 | return -ENODEV; | |
120d45d0 | 585 | } |
120d45d0 | 586 | |
ac28b86d | 587 | return 0; |
09e52d51 KS |
588 | } |
589 | ||
724257d9 | 590 | static int match_rule(struct config_device *dev, struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) |
dac056aa | 591 | { |
724257d9 | 592 | while (1) { |
ac28b86d KS |
593 | /* check for matching bus value */ |
594 | if (dev->bus[0] != '\0') { | |
595 | if (sysfs_device == NULL) { | |
596 | dbg("device has no bus"); | |
c5118442 | 597 | goto try_parent; |
ac28b86d KS |
598 | } |
599 | dbg("check for " FIELD_BUS " dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus); | |
600 | if (strcmp_pattern(dev->bus, sysfs_device->bus) != 0) { | |
601 | dbg(FIELD_BUS " is not matching"); | |
c5118442 | 602 | goto try_parent; |
ac28b86d KS |
603 | } else { |
604 | dbg(FIELD_BUS " matches"); | |
605 | } | |
606 | } | |
120d45d0 | 607 | |
ac28b86d KS |
608 | /* check for matching kernel name*/ |
609 | if (dev->kernel[0] != '\0') { | |
610 | dbg("check for " FIELD_KERNEL " dev->kernel='%s' class_dev->name='%s'", dev->kernel, class_dev->name); | |
611 | if (strcmp_pattern(dev->kernel, class_dev->name) != 0) { | |
612 | dbg(FIELD_KERNEL " is not matching"); | |
c5118442 | 613 | goto try_parent; |
ac28b86d KS |
614 | } else { |
615 | dbg(FIELD_KERNEL " matches"); | |
616 | } | |
617 | } | |
ca1cc0fe | 618 | |
ac28b86d KS |
619 | /* check for matching bus id */ |
620 | if (dev->id[0] != '\0') { | |
621 | dbg("check " FIELD_ID); | |
622 | if (match_id(dev, class_dev, sysfs_device) != 0) { | |
623 | dbg(FIELD_ID " is not matching"); | |
c5118442 | 624 | goto try_parent; |
ac28b86d KS |
625 | } else { |
626 | dbg(FIELD_ID " matches"); | |
627 | } | |
628 | } | |
ca1cc0fe | 629 | |
ac28b86d KS |
630 | /* check for matching place of device */ |
631 | if (dev->place[0] != '\0') { | |
632 | dbg("check " FIELD_PLACE); | |
633 | if (match_place(dev, class_dev, sysfs_device) != 0) { | |
634 | dbg(FIELD_PLACE " is not matching"); | |
c5118442 | 635 | goto try_parent; |
ac28b86d KS |
636 | } else { |
637 | dbg(FIELD_PLACE " matches"); | |
638 | } | |
639 | } | |
8c51bbfe | 640 | |
ac28b86d KS |
641 | /* check for matching sysfs pairs */ |
642 | if (dev->sysfs_pair[0].file[0] != '\0') { | |
643 | dbg("check " FIELD_SYSFS " pairs"); | |
644 | if (match_sysfs_pairs(dev, class_dev, sysfs_device) != 0) { | |
645 | dbg(FIELD_SYSFS " is not matching"); | |
c5118442 | 646 | goto try_parent; |
ac28b86d KS |
647 | } else { |
648 | dbg(FIELD_SYSFS " matches"); | |
649 | } | |
650 | } | |
651 | ||
652 | /* execute external program */ | |
653 | if (dev->program[0] != '\0') { | |
29a3cead KS |
654 | char program[PROGRAM_SIZE]; |
655 | ||
ac28b86d | 656 | dbg("check " FIELD_PROGRAM); |
29a3cead KS |
657 | strfieldcpy(program, dev->program); |
658 | apply_format(udev, program, sizeof(program), class_dev, sysfs_device); | |
659 | if (execute_program(program, udev->program_result, NAME_SIZE) != 0) { | |
35b38379 | 660 | dbg(FIELD_PROGRAM " returned nonzero"); |
c5118442 | 661 | goto try_parent; |
ac28b86d KS |
662 | } else { |
663 | dbg(FIELD_PROGRAM " returned successful"); | |
664 | } | |
665 | } | |
666 | ||
667 | /* check for matching result of external program */ | |
668 | if (dev->result[0] != '\0') { | |
669 | dbg("check for " FIELD_RESULT | |
670 | " dev->result='%s', udev->program_result='%s'", | |
671 | dev->result, udev->program_result); | |
672 | if (strcmp_pattern(dev->result, udev->program_result) != 0) { | |
673 | dbg(FIELD_RESULT " is not matching"); | |
c5118442 | 674 | goto try_parent; |
ac28b86d KS |
675 | } else { |
676 | dbg(FIELD_RESULT " matches"); | |
677 | } | |
678 | } | |
679 | ||
724257d9 GKH |
680 | /* Yeah, we matched! */ |
681 | return 0; | |
682 | ||
c5118442 KS |
683 | try_parent: |
684 | dbg("try parent sysfs device"); | |
724257d9 GKH |
685 | sysfs_device = sysfs_get_device_parent(sysfs_device); |
686 | if (sysfs_device == NULL) | |
687 | return -ENODEV; | |
688 | dbg("sysfs_device->path='%s'", sysfs_device->path); | |
689 | dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id); | |
690 | dbg("sysfs_device->bus='%s'", sysfs_device->bus); | |
691 | } | |
692 | ||
693 | } | |
694 | ||
7a947ce5 | 695 | int namedev_name_device(struct udevice *udev, struct sysfs_class_device *class_dev) |
724257d9 | 696 | { |
707680b1 | 697 | struct sysfs_class_device *class_dev_parent; |
724257d9 GKH |
698 | struct sysfs_device *sysfs_device = NULL; |
699 | struct config_device *dev; | |
700 | struct perm_device *perm; | |
bbbe503e | 701 | struct sysinfo info; |
724257d9 GKH |
702 | char *pos; |
703 | ||
704 | udev->mode = 0; | |
707680b1 KS |
705 | dbg("class_dev->name='%s'", class_dev->name); |
706 | ||
707 | /* Figure out where the "device"-symlink is at. For char devices this will | |
708 | * always be in the class_dev->path. On block devices, only the main block | |
709 | * device will have the device symlink in it's path. All partition devices | |
710 | * need to look at the symlink in its parent directory. | |
711 | */ | |
712 | class_dev_parent = sysfs_get_classdev_parent(class_dev); | |
713 | if (class_dev_parent != NULL) { | |
714 | dbg("given class device has a parent, use this instead"); | |
715 | sysfs_device = sysfs_get_classdev_device(class_dev_parent); | |
716 | } else { | |
717 | sysfs_device = sysfs_get_classdev_device(class_dev); | |
718 | } | |
724257d9 | 719 | |
724257d9 | 720 | if (sysfs_device) { |
7a947ce5 | 721 | dbg("found devices device: path='%s', bus_id='%s', bus='%s'", |
707680b1 | 722 | sysfs_device->path, sysfs_device->bus_id, sysfs_device->bus); |
724257d9 | 723 | strfieldcpy(udev->bus_id, sysfs_device->bus_id); |
7a947ce5 | 724 | strfieldcpy(udev->bus, sysfs_device->bus); |
724257d9 GKH |
725 | } |
726 | ||
727 | strfieldcpy(udev->kernel_name, class_dev->name); | |
1b941027 GKH |
728 | fix_kernel_name(udev); |
729 | dbg("udev->kernel_name = '%s'", udev->kernel_name); | |
724257d9 GKH |
730 | |
731 | /* get kernel number */ | |
732 | pos = class_dev->name + strlen(class_dev->name); | |
733 | while (isdigit(*(pos-1))) | |
734 | pos--; | |
735 | strfieldcpy(udev->kernel_number, pos); | |
736 | dbg("kernel_number='%s'", udev->kernel_number); | |
737 | ||
738 | /* look for a matching rule to apply */ | |
739 | list_for_each_entry(dev, &config_device_list, node) { | |
740 | dbg("process rule"); | |
724257d9 | 741 | if (match_rule(dev, class_dev, udev, sysfs_device) == 0) { |
97ed02ee | 742 | if (dev->name[0] == '\0' && dev->symlink[0] == '\0') { |
c5118442 | 743 | info("configured rule in '%s' at line %i applied, '%s' is ignored", |
bd5f8e7c | 744 | dev->config_file, dev->config_line, udev->kernel_name); |
c5118442 KS |
745 | return -1; |
746 | } | |
747 | ||
97ed02ee | 748 | if (dev->symlink[0] != '\0') { |
615ba3e8 | 749 | char temp[NAME_SIZE]; |
ddd5b5dc | 750 | |
97ed02ee | 751 | info("configured rule in '%s' at line %i applied, added symlink '%s'", |
bd5f8e7c | 752 | dev->config_file, dev->config_line, dev->symlink); |
ddd5b5dc KS |
753 | strfieldcpy(temp, dev->symlink); |
754 | apply_format(udev, temp, sizeof(temp), class_dev, sysfs_device); | |
0a5417a0 KS |
755 | if (udev->symlink[0] != '\0') |
756 | strfieldcat(udev->symlink, " "); | |
ddd5b5dc | 757 | strfieldcat(udev->symlink, temp); |
97ed02ee | 758 | } |
759 | ||
760 | if (dev->name[0] != '\0') { | |
949e32f2 KS |
761 | /* apply all_partitions flag only at a main block device */ |
762 | if (dev->partitions > 0 && | |
763 | (udev->type != 'b' || udev->kernel_number[0] != '\0')) | |
764 | continue; | |
765 | ||
97ed02ee | 766 | info("configured rule in '%s' at line %i applied, '%s' becomes '%s'", |
bd5f8e7c | 767 | dev->config_file, dev->config_line, udev->kernel_name, dev->name); |
97ed02ee | 768 | strfieldcpy(udev->name, dev->name); |
769 | goto found; | |
770 | } | |
724257d9 | 771 | } |
ac28b86d | 772 | } |
ac28b86d | 773 | /* no rule was found so we use the kernel name */ |
7a4877bf | 774 | strfieldcpy(udev->name, udev->kernel_name); |
bbbe503e KS |
775 | if (udev->type == 'n') |
776 | goto done; | |
777 | else | |
778 | goto perms; | |
c2405f50 | 779 | |
09e52d51 | 780 | found: |
0a5417a0 | 781 | apply_format(udev, udev->name, sizeof(udev->name), class_dev, sysfs_device); |
1e799bab KS |
782 | strfieldcpy(udev->config_file, dev->config_file); |
783 | udev->config_line = dev->config_line; | |
f61d732a KS |
784 | |
785 | if (udev->type == 'n') | |
bbbe503e | 786 | goto done; |
f61d732a | 787 | |
50e5de03 | 788 | udev->partitions = dev->partitions; |
e41016d3 | 789 | |
e41016d3 KS |
790 | /* get permissions given in rule */ |
791 | set_empty_perms(udev, dev->mode, | |
792 | dev->owner, | |
793 | dev->group); | |
794 | ||
bbbe503e | 795 | perms: |
e41016d3 | 796 | /* get permissions given in config file or set defaults */ |
61219c75 | 797 | perm = find_perm(udev->name); |
e41016d3 KS |
798 | if (perm != NULL) { |
799 | set_empty_perms(udev, perm->mode, | |
800 | perm->owner, | |
801 | perm->group); | |
61219c75 | 802 | } else { |
e41016d3 KS |
803 | set_empty_perms(udev, get_default_mode(), |
804 | get_default_owner(), | |
805 | get_default_group()); | |
615e05f8 | 806 | } |
e41016d3 | 807 | |
61219c75 GKH |
808 | dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o", |
809 | udev->name, udev->owner, udev->group, udev->mode); | |
7bd22a78 | 810 | |
bbbe503e | 811 | done: |
b608ade8 | 812 | /* store time of action */ |
bbbe503e KS |
813 | sysinfo(&info); |
814 | udev->config_uptime = info.uptime; | |
b608ade8 | 815 | |
120d45d0 | 816 | return 0; |
185a35a4 GKH |
817 | } |
818 | ||
2232cac8 GKH |
819 | int namedev_init(void) |
820 | { | |
821 | int retval; | |
28d6536a | 822 | |
e8baccca | 823 | retval = namedev_init_rules(); |
2232cac8 GKH |
824 | if (retval) |
825 | return retval; | |
826 | ||
827 | retval = namedev_init_permissions(); | |
828 | if (retval) | |
829 | return retval; | |
830 | ||
19feb351 | 831 | dump_config_dev_list(); |
61219c75 | 832 | dump_perm_dev_list(); |
2232cac8 GKH |
833 | return retval; |
834 | } |