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);
43 /* compare string with pattern (supports * ? [0-9] [!A-Z]) */
44 static int strcmp_pattern(const char *p, const char *s)
60 while (*p && (*p != ']')) {
63 if ((*s >= *p) && (*s <= p[2]))
71 while (*p && (*p != ']'))
73 return strcmp_pattern(p+1, s+1);
79 if (strcmp_pattern(p, s+1))
80 return strcmp_pattern(p+1, s);
88 if ((*p == *s) || (*p == '?'))
89 return strcmp_pattern(p+1, s+1);
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 int add_config_dev(struct config_device *new_dev)
105 struct list_head *tmp;
106 struct config_device *tmp_dev;
108 /* update the values if we already have the device */
109 list_for_each(tmp, &config_device_list) {
110 struct config_device *dev = list_entry(tmp, struct config_device, node);
111 if (strcmp_pattern(new_dev->name, dev->name))
113 if (strncmp(dev->bus, new_dev->bus, sizeof(dev->name)))
115 copy_var(dev, new_dev, type);
116 copy_var(dev, new_dev, mode);
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, owner);
125 copy_string(dev, new_dev, group);
129 /* not found, add new structure to the device list */
130 tmp_dev = malloc(sizeof(*tmp_dev));
133 memcpy(tmp_dev, new_dev, sizeof(*tmp_dev));
134 list_add_tail(&tmp_dev->node, &config_device_list);
135 //dump_config_dev(tmp_dev);
139 static mode_t get_default_mode(struct sysfs_class_device *class_dev)
141 /* just default everyone to rw for the world! */
145 static void build_kernel_number(struct sysfs_class_device *class_dev, struct udevice *udev)
149 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
150 dig = class_dev->name + strlen(class_dev->name);
151 while (isdigit(*(dig-1)))
153 strfieldcpy(udev->kernel_number, dig);
154 dbg("kernel_number='%s'", udev->kernel_number);
157 static void apply_format(struct udevice *udev, unsigned char *string)
159 char name[NAME_SIZE];
163 pos = strchr(string, '%');
166 strfieldcpy(name, pos+2);
170 if (strlen(udev->bus_id) == 0)
172 strcat(pos, udev->bus_id);
173 dbg("substitute bus_id '%s'", udev->bus_id);
176 if (strlen(udev->kernel_number) == 0)
178 strcat(pos, udev->kernel_number);
179 dbg("substitute kernel number '%s'", udev->kernel_number);
182 if (strlen(udev->kernel_number) == 0) {
187 strcat(pos, udev->kernel_number);
188 dbg("substitute kernel number '%s'", udev->kernel_number);
191 sprintf(pos, "%u", udev->minor);
192 dbg("substitute minor number '%u'", udev->minor);
195 sprintf(pos, "%u", udev->major);
196 dbg("substitute major number '%u'", udev->major);
199 if (strlen(udev->callout_value) == 0)
201 strcat(pos, udev->callout_value);
202 dbg("substitute callout output '%s'", udev->callout_value);
205 dbg("unknown substitution type '%%%c'", pos[1]);
208 strcat(string, name);
215 static int exec_callout(struct config_device *dev, char *value, int len)
225 char *args[CALLOUT_MAXARG];
228 dbg("callout to '%s'", dev->exec_program);
242 close(STDOUT_FILENO);
243 dup(fds[1]); /* dup write side of pipe to STDOUT */
244 if (strchr(dev->exec_program, ' ')) {
245 /* callout with arguments */
246 arg = dev->exec_program;
247 for (i=0; i < CALLOUT_MAXARG-1; i++) {
248 args[i] = strsep(&arg, " ");
253 dbg("too many args - %d", i);
256 retval = execve(args[0], args, main_envp);
258 retval = execve(dev->exec_program, main_argv, main_envp);
261 dbg("child execve failed");
264 return -1; /* avoid compiler warning */
266 /* parent reads from fds[0] */
270 res = read(fds[0], buffer, sizeof(buffer) - 1);
275 dbg("callout len %d too short", len);
279 dbg("callout value already set");
283 strncpy(value, buffer, len);
286 dbg("callout returned '%s'", value);
290 dbg("wait failed result %d", res);
295 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
296 dbg("callout program status 0x%x", status);
304 static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
306 struct config_device *dev;
307 struct list_head *tmp;
309 list_for_each(tmp, &config_device_list) {
310 dev = list_entry(tmp, struct config_device, node);
311 if (dev->type != CALLOUT)
315 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
316 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
320 /* substitute anything that needs to be in the program name */
321 apply_format(udev, dev->exec_program);
322 if (exec_callout(dev, udev->callout_value, NAME_SIZE))
324 if (strcmp_pattern(dev->id, udev->callout_value) != 0)
326 strfieldcpy(udev->name, dev->name);
327 if (dev->mode != 0) {
328 udev->mode = dev->mode;
329 strfieldcpy(udev->owner, dev->owner);
330 strfieldcpy(udev->group, dev->group);
332 dbg("callout returned matching value '%s', '%s' becomes '%s'"
333 " - owner='%s', group='%s', mode=%#o",
334 dev->id, class_dev->name, udev->name,
335 dev->owner, dev->group, dev->mode);
341 static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
343 struct sysfs_attribute *tmpattr = NULL;
344 struct config_device *dev;
345 struct list_head *tmp;
347 list_for_each(tmp, &config_device_list) {
348 dev = list_entry(tmp, struct config_device, node);
349 if (dev->type != LABEL)
353 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
354 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
358 dbg("look for device attribute '%s'", dev->sysfs_file);
359 /* try to find the attribute in the class device directory */
360 tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file);
364 /* look in the class device directory if present */
366 tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file);
374 tmpattr->value[strlen(tmpattr->value)-1] = 0x00;
375 dbg("compare attribute '%s' value '%s' with '%s'",
376 dev->sysfs_file, tmpattr->value, dev->sysfs_value);
377 if (strcmp(dev->sysfs_value, tmpattr->value) != 0)
380 strfieldcpy(udev->name, dev->name);
381 if (dev->mode != 0) {
382 udev->mode = dev->mode;
383 strfieldcpy(udev->owner, dev->owner);
384 strfieldcpy(udev->group, dev->group);
386 dbg("found matching attribute '%s', '%s' becomes '%s' "
387 "- owner='%s', group='%s', mode=%#o",
388 dev->sysfs_file, class_dev->name, udev->name,
389 dev->owner, dev->group, dev->mode);
396 static int do_number(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
398 struct config_device *dev;
399 struct list_head *tmp;
400 char path[SYSFS_PATH_MAX];
404 /* we have to have a sysfs device for NUMBER to work */
408 list_for_each(tmp, &config_device_list) {
409 dev = list_entry(tmp, struct config_device, node);
410 if (dev->type != NUMBER)
413 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
414 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
418 strfieldcpy(path, sysfs_device->path);
419 temp = strrchr(path, '/');
420 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
421 if (strstr(temp, dev->id) != NULL) {
425 temp = strrchr(path, '/');
426 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
427 if (strstr(temp, dev->id) != NULL)
432 strfieldcpy(udev->name, dev->name);
433 if (dev->mode != 0) {
434 udev->mode = dev->mode;
435 strfieldcpy(udev->owner, dev->owner);
436 strfieldcpy(udev->group, dev->group);
438 dbg("found matching id '%s', '%s' becomes '%s'"
439 " - owner='%s', group ='%s', mode=%#o",
440 dev->id, class_dev->name, udev->name,
441 dev->owner, dev->group, dev->mode);
447 static int do_topology(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
449 struct config_device *dev;
450 struct list_head *tmp;
451 char path[SYSFS_PATH_MAX];
455 /* we have to have a sysfs device for TOPOLOGY to work */
459 list_for_each(tmp, &config_device_list) {
460 dev = list_entry(tmp, struct config_device, node);
461 if (dev->type != TOPOLOGY)
464 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
465 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
469 strfieldcpy(path, sysfs_device->path);
470 temp = strrchr(path, '/');
471 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
472 if (strstr(temp, dev->place) != NULL) {
476 temp = strrchr(path, '/');
477 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
478 if (strstr(temp, dev->place) != NULL)
484 strfieldcpy(udev->name, dev->name);
485 if (dev->mode != 0) {
486 udev->mode = dev->mode;
487 strfieldcpy(udev->owner, dev->owner);
488 strfieldcpy(udev->group, dev->group);
490 dbg("found matching place '%s', '%s' becomes '%s'"
491 " - owner='%s', group ='%s', mode=%#o",
492 dev->place, class_dev->name, udev->name,
493 dev->owner, dev->group, dev->mode);
499 static int do_replace(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
501 struct config_device *dev;
502 struct list_head *tmp;
504 list_for_each(tmp, &config_device_list) {
505 dev = list_entry(tmp, struct config_device, node);
506 if (dev->type != REPLACE)
509 dbg("compare name '%s' with '%s'", dev->kernel_name, class_dev->name);
510 if (strcmp_pattern(dev->kernel_name, class_dev->name) != 0)
513 strfieldcpy(udev->name, dev->name);
514 if (dev->mode != 0) {
515 udev->mode = dev->mode;
516 strfieldcpy(udev->owner, dev->owner);
517 strfieldcpy(udev->group, dev->group);
519 dbg("found name, '%s' becomes '%s'"
520 " - owner='%s', group='%s', mode = %#o",
521 dev->kernel_name, udev->name,
522 dev->owner, dev->group, dev->mode);
529 static void do_kernelname(struct sysfs_class_device *class_dev, struct udevice *udev)
531 struct config_device *dev;
532 struct list_head *tmp;
535 strfieldcpy(udev->name, class_dev->name);
536 /* look for permissions */
537 list_for_each(tmp, &config_device_list) {
538 dev = list_entry(tmp, struct config_device, node);
539 len = strlen(dev->name);
540 if (strcmp_pattern(dev->name, class_dev->name))
542 if (dev->mode != 0) {
543 dbg("found permissions for '%s'", class_dev->name);
544 udev->mode = dev->mode;
545 strfieldcpy(udev->owner, dev->owner);
546 strfieldcpy(udev->group, dev->group);
551 int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev)
553 struct sysfs_device *sysfs_device = NULL;
554 struct sysfs_class_device *class_dev_parent = NULL;
560 /* find the sysfs_device for this class device */
561 /* Wouldn't it really be nice if libsysfs could do this for us? */
562 if (class_dev->sysdevice) {
563 sysfs_device = class_dev->sysdevice;
565 /* bah, let's go backwards up a level to see if the device is there,
566 * as block partitions don't point to the physical device. Need to fix that
567 * up in the kernel...
569 if (strstr(class_dev->path, "block")) {
570 dbg("looking at block device");
571 if (isdigit(class_dev->path[strlen(class_dev->path)-1])) {
572 char path[SYSFS_PATH_MAX];
574 dbg("really is a partition");
575 strfieldcpy(path, class_dev->path);
576 temp = strrchr(path, '/');
578 dbg("looking for a class device at '%s'", path);
579 class_dev_parent = sysfs_open_class_device(path);
580 if (class_dev_parent == NULL) {
581 dbg("sysfs_open_class_device at '%s' failed", path);
583 dbg("class_dev_parent->name='%s'", class_dev_parent->name);
584 if (class_dev_parent->sysdevice)
585 sysfs_device = class_dev_parent->sysdevice;
592 dbg("sysfs_device->path='%s'", sysfs_device->path);
593 dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id);
594 dbg("sysfs_device->bus='%s'", sysfs_device->bus);
595 strfieldcpy(udev->bus_id, sysfs_device->bus_id);
597 dbg("class_dev->name = '%s'", class_dev->name);
600 build_kernel_number(class_dev, udev);
602 /* rules are looked at in priority order */
603 retval = do_callout(class_dev, udev, sysfs_device);
607 retval = do_label(class_dev, udev, sysfs_device);
611 retval = do_number(class_dev, udev, sysfs_device);
615 retval = do_topology(class_dev, udev, sysfs_device);
619 retval = do_replace(class_dev, udev, sysfs_device);
623 do_kernelname(class_dev, udev);
627 /* substitute placeholder in NAME */
628 apply_format(udev, udev->name);
631 /* mode was never set above */
633 udev->mode = get_default_mode(class_dev);
634 udev->owner[0] = 0x00;
635 udev->group[0] = 0x00;
638 if (class_dev_parent)
639 sysfs_close_class_device(class_dev_parent);
644 int namedev_init(void)
648 retval = namedev_init_rules();
652 retval = namedev_init_permissions();
656 dump_config_dev_list();