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
];
206 pos
= strchr(string
, '%');
209 strfieldcpy(name
, pos
+2);
213 if (strlen(udev
->bus_id
) == 0)
215 strcat(pos
, udev
->bus_id
);
216 dbg("substitute bus_id '%s'", udev
->bus_id
);
219 if (strlen(udev
->kernel_number
) == 0)
221 strcat(pos
, udev
->kernel_number
);
222 dbg("substitute kernel number '%s'", udev
->kernel_number
);
225 if (strlen(udev
->kernel_number
) == 0) {
230 strcat(pos
, udev
->kernel_number
);
231 dbg("substitute kernel number '%s'", udev
->kernel_number
);
234 sprintf(pos
, "%u", udev
->minor
);
235 dbg("substitute minor number '%u'", udev
->minor
);
238 sprintf(pos
, "%u", udev
->major
);
239 dbg("substitute major number '%u'", udev
->major
);
242 if (strlen(udev
->callout_value
) == 0)
244 strcat(pos
, udev
->callout_value
);
245 dbg("substitute callout output '%s'", udev
->callout_value
);
248 dbg("unknown substitution type '%%%c'", pos
[1]);
251 strcat(string
, name
);
258 static int exec_callout(struct config_device
*dev
, char *value
, int len
)
268 char *args
[CALLOUT_MAXARG
];
271 dbg("callout to '%s'", dev
->exec_program
);
285 close(STDOUT_FILENO
);
286 dup(fds
[1]); /* dup write side of pipe to STDOUT */
287 if (strchr(dev
->exec_program
, ' ')) {
288 /* callout with arguments */
289 pos
= dev
->exec_program
;
290 for (i
=0; i
< CALLOUT_MAXARG
-1; i
++) {
291 args
[i
] = strsep(&pos
, " ");
296 dbg("too many args - %d", i
);
299 retval
= execve(args
[0], args
, main_envp
);
301 retval
= execve(dev
->exec_program
, main_argv
, main_envp
);
304 dbg("child execve failed");
307 return -1; /* avoid compiler warning */
309 /* parent reads from fds[0] */
313 res
= read(fds
[0], buffer
, sizeof(buffer
) - 1);
318 dbg("callout len %d too short", len
);
322 dbg("callout value already set");
326 strncpy(value
, buffer
, len
);
327 pos
= value
+ strlen(value
)-1;
330 dbg("callout returned '%s'", value
);
336 dbg("wait failed result %d", res
);
341 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
342 dbg("callout program status 0x%x", status
);
350 static int do_callout(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
352 struct config_device
*dev
;
353 struct list_head
*tmp
;
355 list_for_each(tmp
, &config_device_list
) {
356 dev
= list_entry(tmp
, struct config_device
, node
);
357 if (dev
->type
!= CALLOUT
)
361 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
362 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
366 /* substitute anything that needs to be in the program name */
367 apply_format(udev
, dev
->exec_program
);
368 if (exec_callout(dev
, udev
->callout_value
, NAME_SIZE
))
370 if (strcmp_pattern(dev
->id
, udev
->callout_value
) != 0)
372 strfieldcpy(udev
->name
, dev
->name
);
373 strfieldcpy(udev
->symlink
, dev
->symlink
);
374 dbg("callout returned matching value '%s', '%s' becomes '%s'",
375 dev
->id
, class_dev
->name
, udev
->name
);
381 static int do_label(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
383 struct sysfs_attribute
*tmpattr
= NULL
;
384 struct config_device
*dev
;
385 struct list_head
*tmp
;
388 list_for_each(tmp
, &config_device_list
) {
389 dev
= list_entry(tmp
, struct config_device
, node
);
390 if (dev
->type
!= LABEL
)
394 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
395 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
399 dbg("look for device attribute '%s'", dev
->sysfs_file
);
400 /* try to find the attribute in the class device directory */
401 tmpattr
= sysfs_get_classdev_attr(class_dev
, dev
->sysfs_file
);
405 /* look in the class device directory if present */
407 tmpattr
= sysfs_get_device_attr(sysfs_device
, dev
->sysfs_file
);
415 c
= tmpattr
->value
+ strlen(tmpattr
->value
)-1;
418 dbg("compare attribute '%s' value '%s' with '%s'",
419 dev
->sysfs_file
, tmpattr
->value
, dev
->sysfs_value
);
420 if (strcmp_pattern(dev
->sysfs_value
, tmpattr
->value
) != 0)
423 strfieldcpy(udev
->name
, dev
->name
);
424 strfieldcpy(udev
->symlink
, dev
->symlink
);
425 dbg("found matching attribute '%s', '%s' becomes '%s' ",
426 dev
->sysfs_file
, class_dev
->name
, udev
->name
);
433 static int do_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
435 struct config_device
*dev
;
436 struct list_head
*tmp
;
437 char path
[SYSFS_PATH_MAX
];
441 /* we have to have a sysfs device for NUMBER to work */
445 list_for_each(tmp
, &config_device_list
) {
446 dev
= list_entry(tmp
, struct config_device
, node
);
447 if (dev
->type
!= NUMBER
)
450 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
451 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
455 strfieldcpy(path
, sysfs_device
->path
);
456 temp
= strrchr(path
, '/');
457 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
458 if (strstr(temp
, dev
->id
) != NULL
) {
462 temp
= strrchr(path
, '/');
463 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
464 if (strstr(temp
, dev
->id
) != NULL
)
469 strfieldcpy(udev
->name
, dev
->name
);
470 strfieldcpy(udev
->symlink
, dev
->symlink
);
471 dbg("found matching id '%s', '%s' becomes '%s'",
472 dev
->id
, class_dev
->name
, udev
->name
);
478 static int do_topology(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
480 struct config_device
*dev
;
481 struct list_head
*tmp
;
482 char path
[SYSFS_PATH_MAX
];
486 /* we have to have a sysfs device for TOPOLOGY to work */
490 list_for_each(tmp
, &config_device_list
) {
491 dev
= list_entry(tmp
, struct config_device
, node
);
492 if (dev
->type
!= TOPOLOGY
)
495 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
496 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
500 strfieldcpy(path
, sysfs_device
->path
);
501 temp
= strrchr(path
, '/');
502 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
503 if (strstr(temp
, dev
->place
) != NULL
) {
507 temp
= strrchr(path
, '/');
508 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
509 if (strstr(temp
, dev
->place
) != NULL
)
515 strfieldcpy(udev
->name
, dev
->name
);
516 strfieldcpy(udev
->symlink
, dev
->symlink
);
517 dbg("found matching place '%s', '%s' becomes '%s'",
518 dev
->place
, class_dev
->name
, udev
->name
);
524 static int do_replace(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
526 struct config_device
*dev
;
527 struct list_head
*tmp
;
529 list_for_each(tmp
, &config_device_list
) {
530 dev
= list_entry(tmp
, struct config_device
, node
);
531 if (dev
->type
!= REPLACE
)
534 dbg("compare name '%s' with '%s'", dev
->kernel_name
, class_dev
->name
);
535 if (strcmp_pattern(dev
->kernel_name
, class_dev
->name
) != 0)
538 strfieldcpy(udev
->name
, dev
->name
);
539 strfieldcpy(udev
->symlink
, dev
->symlink
);
540 dbg("found name, '%s' becomes '%s'", dev
->kernel_name
, udev
->name
);
547 static void do_kernelname(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
549 /* heh, this is pretty simple... */
550 strfieldcpy(udev
->name
, class_dev
->name
);
553 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
555 struct sysfs_device
*sysfs_device
= NULL
;
556 struct sysfs_class_device
*class_dev_parent
= NULL
;
558 struct perm_device
*perm
;
562 /* find the sysfs_device for this class device */
563 /* Wouldn't it really be nice if libsysfs could do this for us? */
564 sysfs_device
= sysfs_get_classdev_device(class_dev
);
565 if (sysfs_device
== NULL
) {
566 /* bah, let's go backwards up a level to see if the device is there,
567 * as block partitions don't point to the physical device. Need to fix that
568 * up in the kernel...
570 if (strcmp(class_dev
->classname
, SYSFS_BLOCK_NAME
) == 0) {
571 dbg("looking at block device");
572 if (isdigit(class_dev
->path
[strlen(class_dev
->path
)-1])) {
573 dbg("really is a partition");
574 class_dev_parent
= sysfs_get_classdev_parent
576 if (class_dev_parent
== NULL
) {
577 dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev
->name
);
579 dbg("class_dev_parent->name='%s'", class_dev_parent
->name
);
580 sysfs_device
= sysfs_get_classdev_device(class_dev_parent
);
587 dbg("sysfs_device->path='%s'", sysfs_device
->path
);
588 dbg("sysfs_device->bus_id='%s'", sysfs_device
->bus_id
);
589 dbg("sysfs_device->bus='%s'", sysfs_device
->bus
);
590 strfieldcpy(udev
->bus_id
, sysfs_device
->bus_id
);
592 dbg("class_dev->name = '%s'", class_dev
->name
);
595 build_kernel_number(class_dev
, udev
);
597 /* rules are looked at in priority order */
598 retval
= do_callout(class_dev
, udev
, sysfs_device
);
602 retval
= do_label(class_dev
, udev
, sysfs_device
);
606 retval
= do_number(class_dev
, udev
, sysfs_device
);
610 retval
= do_topology(class_dev
, udev
, sysfs_device
);
614 retval
= do_replace(class_dev
, udev
, sysfs_device
);
618 do_kernelname(class_dev
, udev
);
622 /* substitute placeholder */
623 apply_format(udev
, udev
->name
);
624 apply_format(udev
, udev
->symlink
);
627 perm
= find_perm(udev
->name
);
629 udev
->mode
= perm
->mode
;
630 strfieldcpy(udev
->owner
, perm
->owner
);
631 strfieldcpy(udev
->group
, perm
->group
);
633 /* no matching perms found :( */
634 udev
->mode
= get_default_mode(class_dev
);
635 udev
->owner
[0] = 0x00;
636 udev
->group
[0] = 0x00;
638 dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o",
639 udev
->name
, udev
->owner
, udev
->group
, udev
->mode
);
644 int namedev_init(void)
648 retval
= namedev_init_rules();
652 retval
= namedev_init_permissions();
656 dump_config_dev_list();
657 dump_perm_dev_list();