]>
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
);
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 arg
= dev
->exec_program
;
290 for (i
=0; i
< CALLOUT_MAXARG
-1; i
++) {
291 args
[i
] = strsep(&arg
, " ");
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
);
329 dbg("callout returned '%s'", value
);
333 dbg("wait failed result %d", res
);
338 if (!WIFEXITED(status
) || (WEXITSTATUS(status
) != 0)) {
339 dbg("callout program status 0x%x", status
);
347 static int do_callout(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
349 struct config_device
*dev
;
350 struct list_head
*tmp
;
352 list_for_each(tmp
, &config_device_list
) {
353 dev
= list_entry(tmp
, struct config_device
, node
);
354 if (dev
->type
!= CALLOUT
)
358 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
359 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
363 /* substitute anything that needs to be in the program name */
364 apply_format(udev
, dev
->exec_program
);
365 if (exec_callout(dev
, udev
->callout_value
, NAME_SIZE
))
367 if (strcmp_pattern(dev
->id
, udev
->callout_value
) != 0)
369 strfieldcpy(udev
->name
, dev
->name
);
370 strfieldcpy(udev
->symlink
, dev
->symlink
);
371 dbg("callout returned matching value '%s', '%s' becomes '%s'",
372 dev
->id
, class_dev
->name
, udev
->name
);
378 static int do_label(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
380 struct sysfs_attribute
*tmpattr
= NULL
;
381 struct config_device
*dev
;
382 struct list_head
*tmp
;
385 list_for_each(tmp
, &config_device_list
) {
386 dev
= list_entry(tmp
, struct config_device
, node
);
387 if (dev
->type
!= LABEL
)
391 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
392 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
396 dbg("look for device attribute '%s'", dev
->sysfs_file
);
397 /* try to find the attribute in the class device directory */
398 tmpattr
= sysfs_get_classdev_attr(class_dev
, dev
->sysfs_file
);
402 /* look in the class device directory if present */
404 tmpattr
= sysfs_get_device_attr(sysfs_device
, dev
->sysfs_file
);
412 c
= tmpattr
->value
+ strlen(tmpattr
->value
)-1;
415 dbg("compare attribute '%s' value '%s' with '%s'",
416 dev
->sysfs_file
, tmpattr
->value
, dev
->sysfs_value
);
417 if (strcmp_pattern(dev
->sysfs_value
, tmpattr
->value
) != 0)
420 strfieldcpy(udev
->name
, dev
->name
);
421 strfieldcpy(udev
->symlink
, dev
->symlink
);
422 dbg("found matching attribute '%s', '%s' becomes '%s' ",
423 dev
->sysfs_file
, class_dev
->name
, udev
->name
);
430 static int do_number(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
432 struct config_device
*dev
;
433 struct list_head
*tmp
;
434 char path
[SYSFS_PATH_MAX
];
438 /* we have to have a sysfs device for NUMBER to work */
442 list_for_each(tmp
, &config_device_list
) {
443 dev
= list_entry(tmp
, struct config_device
, node
);
444 if (dev
->type
!= NUMBER
)
447 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
448 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
452 strfieldcpy(path
, sysfs_device
->path
);
453 temp
= strrchr(path
, '/');
454 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
455 if (strstr(temp
, dev
->id
) != NULL
) {
459 temp
= strrchr(path
, '/');
460 dbg("search '%s' in '%s', path='%s'", dev
->id
, temp
, path
);
461 if (strstr(temp
, dev
->id
) != NULL
)
466 strfieldcpy(udev
->name
, dev
->name
);
467 strfieldcpy(udev
->symlink
, dev
->symlink
);
468 dbg("found matching id '%s', '%s' becomes '%s'",
469 dev
->id
, class_dev
->name
, udev
->name
);
475 static int do_topology(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
477 struct config_device
*dev
;
478 struct list_head
*tmp
;
479 char path
[SYSFS_PATH_MAX
];
483 /* we have to have a sysfs device for TOPOLOGY to work */
487 list_for_each(tmp
, &config_device_list
) {
488 dev
= list_entry(tmp
, struct config_device
, node
);
489 if (dev
->type
!= TOPOLOGY
)
492 dbg("dev->bus='%s' sysfs_device->bus='%s'", dev
->bus
, sysfs_device
->bus
);
493 if (strcasecmp(dev
->bus
, sysfs_device
->bus
) != 0)
497 strfieldcpy(path
, sysfs_device
->path
);
498 temp
= strrchr(path
, '/');
499 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
500 if (strstr(temp
, dev
->place
) != NULL
) {
504 temp
= strrchr(path
, '/');
505 dbg("search '%s' in '%s', path='%s'", dev
->place
, temp
, path
);
506 if (strstr(temp
, dev
->place
) != NULL
)
512 strfieldcpy(udev
->name
, dev
->name
);
513 strfieldcpy(udev
->symlink
, dev
->symlink
);
514 dbg("found matching place '%s', '%s' becomes '%s'",
515 dev
->place
, class_dev
->name
, udev
->name
);
521 static int do_replace(struct sysfs_class_device
*class_dev
, struct udevice
*udev
, struct sysfs_device
*sysfs_device
)
523 struct config_device
*dev
;
524 struct list_head
*tmp
;
526 list_for_each(tmp
, &config_device_list
) {
527 dev
= list_entry(tmp
, struct config_device
, node
);
528 if (dev
->type
!= REPLACE
)
531 dbg("compare name '%s' with '%s'", dev
->kernel_name
, class_dev
->name
);
532 if (strcmp_pattern(dev
->kernel_name
, class_dev
->name
) != 0)
535 strfieldcpy(udev
->name
, dev
->name
);
536 strfieldcpy(udev
->symlink
, dev
->symlink
);
537 dbg("found name, '%s' becomes '%s'", dev
->kernel_name
, udev
->name
);
544 static void do_kernelname(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
546 /* heh, this is pretty simple... */
547 strfieldcpy(udev
->name
, class_dev
->name
);
550 int namedev_name_device(struct sysfs_class_device
*class_dev
, struct udevice
*udev
)
552 struct sysfs_device
*sysfs_device
= NULL
;
553 struct sysfs_class_device
*class_dev_parent
= NULL
;
556 struct perm_device
*perm
;
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 */
628 apply_format(udev
, udev
->name
);
629 apply_format(udev
, udev
->symlink
);
632 perm
= find_perm(udev
->name
);
634 udev
->mode
= perm
->mode
;
635 strfieldcpy(udev
->owner
, perm
->owner
);
636 strfieldcpy(udev
->group
, perm
->group
);
638 /* no matching perms found :( */
639 udev
->mode
= get_default_mode(class_dev
);
640 udev
->owner
[0] = 0x00;
641 udev
->group
[0] = 0x00;
643 dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o",
644 udev
->name
, udev
->owner
, udev
->group
, udev
->mode
);
646 if (class_dev_parent
)
647 sysfs_close_class_device(class_dev_parent
);
652 int namedev_init(void)
656 retval
= namedev_init_rules();
660 retval
= namedev_init_permissions();
664 dump_config_dev_list();
665 dump_perm_dev_list();