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 mode_t mode = 0600; /* default to owner rw only */
143 if (strlen(default_mode_str) != 0) {
144 mode = strtol(default_mode_str, NULL, 8);
149 static void build_kernel_number(struct sysfs_class_device *class_dev, struct udevice *udev)
153 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
154 dig = class_dev->name + strlen(class_dev->name);
155 while (isdigit(*(dig-1)))
157 strfieldcpy(udev->kernel_number, dig);
158 dbg("kernel_number='%s'", udev->kernel_number);
161 static void apply_format(struct udevice *udev, unsigned char *string)
163 char name[NAME_SIZE];
167 pos = strchr(string, '%');
170 strfieldcpy(name, pos+2);
174 if (strlen(udev->bus_id) == 0)
176 strcat(pos, udev->bus_id);
177 dbg("substitute bus_id '%s'", udev->bus_id);
180 if (strlen(udev->kernel_number) == 0)
182 strcat(pos, udev->kernel_number);
183 dbg("substitute kernel number '%s'", udev->kernel_number);
186 if (strlen(udev->kernel_number) == 0) {
191 strcat(pos, udev->kernel_number);
192 dbg("substitute kernel number '%s'", udev->kernel_number);
195 sprintf(pos, "%u", udev->minor);
196 dbg("substitute minor number '%u'", udev->minor);
199 sprintf(pos, "%u", udev->major);
200 dbg("substitute major number '%u'", udev->major);
203 if (strlen(udev->callout_value) == 0)
205 strcat(pos, udev->callout_value);
206 dbg("substitute callout output '%s'", udev->callout_value);
209 dbg("unknown substitution type '%%%c'", pos[1]);
212 strcat(string, name);
219 static int exec_callout(struct config_device *dev, char *value, int len)
229 char *args[CALLOUT_MAXARG];
232 dbg("callout to '%s'", dev->exec_program);
246 close(STDOUT_FILENO);
247 dup(fds[1]); /* dup write side of pipe to STDOUT */
248 if (strchr(dev->exec_program, ' ')) {
249 /* callout with arguments */
250 arg = dev->exec_program;
251 for (i=0; i < CALLOUT_MAXARG-1; i++) {
252 args[i] = strsep(&arg, " ");
257 dbg("too many args - %d", i);
260 retval = execve(args[0], args, main_envp);
262 retval = execve(dev->exec_program, main_argv, main_envp);
265 dbg("child execve failed");
268 return -1; /* avoid compiler warning */
270 /* parent reads from fds[0] */
274 res = read(fds[0], buffer, sizeof(buffer) - 1);
279 dbg("callout len %d too short", len);
283 dbg("callout value already set");
287 strncpy(value, buffer, len);
290 dbg("callout returned '%s'", value);
294 dbg("wait failed result %d", res);
299 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
300 dbg("callout program status 0x%x", status);
308 static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
310 struct config_device *dev;
311 struct list_head *tmp;
313 list_for_each(tmp, &config_device_list) {
314 dev = list_entry(tmp, struct config_device, node);
315 if (dev->type != CALLOUT)
319 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
320 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
324 /* substitute anything that needs to be in the program name */
325 apply_format(udev, dev->exec_program);
326 if (exec_callout(dev, udev->callout_value, NAME_SIZE))
328 if (strcmp_pattern(dev->id, udev->callout_value) != 0)
330 strfieldcpy(udev->name, dev->name);
331 if (dev->mode != 0) {
332 udev->mode = dev->mode;
333 strfieldcpy(udev->owner, dev->owner);
334 strfieldcpy(udev->group, dev->group);
336 dbg("callout returned matching value '%s', '%s' becomes '%s'"
337 " - owner='%s', group='%s', mode=%#o",
338 dev->id, class_dev->name, udev->name,
339 dev->owner, dev->group, dev->mode);
345 static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
347 struct sysfs_attribute *tmpattr = NULL;
348 struct config_device *dev;
349 struct list_head *tmp;
351 list_for_each(tmp, &config_device_list) {
352 dev = list_entry(tmp, struct config_device, node);
353 if (dev->type != LABEL)
357 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
358 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
362 dbg("look for device attribute '%s'", dev->sysfs_file);
363 /* try to find the attribute in the class device directory */
364 tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file);
368 /* look in the class device directory if present */
370 tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file);
378 tmpattr->value[strlen(tmpattr->value)-1] = 0x00;
379 dbg("compare attribute '%s' value '%s' with '%s'",
380 dev->sysfs_file, tmpattr->value, dev->sysfs_value);
381 if (strcmp(dev->sysfs_value, tmpattr->value) != 0)
384 strfieldcpy(udev->name, dev->name);
385 if (dev->mode != 0) {
386 udev->mode = dev->mode;
387 strfieldcpy(udev->owner, dev->owner);
388 strfieldcpy(udev->group, dev->group);
390 dbg("found matching attribute '%s', '%s' becomes '%s' "
391 "- owner='%s', group='%s', mode=%#o",
392 dev->sysfs_file, class_dev->name, udev->name,
393 dev->owner, dev->group, dev->mode);
400 static int do_number(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
402 struct config_device *dev;
403 struct list_head *tmp;
404 char path[SYSFS_PATH_MAX];
408 /* we have to have a sysfs device for NUMBER to work */
412 list_for_each(tmp, &config_device_list) {
413 dev = list_entry(tmp, struct config_device, node);
414 if (dev->type != NUMBER)
417 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
418 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
422 strfieldcpy(path, sysfs_device->path);
423 temp = strrchr(path, '/');
424 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
425 if (strstr(temp, dev->id) != NULL) {
429 temp = strrchr(path, '/');
430 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
431 if (strstr(temp, dev->id) != NULL)
436 strfieldcpy(udev->name, dev->name);
437 if (dev->mode != 0) {
438 udev->mode = dev->mode;
439 strfieldcpy(udev->owner, dev->owner);
440 strfieldcpy(udev->group, dev->group);
442 dbg("found matching id '%s', '%s' becomes '%s'"
443 " - owner='%s', group ='%s', mode=%#o",
444 dev->id, class_dev->name, udev->name,
445 dev->owner, dev->group, dev->mode);
451 static int do_topology(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
453 struct config_device *dev;
454 struct list_head *tmp;
455 char path[SYSFS_PATH_MAX];
459 /* we have to have a sysfs device for TOPOLOGY to work */
463 list_for_each(tmp, &config_device_list) {
464 dev = list_entry(tmp, struct config_device, node);
465 if (dev->type != TOPOLOGY)
468 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
469 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
473 strfieldcpy(path, sysfs_device->path);
474 temp = strrchr(path, '/');
475 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
476 if (strstr(temp, dev->place) != NULL) {
480 temp = strrchr(path, '/');
481 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
482 if (strstr(temp, dev->place) != NULL)
488 strfieldcpy(udev->name, dev->name);
489 if (dev->mode != 0) {
490 udev->mode = dev->mode;
491 strfieldcpy(udev->owner, dev->owner);
492 strfieldcpy(udev->group, dev->group);
494 dbg("found matching place '%s', '%s' becomes '%s'"
495 " - owner='%s', group ='%s', mode=%#o",
496 dev->place, class_dev->name, udev->name,
497 dev->owner, dev->group, dev->mode);
503 static int do_replace(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
505 struct config_device *dev;
506 struct list_head *tmp;
508 list_for_each(tmp, &config_device_list) {
509 dev = list_entry(tmp, struct config_device, node);
510 if (dev->type != REPLACE)
513 dbg("compare name '%s' with '%s'", dev->kernel_name, class_dev->name);
514 if (strcmp_pattern(dev->kernel_name, class_dev->name) != 0)
517 strfieldcpy(udev->name, dev->name);
518 if (dev->mode != 0) {
519 udev->mode = dev->mode;
520 strfieldcpy(udev->owner, dev->owner);
521 strfieldcpy(udev->group, dev->group);
523 dbg("found name, '%s' becomes '%s'"
524 " - owner='%s', group='%s', mode = %#o",
525 dev->kernel_name, udev->name,
526 dev->owner, dev->group, dev->mode);
533 static void do_kernelname(struct sysfs_class_device *class_dev, struct udevice *udev)
535 struct config_device *dev;
536 struct list_head *tmp;
539 strfieldcpy(udev->name, class_dev->name);
540 /* look for permissions */
541 list_for_each(tmp, &config_device_list) {
542 dev = list_entry(tmp, struct config_device, node);
543 len = strlen(dev->name);
544 if (strcmp_pattern(dev->name, class_dev->name))
546 if (dev->mode != 0) {
547 dbg("found permissions for '%s'", class_dev->name);
548 udev->mode = dev->mode;
549 strfieldcpy(udev->owner, dev->owner);
550 strfieldcpy(udev->group, dev->group);
555 int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev)
557 struct sysfs_device *sysfs_device = NULL;
558 struct sysfs_class_device *class_dev_parent = NULL;
564 /* find the sysfs_device for this class device */
565 /* Wouldn't it really be nice if libsysfs could do this for us? */
566 if (class_dev->sysdevice) {
567 sysfs_device = class_dev->sysdevice;
569 /* bah, let's go backwards up a level to see if the device is there,
570 * as block partitions don't point to the physical device. Need to fix that
571 * up in the kernel...
573 if (strstr(class_dev->path, "block")) {
574 dbg("looking at block device");
575 if (isdigit(class_dev->path[strlen(class_dev->path)-1])) {
576 char path[SYSFS_PATH_MAX];
578 dbg("really is a partition");
579 strfieldcpy(path, class_dev->path);
580 temp = strrchr(path, '/');
582 dbg("looking for a class device at '%s'", path);
583 class_dev_parent = sysfs_open_class_device(path);
584 if (class_dev_parent == NULL) {
585 dbg("sysfs_open_class_device at '%s' failed", path);
587 dbg("class_dev_parent->name='%s'", class_dev_parent->name);
588 if (class_dev_parent->sysdevice)
589 sysfs_device = class_dev_parent->sysdevice;
596 dbg("sysfs_device->path='%s'", sysfs_device->path);
597 dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id);
598 dbg("sysfs_device->bus='%s'", sysfs_device->bus);
599 strfieldcpy(udev->bus_id, sysfs_device->bus_id);
601 dbg("class_dev->name = '%s'", class_dev->name);
604 build_kernel_number(class_dev, udev);
606 /* rules are looked at in priority order */
607 retval = do_callout(class_dev, udev, sysfs_device);
611 retval = do_label(class_dev, udev, sysfs_device);
615 retval = do_number(class_dev, udev, sysfs_device);
619 retval = do_topology(class_dev, udev, sysfs_device);
623 retval = do_replace(class_dev, udev, sysfs_device);
627 do_kernelname(class_dev, udev);
631 /* substitute placeholder in NAME */
632 apply_format(udev, udev->name);
635 /* mode was never set above */
637 udev->mode = get_default_mode(class_dev);
638 udev->owner[0] = 0x00;
639 udev->group[0] = 0x00;
642 if (class_dev_parent)
643 sysfs_close_class_device(class_dev_parent);
648 int namedev_init(void)
652 retval = namedev_init_rules();
656 retval = namedev_init_permissions();
660 dump_config_dev_list();