]>
git.ipfire.org Git - thirdparty/systemd.git/blob - namedev.c
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
);
127 /* not found, add new structure to the device list */
128 tmp_dev
= malloc(sizeof(*tmp_dev
));
131 memcpy(tmp_dev
, new_dev
, sizeof(*tmp_dev
));
132 list_add_tail(&tmp_dev
->node
, &config_device_list
);
133 //dump_config_dev(tmp_dev);
137 int add_perm_dev(struct perm_device
*new_dev
)
139 struct list_head
*tmp
;
140 struct perm_device
*tmp_dev
;
142 /* update the values if we already have the device */
143 list_for_each(tmp
, &perm_device_list
) {
144 struct perm_device
*dev
= list_entry(tmp
, struct perm_device
, node
);
145 if (strcmp_pattern(new_dev
->name
, dev
->name
))
147 copy_var(dev
, new_dev
, mode
);
148 copy_string(dev
, new_dev
, owner
);
149 copy_string(dev
, new_dev
, group
);
153 /* not found, add new structure to the perm list */
154 tmp_dev
= malloc(sizeof(*tmp_dev
));
157 memcpy(tmp_dev
, new_dev
, sizeof(*tmp_dev
));
158 list_add_tail(&tmp_dev
->node
, &perm_device_list
);
159 //dump_perm_dev(tmp_dev);
163 static struct perm_device
*find_perm(char *name
)
165 struct list_head
*tmp
;
166 struct perm_device
*perm
= NULL
;
168 list_for_each(tmp
, &perm_device_list
) {
169 perm
= list_entry(tmp
, struct perm_device
, node
);
170 if (strcmp_pattern(perm
->name
, name
))
177 static mode_t
get_default_mode(struct sysfs_class_device
*class_dev
)
179 mode_t mode
= 0600; /* default to owner rw only */
181 if (strlen(default_mode_str
) != 0) {
182 mode
= strtol(default_mode_str
, NULL
, 8);
187 static void build_kernel_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
191 /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */
192 dig
= class_dev
->name
+ strlen(class_dev
->name
);
193 while (isdigit(*(dig
-1)))
195 strfieldcpy(udev
->kernel_number
, dig
);
196 dbg("kernel_number='%s'", udev
->kernel_number
);
199 static void apply_format(struct udevice
*udev
, unsigned char *string
)
201 char name
[NAME_SIZE
];
205 pos
= strchr(string
, '%');
208 strfieldcpy(name
, pos
+2);
212 if (strlen(udev
->bus_id
) == 0)
214 strcat(pos
, udev
->bus_id
);
215 dbg("substitute bus_id '%s'", udev
->bus_id
);
218 if (strlen(udev
->kernel_number
) == 0)
220 strcat(pos
, udev
->kernel_number
);
221 dbg("substitute kernel number '%s'", udev
->kernel_number
);
224 if (strlen(udev
->kernel_number
) == 0) {
229 strcat(pos
, udev
->kernel_number
);
230 dbg("substitute kernel number '%s'", udev
->kernel_number
);
233 sprintf(pos
, "%u", udev
->minor
);
234 dbg("substitute minor number '%u'", udev
->minor
);
237 sprintf(pos
, "%u", udev
->major
);
238 dbg("substitute major number '%u'", udev
->major
);
241 if (strlen(udev
->callout_value
) == 0)
243 strcat(pos
, udev
->callout_value
);
244 dbg("substitute callout output '%s'", udev
->callout_value
);
247 dbg("unknown substitution type '%%%c'", pos
[1]);
250 strcat(string
, name
);
257 static int exec_callout(struct config_device
*dev
, char *value
, int len
)
267 char *args
[CALLOUT_MAXARG
];
270 dbg("callout to '%s'", dev
->exec_program
);
284 close(STDOUT_FILENO
);
285 dup(fds
[1]); /* dup write side of pipe to STDOUT */
286 if (strchr(dev
->exec_program
, ' ')) {
287 /* callout with arguments */
288 arg
= dev
->exec_program
;
289 for (i
=0; i
< CALLOUT_MAXARG
-1; i
++) {
290 args
[i
] = strsep(&arg
, " ");
295 dbg("too many args - %d", i
);
298 retval
= execve(args
[0], args
, main_envp
);
300 retval
= execve(dev
->exec_program
, main_argv
, main_envp
);
303 dbg("child execve failed");
306 return -1; /* avoid compiler warning */
308 /* parent reads from fds[0] */
312 res
= read(fds
[0], buffer
, sizeof(buffer
) - 1);
317 dbg("callout len %d too short", len
);
321 dbg("callout value already set");
325 strncpy(value
, buffer
, len
);
328 dbg("callout returned '%s'", value
);
332 dbg("wait failed result %d", res
);
337 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
338 dbg("callout program status 0x%x", status
);
346 static int do_callout(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
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
!= CALLOUT
)
357 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
358 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
362 /* substitute anything that needs to be in the program name */
363 apply_format(udev
, dev
->exec_program
);
364 if (exec_callout(dev
, udev
->callout_value
, NAME_SIZE
))
366 if (strcmp_pattern(dev
->id
, udev
->callout_value
) != 0)
368 strfieldcpy(udev
->name
, dev
->name
);
369 dbg("callout returned matching value '%s', '%s' becomes '%s'",
370 dev
->id
, class_dev
->name
, udev
->name
);
376 static int do_label(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
378 struct sysfs_attribute
*tmpattr
= NULL
;
379 struct config_device
*dev
;
380 struct list_head
*tmp
;
382 list_for_each(tmp
, &config_device_list
) {
383 dev
= list_entry(tmp
, struct config_device
, node
);
384 if (dev
->type
!= LABEL
)
388 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
389 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
393 dbg("look for device attribute '%s'", dev
->sysfs_file
);
394 /* try to find the attribute in the class device directory */
395 tmpattr
= sysfs_get_classdev_attr(class_dev
, dev
->sysfs_file
);
399 /* look in the class device directory if present */
401 tmpattr
= sysfs_get_device_attr(sysfs_device
, dev
->sysfs_file
);
409 tmpattr
->value
[strlen(tmpattr
->value
)-1] = 0x00;
410 dbg("compare attribute '%s' value '%s' with '%s'",
411 dev
->sysfs_file
, tmpattr
->value
, dev
->sysfs_value
);
412 if (strcmp(dev
->sysfs_value
, tmpattr
->value
) != 0)
415 strfieldcpy(udev
->name
, dev
->name
);
416 dbg("found matching attribute '%s', '%s' becomes '%s' ",
417 dev
->sysfs_file
, class_dev
->name
, udev
->name
);
424 static int do_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
426 struct config_device
*dev
;
427 struct list_head
*tmp
;
428 char path
[SYSFS_PATH_MAX
];
432 /* we have to have a sysfs device for NUMBER to work */
436 list_for_each(tmp
, &config_device_list
) {
437 dev
= list_entry(tmp
, struct config_device
, node
);
438 if (dev
->type
!= NUMBER
)
441 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
442 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
446 strfieldcpy(path
, sysfs_device
->path
);
447 temp
= strrchr(path
, '/');
448 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
449 if (strstr(temp
, dev
->id
) != NULL
) {
453 temp
= strrchr(path
, '/');
454 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
455 if (strstr(temp
, dev
->id
) != NULL
)
460 strfieldcpy(udev
->name
, dev
->name
);
461 dbg("found matching id '%s', '%s' becomes '%s'",
462 dev
->id
, class_dev
->name
, udev
->name
);
468 static int do_topology(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
470 struct config_device
*dev
;
471 struct list_head
*tmp
;
472 char path
[SYSFS_PATH_MAX
];
476 /* we have to have a sysfs device for TOPOLOGY to work */
480 list_for_each(tmp
, &config_device_list
) {
481 dev
= list_entry(tmp
, struct config_device
, node
);
482 if (dev
->type
!= TOPOLOGY
)
485 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
486 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
490 strfieldcpy(path
, sysfs_device
->path
);
491 temp
= strrchr(path
, '/');
492 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
493 if (strstr(temp
, dev
->place
) != NULL
) {
497 temp
= strrchr(path
, '/');
498 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
499 if (strstr(temp
, dev
->place
) != NULL
)
505 strfieldcpy(udev
->name
, dev
->name
);
506 dbg("found matching place '%s', '%s' becomes '%s'",
507 dev
->place
, class_dev
->name
, udev
->name
);
513 static int do_replace(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
515 struct config_device
*dev
;
516 struct list_head
*tmp
;
518 list_for_each(tmp
, &config_device_list
) {
519 dev
= list_entry(tmp
, struct config_device
, node
);
520 if (dev
->type
!= REPLACE
)
523 dbg("compare name '%s' with '%s'", dev
->kernel_name
, class_dev
->name
);
524 if (strcmp_pattern(dev
->kernel_name
, class_dev
->name
) != 0)
527 strfieldcpy(udev
->name
, dev
->name
);
528 dbg("found name, '%s' becomes '%s'", dev
->kernel_name
, udev
->name
);
535 static void do_kernelname(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
537 /* heh, this is pretty simple... */
538 strfieldcpy(udev
->name
, class_dev
->name
);
541 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
543 struct sysfs_device
*sysfs_device
= NULL
;
544 struct sysfs_class_device
*class_dev_parent
= NULL
;
547 struct perm_device
*perm
;
551 /* find the sysfs_device for this class device */
552 /* Wouldn't it really be nice if libsysfs could do this for us? */
553 if (class_dev
->sysdevice
) {
554 sysfs_device
= class_dev
->sysdevice
;
556 /* bah, let's go backwards up a level to see if the device is there,
557 * as block partitions don't point to the physical device. Need to fix that
558 * up in the kernel...
560 if (strstr(class_dev
->path
, "block")) {
561 dbg("looking at block device");
562 if (isdigit(class_dev
->path
[strlen(class_dev
->path
)-1])) {
563 char path
[SYSFS_PATH_MAX
];
565 dbg("really is a partition");
566 strfieldcpy(path
, class_dev
->path
);
567 temp
= strrchr(path
, '/');
569 dbg("looking for a class device at '%s'", path
);
570 class_dev_parent
= sysfs_open_class_device(path
);
571 if (class_dev_parent
== NULL
) {
572 dbg("sysfs_open_class_device at '%s' failed", path
);
574 dbg("class_dev_parent->name='%s'", class_dev_parent
->name
);
575 if (class_dev_parent
->sysdevice
)
576 sysfs_device
= class_dev_parent
->sysdevice
;
583 dbg("sysfs_device->path='%s'", sysfs_device
->path
);
584 dbg("sysfs_device->bus_id='%s'", sysfs_device
->bus_id
);
585 dbg("sysfs_device->bus='%s'", sysfs_device
->bus
);
586 strfieldcpy(udev
->bus_id
, sysfs_device
->bus_id
);
588 dbg("class_dev->name = '%s'", class_dev
->name
);
591 build_kernel_number(class_dev
, udev
);
593 /* rules are looked at in priority order */
594 retval
= do_callout(class_dev
, udev
, sysfs_device
);
598 retval
= do_label(class_dev
, udev
, sysfs_device
);
602 retval
= do_number(class_dev
, udev
, sysfs_device
);
606 retval
= do_topology(class_dev
, udev
, sysfs_device
);
610 retval
= do_replace(class_dev
, udev
, sysfs_device
);
614 do_kernelname(class_dev
, udev
);
618 /* substitute placeholder in NAME */
619 apply_format(udev
, udev
->name
);
622 perm
= find_perm(udev
->name
);
624 udev
->mode
= perm
->mode
;
625 strfieldcpy(udev
->owner
, perm
->owner
);
626 strfieldcpy(udev
->group
, perm
->group
);
628 /* no matching perms found :( */
629 udev
->mode
= get_default_mode(class_dev
);
630 udev
->owner
[0] = 0x00;
631 udev
->group
[0] = 0x00;
633 dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o",
634 udev
->name
, udev
->owner
, udev
->group
, udev
->mode
);
636 if (class_dev_parent
)
637 sysfs_close_class_device(class_dev_parent
);
642 int namedev_init(void)
646 retval
= namedev_init_rules();
650 retval
= namedev_init_permissions();
654 dump_config_dev_list();
655 dump_perm_dev_list();