]> git.ipfire.org Git - thirdparty/systemd.git/blob - namedev.c
[PATCH] udev - introduce format escape char
[thirdparty/systemd.git] / namedev.c
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>
32 #include <sys/wait.h>
33 #include <sys/stat.h>
34
35 #include "list.h"
36 #include "udev.h"
37 #include "udev_version.h"
38 #include "namedev.h"
39 #include "libsysfs/libsysfs.h"
40 #include "klibc_fixups.h"
41
42 LIST_HEAD(config_device_list);
43 LIST_HEAD(perm_device_list);
44
45 /* compare string with pattern (supports * ? [0-9] [!A-Z]) */
46 static int strcmp_pattern(const char *p, const char *s)
47 {
48 if (*s == '\0') {
49 while (*p == '*')
50 p++;
51 return (*p != '\0');
52 }
53 switch (*p) {
54 case '[':
55 {
56 int not = 0;
57 p++;
58 if (*p == '!') {
59 not = 1;
60 p++;
61 }
62 while (*p && (*p != ']')) {
63 int match = 0;
64 if (p[1] == '-') {
65 if ((*s >= *p) && (*s <= p[2]))
66 match = 1;
67 p += 3;
68 } else {
69 match = (*p == *s);
70 p++;
71 }
72 if (match ^ not) {
73 while (*p && (*p != ']'))
74 p++;
75 return strcmp_pattern(p+1, s+1);
76 }
77 }
78 }
79 break;
80 case '*':
81 if (strcmp_pattern(p, s+1))
82 return strcmp_pattern(p+1, s);
83 return 0;
84 case '\0':
85 if (*s == '\0') {
86 return 0;
87 }
88 break;
89 default:
90 if ((*p == *s) || (*p == '?'))
91 return strcmp_pattern(p+1, s+1);
92 break;
93 }
94 return 1;
95 }
96
97 #define copy_var(a, b, var) \
98 if (b->var) \
99 a->var = b->var;
100
101 #define copy_string(a, b, var) \
102 if (strlen(b->var)) \
103 strcpy(a->var, b->var);
104
105 int add_perm_dev(struct perm_device *new_dev)
106 {
107 struct perm_device *dev;
108 struct perm_device *tmp_dev;
109
110 /* update the values if we already have the device */
111 list_for_each_entry(dev, &perm_device_list, node) {
112 if (strcmp_pattern(new_dev->name, dev->name))
113 continue;
114 copy_var(dev, new_dev, mode);
115 copy_string(dev, new_dev, owner);
116 copy_string(dev, new_dev, group);
117 return 0;
118 }
119
120 /* not found, add new structure to the perm list */
121 tmp_dev = malloc(sizeof(*tmp_dev));
122 if (!tmp_dev)
123 return -ENOMEM;
124 memcpy(tmp_dev, new_dev, sizeof(*tmp_dev));
125 list_add_tail(&tmp_dev->node, &perm_device_list);
126 //dump_perm_dev(tmp_dev);
127 return 0;
128 }
129
130 static struct perm_device *find_perm(char *name)
131 {
132 struct perm_device *perm;
133
134 list_for_each_entry(perm, &perm_device_list, node) {
135 if (strcmp_pattern(perm->name, name))
136 continue;
137 return perm;
138 }
139 return NULL;
140 }
141
142 static mode_t get_default_mode(struct sysfs_class_device *class_dev)
143 {
144 mode_t mode = 0600; /* default to owner rw only */
145
146 if (strlen(default_mode_str) != 0) {
147 mode = strtol(default_mode_str, NULL, 8);
148 }
149 return mode;
150 }
151
152 static void apply_format(struct udevice *udev, unsigned char *string)
153 {
154 char temp[NAME_SIZE];
155 char temp1[NAME_SIZE];
156 char *tail;
157 char *pos;
158 char *pos2;
159 char *pos3;
160 int num;
161
162 pos = string;
163 while (1) {
164 num = 0;
165 pos = strchr(pos, '%');
166
167 if (pos) {
168 pos[0] = '\0';
169 tail = pos+1;
170 if (isdigit(tail[0])) {
171 num = (int) strtoul(&pos[1], &tail, 10);
172 if (tail == NULL) {
173 dbg("format parsing error '%s'", pos+1);
174 break;
175 }
176 }
177 strfieldcpy(temp, tail+1);
178
179 switch (tail[0]) {
180 case 'b':
181 if (strlen(udev->bus_id) == 0)
182 break;
183 strcat(pos, udev->bus_id);
184 dbg("substitute bus_id '%s'", udev->bus_id);
185 break;
186 case 'k':
187 if (strlen(udev->kernel_name) == 0)
188 break;
189 strcat(pos, udev->kernel_name);
190 dbg("substitute kernel name '%s'", udev->kernel_name);
191 break;
192 case 'n':
193 if (strlen(udev->kernel_number) == 0)
194 break;
195 strcat(pos, udev->kernel_number);
196 dbg("substitute kernel number '%s'", udev->kernel_number);
197 break;
198 case 'D':
199 if (strlen(udev->kernel_number) == 0) {
200 strcat(pos, "disc");
201 dbg("substitute devfs disc");
202 break;
203 }
204 strcat(pos, "part");
205 strcat(pos, udev->kernel_number);
206 dbg("substitute devfs part '%s'", udev->kernel_number);
207 break;
208 case 'm':
209 sprintf(pos, "%u", udev->minor);
210 dbg("substitute minor number '%u'", udev->minor);
211 break;
212 case 'M':
213 sprintf(pos, "%u", udev->major);
214 dbg("substitute major number '%u'", udev->major);
215 break;
216 case 'c':
217 if (strlen(udev->program_result) == 0)
218 break;
219 if (num) {
220 /* get part of return string */
221 strncpy(temp1, udev->program_result, sizeof(temp1));
222 pos2 = temp1;
223 while (num) {
224 num--;
225 pos3 = strsep(&pos2, " ");
226 if (pos3 == NULL) {
227 dbg("requested part of result string not found");
228 break;
229 }
230 }
231 if (pos3) {
232 strcat(pos, pos3);
233 dbg("substitute part of result string '%s'", pos3);
234 }
235 } else {
236 strcat(pos, udev->program_result);
237 dbg("substitute result string '%s'", udev->program_result);
238 }
239 break;
240 case '%':
241 strcat(pos, "%");
242 pos++;
243 break;
244 default:
245 dbg("unknown substitution type '%%%c'", pos[1]);
246 break;
247 }
248 strcat(string, temp);
249 } else
250 break;
251 }
252 }
253
254 static struct bus_file {
255 char *bus;
256 char *file;
257 } bus_files[] = {
258 { .bus = "scsi", .file = "vendor" },
259 { .bus = "usb", .file = "idVendor" },
260 { .bus = "usb-serial", .file = "detach_state" },
261 { .bus = "ide", .file = "detach_state" },
262 { .bus = "pci", .file = "vendor" },
263 {}
264 };
265
266 #define SECONDS_TO_WAIT_FOR_FILE 10
267 static void wait_for_device_to_initialize(struct sysfs_device *sysfs_device)
268 {
269 /* sleep until we see the file for this specific bus type show up this
270 * is needed because we can easily out-run the kernel in looking for
271 * these files before the paticular subsystem has created them in the
272 * sysfs tree properly.
273 *
274 * And people thought that the /sbin/hotplug event system was going to
275 * be slow, poo on you for arguing that before even testing it...
276 */
277 struct bus_file *b = &bus_files[0];
278 struct sysfs_attribute *tmpattr;
279 int loop;
280
281 while (1) {
282 if (b->bus == NULL)
283 break;
284 if (strcmp(sysfs_device->bus, b->bus) == 0) {
285 tmpattr = NULL;
286 loop = SECONDS_TO_WAIT_FOR_FILE;
287 while (loop--) {
288 dbg("looking for file '%s' on bus '%s'", b->file, b->bus);
289 tmpattr = sysfs_get_device_attr(sysfs_device, b->file);
290 if (tmpattr) {
291 /* found it! */
292 goto exit;
293 }
294 /* sleep to give the kernel a chance to create the file */
295 sleep(1);
296 }
297 dbg("Timed out waiting for '%s' file, continuing on anyway...", b->file);
298 goto exit;
299 }
300 b++;
301 }
302 dbg("Did not find bus type '%s' on list of bus_id_files, contact greg@kroah.com", sysfs_device->bus);
303 exit:
304 return; /* here to prevent compiler warning... */
305 }
306
307 static int execute_program(char *path, char *value, int len)
308 {
309 int retval;
310 int res;
311 int status;
312 int fds[2];
313 pid_t pid;
314 int value_set = 0;
315 char buffer[256];
316 char *pos;
317 char *args[PROGRAM_MAXARG];
318 int i;
319
320 dbg("executing '%s'", path);
321 retval = pipe(fds);
322 if (retval != 0) {
323 dbg("pipe failed");
324 return -1;
325 }
326 pid = fork();
327 if (pid == -1) {
328 dbg("fork failed");
329 return -1;
330 }
331
332 if (pid == 0) {
333 /* child */
334 close(STDOUT_FILENO);
335 dup(fds[1]); /* dup write side of pipe to STDOUT */
336 if (strchr(path, ' ')) {
337 /* exec with arguments */
338 pos = path;
339 for (i=0; i < PROGRAM_MAXARG-1; i++) {
340 args[i] = strsep(&pos, " ");
341 if (args[i] == NULL)
342 break;
343 }
344 if (args[i]) {
345 dbg("too many args - %d", i);
346 args[i] = NULL;
347 }
348 retval = execve(args[0], args, main_envp);
349 } else {
350 retval = execve(path, main_argv, main_envp);
351 }
352 if (retval != 0) {
353 dbg("child execve failed");
354 exit(1);
355 }
356 return -1; /* avoid compiler warning */
357 } else {
358 /* parent reads from fds[0] */
359 close(fds[1]);
360 retval = 0;
361 while (1) {
362 res = read(fds[0], buffer, sizeof(buffer) - 1);
363 if (res <= 0)
364 break;
365 buffer[res] = '\0';
366 if (res > len) {
367 dbg("result len %d too short", len);
368 retval = -1;
369 }
370 if (value_set) {
371 dbg("result value already set");
372 retval = -1;
373 } else {
374 value_set = 1;
375 strncpy(value, buffer, len);
376 pos = value + strlen(value)-1;
377 if (pos[0] == '\n')
378 pos[0] = '\0';
379 dbg("result is '%s'", value);
380 }
381 }
382 close(fds[0]);
383 res = wait(&status);
384 if (res < 0) {
385 dbg("wait failed result %d", res);
386 retval = -1;
387 }
388
389 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
390 dbg("exec program status 0x%x", status);
391 retval = -1;
392 }
393 }
394 return retval;
395 }
396
397 static int compare_sysfs_attribute(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, struct sysfs_pair *pair)
398 {
399 struct sysfs_attribute *tmpattr = NULL;
400 char *c;
401
402 if ((pair == NULL) || (pair->file[0] == '\0') || (pair->value == '\0'))
403 return -ENODEV;
404
405 dbg("look for device attribute '%s'", pair->file);
406 /* try to find the attribute in the class device directory */
407 tmpattr = sysfs_get_classdev_attr(class_dev, pair->file);
408 if (tmpattr)
409 goto label_found;
410
411 /* look in the class device directory if present */
412 if (sysfs_device) {
413 tmpattr = sysfs_get_device_attr(sysfs_device, pair->file);
414 if (tmpattr)
415 goto label_found;
416 }
417 return -ENODEV;
418
419 label_found:
420 c = tmpattr->value + strlen(tmpattr->value)-1;
421 if (*c == '\n')
422 *c = 0x00;
423 dbg("compare attribute '%s' value '%s' with '%s'",
424 pair->file, tmpattr->value, pair->value);
425 if (strcmp_pattern(pair->value, tmpattr->value) != 0)
426 return -ENODEV;
427
428 dbg("found matching attribute '%s' with value '%s'",
429 pair->file, pair->value);
430 return 0;
431 }
432
433 static int match_sysfs_pairs(struct config_device *dev, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device)
434 {
435 struct sysfs_pair *pair;
436 int i;
437
438 for (i = 0; i < MAX_SYSFS_PAIRS; ++i) {
439 pair = &dev->sysfs_pair[i];
440 if ((pair->file[0] == '\0') || (pair->value[0] == '\0'))
441 break;
442 if (compare_sysfs_attribute(class_dev, sysfs_device, pair) != 0) {
443 dbg("sysfs attribute doesn't match");
444 return -ENODEV;
445 }
446 }
447
448 return 0;
449 }
450
451 static int match_id(struct config_device *dev, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device)
452 {
453 char path[SYSFS_PATH_MAX];
454 int found;
455 char *temp = NULL;
456
457 /* we have to have a sysfs device for ID to work */
458 if (!sysfs_device)
459 return -ENODEV;
460
461 found = 0;
462 strfieldcpy(path, sysfs_device->path);
463 temp = strrchr(path, '/');
464 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
465 if (strstr(temp, dev->id) != NULL) {
466 found = 1;
467 } else {
468 *temp = 0x00;
469 temp = strrchr(path, '/');
470 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
471 if (strstr(temp, dev->id) != NULL)
472 found = 1;
473 }
474 if (!found) {
475 dbg("id doesn't match");
476 return -ENODEV;
477 }
478
479 return 0;
480 }
481
482 static int match_place(struct config_device *dev, struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device)
483 {
484 char path[SYSFS_PATH_MAX];
485 int found;
486 char *temp = NULL;
487
488 /* we have to have a sysfs device for PLACE to work */
489 if (!sysfs_device)
490 return -ENODEV;
491
492 found = 0;
493 strfieldcpy(path, sysfs_device->path);
494 temp = strrchr(path, '/');
495 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
496 if (strstr(temp, dev->place) != NULL) {
497 found = 1;
498 } else {
499 *temp = 0x00;
500 temp = strrchr(path, '/');
501 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
502 if (strstr(temp, dev->place) != NULL)
503 found = 1;
504 }
505 if (!found) {
506 dbg("place doesn't match");
507 return -ENODEV;
508 }
509
510 return 0;
511 }
512
513 static struct sysfs_device *get_sysfs_device(struct sysfs_class_device *class_dev)
514 {
515 struct sysfs_device *sysfs_device;
516 struct sysfs_class_device *class_dev_parent;
517 int loop;
518 char filename[SYSFS_PATH_MAX + 6];
519 int retval;
520 char *temp;
521 int partition = 0;
522
523 /* Figure out where the device symlink is at. For char devices this will
524 * always be in the class_dev->path. But for block devices, it's different.
525 * The main block device will have the device symlink in it's path, but
526 * all partitions have the symlink in its parent directory.
527 * But we need to watch out for block devices that do not have parents, yet
528 * look like a partition (fd0, loop0, etc.) They all do not have a device
529 * symlink yet. We do sit and spin on waiting for them right now, we should
530 * possibly have a whitelist for these devices here...
531 */
532 strcpy(filename, class_dev->path);
533 dbg("filename = %s", filename);
534 if (strcmp(class_dev->classname, SYSFS_BLOCK_NAME) == 0) {
535 if (isdigit(class_dev->path[strlen(class_dev->path)-1])) {
536 temp = strrchr(filename, '/');
537 if (temp) {
538 char *temp2 = strrchr(filename, '/');
539 partition = 1;
540 *temp = 0x00;
541 dbg("temp2 = %s", temp2);
542 if (temp2 && (strcmp(temp2, "/block") == 0)) {
543 /* oops, we have no parent block device, so go back to original directory */
544 strcpy(filename, class_dev->path);
545 partition = 0;
546 }
547 }
548 }
549 }
550 strcat(filename, "/device");
551
552 loop = 2;
553 while (loop--) {
554 struct stat buf;
555 dbg("looking for '%s'", filename);
556 retval = stat(filename, &buf);
557 if (!retval)
558 break;
559 /* sleep to give the kernel a chance to create the device file */
560 sleep(1);
561 }
562
563 loop = 1; /* FIXME put a real value in here for when everything is fixed... */
564 while (loop--) {
565 /* find the sysfs_device for this class device */
566 /* Wouldn't it really be nice if libsysfs could do this for us? */
567 sysfs_device = sysfs_get_classdev_device(class_dev);
568 if (sysfs_device != NULL)
569 goto exit;
570
571 /* if it's a partition, we need to get the parent device */
572 if (partition) {
573 /* FIXME HACK HACK HACK HACK
574 * for some reason partitions need this extra sleep here, in order
575 * to wait for the device properly. Once the libsysfs code is
576 * fixed properly, this sleep should go away, and we can just loop above.
577 */
578 sleep(1);
579 dbg("really is a partition");
580 class_dev_parent = sysfs_get_classdev_parent(class_dev);
581 if (class_dev_parent == NULL) {
582 dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name);
583 } else {
584 dbg("class_dev_parent->name='%s'", class_dev_parent->name);
585 sysfs_device = sysfs_get_classdev_device(class_dev_parent);
586 if (sysfs_device != NULL)
587 goto exit;
588 }
589 }
590 /* sleep to give the kernel a chance to create the link */
591 /* FIXME remove comment...
592 sleep(1); */
593 }
594 dbg("Timed out waiting for device symlink, continuing on anyway...");
595 exit:
596 return sysfs_device;
597 }
598
599 int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev)
600 {
601 struct sysfs_device *sysfs_device = NULL;
602 struct config_device *dev;
603 struct perm_device *perm;
604 char *pos;
605
606 udev->mode = 0;
607
608 /* find the sysfs_device associated with this class device */
609 sysfs_device = get_sysfs_device(class_dev);
610 if (sysfs_device) {
611 dbg("sysfs_device->path='%s'", sysfs_device->path);
612 dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id);
613 dbg("sysfs_device->bus='%s'", sysfs_device->bus);
614 strfieldcpy(udev->bus_id, sysfs_device->bus_id);
615 wait_for_device_to_initialize(sysfs_device);
616 } else {
617 dbg("class_dev->name = '%s'", class_dev->name);
618 }
619
620 strfieldcpy(udev->kernel_name, class_dev->name);
621
622 /* get kernel number */
623 pos = class_dev->name + strlen(class_dev->name);
624 while (isdigit(*(pos-1)))
625 pos--;
626 strfieldcpy(udev->kernel_number, pos);
627 dbg("kernel_number='%s'", udev->kernel_number);
628
629 /* look for a matching rule to apply */
630 list_for_each_entry(dev, &config_device_list, node) {
631 dbg("process rule");
632
633 /* check for matching bus value */
634 if (dev->bus[0] != '\0') {
635 if (sysfs_device == NULL) {
636 dbg("device has no bus");
637 continue;
638 }
639 dbg("check for " FIELD_BUS " dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
640 if (strcmp_pattern(dev->bus, sysfs_device->bus) != 0) {
641 dbg(FIELD_BUS " is not matching");
642 continue;
643 } else {
644 dbg(FIELD_BUS " matches");
645 }
646 }
647
648 /* check for matching kernel name*/
649 if (dev->kernel[0] != '\0') {
650 dbg("check for " FIELD_KERNEL " dev->kernel='%s' class_dev->name='%s'", dev->kernel, class_dev->name);
651 if (strcmp_pattern(dev->kernel, class_dev->name) != 0) {
652 dbg(FIELD_KERNEL " is not matching");
653 continue;
654 } else {
655 dbg(FIELD_KERNEL " matches");
656 }
657 }
658
659 /* check for matching bus id */
660 if (dev->id[0] != '\0') {
661 dbg("check " FIELD_ID);
662 if (match_id(dev, class_dev, sysfs_device) != 0) {
663 dbg(FIELD_ID " is not matching");
664 continue;
665 } else {
666 dbg(FIELD_ID " matches");
667 }
668 }
669
670 /* check for matching place of device */
671 if (dev->place[0] != '\0') {
672 dbg("check " FIELD_PLACE);
673 if (match_place(dev, class_dev, sysfs_device) != 0) {
674 dbg(FIELD_PLACE " is not matching");
675 continue;
676 } else {
677 dbg(FIELD_PLACE " matches");
678 }
679 }
680
681 /* check for matching sysfs pairs */
682 if (dev->sysfs_pair[0].file[0] != '\0') {
683 dbg("check " FIELD_SYSFS " pairs");
684 if (match_sysfs_pairs(dev, class_dev, sysfs_device) != 0) {
685 dbg(FIELD_SYSFS " is not matching");
686 continue;
687 } else {
688 dbg(FIELD_SYSFS " matches");
689 }
690 }
691
692 /* execute external program */
693 if (dev->program[0] != '\0') {
694 dbg("check " FIELD_PROGRAM);
695 apply_format(udev, dev->program);
696 if (execute_program(dev->program, udev->program_result, NAME_SIZE) != 0) {
697 dbg(FIELD_PROGRAM " returned nozero");
698 continue;
699 } else {
700 dbg(FIELD_PROGRAM " returned successful");
701 }
702 }
703
704 /* check for matching result of external program */
705 if (dev->result[0] != '\0') {
706 dbg("check for " FIELD_RESULT
707 " dev->result='%s', udev->program_result='%s'",
708 dev->result, udev->program_result);
709 if (strcmp_pattern(dev->result, udev->program_result) != 0) {
710 dbg(FIELD_RESULT " is not matching");
711 continue;
712 } else {
713 dbg(FIELD_RESULT " matches");
714 }
715 }
716
717 /* check if we are instructed to ignore this device */
718 if (dev->name[0] == '\0') {
719 dbg("instructed to ignore this device");
720 return -1;
721 }
722
723 /* Yup, this rule belongs to us! */
724 dbg("found matching rule, '%s' becomes '%s'", dev->kernel, dev->name);
725 strfieldcpy(udev->name, dev->name);
726 strfieldcpy(udev->symlink, dev->symlink);
727 goto found;
728 }
729
730 /* no rule was found so we use the kernel name */
731 strfieldcpy(udev->name, class_dev->name);
732 goto done;
733
734 found:
735 /* substitute placeholder */
736 apply_format(udev, udev->name);
737 apply_format(udev, udev->symlink);
738
739 done:
740 perm = find_perm(udev->name);
741 if (perm) {
742 udev->mode = perm->mode;
743 strfieldcpy(udev->owner, perm->owner);
744 strfieldcpy(udev->group, perm->group);
745 } else {
746 /* no matching perms found :( */
747 udev->mode = get_default_mode(class_dev);
748 udev->owner[0] = 0x00;
749 udev->group[0] = 0x00;
750 }
751 dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o",
752 udev->name, udev->owner, udev->group, udev->mode);
753
754 return 0;
755 }
756
757 int namedev_init(void)
758 {
759 int retval;
760
761 retval = namedev_init_rules();
762 if (retval)
763 return retval;
764
765 retval = namedev_init_permissions();
766 if (retval)
767 return retval;
768
769 dump_config_dev_list();
770 dump_perm_dev_list();
771 return retval;
772 }