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