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 /* s2 may end with '*' to match everything */
44 static int strncmp_wildcard(char *s1, char *s2, int max)
53 return strncmp(s1, s2, len);
56 #define copy_var(a, b, var) \
60 #define copy_string(a, b, var) \
62 strcpy(a->var, b->var);
64 int add_config_dev(struct config_device *new_dev)
66 struct list_head *tmp;
67 struct config_device *tmp_dev;
69 /* update the values if we already have the device */
70 list_for_each(tmp, &config_device_list) {
71 struct config_device *dev = list_entry(tmp, struct config_device, node);
72 if (strncmp_wildcard(dev->name, new_dev->name, sizeof(dev->name)))
74 if (strncmp(dev->bus, new_dev->bus, sizeof(dev->name)))
76 copy_var(dev, new_dev, type);
77 copy_var(dev, new_dev, mode);
78 copy_string(dev, new_dev, bus);
79 copy_string(dev, new_dev, sysfs_file);
80 copy_string(dev, new_dev, sysfs_value);
81 copy_string(dev, new_dev, id);
82 copy_string(dev, new_dev, place);
83 copy_string(dev, new_dev, kernel_name);
84 copy_string(dev, new_dev, exec_program);
85 copy_string(dev, new_dev, owner);
86 copy_string(dev, new_dev, group);
90 /* not found, add new structure to the device list */
91 tmp_dev = malloc(sizeof(*tmp_dev));
94 memcpy(tmp_dev, new_dev, sizeof(*tmp_dev));
95 list_add_tail(&tmp_dev->node, &config_device_list);
96 //dump_config_dev(tmp_dev);
100 static mode_t get_default_mode(struct sysfs_class_device *class_dev)
102 /* just default everyone to rw for the world! */
106 static void build_kernel_number(struct sysfs_class_device *class_dev, struct udevice *udev)
110 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
111 dig = class_dev->name + strlen(class_dev->name);
112 while (isdigit(*(dig-1)))
114 strfieldcpy(udev->kernel_number, dig);
115 dbg("kernel_number='%s'", udev->kernel_number);
118 static void apply_format(struct udevice *udev, unsigned char *string)
120 char name[NAME_SIZE];
124 pos = strchr(string, '%');
127 strfieldcpy(name, pos+2);
131 if (strlen(udev->bus_id) == 0)
133 strcat(pos, udev->bus_id);
134 dbg("substitute bus_id '%s'", udev->bus_id);
137 if (strlen(udev->kernel_number) == 0)
139 strcat(pos, udev->kernel_number);
140 dbg("substitute kernel number '%s'", udev->kernel_number);
143 if (strlen(udev->kernel_number) == 0) {
148 strcat(pos, udev->kernel_number);
149 dbg("substitute kernel number '%s'", udev->kernel_number);
152 sprintf(pos, "%u", udev->minor);
153 dbg("substitute minor number '%u'", udev->minor);
156 sprintf(pos, "%u", udev->major);
157 dbg("substitute major number '%u'", udev->major);
160 if (strlen(udev->callout_value) == 0)
162 strcat(pos, udev->callout_value);
163 dbg("substitute callout output '%s'", udev->callout_value);
166 dbg("unknown substitution type '%%%c'", pos[1]);
169 strcat(string, name);
176 static int exec_callout(struct config_device *dev, char *value, int len)
186 char *args[CALLOUT_MAXARG];
189 dbg("callout to '%s'", dev->exec_program);
203 close(STDOUT_FILENO);
204 dup(fds[1]); /* dup write side of pipe to STDOUT */
205 if (strchr(dev->exec_program, ' ')) {
206 /* callout with arguments */
207 arg = dev->exec_program;
208 for (i=0; i < CALLOUT_MAXARG-1; i++) {
209 args[i] = strsep(&arg, " ");
214 dbg("too many args - %d", i);
217 retval = execve(args[0], args, main_envp);
219 retval = execve(dev->exec_program, main_argv, main_envp);
222 dbg("child execve failed");
225 return -1; /* avoid compiler warning */
227 /* parent reads from fds[0] */
231 res = read(fds[0], buffer, sizeof(buffer) - 1);
236 dbg("callout len %d too short", len);
240 dbg("callout value already set");
244 strncpy(value, buffer, len);
247 dbg("callout returned '%s'", value);
251 dbg("wait failed result %d", res);
256 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
257 dbg("callout program status 0x%x", status);
265 static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
267 struct config_device *dev;
268 struct list_head *tmp;
270 list_for_each(tmp, &config_device_list) {
271 dev = list_entry(tmp, struct config_device, node);
272 if (dev->type != CALLOUT)
276 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
277 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
281 /* substitute anything that needs to be in the program name */
282 apply_format(udev, dev->exec_program);
283 if (exec_callout(dev, udev->callout_value, NAME_SIZE))
285 if (strncmp_wildcard(udev->callout_value, dev->id, NAME_SIZE) != 0)
287 strfieldcpy(udev->name, dev->name);
288 if (dev->mode != 0) {
289 udev->mode = dev->mode;
290 strfieldcpy(udev->owner, dev->owner);
291 strfieldcpy(udev->group, dev->group);
293 dbg("callout returned matching value '%s', '%s' becomes '%s'"
294 " - owner='%s', group='%s', mode=%#o",
295 dev->id, class_dev->name, udev->name,
296 dev->owner, dev->group, dev->mode);
302 static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
304 struct sysfs_attribute *tmpattr = NULL;
305 struct config_device *dev;
306 struct list_head *tmp;
308 list_for_each(tmp, &config_device_list) {
309 dev = list_entry(tmp, struct config_device, node);
310 if (dev->type != LABEL)
314 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
315 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
319 dbg("look for device attribute '%s'", dev->sysfs_file);
320 /* try to find the attribute in the class device directory */
321 tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file);
325 /* look in the class device directory if present */
327 tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file);
335 tmpattr->value[strlen(tmpattr->value)-1] = 0x00;
336 dbg("compare attribute '%s' value '%s' with '%s'",
337 dev->sysfs_file, tmpattr->value, dev->sysfs_value);
338 if (strcmp(dev->sysfs_value, tmpattr->value) != 0)
341 strfieldcpy(udev->name, dev->name);
342 if (dev->mode != 0) {
343 udev->mode = dev->mode;
344 strfieldcpy(udev->owner, dev->owner);
345 strfieldcpy(udev->group, dev->group);
347 dbg("found matching attribute '%s', '%s' becomes '%s' "
348 "- owner='%s', group='%s', mode=%#o",
349 dev->sysfs_file, class_dev->name, udev->name,
350 dev->owner, dev->group, dev->mode);
357 static int do_number(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
359 struct config_device *dev;
360 struct list_head *tmp;
361 char path[SYSFS_PATH_MAX];
365 /* we have to have a sysfs device for NUMBER to work */
369 list_for_each(tmp, &config_device_list) {
370 dev = list_entry(tmp, struct config_device, node);
371 if (dev->type != NUMBER)
374 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
375 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
379 strfieldcpy(path, sysfs_device->path);
380 temp = strrchr(path, '/');
381 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
382 if (strstr(temp, dev->id) != NULL) {
386 temp = strrchr(path, '/');
387 dbg("search '%s' in '%s', path='%s'", dev->id, temp, path);
388 if (strstr(temp, dev->id) != NULL)
393 strfieldcpy(udev->name, dev->name);
394 if (dev->mode != 0) {
395 udev->mode = dev->mode;
396 strfieldcpy(udev->owner, dev->owner);
397 strfieldcpy(udev->group, dev->group);
399 dbg("found matching id '%s', '%s' becomes '%s'"
400 " - owner='%s', group ='%s', mode=%#o",
401 dev->id, class_dev->name, udev->name,
402 dev->owner, dev->group, dev->mode);
408 static int do_topology(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
410 struct config_device *dev;
411 struct list_head *tmp;
412 char path[SYSFS_PATH_MAX];
416 /* we have to have a sysfs device for TOPOLOGY to work */
420 list_for_each(tmp, &config_device_list) {
421 dev = list_entry(tmp, struct config_device, node);
422 if (dev->type != TOPOLOGY)
425 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus);
426 if (strcasecmp(dev->bus, sysfs_device->bus) != 0)
430 strfieldcpy(path, sysfs_device->path);
431 temp = strrchr(path, '/');
432 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
433 if (strstr(temp, dev->place) != NULL) {
437 temp = strrchr(path, '/');
438 dbg("search '%s' in '%s', path='%s'", dev->place, temp, path);
439 if (strstr(temp, dev->place) != NULL)
445 strfieldcpy(udev->name, dev->name);
446 if (dev->mode != 0) {
447 udev->mode = dev->mode;
448 strfieldcpy(udev->owner, dev->owner);
449 strfieldcpy(udev->group, dev->group);
451 dbg("found matching place '%s', '%s' becomes '%s'"
452 " - owner='%s', group ='%s', mode=%#o",
453 dev->place, class_dev->name, udev->name,
454 dev->owner, dev->group, dev->mode);
460 static int do_replace(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
462 struct config_device *dev;
463 struct list_head *tmp;
465 list_for_each(tmp, &config_device_list) {
466 dev = list_entry(tmp, struct config_device, node);
467 if (dev->type != REPLACE)
470 dbg("compare name '%s' with '%s'", dev->kernel_name, class_dev->name);
471 if (strncmp_wildcard(class_dev->name, dev->kernel_name, NAME_SIZE) != 0)
474 strfieldcpy(udev->name, dev->name);
475 if (dev->mode != 0) {
476 udev->mode = dev->mode;
477 strfieldcpy(udev->owner, dev->owner);
478 strfieldcpy(udev->group, dev->group);
480 dbg("found name, '%s' becomes '%s'"
481 " - owner='%s', group='%s', mode = %#o",
482 dev->kernel_name, udev->name,
483 dev->owner, dev->group, dev->mode);
490 static void do_kernelname(struct sysfs_class_device *class_dev, struct udevice *udev)
492 struct config_device *dev;
493 struct list_head *tmp;
496 strfieldcpy(udev->name, class_dev->name);
497 /* look for permissions */
498 list_for_each(tmp, &config_device_list) {
499 dev = list_entry(tmp, struct config_device, node);
500 len = strlen(dev->name);
501 if (strncmp_wildcard(class_dev->name, dev->name, sizeof(dev->name)))
503 if (dev->mode != 0) {
504 dbg("found permissions for '%s'", class_dev->name);
505 udev->mode = dev->mode;
506 strfieldcpy(udev->owner, dev->owner);
507 strfieldcpy(udev->group, dev->group);
512 int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev)
514 struct sysfs_device *sysfs_device = NULL;
515 struct sysfs_class_device *class_dev_parent = NULL;
521 /* find the sysfs_device for this class device */
522 /* Wouldn't it really be nice if libsysfs could do this for us? */
523 if (class_dev->sysdevice) {
524 sysfs_device = class_dev->sysdevice;
526 /* bah, let's go backwards up a level to see if the device is there,
527 * as block partitions don't point to the physical device. Need to fix that
528 * up in the kernel...
530 if (strstr(class_dev->path, "block")) {
531 dbg("looking at block device");
532 if (isdigit(class_dev->path[strlen(class_dev->path)-1])) {
533 char path[SYSFS_PATH_MAX];
535 dbg("really is a partition");
536 strfieldcpy(path, class_dev->path);
537 temp = strrchr(path, '/');
539 dbg("looking for a class device at '%s'", path);
540 class_dev_parent = sysfs_open_class_device(path);
541 if (class_dev_parent == NULL) {
542 dbg("sysfs_open_class_device at '%s' failed", path);
544 dbg("class_dev_parent->name='%s'", class_dev_parent->name);
545 if (class_dev_parent->sysdevice)
546 sysfs_device = class_dev_parent->sysdevice;
553 dbg("sysfs_device->path='%s'", sysfs_device->path);
554 dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id);
555 dbg("sysfs_device->bus='%s'", sysfs_device->bus);
556 strfieldcpy(udev->bus_id, sysfs_device->bus_id);
558 dbg("class_dev->name = '%s'", class_dev->name);
561 build_kernel_number(class_dev, udev);
563 /* rules are looked at in priority order */
564 retval = do_callout(class_dev, udev, sysfs_device);
568 retval = do_label(class_dev, udev, sysfs_device);
572 retval = do_number(class_dev, udev, sysfs_device);
576 retval = do_topology(class_dev, udev, sysfs_device);
580 retval = do_replace(class_dev, udev, sysfs_device);
584 do_kernelname(class_dev, udev);
588 /* substitute placeholder in NAME */
589 apply_format(udev, udev->name);
592 /* mode was never set above */
594 udev->mode = get_default_mode(class_dev);
595 udev->owner[0] = 0x00;
596 udev->group[0] = 0x00;
599 if (class_dev_parent)
600 sysfs_close_class_device(class_dev_parent);
605 int namedev_init(void)
609 retval = namedev_init_config();
613 retval = namedev_init_permissions();
617 dump_config_dev_list();