6 * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
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.
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.
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.
24 /* define this to enable parsing debugging */
25 /* #define DEBUG_PARSER */
39 #include "udev_version.h"
41 #include "libsysfs/libsysfs.h"
43 #define TYPE_LABEL "LABEL"
44 #define TYPE_NUMBER "NUMBER"
45 #define TYPE_TOPOLOGY "TOPOLOGY"
46 #define TYPE_REPLACE "REPLACE"
47 #define TYPE_CALLOUT "CALLOUT"
49 static LIST_HEAD(config_device_list
);
51 static void dump_dev(struct config_device
*dev
)
55 dbg_parse("KERNEL name='%s' ,"
56 "owner='%s', group='%s', mode=%#o",
57 dev
->name
, dev
->owner
, dev
->group
, dev
->mode
);
60 dbg_parse("LABEL name='%s', bus='%s', sysfs_file='%s', sysfs_value='%s', "
61 "owner='%s', group='%s', mode=%#o",
62 dev
->name
, dev
->bus
, dev
->sysfs_file
, dev
->sysfs_value
,
63 dev
->owner
, dev
->group
, dev
->mode
);
66 dbg_parse("NUMBER name='%s', bus='%s', id='%s', "
67 "owner='%s', group='%s', mode=%#o",
68 dev
->name
, dev
->bus
, dev
->id
,
69 dev
->owner
, dev
->group
, dev
->mode
);
72 dbg_parse("TOPOLOGY name='%s', bus='%s', place='%s', "
73 "owner='%s', group='%s', mode=%#o",
74 dev
->name
, dev
->bus
, dev
->place
,
75 dev
->owner
, dev
->group
, dev
->mode
);
78 dbg_parse("REPLACE name=%s, kernel_name=%s, "
79 "owner='%s', group='%s', mode=%#o",
80 dev
->name
, dev
->kernel_name
,
81 dev
->owner
, dev
->group
, dev
->mode
);
84 dbg_parse("CALLOUT name='%s', program='%s', bus='%s', id='%s', "
85 "owner='%s', group='%s', mode=%#o",
86 dev
->name
, dev
->exec_program
, dev
->bus
, dev
->id
,
87 dev
->owner
, dev
->group
, dev
->mode
);
90 dbg_parse("unknown type of method");
94 #define copy_var(a, b, var) \
98 #define copy_string(a, b, var) \
100 strcpy(a->var, b->var);
102 static int add_dev(struct config_device
*new_dev
)
104 struct list_head
*tmp
;
105 struct config_device
*tmp_dev
;
107 /* update the values if we already have the device */
108 list_for_each(tmp
, &config_device_list
) {
109 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
110 int len
= strlen(new_dev
->name
);
111 if (new_dev
->name
[len
-1] == '*') {
113 if (strncmp(dev
->name
, new_dev
->name
, len
))
116 if (strcmp(dev
->name
, new_dev
->name
))
119 /* the same, copy the new info into this structure */
120 copy_var(dev
, new_dev
, type
);
121 copy_var(dev
, new_dev
, mode
);
122 copy_string(dev
, new_dev
, bus
);
123 copy_string(dev
, new_dev
, sysfs_file
);
124 copy_string(dev
, new_dev
, sysfs_value
);
125 copy_string(dev
, new_dev
, id
);
126 copy_string(dev
, new_dev
, place
);
127 copy_string(dev
, new_dev
, kernel_name
);
128 copy_string(dev
, new_dev
, owner
);
129 copy_string(dev
, new_dev
, group
);
133 /* not found, add new structure to the device list */
134 tmp_dev
= malloc(sizeof(*tmp_dev
));
137 memcpy(tmp_dev
, new_dev
, sizeof(*tmp_dev
));
138 list_add(&tmp_dev
->node
, &config_device_list
);
143 static void dump_dev_list(void)
145 struct list_head
*tmp
;
147 list_for_each(tmp
, &config_device_list
) {
148 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
153 static int get_pair(char **orig_string
, char **left
, char **right
)
156 char *string
= *orig_string
;
161 /* eat any whitespace */
162 while (isspace(*string
))
165 /* split based on '=' */
166 temp
= strsep(&string
, "=");
171 /* take the right side and strip off the '"' */
172 while (isspace(*string
))
179 temp
= strsep(&string
, "\"");
180 if (!string
|| *temp
== '\0')
183 *orig_string
= string
;
188 static int get_value(const char *left
, char **orig_string
, char **ret_string
)
193 retval
= get_pair(orig_string
, &left_string
, ret_string
);
196 if (strcasecmp(left_string
, left
) != 0)
201 static int namedev_init_config(void)
210 struct config_device dev
;
212 dbg("opening %s to read as config", udev_config_filename
);
213 fd
= fopen(udev_config_filename
, "r");
215 dbg("can't open %s", udev_config_filename
);
219 /* loop through the whole file */
223 temp
= fgets(line
, sizeof(line
), fd
);
228 dbg_parse("read %s", temp
);
230 /* eat the whitespace at the beginning of the line */
231 while (isspace(*temp
))
238 /* see if this is a comment */
239 if (*temp
== COMMENT_CHARACTER
)
242 memset(&dev
, 0x00, sizeof(struct config_device
));
245 temp2
= strsep(&temp
, ",");
246 if (strcasecmp(temp2
, TYPE_LABEL
) == 0) {
251 retval
= get_value("BUS", &temp
, &temp3
);
254 strfieldcpy(dev
.bus
, temp3
);
257 temp2
= strsep(&temp
, ",");
258 retval
= get_pair(&temp
, &temp2
, &temp3
);
261 strfieldcpy(dev
.sysfs_file
, temp2
);
262 strfieldcpy(dev
.sysfs_value
, temp3
);
264 /* NAME="new_name" */
265 temp2
= strsep(&temp
, ",");
266 retval
= get_value("NAME", &temp
, &temp3
);
269 strfieldcpy(dev
.name
, temp3
);
271 dbg_parse("LABEL name='%s', bus='%s', "
272 "sysfs_file='%s', sysfs_value='%s'",
273 dev
.name
, dev
.bus
, dev
.sysfs_file
,
277 if (strcasecmp(temp2
, TYPE_NUMBER
) == 0) {
282 retval
= get_value("BUS", &temp
, &temp3
);
285 strfieldcpy(dev
.bus
, temp3
);
288 temp2
= strsep(&temp
, ",");
289 retval
= get_value("id", &temp
, &temp3
);
292 strfieldcpy(dev
.id
, temp3
);
294 /* NAME="new_name" */
295 temp2
= strsep(&temp
, ",");
296 retval
= get_value("NAME", &temp
, &temp3
);
299 strfieldcpy(dev
.name
, temp3
);
301 dbg_parse("NUMBER name='%s', bus='%s', id='%s'",
302 dev
.name
, dev
.bus
, dev
.id
);
305 if (strcasecmp(temp2
, TYPE_TOPOLOGY
) == 0) {
310 retval
= get_value("BUS", &temp
, &temp3
);
313 strfieldcpy(dev
.bus
, temp3
);
316 temp2
= strsep(&temp
, ",");
317 retval
= get_value("place", &temp
, &temp3
);
320 strfieldcpy(dev
.place
, temp3
);
322 /* NAME="new_name" */
323 temp2
= strsep(&temp
, ",");
324 retval
= get_value("NAME", &temp
, &temp3
);
327 strfieldcpy(dev
.name
, temp3
);
329 dbg_parse("TOPOLOGY name='%s', bus='%s', place='%s'",
330 dev
.name
, dev
.bus
, dev
.place
);
333 if (strcasecmp(temp2
, TYPE_REPLACE
) == 0) {
337 /* KERNEL="kernel_name" */
338 retval
= get_value("KERNEL", &temp
, &temp3
);
341 strfieldcpy(dev
.kernel_name
, temp3
);
343 /* NAME="new_name" */
344 temp2
= strsep(&temp
, ",");
345 retval
= get_value("NAME", &temp
, &temp3
);
348 strfieldcpy(dev
.name
, temp3
);
349 dbg_parse("REPLACE name='%s', kernel_name='%s'",
350 dev
.name
, dev
.kernel_name
);
352 if (strcasecmp(temp2
, TYPE_CALLOUT
) == 0) {
356 /* PROGRAM="executable" */
357 retval
= get_value("PROGRAM", &temp
, &temp3
);
360 strfieldcpy(dev
.exec_program
, temp3
);
363 temp2
= strsep(&temp
, ",");
364 retval
= get_value("BUS", &temp
, &temp3
);
367 strfieldcpy(dev
.bus
, temp3
);
370 temp2
= strsep(&temp
, ",");
371 retval
= get_value("ID", &temp
, &temp3
);
374 strfieldcpy(dev
.id
, temp3
);
376 /* NAME="new_name" */
377 temp2
= strsep(&temp
, ",");
378 retval
= get_value("NAME", &temp
, &temp3
);
381 strfieldcpy(dev
.name
, temp3
);
382 dbg_parse("CALLOUT name='%s', program='%s'",
383 dev
.name
, dev
.exec_program
);
386 retval
= add_dev(&dev
);
388 dbg("add_dev returned with error %d", retval
);
392 dbg_parse("%s:%d:%Zd: error parsing '%s'", udev_config_filename
,
393 lineno
, temp
- line
, temp
);
400 static int namedev_init_permissions(void)
407 struct config_device dev
;
409 dbg("opening %s to read as permissions config", udev_config_permission_filename
);
410 fd
= fopen(udev_config_permission_filename
, "r");
412 dbg("can't open %s", udev_config_permission_filename
);
416 /* loop through the whole file */
419 temp
= fgets(line
, sizeof(line
), fd
);
423 dbg_parse("read %s", temp
);
425 /* eat the whitespace at the beginning of the line */
426 while (isspace(*temp
))
433 /* see if this is a comment */
434 if (*temp
== COMMENT_CHARACTER
)
437 memset(&dev
, 0x00, sizeof(dev
));
440 temp2
= strsep(&temp
, ":");
441 strncpy(dev
.name
, temp2
, sizeof(dev
.name
));
443 temp2
= strsep(&temp
, ":");
444 strncpy(dev
.owner
, temp2
, sizeof(dev
.owner
));
446 temp2
= strsep(&temp
, ":");
447 strncpy(dev
.group
, temp2
, sizeof(dev
.owner
));
449 dev
.mode
= strtol(temp
, NULL
, 8);
451 dbg_parse("name='%s', owner='%s', group='%s', mode=%#o",
452 dev
.name
, dev
.owner
, dev
.group
,
454 retval
= add_dev(&dev
);
456 dbg("add_dev returned with error %d", retval
);
466 static mode_t
get_default_mode(struct sysfs_class_device
*class_dev
)
468 /* just default everyone to rw for the world! */
473 static int exec_callout(struct config_device
*dev
, char *value
, int len
)
483 dbg("callout to '%s'", dev
->exec_program
);
497 close(STDOUT_FILENO
);
498 dup(fds
[1]); /* dup write side of pipe to STDOUT */
499 retval
= execve(dev
->exec_program
, main_argv
, main_envp
);
501 dbg("child execve failed");
504 return -1; /* avoid compiler warning */
506 /* parent reads from fds[0] */
510 res
= read(fds
[0], buffer
, sizeof(buffer
) - 1);
515 dbg("callout len %d too short\n", len
);
519 dbg("callout value already set");
523 strncpy(value
, buffer
, len
);
529 dbg("wait failed result %d", res
);
534 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
535 dbg("callout program status 0x%x", status
);
543 static int do_callout(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
545 struct config_device
*dev
;
546 struct list_head
*tmp
;
549 list_for_each(tmp
, &config_device_list
) {
550 dev
= list_entry(tmp
, struct config_device
, node
);
551 if (dev
->type
!= CALLOUT
)
554 if (exec_callout(dev
, value
, sizeof(value
)))
556 if (strncmp(value
, dev
->id
, sizeof(value
)) != 0)
558 strfieldcpy(udev
->name
, dev
->name
);
559 if (dev
->mode
!= 0) {
560 udev
->mode
= dev
->mode
;
561 strfieldcpy(udev
->owner
, dev
->owner
);
562 strfieldcpy(udev
->group
, dev
->group
);
564 dbg_parse("callout returned matching value '%s', '%s' becomes '%s'"
565 " - owner='%s', group='%s', mode =%#o",
566 dev
->id
, class_dev
->name
, udev
->name
,
567 dev
->owner
, dev
->group
, dev
->mode
);
573 static int do_label(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
575 struct sysfs_attribute
*tmpattr
= NULL
;
576 struct config_device
*dev
;
577 struct list_head
*tmp
;
579 list_for_each(tmp
, &config_device_list
) {
580 dev
= list_entry(tmp
, struct config_device
, node
);
581 if (dev
->type
!= LABEL
)
584 dbg_parse("look for device attribute '%s'", dev
->sysfs_file
);
585 /* try to find the attribute in the class device directory */
586 tmpattr
= sysfs_get_classdev_attr(class_dev
, dev
->sysfs_file
);
590 /* look in the class device directory if present */
592 tmpattr
= sysfs_get_device_attr(sysfs_device
, dev
->sysfs_file
);
600 tmpattr
->value
[strlen(tmpattr
->value
)-1] = 0x00;
601 dbg_parse("compare attribute '%s' value '%s' with '%s'",
602 dev
->sysfs_file
, tmpattr
->value
, dev
->sysfs_value
);
603 if (strcmp(dev
->sysfs_value
, tmpattr
->value
) != 0)
606 strfieldcpy(udev
->name
, dev
->name
);
607 if (dev
->mode
!= 0) {
608 udev
->mode
= dev
->mode
;
609 strfieldcpy(udev
->owner
, dev
->owner
);
610 strfieldcpy(udev
->group
, dev
->group
);
612 dbg_parse("found matching attribute '%s', '%s' becomes '%s' "
613 "- owner='%s', group='%s', mode=%#o",
614 dev
->sysfs_file
, class_dev
->name
, udev
->name
,
615 dev
->owner
, dev
->group
, dev
->mode
);
622 static int do_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
624 struct config_device
*dev
;
625 struct list_head
*tmp
;
626 char path
[SYSFS_PATH_MAX
];
630 /* we have to have a sysfs device for NUMBER to work */
634 list_for_each(tmp
, &config_device_list
) {
635 dev
= list_entry(tmp
, struct config_device
, node
);
636 if (dev
->type
!= NUMBER
)
640 strfieldcpy(path
, sysfs_device
->path
);
641 temp
= strrchr(path
, '/');
642 dbg_parse("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
643 if (strstr(temp
, dev
->id
) != NULL
) {
647 temp
= strrchr(path
, '/');
648 dbg_parse("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
649 if (strstr(temp
, dev
->id
) != NULL
)
654 strfieldcpy(udev
->name
, dev
->name
);
655 if (dev
->mode
!= 0) {
656 udev
->mode
= dev
->mode
;
657 strfieldcpy(udev
->owner
, dev
->owner
);
658 strfieldcpy(udev
->group
, dev
->group
);
660 dbg_parse("found matching id '%s', '%s' becomes '%s'"
661 " - owner='%s', group ='%s', mode=%#o",
662 dev
->id
, class_dev
->name
, udev
->name
,
663 dev
->owner
, dev
->group
, dev
->mode
);
670 static int do_topology(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
672 struct config_device
*dev
;
673 struct list_head
*tmp
;
674 char path
[SYSFS_PATH_MAX
];
678 /* we have to have a sysfs device for TOPOLOGY to work */
682 list_for_each(tmp
, &config_device_list
) {
683 dev
= list_entry(tmp
, struct config_device
, node
);
684 if (dev
->type
!= TOPOLOGY
)
688 strfieldcpy(path
, sysfs_device
->path
);
689 temp
= strrchr(path
, '/');
690 dbg_parse("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
691 if (strstr(temp
, dev
->place
) != NULL
) {
695 temp
= strrchr(path
, '/');
696 dbg_parse("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
697 if (strstr(temp
, dev
->place
) != NULL
)
703 strfieldcpy(udev
->name
, dev
->name
);
704 if (dev
->mode
!= 0) {
705 udev
->mode
= dev
->mode
;
706 strfieldcpy(udev
->owner
, dev
->owner
);
707 strfieldcpy(udev
->group
, dev
->group
);
709 dbg_parse("found matching place '%s', '%s' becomes '%s'"
710 " - owner='%s', group ='%s', mode=%#o",
711 dev
->place
, class_dev
->name
, udev
->name
,
712 dev
->owner
, dev
->group
, dev
->mode
);
718 static int do_replace(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
720 struct config_device
*dev
;
721 struct list_head
*tmp
;
723 list_for_each(tmp
, &config_device_list
) {
724 dev
= list_entry(tmp
, struct config_device
, node
);
725 if (dev
->type
!= REPLACE
)
728 dbg_parse("compare name '%s' with '%s'",
729 dev
->kernel_name
, dev
->name
);
730 if (strcmp(dev
->kernel_name
, class_dev
->name
) != 0)
733 strfieldcpy(udev
->name
, dev
->name
);
734 if (dev
->mode
!= 0) {
735 udev
->mode
= dev
->mode
;
736 strfieldcpy(udev
->owner
, dev
->owner
);
737 strfieldcpy(udev
->group
, dev
->group
);
739 dbg_parse("found name, '%s' becomes '%s' - owner='%s', group='%s', mode = %#o",
740 dev
->kernel_name
, udev
->name
,
741 dev
->owner
, dev
->group
, dev
->mode
);
748 static void do_kernelname(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
750 struct config_device
*dev
;
751 struct list_head
*tmp
;
754 strfieldcpy(udev
->name
, class_dev
->name
);
755 list_for_each(tmp
, &config_device_list
) {
756 dev
= list_entry(tmp
, struct config_device
, node
);
757 len
= strlen(dev
->name
);
758 if (dev
->name
[len
-1] == '*') {
760 if (strncmp(dev
->name
, class_dev
->name
, len
))
763 if (strcmp(dev
->name
, class_dev
->name
))
766 if (dev
->mode
!= 0) {
767 dbg_parse("found permissions for '%s'", class_dev
->name
);
768 udev
->mode
= dev
->mode
;
769 strfieldcpy(udev
->owner
, dev
->owner
);
770 strfieldcpy(udev
->group
, dev
->group
);
775 static int get_attr(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
777 struct sysfs_device
*sysfs_device
= NULL
;
778 struct sysfs_class_device
*class_dev_parent
= NULL
;
784 /* find the sysfs_device for this class device */
785 /* Wouldn't it really be nice if libsysfs could do this for us? */
786 if (class_dev
->sysdevice
) {
787 sysfs_device
= class_dev
->sysdevice
;
789 /* bah, let's go backwards up a level to see if the device is there,
790 * as block partitions don't point to the physical device. Need to fix that
791 * up in the kernel...
793 if (strstr(class_dev
->path
, "block")) {
794 dbg_parse("looking at block device...");
795 if (isdigit(class_dev
->path
[strlen(class_dev
->path
)-1])) {
796 char path
[SYSFS_PATH_MAX
];
798 dbg_parse("really is a partition...");
799 strfieldcpy(path
, class_dev
->path
);
800 temp
= strrchr(path
, '/');
802 dbg_parse("looking for a class device at '%s'", path
);
803 class_dev_parent
= sysfs_open_class_device(path
);
804 if (class_dev_parent
== NULL
) {
805 dbg("sysfs_open_class_device at '%s' failed", path
);
807 dbg_parse("class_dev_parent->name=%s", class_dev_parent
->name
);
808 if (class_dev_parent
->sysdevice
)
809 sysfs_device
= class_dev_parent
->sysdevice
;
816 dbg_parse("sysfs_device->path='%s'", sysfs_device
->path
);
817 dbg_parse("sysfs_device->bus_id='%s'", sysfs_device
->bus_id
);
819 dbg_parse("class_dev->name = '%s'", class_dev
->name
);
822 /* rules are looked at in priority order */
823 retval
= do_callout(class_dev
, udev
);
827 retval
= do_label(class_dev
, udev
, sysfs_device
);
831 retval
= do_number(class_dev
, udev
, sysfs_device
);
835 retval
= do_topology(class_dev
, udev
, sysfs_device
);
839 retval
= do_replace(class_dev
, udev
);
843 do_kernelname(class_dev
, udev
);
847 /* substitute placeholder in NAME */
849 char *pos
= strchr(udev
->name
, '%');
851 char name
[NAME_SIZE
];
853 strfieldcpy(name
, pos
+2);
859 strcat(udev
->name
, sysfs_device
->bus_id
);
860 dbg("bus_id inserted: %s",
861 sysfs_device
->bus_id
);
864 dig
= class_dev
->name
+ strlen(class_dev
->name
);
865 while (isdigit(*(dig
-1)))
867 strcat(udev
->name
, dig
);
868 dbg("substitute kernel number '%s'", dig
);
871 sprintf(pos
, "%u", udev
->minor
);
872 dbg("substitute minor number '%u'", udev
->minor
);
875 sprintf(pos
, "%u", udev
->major
);
876 dbg("substitute major number '%u'", udev
->major
);
879 dbg("unknown substitution type '%%%c'", pos
[1]);
882 strcat(udev
->name
, name
);
888 /* mode was never set above */
890 udev
->mode
= get_default_mode(class_dev
);
891 udev
->owner
[0] = 0x00;
892 udev
->group
[0] = 0x00;
895 if (class_dev_parent
)
896 sysfs_close_class_device(class_dev_parent
);
901 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct udevice
*dev
)
905 retval
= get_attr(class_dev
, dev
);
907 dbg("get_attr failed");
912 int namedev_init(void)
916 retval
= namedev_init_config();
920 retval
= namedev_init_permissions();