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.
36 #include "udev_version.h"
38 #include "libsysfs/libsysfs.h"
39 #include "klibc_fixups.h"
41 LIST_HEAD(config_device_list
);
42 LIST_HEAD(perm_device_list
);
44 /* compare string with pattern (supports * ? [0-9] [!A-Z]) */
45 static int strcmp_pattern(const char *p
, const char *s
)
61 while (*p
&& (*p
!= ']')) {
64 if ((*s
>= *p
) && (*s
<= p
[2]))
72 while (*p
&& (*p
!= ']'))
74 return strcmp_pattern(p
+1, s
+1);
80 if (strcmp_pattern(p
, s
+1))
81 return strcmp_pattern(p
+1, s
);
89 if ((*p
== *s
) || (*p
== '?'))
90 return strcmp_pattern(p
+1, s
+1);
96 #define copy_var(a, b, var) \
100 #define copy_string(a, b, var) \
101 if (strlen(b->var)) \
102 strcpy(a->var, b->var);
104 int add_config_dev(struct config_device
*new_dev
)
106 struct list_head
*tmp
;
107 struct config_device
*tmp_dev
;
109 /* update the values if we already have the device */
110 list_for_each(tmp
, &config_device_list
) {
111 struct config_device
*dev
= list_entry(tmp
, struct config_device
, node
);
112 if (strcmp_pattern(new_dev
->name
, dev
->name
))
114 if (strncmp(dev
->bus
, new_dev
->bus
, sizeof(dev
->name
)))
116 copy_var(dev
, new_dev
, type
);
117 copy_string(dev
, new_dev
, bus
);
118 copy_string(dev
, new_dev
, sysfs_file
);
119 copy_string(dev
, new_dev
, sysfs_value
);
120 copy_string(dev
, new_dev
, id
);
121 copy_string(dev
, new_dev
, place
);
122 copy_string(dev
, new_dev
, kernel_name
);
123 copy_string(dev
, new_dev
, exec_program
);
124 copy_string(dev
, new_dev
, symlink
);
128 /* not found, add new structure to the device list */
129 tmp_dev
= malloc(sizeof(*tmp_dev
));
132 memcpy(tmp_dev
, new_dev
, sizeof(*tmp_dev
));
133 list_add_tail(&tmp_dev
->node
, &config_device_list
);
134 //dump_config_dev(tmp_dev);
138 int add_perm_dev(struct perm_device
*new_dev
)
140 struct list_head
*tmp
;
141 struct perm_device
*tmp_dev
;
143 /* update the values if we already have the device */
144 list_for_each(tmp
, &perm_device_list
) {
145 struct perm_device
*dev
= list_entry(tmp
, struct perm_device
, node
);
146 if (strcmp_pattern(new_dev
->name
, dev
->name
))
148 copy_var(dev
, new_dev
, mode
);
149 copy_string(dev
, new_dev
, owner
);
150 copy_string(dev
, new_dev
, group
);
154 /* not found, add new structure to the perm list */
155 tmp_dev
= malloc(sizeof(*tmp_dev
));
158 memcpy(tmp_dev
, new_dev
, sizeof(*tmp_dev
));
159 list_add_tail(&tmp_dev
->node
, &perm_device_list
);
160 //dump_perm_dev(tmp_dev);
164 static struct perm_device
*find_perm(char *name
)
166 struct list_head
*tmp
;
167 struct perm_device
*perm
= NULL
;
169 list_for_each(tmp
, &perm_device_list
) {
170 perm
= list_entry(tmp
, struct perm_device
, node
);
171 if (strcmp_pattern(perm
->name
, name
))
178 static mode_t
get_default_mode(struct sysfs_class_device
*class_dev
)
180 mode_t mode
= 0600; /* default to owner rw only */
182 if (strlen(default_mode_str
) != 0) {
183 mode
= strtol(default_mode_str
, NULL
, 8);
188 static void build_kernel_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
192 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
193 dig
= class_dev
->name
+ strlen(class_dev
->name
);
194 while (isdigit(*(dig
-1)))
196 strfieldcpy(udev
->kernel_number
, dig
);
197 dbg("kernel_number='%s'", udev
->kernel_number
);
200 static void apply_format(struct udevice
*udev
, unsigned char *string
)
202 char name
[NAME_SIZE
];
203 char temp
[NAME_SIZE
];
212 pos
= strchr(string
, '%');
217 if (isdigit(tail
[0])) {
218 num
= (int) strtoul(&pos
[1], &tail
, 10);
220 dbg("format parsing error '%s'", pos
+1);
224 strfieldcpy(name
, tail
+1);
228 if (strlen(udev
->bus_id
) == 0)
230 strcat(pos
, udev
->bus_id
);
231 dbg("substitute bus_id '%s'", udev
->bus_id
);
234 if (strlen(udev
->kernel_number
) == 0)
236 strcat(pos
, udev
->kernel_number
);
237 dbg("substitute kernel number '%s'", udev
->kernel_number
);
240 if (strlen(udev
->kernel_number
) == 0) {
245 strcat(pos
, udev
->kernel_number
);
246 dbg("substitute kernel number '%s'", udev
->kernel_number
);
249 sprintf(pos
, "%u", udev
->minor
);
250 dbg("substitute minor number '%u'", udev
->minor
);
253 sprintf(pos
, "%u", udev
->major
);
254 dbg("substitute major number '%u'", udev
->major
);
257 if (strlen(udev
->callout_value
) == 0)
260 /* get part of return string */
261 strncpy(temp
, udev
->callout_value
, sizeof(temp
));
265 pos3
= strsep(&pos2
, " ");
267 dbg("requested part of callout string not found");
272 dbg("substitute partial callout output '%s'", pos3
);
274 strcat(pos
, udev
->callout_value
);
275 dbg("substitute callout output '%s'", udev
->callout_value
);
279 dbg("unknown substitution type '%%%c'", pos
[1]);
282 strcat(string
, name
);
289 static int exec_callout(struct config_device
*dev
, char *value
, int len
)
299 char *args
[CALLOUT_MAXARG
];
302 dbg("callout to '%s'", dev
->exec_program
);
316 close(STDOUT_FILENO
);
317 dup(fds
[1]); /* dup write side of pipe to STDOUT */
318 if (strchr(dev
->exec_program
, ' ')) {
319 /* callout with arguments */
320 pos
= dev
->exec_program
;
321 for (i
=0; i
< CALLOUT_MAXARG
-1; i
++) {
322 args
[i
] = strsep(&pos
, " ");
327 dbg("too many args - %d", i
);
330 retval
= execve(args
[0], args
, main_envp
);
332 retval
= execve(dev
->exec_program
, main_argv
, main_envp
);
335 dbg("child execve failed");
338 return -1; /* avoid compiler warning */
340 /* parent reads from fds[0] */
344 res
= read(fds
[0], buffer
, sizeof(buffer
) - 1);
349 dbg("callout len %d too short", len
);
353 dbg("callout value already set");
357 strncpy(value
, buffer
, len
);
358 pos
= value
+ strlen(value
)-1;
361 dbg("callout returned '%s'", value
);
367 dbg("wait failed result %d", res
);
372 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
373 dbg("callout program status 0x%x", status
);
381 static int do_callout(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
383 struct config_device
*dev
;
384 struct list_head
*tmp
;
386 list_for_each(tmp
, &config_device_list
) {
387 dev
= list_entry(tmp
, struct config_device
, node
);
388 if (dev
->type
!= CALLOUT
)
392 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
393 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
397 /* substitute anything that needs to be in the program name */
398 apply_format(udev
, dev
->exec_program
);
399 if (exec_callout(dev
, udev
->callout_value
, NAME_SIZE
))
401 if (strcmp_pattern(dev
->id
, udev
->callout_value
) != 0)
403 strfieldcpy(udev
->name
, dev
->name
);
404 strfieldcpy(udev
->symlink
, dev
->symlink
);
405 dbg("callout returned matching value '%s', '%s' becomes '%s'",
406 dev
->id
, class_dev
->name
, udev
->name
);
412 static int do_label(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
414 struct sysfs_attribute
*tmpattr
= NULL
;
415 struct config_device
*dev
;
416 struct list_head
*tmp
;
419 list_for_each(tmp
, &config_device_list
) {
420 dev
= list_entry(tmp
, struct config_device
, node
);
421 if (dev
->type
!= LABEL
)
425 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
426 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
430 dbg("look for device attribute '%s'", dev
->sysfs_file
);
431 /* try to find the attribute in the class device directory */
432 tmpattr
= sysfs_get_classdev_attr(class_dev
, dev
->sysfs_file
);
436 /* look in the class device directory if present */
438 tmpattr
= sysfs_get_device_attr(sysfs_device
, dev
->sysfs_file
);
446 c
= tmpattr
->value
+ strlen(tmpattr
->value
)-1;
449 dbg("compare attribute '%s' value '%s' with '%s'",
450 dev
->sysfs_file
, tmpattr
->value
, dev
->sysfs_value
);
451 if (strcmp_pattern(dev
->sysfs_value
, tmpattr
->value
) != 0)
454 strfieldcpy(udev
->name
, dev
->name
);
455 strfieldcpy(udev
->symlink
, dev
->symlink
);
456 dbg("found matching attribute '%s', '%s' becomes '%s' ",
457 dev
->sysfs_file
, class_dev
->name
, udev
->name
);
464 static int do_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
466 struct config_device
*dev
;
467 struct list_head
*tmp
;
468 char path
[SYSFS_PATH_MAX
];
472 /* we have to have a sysfs device for NUMBER to work */
476 list_for_each(tmp
, &config_device_list
) {
477 dev
= list_entry(tmp
, struct config_device
, node
);
478 if (dev
->type
!= NUMBER
)
481 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
482 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
486 strfieldcpy(path
, sysfs_device
->path
);
487 temp
= strrchr(path
, '/');
488 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
489 if (strstr(temp
, dev
->id
) != NULL
) {
493 temp
= strrchr(path
, '/');
494 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
495 if (strstr(temp
, dev
->id
) != NULL
)
500 strfieldcpy(udev
->name
, dev
->name
);
501 strfieldcpy(udev
->symlink
, dev
->symlink
);
502 dbg("found matching id '%s', '%s' becomes '%s'",
503 dev
->id
, class_dev
->name
, udev
->name
);
509 static int do_topology(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
511 struct config_device
*dev
;
512 struct list_head
*tmp
;
513 char path
[SYSFS_PATH_MAX
];
517 /* we have to have a sysfs device for TOPOLOGY to work */
521 list_for_each(tmp
, &config_device_list
) {
522 dev
= list_entry(tmp
, struct config_device
, node
);
523 if (dev
->type
!= TOPOLOGY
)
526 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
527 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
531 strfieldcpy(path
, sysfs_device
->path
);
532 temp
= strrchr(path
, '/');
533 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
534 if (strstr(temp
, dev
->place
) != NULL
) {
538 temp
= strrchr(path
, '/');
539 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
540 if (strstr(temp
, dev
->place
) != NULL
)
546 strfieldcpy(udev
->name
, dev
->name
);
547 strfieldcpy(udev
->symlink
, dev
->symlink
);
548 dbg("found matching place '%s', '%s' becomes '%s'",
549 dev
->place
, class_dev
->name
, udev
->name
);
555 static int do_replace(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
557 struct config_device
*dev
;
558 struct list_head
*tmp
;
560 list_for_each(tmp
, &config_device_list
) {
561 dev
= list_entry(tmp
, struct config_device
, node
);
562 if (dev
->type
!= REPLACE
)
565 dbg("compare name '%s' with '%s'", dev
->kernel_name
, class_dev
->name
);
566 if (strcmp_pattern(dev
->kernel_name
, class_dev
->name
) != 0)
569 strfieldcpy(udev
->name
, dev
->name
);
570 strfieldcpy(udev
->symlink
, dev
->symlink
);
571 dbg("found name, '%s' becomes '%s'", dev
->kernel_name
, udev
->name
);
578 static void do_kernelname(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
580 /* heh, this is pretty simple... */
581 strfieldcpy(udev
->name
, class_dev
->name
);
584 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
586 struct sysfs_device
*sysfs_device
= NULL
;
587 struct sysfs_class_device
*class_dev_parent
= NULL
;
589 struct perm_device
*perm
;
593 /* find the sysfs_device for this class device */
594 /* Wouldn't it really be nice if libsysfs could do this for us? */
595 sysfs_device
= sysfs_get_classdev_device(class_dev
);
596 if (sysfs_device
== NULL
) {
597 /* bah, let's go backwards up a level to see if the device is there,
598 * as block partitions don't point to the physical device. Need to fix that
599 * up in the kernel...
601 if (strcmp(class_dev
->classname
, SYSFS_BLOCK_NAME
) == 0) {
602 dbg("looking at block device");
603 if (isdigit(class_dev
->path
[strlen(class_dev
->path
)-1])) {
604 dbg("really is a partition");
605 class_dev_parent
= sysfs_get_classdev_parent
607 if (class_dev_parent
== NULL
) {
608 dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev
->name
);
610 dbg("class_dev_parent->name='%s'", class_dev_parent
->name
);
611 sysfs_device
= sysfs_get_classdev_device(class_dev_parent
);
618 dbg("sysfs_device->path='%s'", sysfs_device
->path
);
619 dbg("sysfs_device->bus_id='%s'", sysfs_device
->bus_id
);
620 dbg("sysfs_device->bus='%s'", sysfs_device
->bus
);
621 strfieldcpy(udev
->bus_id
, sysfs_device
->bus_id
);
623 dbg("class_dev->name = '%s'", class_dev
->name
);
626 build_kernel_number(class_dev
, udev
);
628 /* rules are looked at in priority order */
629 retval
= do_callout(class_dev
, udev
, sysfs_device
);
633 retval
= do_label(class_dev
, udev
, sysfs_device
);
637 retval
= do_number(class_dev
, udev
, sysfs_device
);
641 retval
= do_topology(class_dev
, udev
, sysfs_device
);
645 retval
= do_replace(class_dev
, udev
, sysfs_device
);
649 do_kernelname(class_dev
, udev
);
653 /* substitute placeholder */
654 apply_format(udev
, udev
->name
);
655 apply_format(udev
, udev
->symlink
);
658 perm
= find_perm(udev
->name
);
660 udev
->mode
= perm
->mode
;
661 strfieldcpy(udev
->owner
, perm
->owner
);
662 strfieldcpy(udev
->group
, perm
->group
);
664 /* no matching perms found :( */
665 udev
->mode
= get_default_mode(class_dev
);
666 udev
->owner
[0] = 0x00;
667 udev
->group
[0] = 0x00;
669 dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o",
670 udev
->name
, udev
->owner
, udev
->group
, udev
->mode
);
675 int namedev_init(void)
679 retval
= namedev_init_rules();
683 retval
= namedev_init_permissions();
687 dump_config_dev_list();
688 dump_perm_dev_list();