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'",
58 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
61 dbg_parse("LABEL name = '%s', bus = '%s', sysfs_file = '%s', sysfs_value = '%s'"
62 " owner = '%s', group = '%s', mode = '%#o'",
63 dev
->attr
.name
, dev
->bus
, dev
->sysfs_file
, dev
->sysfs_value
,
64 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
67 dbg_parse("NUMBER name = '%s', bus = '%s', id = '%s'"
68 " owner = '%s', group = '%s', mode = '%#o'",
69 dev
->attr
.name
, dev
->bus
, dev
->id
,
70 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
73 dbg_parse("TOPOLOGY name = '%s', bus = '%s', place = '%s'"
74 " owner = '%s', group = '%s', mode = '%#o'",
75 dev
->attr
.name
, dev
->bus
, dev
->place
,
76 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
79 dbg_parse("REPLACE name = %s, kernel_name = %s"
80 " owner = '%s', group = '%s', mode = '%#o'",
81 dev
->attr
.name
, dev
->kernel_name
,
82 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
85 dbg_parse("CALLOUT name = '%s', program ='%s', bus = '%s', id = '%s'"
86 " owner = '%s', group = '%s', mode = '%#o'",
87 dev
->attr
.name
, dev
->exec_program
, dev
->bus
, dev
->id
,
88 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
91 dbg_parse("Unknown type of device!");
95 #define copy_var(a, b, var) \
99 #define copy_string(a, b, var) \
100 if (strlen(b->var)) \
101 strcpy(a->var, b->var);
103 static int add_dev(struct config_device
*new_dev
)
105 struct list_head
*tmp
;
106 struct config_device
*tmp_dev
;
108 /* loop through the whole list of devices to see if we already have
110 list_for_each(tmp
, &config_device_list
) {
111 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
112 if (strcmp(dev
->attr
.name
, new_dev
->attr
.name
) == 0) {
113 /* the same, copy the new info into this structure */
114 copy_var(dev
, new_dev
, type
);
115 copy_var(dev
, new_dev
, attr
.mode
);
116 copy_string(dev
, new_dev
, bus
);
117 copy_string(dev
, new_dev
, sysfs_file
);
118 copy_string(dev
, new_dev
, sysfs_value
);
119 copy_string(dev
, new_dev
, id
);
120 copy_string(dev
, new_dev
, place
);
121 copy_string(dev
, new_dev
, kernel_name
);
122 copy_string(dev
, new_dev
, attr
.owner
);
123 copy_string(dev
, new_dev
, attr
.group
);
128 /* not found, lets create a new structure, and add it to the list */
129 tmp_dev
= malloc(sizeof(*tmp_dev
));
132 memcpy(tmp_dev
, new_dev
, sizeof(*tmp_dev
));
133 list_add(&tmp_dev
->node
, &config_device_list
);
138 static void dump_dev_list(void)
140 struct list_head
*tmp
;
142 list_for_each(tmp
, &config_device_list
) {
143 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
148 static int get_value(const char *left
, char **orig_string
, char **ret_string
)
151 char *string
= *orig_string
;
153 /* eat any whitespace */
154 while (isspace(*string
))
157 /* split based on '=' */
158 temp
= strsep(&string
, "=");
159 if (strcasecmp(temp
, left
) == 0) {
160 /* got it, now strip off the '"' */
161 while (isspace(*string
))
165 temp
= strsep(&string
, "\"");
167 *orig_string
= string
;
173 static int get_pair(char **orig_string
, char **left
, char **right
)
176 char *string
= *orig_string
;
178 /* eat any whitespace */
179 while (isspace(*string
))
182 /* split based on '=' */
183 temp
= strsep(&string
, "=");
186 /* take the right side and strip off the '"' */
187 while (isspace(*string
))
191 temp
= strsep(&string
, "\"");
193 *orig_string
= string
;
198 static int namedev_init_config(void)
207 struct config_device dev
;
209 strcpy(filename
, UDEV_CONFIG_DIR NAMEDEV_CONFIG_FILE
);
210 dbg("opening %s to read as permissions config", filename
);
211 fd
= fopen(filename
, "r");
213 dbg("Can't open %s", filename
);
217 /* loop through the whole file */
220 temp
= fgets(line
, sizeof(line
), fd
);
224 dbg_parse("read %s", temp
);
226 /* eat the whitespace at the beginning of the line */
227 while (isspace(*temp
))
234 /* see if this is a comment */
235 if (*temp
== COMMENT_CHARACTER
)
238 memset(&dev
, 0x00, sizeof(struct config_device
));
241 temp2
= strsep(&temp
, ",");
242 if (strcasecmp(temp2
, TYPE_LABEL
) == 0) {
247 retval
= get_value("BUS", &temp
, &temp3
);
250 strcpy(dev
.bus
, temp3
);
253 temp2
= strsep(&temp
, ",");
254 retval
= get_pair(&temp
, &temp2
, &temp3
);
257 strcpy(dev
.sysfs_file
, temp2
);
258 strcpy(dev
.sysfs_value
, temp3
);
260 /* NAME="new_name" */
261 temp2
= strsep(&temp
, ",");
262 retval
= get_value("NAME", &temp
, &temp3
);
265 strcpy(dev
.attr
.name
, temp3
);
267 dbg_parse("LABEL name = '%s', bus = '%s', "
268 "sysfs_file = '%s', sysfs_value = '%s'",
269 dev
.attr
.name
, dev
.bus
, dev
.sysfs_file
,
273 if (strcasecmp(temp2
, TYPE_NUMBER
) == 0) {
278 retval
= get_value("BUS", &temp
, &temp3
);
281 strcpy(dev
.bus
, temp3
);
284 temp2
= strsep(&temp
, ",");
285 retval
= get_value("id", &temp
, &temp3
);
288 strcpy(dev
.id
, temp3
);
290 /* NAME="new_name" */
291 temp2
= strsep(&temp
, ",");
292 retval
= get_value("NAME", &temp
, &temp3
);
295 strcpy(dev
.attr
.name
, temp3
);
297 dbg_parse("NUMBER name = '%s', bus = '%s', id = '%s'",
298 dev
.attr
.name
, dev
.bus
, dev
.id
);
301 if (strcasecmp(temp2
, TYPE_TOPOLOGY
) == 0) {
306 retval
= get_value("BUS", &temp
, &temp3
);
309 strcpy(dev
.bus
, temp3
);
312 temp2
= strsep(&temp
, ",");
313 retval
= get_value("place", &temp
, &temp3
);
316 strcpy(dev
.place
, temp3
);
318 /* NAME="new_name" */
319 temp2
= strsep(&temp
, ",");
320 retval
= get_value("NAME", &temp
, &temp3
);
323 strcpy(dev
.attr
.name
, temp3
);
325 dbg_parse("TOPOLOGY name = '%s', bus = '%s', place = '%s'",
326 dev
.attr
.name
, dev
.bus
, dev
.place
);
329 if (strcasecmp(temp2
, TYPE_REPLACE
) == 0) {
333 /* KERNEL="kernel_name" */
334 retval
= get_value("KERNEL", &temp
, &temp3
);
337 strcpy(dev
.kernel_name
, temp3
);
339 /* NAME="new_name" */
340 temp2
= strsep(&temp
, ",");
341 retval
= get_value("NAME", &temp
, &temp3
);
344 strcpy(dev
.attr
.name
, temp3
);
345 dbg_parse("REPLACE name = %s, kernel_name = %s",
346 dev
.attr
.name
, dev
.kernel_name
);
348 if (strcasecmp(temp2
, TYPE_CALLOUT
) == 0) {
352 /* PROGRAM="executable" */
353 retval
= get_value("PROGRAM", &temp
, &temp3
);
356 strcpy(dev
.exec_program
, temp3
);
359 temp2
= strsep(&temp
, ",");
360 retval
= get_value("BUS", &temp
, &temp3
);
363 strcpy(dev
.bus
, temp3
);
366 temp2
= strsep(&temp
, ",");
367 retval
= get_value("ID", &temp
, &temp3
);
370 strcpy(dev
.id
, temp3
);
372 /* NAME="new_name" */
373 temp2
= strsep(&temp
, ",");
374 retval
= get_value("NAME", &temp
, &temp3
);
377 strcpy(dev
.attr
.name
, temp3
);
378 dbg_parse("CALLOUT name = %s, program = %s",
379 dev
.attr
.name
, dev
.exec_program
);
382 retval
= add_dev(&dev
);
384 dbg("add_dev returned with error %d", retval
);
395 static int namedev_init_permissions(void)
403 struct config_device dev
;
405 strcpy(filename
, UDEV_CONFIG_DIR NAMEDEV_CONFIG_PERMISSION_FILE
);
406 dbg("opening %s to read as permissions config", filename
);
407 fd
= fopen(filename
, "r");
409 dbg("Can't open %s", filename
);
413 /* loop through the whole file */
416 temp
= fgets(line
, sizeof(line
), fd
);
420 dbg_parse("read %s", temp
);
422 /* eat the whitespace at the beginning of the line */
423 while (isspace(*temp
))
430 /* see if this is a comment */
431 if (*temp
== COMMENT_CHARACTER
)
434 memset(&dev
, 0x00, sizeof(dev
));
437 temp2
= strsep(&temp
, ":");
438 strncpy(dev
.attr
.name
, temp2
, sizeof(dev
.attr
.name
));
440 temp2
= strsep(&temp
, ":");
441 strncpy(dev
.attr
.owner
, temp2
, sizeof(dev
.attr
.owner
));
443 temp2
= strsep(&temp
, ":");
444 strncpy(dev
.attr
.group
, temp2
, sizeof(dev
.attr
.owner
));
446 dev
.attr
.mode
= strtol(temp
, NULL
, 8);
448 dbg_parse("name = %s, owner = %s, group = %s, mode = %#o",
449 dev
.attr
.name
, dev
.attr
.owner
, dev
.attr
.group
, dev
.attr
.mode
);
450 retval
= add_dev(&dev
);
452 dbg("add_dev returned with error %d", retval
);
462 static int get_default_mode(struct sysfs_class_device
*class_dev
)
464 /* just default everyone to rw for the world! */
469 static int exec_callout(struct config_device
*dev
, char *value
, int len
)
479 dbg("callout to %s\n", dev
->exec_program
);
495 close(STDOUT_FILENO
);
496 dup(fds
[1]); /* dup write side of pipe to STDOUT */
497 retval
= execve(dev
->exec_program
, main_argv
, main_envp
);
499 dbg("child execve failed");
502 return -1; /* avoid compiler warning */
505 * 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
);
533 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
534 dbg("callout program status 0x%x", status
);
541 static int get_attr(struct sysfs_class_device
*class_dev
, struct device_attr
*attr
)
543 struct list_head
*tmp
;
548 if (class_dev
->sysdevice
) {
549 dbg_parse("class_dev->sysdevice->directory->path = '%s'", class_dev
->sysdevice
->directory
->path
);
550 dbg_parse("class_dev->sysdevice->bus_id = '%s'", class_dev
->sysdevice
->bus_id
);
552 dbg_parse("class_dev->name = '%s'", class_dev
->name
);
554 list_for_each(tmp
, &config_device_list
) {
555 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
561 dbg_parse("LABEL: match file '%s' with value '%s'",
562 dev
->sysfs_file
, dev
->sysfs_value
);
563 /* try to find the attribute in the class device directory */
564 temp
= sysfs_get_value_from_attributes(class_dev
->directory
->attributes
, dev
->sysfs_file
);
568 /* look in the class device device directory if present */
569 if (class_dev
->sysdevice
) {
570 temp
= sysfs_get_value_from_attributes(class_dev
->sysdevice
->directory
->attributes
, dev
->sysfs_file
);
575 /* bah, let's go backwards up a level to see if the device is there,
576 * as block partitions don't point to the physical device. Need to fix that
577 * up in the kernel...
579 if (strstr(class_dev
->directory
->path
, "block")) {
580 dbg_parse("looking at block device...");
581 if (isdigit(class_dev
->directory
->path
[strlen(class_dev
->directory
->path
)-1])) {
582 char path
[SYSFS_PATH_MAX
];
583 struct sysfs_class_device
*class_dev_parent
;
585 dbg_parse("really is a partition...");
586 strcpy(path
, class_dev
->directory
->path
);
587 temp
= strrchr(path
, '/');
589 dbg_parse("looking for a class device at '%s'", path
);
590 class_dev_parent
= sysfs_open_class_device(path
);
591 if (class_dev_parent
== NULL
) {
592 dbg("sysfs_open_class_device at '%s' failed", path
);
595 dbg_parse("class_dev_parent->name = %s", class_dev_parent
->name
);
597 /* try to find the attribute in the class device directory */
598 temp
= sysfs_get_value_from_attributes(class_dev_parent
->directory
->attributes
, dev
->sysfs_file
);
600 //sysfs_close_class_device(class_dev_parent);
604 /* look in the class device device directory if present */
605 if (class_dev_parent
->sysdevice
) {
606 temp
= sysfs_get_value_from_attributes(class_dev_parent
->sysdevice
->directory
->attributes
, dev
->sysfs_file
);
608 // sysfs_close_class_device(class_dev_parent);
618 temp
[strlen(temp
)-1] = 0x00;
619 dbg_parse("file '%s' found with value '%s' compare with '%s'", dev
->sysfs_file
, temp
, dev
->sysfs_value
);
620 if (strcmp(dev
->sysfs_value
, temp
) != 0)
623 strcpy(attr
->name
, dev
->attr
.name
);
624 if (isdigit(class_dev
->directory
->path
[strlen(class_dev
->directory
->path
)-1])) {
625 temp
[0] = class_dev
->directory
->path
[strlen(class_dev
->directory
->path
)-1];
627 strcat(attr
->name
, temp
);
629 if (dev
->attr
.mode
!= 0) {
630 attr
->mode
= dev
->attr
.mode
;
631 strcpy(attr
->owner
, dev
->attr
.owner
);
632 strcpy(attr
->group
, dev
->attr
.group
);
634 dbg_parse("file '%s' with value '%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
635 dev
->sysfs_file
, dev
->sysfs_value
, attr
->name
,
636 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
642 char path
[SYSFS_PATH_MAX
];
646 if (!class_dev
->sysdevice
)
648 strcpy(path
, class_dev
->sysdevice
->directory
->path
);
649 temp
= strrchr(path
, '/');
650 dbg_parse("NUMBER path = '%s'", path
);
651 dbg_parse("NUMBER temp = '%s' id = '%s'", temp
, dev
->id
);
652 if (strstr(temp
, dev
->id
) != NULL
) {
656 temp
= strrchr(path
, '/');
657 dbg_parse("NUMBERY temp = '%s' id = '%s'", temp
, dev
->id
);
658 if (strstr(temp
, dev
->id
) != NULL
)
664 strcpy(attr
->name
, dev
->attr
.name
);
665 if (dev
->attr
.mode
!= 0) {
666 attr
->mode
= dev
->attr
.mode
;
667 strcpy(attr
->owner
, dev
->attr
.owner
);
668 strcpy(attr
->group
, dev
->attr
.group
);
670 dbg_parse("device id '%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
672 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
678 char path
[SYSFS_PATH_MAX
];
681 if (!class_dev
->sysdevice
)
684 strcpy(path
, class_dev
->sysdevice
->directory
->path
);
685 temp
= strrchr(path
, '/');
686 dbg_parse("TOPOLOGY path = '%s'", path
);
687 dbg_parse("TOPOLOGY temp = '%s' place = '%s'", temp
, dev
->place
);
688 if (strstr(temp
, dev
->place
) != NULL
) {
692 temp
= strrchr(path
, '/');
693 dbg_parse("TOPOLOGY temp = '%s' place = '%s'", temp
, dev
->place
);
694 if (strstr(temp
, dev
->place
) != NULL
)
700 strcpy(attr
->name
, dev
->attr
.name
);
701 if (dev
->attr
.mode
!= 0) {
702 attr
->mode
= dev
->attr
.mode
;
703 strcpy(attr
->owner
, dev
->attr
.owner
);
704 strcpy(attr
->group
, dev
->attr
.group
);
706 dbg_parse("device at '%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
707 dev
->place
, attr
->name
,
708 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
716 if (exec_callout(dev
, value
, sizeof(value
)))
718 if (strncmp(value
, dev
->id
, sizeof(value
)) != 0)
720 strcpy(attr
->name
, dev
->attr
.name
);
721 if (dev
->attr
.mode
!= 0) {
722 attr
->mode
= dev
->attr
.mode
;
723 strcpy(attr
->owner
, dev
->attr
.owner
);
724 strcpy(attr
->group
, dev
->attr
.group
);
726 dbg_parse("device callout '%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
728 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
733 if (strcmp(dev
->kernel_name
, class_dev
->name
) != 0)
735 strcpy(attr
->name
, dev
->attr
.name
);
736 if (dev
->attr
.mode
!= 0) {
737 attr
->mode
= dev
->attr
.mode
;
738 strcpy(attr
->owner
, dev
->attr
.owner
);
739 strcpy(attr
->group
, dev
->attr
.group
);
741 dbg_parse("'%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
742 dev
->kernel_name
, attr
->name
,
743 dev
->attr
.owner
, dev
->attr
.group
, dev
->attr
.mode
);
749 dbg_parse("Unknown type of device '%d'", dev
->type
);
753 strcpy(attr
->name
, class_dev
->name
);
756 if (attr
->mode
== -1) {
757 attr
->mode
= get_default_mode(class_dev
);
758 attr
->owner
[0] = 0x00;
759 attr
->group
[0] = 0x00;
764 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct device_attr
*attr
)
768 retval
= get_attr(class_dev
, attr
);
770 dbg("get_attr failed");
775 int namedev_init(void)
779 retval
= namedev_init_config();
783 retval
= namedev_init_permissions();