From: Kay Sievers Date: Thu, 16 Oct 2008 16:13:48 +0000 (+0200) Subject: rename udev source files X-Git-Tag: 174~1444 X-Git-Url: http://git-history.diyao.me/?a=commitdiff_plain;h=8460299b572eb13e7b97a0956b59c053f9009afa;p=systemd%2F.git rename udev source files --- diff --git a/extras/floppy/Makefile.am b/extras/floppy/Makefile.am index 0b154498d0..c812689586 100644 --- a/extras/floppy/Makefile.am +++ b/extras/floppy/Makefile.am @@ -10,11 +10,11 @@ create_floppy_devices_SOURCES = \ ../../udev/lib/libudev.c \ ../../udev/lib/libudev-list.c \ ../../udev/lib/libudev-util.c \ - ../../udev/udev_utils.c + ../../udev/udev-util.c if USE_SELINUX create_floppy_devices_SOURCES += \ - ../../udev/udev_selinux.c + ../../udev/udev-selinux.c create_floppy_devices_LDADD = \ $(SELINUX_LIBS) endif diff --git a/udev/Makefile.am b/udev/Makefile.am index 9c765ffe96..cbab15d34b 100644 --- a/udev/Makefile.am +++ b/udev/Makefile.am @@ -14,14 +14,14 @@ common_ldadd = common_files = \ udev.h \ - udev_rules.h \ - udev_sysdeps.h \ - udev_device_event.c \ - udev_node.c \ - udev_rules.c \ - udev_rules_parse.c \ - udev_utils.c \ - udev_utils_file.c \ + udev-rules.h \ + udev-sysdeps.h \ + udev-event.c \ + udev-node.c \ + udev-rules.c \ + udev-rules-parse.c \ + udev-util.c \ + udev-util-file.c \ list.h \ lib/libudev.h \ lib/libudev-private.h \ @@ -37,7 +37,7 @@ common_files = \ if USE_SELINUX common_files += \ - udev_selinux.c + udev-selinux.c common_ldadd += \ $(SELINUX_LIBS) endif diff --git a/udev/test-udev.c b/udev/test-udev.c index 6c327a6e08..43c97aeb34 100644 --- a/udev/test-udev.c +++ b/udev/test-udev.c @@ -29,7 +29,7 @@ #include #include "udev.h" -#include "udev_rules.h" +#include "udev-rules.h" static void asmlinkage sig_handler(int signum) { diff --git a/udev/udev-event.c b/udev/udev-event.c new file mode 100644 index 0000000000..24f7c55bbc --- /dev/null +++ b/udev/udev-event.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev-rules.h" + +struct udev_event *udev_event_new(struct udev_device *dev) +{ + struct udev_event *event; + + event = malloc(sizeof(struct udev_event)); + if (event == NULL) + return NULL; + memset(event, 0x00, sizeof(struct udev_event)); + + event->dev = dev; + event->udev = udev_device_get_udev(dev); + udev_list_init(&event->run_list); + event->mode = 0660; + util_strlcpy(event->owner, "0", sizeof(event->owner)); + util_strlcpy(event->group, "0", sizeof(event->group)); + + dbg(event->udev, "allocated event %p\n", event); + return event; +} + +void udev_event_unref(struct udev_event *event) +{ + udev_list_cleanup(event->udev, &event->run_list); + dbg(event->udev, "free event %p\n", event); + free(event); +} + +static void kernel_log(struct ifreq ifr) +{ + int klog; + FILE *f; + + klog = open("/dev/kmsg", O_WRONLY); + if (klog < 0) + return; + + f = fdopen(klog, "w"); + if (f == NULL) { + close(klog); + return; + } + + fprintf(f, "<6>udev: renamed network interface %s to %s\n", + ifr.ifr_name, ifr.ifr_newname); + fclose(f); +} + +static int rename_netif(struct udev_event *event) +{ + struct udev_device *dev = event->dev; + int sk; + struct ifreq ifr; + int err; + + info(event->udev, "changing net interface name from '%s' to '%s'\n", + udev_device_get_sysname(dev), event->name); + if (event->test) + return 0; + + sk = socket(PF_INET, SOCK_DGRAM, 0); + if (sk < 0) { + err(event->udev, "error opening socket: %m\n"); + return -1; + } + + memset(&ifr, 0x00, sizeof(struct ifreq)); + util_strlcpy(ifr.ifr_name, udev_device_get_sysname(dev), IFNAMSIZ); + util_strlcpy(ifr.ifr_newname, event->name, IFNAMSIZ); + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err == 0) + kernel_log(ifr); + else { + int loop; + + /* see if the destination interface name already exists */ + if (errno != EEXIST) { + err(event->udev, "error changing netif name %s to %s: %m\n", + ifr.ifr_name, ifr.ifr_newname); + goto exit; + } + + /* free our own name, another process may wait for us */ + util_strlcpy(ifr.ifr_newname, udev_device_get_sysname(dev), IFNAMSIZ); + util_strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ); + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err != 0) { + err(event->udev, "error changing netif name %s to %s: %m\n", + ifr.ifr_name, ifr.ifr_newname); + goto exit; + } + + /* wait 30 seconds for our target to become available */ + util_strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ); + util_strlcpy(ifr.ifr_newname, udev_device_get_devnode(dev), IFNAMSIZ); + loop = 30 * 20; + while (loop--) { + err = ioctl(sk, SIOCSIFNAME, &ifr); + if (err == 0) { + kernel_log(ifr); + break; + } + + if (errno != EEXIST) { + err(event->udev, "error changing net interface name %s to %s: %m\n", + ifr.ifr_name, ifr.ifr_newname); + break; + } + dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", + udev_device_get_devnode(dev), (30 * 20) - loop); + usleep(1000 * 1000 / 20); + } + } + +exit: + close(sk); + return err; +} + +int udev_event_run(struct udev_event *event, struct udev_rules *rules) +{ + struct udev_device *dev = event->dev; + int err = 0; + + if (udev_device_get_devpath_old(dev) != NULL) { + if (udev_device_rename_db(dev, udev_device_get_devpath(dev)) == 0) + info(event->udev, "moved database from '%s' to '%s'\n", + udev_device_get_devpath_old(dev), udev_device_get_devpath(dev)); + } + + /* add device node */ + if (major(udev_device_get_devnum(dev)) != 0 && + (strcmp(udev_device_get_action(dev), "add") == 0 || strcmp(udev_device_get_action(dev), "change") == 0)) { + char filename[UTIL_PATH_SIZE]; + struct udev_device *dev_old; + + dbg(event->udev, "device node add '%s'\n", udev_device_get_devpath(dev)); + + udev_rules_get_name(rules, event); + if (event->ignore_device) { + info(event->udev, "device event will be ignored\n"); + goto exit; + } + + if (event->name[0] == '\0') { + info(event->udev, "device node creation supressed\n"); + goto exit; + } + + /* set device node name */ + util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, event->name, sizeof(filename)); + udev_device_set_devnode(dev, filename); + + /* read current database entry; cleanup, if it is known device */ + dev_old = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); + if (dev_old != NULL) { + info(event->udev, "device '%s' already in database, updating\n", + udev_device_get_devpath(dev)); + udev_node_update_old_links(dev, dev_old, event->test); + udev_device_unref(dev_old); + } + + udev_device_update_db(dev); + + err = udev_node_add(dev, event->mode, event->owner, event->group, event->test); + if (err != 0) + goto exit; + + goto exit; + } + + /* add netif */ + if (strcmp(udev_device_get_subsystem(dev), "net") == 0 && strcmp(udev_device_get_action(dev), "add") == 0) { + dbg(event->udev, "netif add '%s'\n", udev_device_get_devpath(dev)); + + udev_rules_get_name(rules, event); + if (event->ignore_device) { + info(event->udev, "device event will be ignored\n"); + goto exit; + } + if (event->name[0] == '\0') { + info(event->udev, "device renaming supressed\n"); + goto exit; + } + + /* look if we want to change the name of the netif */ + if (strcmp(event->name, udev_device_get_sysname(dev)) != 0) { + char syspath[UTIL_PATH_SIZE]; + char *pos; + + err = rename_netif(event); + if (err != 0) + goto exit; + info(event->udev, "renamed netif to '%s'\n", event->name); + + /* remember old name */ + udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); + + /* now change the devpath, because the kernel device name has changed */ + util_strlcpy(syspath, udev_device_get_syspath(dev), sizeof(syspath)); + pos = strrchr(syspath, '/'); + if (pos != NULL) { + pos[1] = '\0'; + util_strlcat(syspath, event->name, sizeof(syspath)); + udev_device_set_syspath(event->dev, syspath); + udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); + info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); + } + } + goto exit; + } + + /* remove device node */ + if (major(udev_device_get_devnum(dev)) != 0 && strcmp(udev_device_get_action(dev), "remove") == 0) { + /* import database entry and delete it */ + udev_device_read_db(dev); + if (!event->test) + udev_device_delete_db(dev); + + if (udev_device_get_devnode(dev) == NULL) { + char devnode[UTIL_PATH_SIZE]; + + info(event->udev, "'%s' not found in database, using kernel name '%s'\n", + udev_device_get_syspath(dev), udev_device_get_sysname(dev)); + util_strlcpy(devnode, udev_get_dev_path(event->udev), sizeof(devnode)); + util_strlcat(devnode, "/", sizeof(devnode)); + util_strlcat(devnode, udev_device_get_sysname(dev), sizeof(devnode)); + udev_device_set_devnode(dev, devnode); + } + + udev_rules_get_run(rules, event); + if (event->ignore_device) { + info(event->udev, "device event will be ignored\n"); + goto exit; + } + + if (udev_device_get_ignore_remove(dev)) { + info(event->udev, "ignore_remove for '%s'\n", udev_device_get_devnode(dev)); + goto exit; + } + + err = udev_node_remove(dev, event->test); + goto exit; + } + + /* default devices */ + udev_rules_get_run(rules, event); + if (event->ignore_device) + info(event->udev, "device event will be ignored\n"); +exit: + return err; +} diff --git a/udev/udev-node.c b/udev/udev-node.c new file mode 100644 index 0000000000..9f2b6cf014 --- /dev/null +++ b/udev/udev-node.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev-rules.h" + +#define TMP_FILE_EXT ".udev-tmp" + +/* reverse mapping from the device file name to the devpath */ +static int name_index(struct udev *udev, const char *devpath, const char *name, int add, int test) +{ + char device[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE * 2]; + size_t devlen = strlen(udev_get_dev_path(udev))+1; + size_t start; + int fd; + + /* directory with device name */ + util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); + start = util_strlcat(filename, "/.udev/names/", sizeof(filename)); + util_strlcat(filename, &name[devlen], sizeof(filename)); + util_path_encode(&filename[start], sizeof(filename) - start); + /* entry with the devpath */ + util_strlcpy(device, devpath, sizeof(device)); + util_path_encode(device, sizeof(device)); + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, device, sizeof(filename)); + + if (add) { + info(udev, "creating index: '%s'\n", filename); + create_path(udev, filename); + fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); + if (fd > 0) + close(fd); + } else { + info(udev, "removing index: '%s'\n", filename); + unlink(filename); + delete_path(udev, filename); + } + return 0; +} + +int udev_node_mknod(struct udev_device *dev, const char *file, dev_t devnum, mode_t mode, uid_t uid, gid_t gid) +{ + struct udev *udev = udev_device_get_udev(dev); + char file_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; + struct stat stats; + int preserve = 0; + int err = 0; + + if (major(devnum) == 0) + devnum = udev_device_get_devnum(dev); + + if (strcmp(udev_device_get_subsystem(dev), "block") == 0) + mode |= S_IFBLK; + else + mode |= S_IFCHR; + + if (file == NULL) + file = udev_device_get_devnode(dev); + + if (lstat(file, &stats) == 0) { + if (((stats.st_mode & S_IFMT) == (mode & S_IFMT)) && (stats.st_rdev == devnum)) { + info(udev, "preserve file '%s', because it has correct dev_t\n", file); + preserve = 1; + udev_selinux_lsetfilecon(udev, file, mode); + } else { + info(udev, "atomically replace existing file '%s'\n", file); + util_strlcpy(file_tmp, file, sizeof(file_tmp)); + util_strlcat(file_tmp, TMP_FILE_EXT, sizeof(file_tmp)); + unlink(file_tmp); + udev_selinux_setfscreatecon(udev, file_tmp, mode); + err = mknod(file_tmp, mode, devnum); + udev_selinux_resetfscreatecon(udev); + if (err != 0) { + err(udev, "mknod(%s, %#o, %u, %u) failed: %m\n", + file_tmp, mode, major(devnum), minor(devnum)); + goto exit; + } + err = rename(file_tmp, file); + if (err != 0) { + err(udev, "rename(%s, %s) failed: %m\n", file_tmp, file); + unlink(file_tmp); + } + } + } else { + info(udev, "mknod(%s, %#o, (%u,%u))\n", file, mode, major(devnum), minor(devnum)); + udev_selinux_setfscreatecon(udev, file, mode); + err = mknod(file, mode, devnum); + udev_selinux_resetfscreatecon(udev); + if (err != 0) { + err(udev, "mknod(%s, %#o, (%u,%u) failed: %m\n", file, mode, major(devnum), minor(devnum)); + goto exit; + } + } + + if (!preserve || stats.st_mode != mode) { + info(udev, "chmod(%s, %#o)\n", file, mode); + err = chmod(file, mode); + if (err != 0) { + err(udev, "chmod(%s, %#o) failed: %m\n", file, mode); + goto exit; + } + } + + if (!preserve || stats.st_uid != uid || stats.st_gid != gid) { + info(udev, "chown(%s, %u, %u)\n", file, uid, gid); + err = chown(file, uid, gid); + if (err != 0) { + err(udev, "chown(%s, %u, %u) failed: %m\n", file, uid, gid); + goto exit; + } + } +exit: + return err; +} + +static int node_symlink(struct udev *udev, const char *node, const char *slink) +{ + struct stat stats; + char target[UTIL_PATH_SIZE] = ""; + char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; + int i = 0; + int tail = 0; + int len; + int err = 0; + + /* use relative link */ + while (node[i] && (node[i] == slink[i])) { + if (node[i] == '/') + tail = i+1; + i++; + } + while (slink[i] != '\0') { + if (slink[i] == '/') + util_strlcat(target, "../", sizeof(target)); + i++; + } + util_strlcat(target, &node[tail], sizeof(target)); + + /* preserve link with correct target, do not replace node of other device */ + if (lstat(slink, &stats) == 0) { + if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { + struct stat stats2; + + info(udev, "found existing node instead of symlink '%s'\n", slink); + if (lstat(node, &stats2) == 0) { + if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && + stats.st_rdev == stats2.st_rdev) { + info(udev, "replace device node '%s' with symlink to our node '%s'\n", + slink, node); + } else { + err(udev, "device node '%s' already exists, " + "link to '%s' will not overwrite it\n", + slink, node); + goto exit; + } + } + } else if (S_ISLNK(stats.st_mode)) { + char buf[UTIL_PATH_SIZE]; + + info(udev, "found existing symlink '%s'\n", slink); + len = readlink(slink, buf, sizeof(buf)); + if (len > 0) { + buf[len] = '\0'; + if (strcmp(target, buf) == 0) { + info(udev, "preserve already existing symlink '%s' to '%s'\n", + slink, target); + udev_selinux_lsetfilecon(udev, slink, S_IFLNK); + goto exit; + } + } + } + } else { + info(udev, "creating symlink '%s' to '%s'\n", slink, target); + udev_selinux_setfscreatecon(udev, slink, S_IFLNK); + err = symlink(target, slink); + udev_selinux_resetfscreatecon(udev); + if (err == 0) + goto exit; + } + + info(udev, "atomically replace '%s'\n", slink); + util_strlcpy(slink_tmp, slink, sizeof(slink_tmp)); + util_strlcat(slink_tmp, TMP_FILE_EXT, sizeof(slink_tmp)); + unlink(slink_tmp); + udev_selinux_setfscreatecon(udev, slink, S_IFLNK); + err = symlink(target, slink_tmp); + udev_selinux_resetfscreatecon(udev); + if (err != 0) { + err(udev, "symlink(%s, %s) failed: %m\n", target, slink_tmp); + goto exit; + } + err = rename(slink_tmp, slink); + if (err != 0) { + err(udev, "rename(%s, %s) failed: %m\n", slink_tmp, slink); + unlink(slink_tmp); + goto exit; + } +exit: + return err; +} + +static int get_devices_by_name(struct udev *udev, const char *name, struct list_head *name_list) +{ + char dirname[PATH_MAX]; + size_t devlen = strlen(udev_get_dev_path(udev))+1; + size_t start; + DIR *dir; + int count = 0; + + util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname)); + start = util_strlcat(dirname, "/.udev/names/", sizeof(dirname)); + util_strlcat(dirname, &name[devlen], sizeof(dirname)); + util_path_encode(&dirname[start], sizeof(dirname) - start); + + dir = opendir(dirname); + if (dir == NULL) { + info(udev, "no index directory '%s': %m\n", dirname); + count = -1; + goto out; + } + + info(udev, "found index directory '%s'\n", dirname); + while (1) { + struct dirent *ent; + char device[UTIL_PATH_SIZE]; + + ent = readdir(dir); + if (ent == NULL || ent->d_name[0] == '\0') + break; + if (ent->d_name[0] == '.') + continue; + + util_strlcpy(device, udev_get_sys_path(udev), sizeof(device)); + util_strlcat(device, ent->d_name, sizeof(device)); + util_path_decode(device); + name_list_add(udev, name_list, device, 0); + count++; + } + closedir(dir); +out: + return count; +} + +static int update_link(struct udev_device *dev, const char *slink, int test) +{ + struct udev *udev = udev_device_get_udev(dev); + LIST_HEAD(name_list); + struct name_entry *device; + char target[UTIL_PATH_SIZE] = ""; + int count; + int priority = 0; + int rc = 0; + + info(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev)); + + count = get_devices_by_name(udev, slink, &name_list); + info(udev, "found %i devices with name '%s'\n", count, slink); + + /* if we don't have a reference, delete it */ + if (count <= 0) { + info(udev, "no reference left, remove '%s'\n", slink); + if (!test) { + unlink(slink); + delete_path(udev, slink); + } + goto out; + } + + /* find the device with the highest priority */ + list_for_each_entry(device, &name_list, node) { + struct udev_device *dev_db; + const char *devnode; + + info(udev, "found '%s' for '%s'\n", device->name, slink); + + /* did we find ourself? we win, if we have the same priority */ + if (strcmp(udev_device_get_syspath(dev), device->name) == 0) { + info(udev, "compare (our own) priority of '%s' %i >= %i\n", + udev_device_get_devpath(dev), udev_device_get_devlink_priority(dev), priority); + if (strcmp(udev_device_get_devnode(dev), slink) == 0) { + info(udev, "'%s' is our device node, database inconsistent, skip link update\n", + udev_device_get_devnode(dev)); + } else if (target[0] == '\0' || udev_device_get_devlink_priority(dev) >= priority) { + priority = udev_device_get_devlink_priority(dev); + util_strlcpy(target, udev_device_get_devnode(dev), sizeof(target)); + } + continue; + } + + /* another device, read priority from database */ + dev_db = udev_device_new_from_syspath(udev, device->name); + if (dev_db == NULL) + continue; + devnode = udev_device_get_devnode(dev_db); + if (devnode != NULL) { + if (strcmp(devnode, slink) == 0) { + info(udev, "'%s' is a device node of '%s', skip link update\n", + devnode, device->name); + } else { + info(udev, "compare priority of '%s' %i > %i\n", + udev_device_get_devpath(dev_db), + udev_device_get_devlink_priority(dev_db), + priority); + if (target[0] == '\0' || udev_device_get_devlink_priority(dev_db) > priority) { + priority = udev_device_get_devlink_priority(dev_db); + util_strlcpy(target, devnode, sizeof(target)); + } + } + } + udev_device_unref(dev_db); + } + name_list_cleanup(udev, &name_list); + + if (target[0] == '\0') { + info(udev, "no current target for '%s' found\n", slink); + rc = 1; + goto out; + } + + /* create symlink to the target with the highest priority */ + info(udev, "'%s' with target '%s' has the highest priority %i, create it\n", slink, target, priority); + if (!test) { + create_path(udev, slink); + node_symlink(udev, target, slink); + } +out: + return rc; +} + +void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old, int test) +{ + struct udev *udev = udev_device_get_udev(dev); + struct udev_list_entry *list_entry; + const char *devnode_old; + + /* update possible left-over symlinks */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { + struct udev_list_entry *list_entry_current; + + udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { + if (strcmp(udev_list_entry_get_name(list_entry_current), + udev_list_entry_get_name(list_entry)) == 0) + continue; + } + /* link does no longer belong to this device */ + info(udev, "update old symlink '%s' no longer belonging to '%s'\n", + udev_list_entry_get_name(list_entry), udev_device_get_devpath(dev)); + update_link(dev, udev_list_entry_get_name(list_entry), test); + } + + /* + * if the node name has changed, delete the node, + * and possibly restore a symlink of another device + */ + devnode_old = udev_device_get_devnode(dev_old); + if (devnode_old != NULL) { + const char *devnode = udev_device_get_devnode(dev); + + if (devnode != NULL && strcmp(devnode_old, devnode) != 0) + update_link(dev, devnode_old, test); + } +} + +int udev_node_add(struct udev_device *dev, mode_t mode, const char *owner, const char *group, int test) +{ + struct udev *udev = udev_device_get_udev(dev); + uid_t uid; + gid_t gid; + int i; + int num; + struct udev_list_entry *list_entry; + int err = 0; + + create_path(udev, udev_device_get_devnode(dev)); + + if (strcmp(owner, "root") == 0) + uid = 0; + else { + char *endptr; + unsigned long id; + + id = strtoul(owner, &endptr, 10); + if (endptr[0] == '\0') + uid = (uid_t) id; + else + uid = lookup_user(udev, owner); + } + + if (strcmp(group, "root") == 0) + gid = 0; + else { + char *endptr; + unsigned long id; + + id = strtoul(group, &endptr, 10); + if (endptr[0] == '\0') + gid = (gid_t) id; + else + gid = lookup_group(udev, group); + } + + info(udev, "creating device node '%s', devnum=%d:%d, mode=%#o, uid=%d, gid=%d\n", + udev_device_get_devnode(dev), + major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)), + mode, uid, gid); + + if (!test) + if (udev_node_mknod(dev, NULL, makedev(0,0), mode, uid, gid) != 0) { + err = -1; + goto exit; + } + + /* create all_partitions if requested */ + num = udev_device_get_num_fake_partitions(dev); + if (num > 0) { + info(udev, "creating device partition nodes '%s[1-%i]'\n", udev_device_get_devnode(dev), num); + if (!test) { + for (i = 1; i <= num; i++) { + char partitionname[UTIL_PATH_SIZE]; + dev_t part_devnum; + + snprintf(partitionname, sizeof(partitionname), "%s%d", + udev_device_get_devnode(dev), i); + partitionname[sizeof(partitionname)-1] = '\0'; + part_devnum = makedev(major(udev_device_get_devnum(dev)), + minor(udev_device_get_devnum(dev)) + i); + udev_node_mknod(dev, partitionname, part_devnum, mode, uid, gid); + } + } + } + + /* add node and to name index */ + name_index(udev, udev_device_get_devpath(dev), udev_device_get_devnode(dev), 1, test); + + /* create/update symlinks, add symlinks to name index */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { + name_index(udev, udev_device_get_devpath(dev), udev_list_entry_get_name(list_entry), 1, test); + update_link(dev, udev_list_entry_get_name(list_entry), test); + } +exit: + return err; +} + +extern int udev_node_remove(struct udev_device *dev, int test) +{ + struct udev *udev = udev_device_get_udev(dev); + struct udev_list_entry *list_entry; + const char *devnode; + char partitionname[UTIL_PATH_SIZE]; + struct stat stats; + int err = 0; + int num; + + /* remove node from name index */ + name_index(udev, udev_device_get_devpath(dev), udev_device_get_devnode(dev), 0, test); + + /* remove,update symlinks, remove symlinks from name index */ + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { + name_index(udev, udev_device_get_devpath(dev), udev_list_entry_get_name(list_entry), 0, test); + update_link(dev, udev_list_entry_get_name(list_entry), test); + } + + devnode = udev_device_get_devnode(dev); + if (devnode == NULL) + return 0; + if (stat(devnode, &stats) != 0) { + info(udev, "device node '%s' not found\n", devnode); + return 0; + } + if (stats.st_rdev != udev_device_get_devnum(dev)) { + info(udev, "device node '%s' points to a different device, skip removal\n", devnode); + return -1; + } + + info(udev, "removing device node '%s'\n", devnode); + if (!test) + err = unlink_secure(udev, devnode); + if (err) + return err; + + num = udev_device_get_num_fake_partitions(dev); + if (num > 0) { + int i; + + info(udev, "removing all_partitions '%s[1-%i]'\n", devnode, num); + if (num > 255) + return -1; + for (i = 1; i <= num; i++) { + snprintf(partitionname, sizeof(partitionname), "%s%d", devnode, i); + partitionname[sizeof(partitionname)-1] = '\0'; + if (!test) + unlink_secure(udev, partitionname); + } + } + delete_path(udev, devnode); + return err; +} diff --git a/udev/udev-rules-parse.c b/udev/udev-rules-parse.c new file mode 100644 index 0000000000..354ed2882d --- /dev/null +++ b/udev/udev-rules-parse.c @@ -0,0 +1,850 @@ +/* + * Copyright (C) 2003,2004 Greg Kroah-Hartman + * Copyright (C) 2003-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev-rules.h" + + +void udev_rules_iter_init(struct udev_rules_iter *iter, struct udev_rules *rules) +{ + dbg(rules->udev, "bufsize=%zi\n", rules->bufsize); + iter->rules = rules; + iter->current = 0; +} + +struct udev_rule *udev_rules_iter_next(struct udev_rules_iter *iter) +{ + struct udev_rules *rules; + struct udev_rule *rule; + + rules = iter->rules; + if (!rules) + return NULL; + + dbg(rules->udev, "current=%zi\n", iter->current); + if (iter->current >= rules->bufsize) { + dbg(rules->udev, "no more rules\n"); + return NULL; + } + + /* get next rule */ + rule = (struct udev_rule *) (rules->buf + iter->current); + iter->current += sizeof(struct udev_rule) + rule->bufsize; + + return rule; +} + +struct udev_rule *udev_rules_iter_goto(struct udev_rules_iter *iter, size_t rule_off) +{ + struct udev_rules *rules = iter->rules; + struct udev_rule *rule; + + dbg(rules->udev, "current=%zi\n", iter->current); + iter->current = rule_off; + rule = (struct udev_rule *) (rules->buf + iter->current); + + return rule; +} + +static size_t find_label(const struct udev_rules_iter *iter, const char *label) +{ + struct udev_rule *rule; + struct udev_rules *rules = iter->rules; + size_t current = iter->current; + +next: + dbg(rules->udev, "current=%zi\n", current); + if (current >= rules->bufsize) { + dbg(rules->udev, "LABEL='%s' not found\n", label); + return 0; + } + rule = (struct udev_rule *) (rules->buf + current); + + if (strcmp(&rule->buf[rule->label.val_off], label) != 0) { + dbg(rules->udev, "moving forward, looking for label '%s'\n", label); + current += sizeof(struct udev_rule) + rule->bufsize; + goto next; + } + + dbg(rules->udev, "found label '%s'\n", label); + return current; +} + +static int get_key(struct udev_rules *rules, char **line, char **key, enum key_operation *operation, char **value) +{ + char *linepos; + char *temp; + + linepos = *line; + if (linepos == NULL && linepos[0] == '\0') + return -1; + + /* skip whitespace */ + while (isspace(linepos[0]) || linepos[0] == ',') + linepos++; + + /* get the key */ + if (linepos[0] == '\0') + return -1; + *key = linepos; + + while (1) { + linepos++; + if (linepos[0] == '\0') + return -1; + if (isspace(linepos[0])) + break; + if (linepos[0] == '=') + break; + if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) + if (linepos[1] == '=') + break; + } + + /* remember end of key */ + temp = linepos; + + /* skip whitespace after key */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get operation type */ + if (linepos[0] == '=' && linepos[1] == '=') { + *operation = KEY_OP_MATCH; + linepos += 2; + dbg(rules->udev, "operator=match\n"); + } else if (linepos[0] == '!' && linepos[1] == '=') { + *operation = KEY_OP_NOMATCH; + linepos += 2; + dbg(rules->udev, "operator=nomatch\n"); + } else if (linepos[0] == '+' && linepos[1] == '=') { + *operation = KEY_OP_ADD; + linepos += 2; + dbg(rules->udev, "operator=add\n"); + } else if (linepos[0] == '=') { + *operation = KEY_OP_ASSIGN; + linepos++; + dbg(rules->udev, "operator=assign\n"); + } else if (linepos[0] == ':' && linepos[1] == '=') { + *operation = KEY_OP_ASSIGN_FINAL; + linepos += 2; + dbg(rules->udev, "operator=assign_final\n"); + } else + return -1; + + /* terminate key */ + temp[0] = '\0'; + dbg(rules->udev, "key='%s'\n", *key); + + /* skip whitespace after operator */ + while (isspace(linepos[0])) + linepos++; + if (linepos[0] == '\0') + return -1; + + /* get the value*/ + if (linepos[0] == '"') + linepos++; + else + return -1; + *value = linepos; + + temp = strchr(linepos, '"'); + if (!temp) + return -1; + temp[0] = '\0'; + temp++; + dbg(rules->udev, "value='%s'\n", *value); + + /* move line to next key */ + *line = temp; + + return 0; +} + +/* extract possible KEY{attr} */ +static char *get_key_attribute(struct udev_rules *rules, char *str) +{ + char *pos; + char *attr; + + attr = strchr(str, '{'); + if (attr != NULL) { + attr++; + pos = strchr(attr, '}'); + if (pos == NULL) { + err(rules->udev, "missing closing brace for format\n"); + return NULL; + } + pos[0] = '\0'; + dbg(rules->udev, "attribute='%s'\n", attr); + return attr; + } + + return NULL; +} + +static int add_rule_key(struct udev_rule *rule, struct key *key, + enum key_operation operation, const char *value) +{ + size_t val_len = strnlen(value, UTIL_PATH_SIZE); + + key->operation = operation; + + key->val_off = rule->bufsize; + util_strlcpy(rule->buf + rule->bufsize, value, val_len+1); + rule->bufsize += val_len+1; + + return 0; +} + +static int add_rule_key_pair(struct udev_rules *rules, struct udev_rule *rule, struct key_pairs *pairs, + enum key_operation operation, const char *key, const char *value) +{ + size_t key_len = strnlen(key, UTIL_PATH_SIZE); + + if (pairs->count >= PAIRS_MAX) { + err(rules->udev, "skip, too many keys of the same type in a single rule\n"); + return -1; + } + + add_rule_key(rule, &pairs->keys[pairs->count].key, operation, value); + + /* add the key-name of the pair */ + pairs->keys[pairs->count].key_name_off = rule->bufsize; + util_strlcpy(rule->buf + rule->bufsize, key, key_len+1); + rule->bufsize += key_len+1; + + pairs->count++; + + return 0; +} + +static int add_to_rules(struct udev_rules *rules, char *line, const char *filename, unsigned int lineno) +{ + char buf[sizeof(struct udev_rule) + UTIL_LINE_SIZE]; + struct udev_rule *rule; + size_t rule_size; + int valid; + char *linepos; + char *attr; + size_t padding; + int physdev = 0; + int retval; + + memset(buf, 0x00, sizeof(buf)); + rule = (struct udev_rule *) buf; + rule->event_timeout = -1; + linepos = line; + valid = 0; + + /* get all the keys */ + while (1) { + char *key; + char *value; + enum key_operation operation = KEY_OP_UNSET; + + retval = get_key(rules, &linepos, &key, &operation, &value); + if (retval) + break; + + if (strcasecmp(key, "ACTION") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid ACTION operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->action, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "DEVPATH") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid DEVPATH operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->devpath, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "KERNEL") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid KERNEL operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->kernel, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "SUBSYSTEM") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid SUBSYSTEM operation\n"); + goto invalid; + } + /* bus, class, subsystem events should all be the same */ + if (strcmp(value, "subsystem") == 0 || + strcmp(value, "bus") == 0 || + strcmp(value, "class") == 0) { + if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) + err(rules->udev, "'%s' must be specified as 'subsystem' \n" + "please fix it in %s:%u", value, filename, lineno); + add_rule_key(rule, &rule->subsystem, operation, "subsystem|class|bus"); + } else + add_rule_key(rule, &rule->subsystem, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "DRIVER") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid DRIVER operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->driver, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { + attr = get_key_attribute(rules, key + sizeof("ATTR")-1); + if (attr == NULL) { + err(rules->udev, "error parsing ATTR attribute\n"); + goto invalid; + } + if (add_rule_key_pair(rules, rule, &rule->attr, operation, attr, value) != 0) + goto invalid; + valid = 1; + continue; + } + + if (strcasecmp(key, "KERNELS") == 0 || + strcasecmp(key, "ID") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid KERNELS operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->kernels, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "SUBSYSTEMS") == 0 || + strcasecmp(key, "BUS") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid SUBSYSTEMS operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->subsystems, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "DRIVERS") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid DRIVERS operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->drivers, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 || + strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid ATTRS operation\n"); + goto invalid; + } + attr = get_key_attribute(rules, key + sizeof("ATTRS")-1); + if (attr == NULL) { + err(rules->udev, "error parsing ATTRS attribute\n"); + goto invalid; + } + if (strncmp(attr, "device/", 7) == 0) + err(rules->udev, "the 'device' link is deprecated and will be removed from a future kernel, \n" + "please fix it in %s:%u", filename, lineno); + else if (strstr(attr, "../") != NULL) + err(rules->udev, "do not reference parent sysfs directories directly, that may break with a future kernel, \n" + "please fix it in %s:%u", filename, lineno); + if (add_rule_key_pair(rules, rule, &rule->attrs, operation, attr, value) != 0) + goto invalid; + valid = 1; + continue; + } + + if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) { + attr = get_key_attribute(rules, key + sizeof("ENV")-1); + if (attr == NULL) { + err(rules->udev, "error parsing ENV attribute\n"); + goto invalid; + } + if (strncmp(attr, "PHYSDEV", 7) == 0) + physdev = 1; + if (add_rule_key_pair(rules, rule, &rule->env, operation, attr, value) != 0) + goto invalid; + valid = 1; + continue; + } + + if (strcasecmp(key, "PROGRAM") == 0) { + add_rule_key(rule, &rule->program, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "RESULT") == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid RESULT operation\n"); + goto invalid; + } + add_rule_key(rule, &rule->result, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { + attr = get_key_attribute(rules, key + sizeof("IMPORT")-1); + if (attr != NULL && strstr(attr, "program")) { + dbg(rules->udev, "IMPORT will be executed\n"); + rule->import_type = IMPORT_PROGRAM; + } else if (attr != NULL && strstr(attr, "file")) { + dbg(rules->udev, "IMPORT will be included as file\n"); + rule->import_type = IMPORT_FILE; + } else if (attr != NULL && strstr(attr, "parent")) { + dbg(rules->udev, "IMPORT will include the parent values\n"); + rule->import_type = IMPORT_PARENT; + } else { + /* figure it out if it is executable */ + char file[UTIL_PATH_SIZE]; + char *pos; + struct stat statbuf; + + util_strlcpy(file, value, sizeof(file)); + pos = strchr(file, ' '); + if (pos) + pos[0] = '\0'; + + /* allow programs in /lib/udev called without the path */ + if (strchr(file, '/') == NULL) { + util_strlcpy(file, UDEV_PREFIX "/lib/udev/", sizeof(file)); + util_strlcat(file, value, sizeof(file)); + pos = strchr(file, ' '); + if (pos) + pos[0] = '\0'; + } + + dbg(rules->udev, "IMPORT auto mode for '%s'\n", file); + if (!lstat(file, &statbuf) && (statbuf.st_mode & S_IXUSR)) { + dbg(rules->udev, "IMPORT is executable, will be executed (autotype)\n"); + rule->import_type = IMPORT_PROGRAM; + } else { + dbg(rules->udev, "IMPORT is not executable, will be included as file (autotype)\n"); + rule->import_type = IMPORT_FILE; + } + } + add_rule_key(rule, &rule->import, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) { + if (operation != KEY_OP_MATCH && + operation != KEY_OP_NOMATCH) { + err(rules->udev, "invalid TEST operation\n"); + goto invalid; + } + attr = get_key_attribute(rules, key + sizeof("TEST")-1); + if (attr != NULL) + rule->test_mode_mask = strtol(attr, NULL, 8); + add_rule_key(rule, &rule->test, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "RUN", sizeof("RUN")-1) == 0) { + attr = get_key_attribute(rules, key + sizeof("RUN")-1); + if (attr != NULL) { + if (strstr(attr, "ignore_error")) + rule->run_ignore_error = 1; + } + add_rule_key(rule, &rule->run, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "WAIT_FOR") == 0 || strcasecmp(key, "WAIT_FOR_SYSFS") == 0) { + add_rule_key(rule, &rule->wait_for, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "LABEL") == 0) { + add_rule_key(rule, &rule->label, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "GOTO") == 0) { + add_rule_key(rule, &rule->goto_label, operation, value); + valid = 1; + continue; + } + + if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) { + attr = get_key_attribute(rules, key + sizeof("NAME")-1); + if (attr != NULL) { + if (strstr(attr, "all_partitions") != NULL) { + dbg(rules->udev, "creation of partition nodes requested\n"); + rule->partitions = DEFAULT_PARTITIONS_COUNT; + } + if (strstr(attr, "ignore_remove") != NULL) { + dbg(rules->udev, "remove event should be ignored\n"); + rule->ignore_remove = 1; + } + } + if (value[0] == '\0') + dbg(rules->udev, "name empty, node creation supressed\n"); + add_rule_key(rule, &rule->name, operation, value); + continue; + } + + if (strcasecmp(key, "SYMLINK") == 0) { + if (operation == KEY_OP_MATCH || + operation == KEY_OP_NOMATCH) + add_rule_key(rule, &rule->symlink_match, operation, value); + else + add_rule_key(rule, &rule->symlink, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "OWNER") == 0) { + valid = 1; + if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { + char *endptr; + strtoul(value, &endptr, 10); + if (endptr[0] != '\0') { + char owner[32]; + uid_t uid = lookup_user(rules->udev, value); + dbg(rules->udev, "replacing username='%s' by id=%i\n", value, uid); + sprintf(owner, "%u", (unsigned int) uid); + add_rule_key(rule, &rule->owner, operation, owner); + continue; + } + } + + add_rule_key(rule, &rule->owner, operation, value); + continue; + } + + if (strcasecmp(key, "GROUP") == 0) { + valid = 1; + if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { + char *endptr; + strtoul(value, &endptr, 10); + if (endptr[0] != '\0') { + char group[32]; + gid_t gid = lookup_group(rules->udev, value); + dbg(rules->udev, "replacing groupname='%s' by id=%i\n", value, gid); + sprintf(group, "%u", (unsigned int) gid); + add_rule_key(rule, &rule->group, operation, group); + continue; + } + } + + add_rule_key(rule, &rule->group, operation, value); + continue; + } + + if (strcasecmp(key, "MODE") == 0) { + add_rule_key(rule, &rule->mode, operation, value); + valid = 1; + continue; + } + + if (strcasecmp(key, "OPTIONS") == 0) { + const char *pos; + + if (strstr(value, "last_rule") != NULL) { + dbg(rules->udev, "last rule to be applied\n"); + rule->last_rule = 1; + } + if (strstr(value, "ignore_device") != NULL) { + dbg(rules->udev, "device should be ignored\n"); + rule->ignore_device = 1; + } + if (strstr(value, "ignore_remove") != NULL) { + dbg(rules->udev, "remove event should be ignored\n"); + rule->ignore_remove = 1; + } + pos = strstr(value, "link_priority="); + if (pos != NULL) { + rule->link_priority = atoi(&pos[strlen("link_priority=")]); + dbg(rules->udev, "link priority=%i\n", rule->link_priority); + } + pos = strstr(value, "event_timeout="); + if (pos != NULL) { + rule->event_timeout = atoi(&pos[strlen("event_timeout=")]); + dbg(rules->udev, "event timout=%i\n", rule->event_timeout); + } + pos = strstr(value, "string_escape="); + if (pos != NULL) { + pos = &pos[strlen("string_escape=")]; + if (strncmp(pos, "none", strlen("none")) == 0) + rule->string_escape = ESCAPE_NONE; + else if (strncmp(pos, "replace", strlen("replace")) == 0) + rule->string_escape = ESCAPE_REPLACE; + } + if (strstr(value, "all_partitions") != NULL) { + dbg(rules->udev, "creation of partition nodes requested\n"); + rule->partitions = DEFAULT_PARTITIONS_COUNT; + } + valid = 1; + continue; + } + + err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); + } + + if (physdev && rule->wait_for.operation == KEY_OP_UNSET) + err(rules->udev, "PHYSDEV* values are deprecated and will be removed from a future kernel, \n" + "please fix it in %s:%u", filename, lineno); + + /* skip line if not any valid key was found */ + if (!valid) + goto invalid; + + /* grow buffer and add rule */ + rule_size = sizeof(struct udev_rule) + rule->bufsize; + padding = (sizeof(size_t) - rule_size % sizeof(size_t)) % sizeof(size_t); + dbg(rules->udev, "add %zi padding bytes\n", padding); + rule_size += padding; + rule->bufsize += padding; + + rules->buf = realloc(rules->buf, rules->bufsize + rule_size); + if (!rules->buf) { + err(rules->udev, "realloc failed\n"); + goto exit; + } + dbg(rules->udev, "adding rule to offset %zi\n", rules->bufsize); + memcpy(rules->buf + rules->bufsize, rule, rule_size); + rules->bufsize += rule_size; +exit: + return 0; + +invalid: + err(rules->udev, "invalid rule '%s:%u'\n", filename, lineno); + return -1; +} + +static int parse_file(struct udev_rules *rules, const char *filename) +{ + char line[UTIL_LINE_SIZE]; + char *bufline; + unsigned int lineno; + char *buf; + size_t bufsize; + size_t cur; + size_t count; + int retval = 0; + size_t start; + struct udev_rule *rule; + struct udev_rules_iter iter; + + start = rules->bufsize; + + if (file_map(filename, &buf, &bufsize) != 0) { + err(rules->udev, "can't open '%s' as rules file: %m\n", filename); + return -1; + } + info(rules->udev, "reading '%s' as rules file\n", filename); + + /* loop through the whole file */ + cur = 0; + lineno = 0; + while (cur < bufsize) { + unsigned int i, j; + + count = buf_get_line(buf, bufsize, cur); + bufline = &buf[cur]; + cur += count+1; + lineno++; + + /* eat the whitespace */ + while ((count > 0) && isspace(bufline[0])) { + bufline++; + count--; + } + if (count == 0) + continue; + + /* see if this is a comment */ + if (bufline[0] == '#') + continue; + + if (count >= sizeof(line)) { + err(rules->udev, "line too long, rule skipped '%s:%u'\n", filename, lineno); + continue; + } + + /* skip backslash and newline from multiline rules */ + for (i = j = 0; i < count; i++) { + if (bufline[i] == '\\' && bufline[i+1] == '\n') + continue; + + line[j++] = bufline[i]; + } + line[j] = '\0'; + + dbg(rules->udev, "read '%s'\n", line); + add_to_rules(rules, line, filename, lineno); + } + + /* Compute all goto targets within this file */ + udev_rules_iter_init(&iter, rules); + udev_rules_iter_goto(&iter, start); + while((rule = udev_rules_iter_next(&iter))) { + if (rule->goto_label.operation != KEY_OP_UNSET) { + char *goto_label = &rule->buf[rule->goto_label.val_off]; + + dbg(rules->udev, "resolving goto label '%s'\n", goto_label); + rule->goto_rule_off = find_label(&iter, goto_label); + if (rule->goto_rule_off == 0) { + err(rules->udev, "ignore goto to nonexistent label '%s' in '%s'\n", + goto_label, filename); + rule->goto_rule_off = iter.current; + } + } + } + + file_unmap(buf, bufsize); + return retval; +} + +int udev_rules_init(struct udev *udev, struct udev_rules *rules, int resolve_names) +{ + struct stat statbuf; + char filename[PATH_MAX]; + LIST_HEAD(name_list); + LIST_HEAD(sort_list); + struct name_entry *name_loop, *name_tmp; + struct name_entry *sort_loop, *sort_tmp; + int retval = 0; + + memset(rules, 0x00, sizeof(struct udev_rules)); + rules->udev = udev; + rules->resolve_names = resolve_names; + + if (udev_get_rules_path(udev) != NULL) { + /* custom rules location for testing */ + add_matching_files(udev, &name_list, udev_get_rules_path(udev), ".rules"); + } else { + /* read user/custom rules */ + add_matching_files(udev, &name_list, SYSCONFDIR "/udev/rules.d", ".rules"); + + /* read dynamic/temporary rules */ + util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); + util_strlcat(filename, "/.udev/rules.d", sizeof(filename)); + if (stat(filename, &statbuf) != 0) { + create_path(udev, filename); + udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755); + mkdir(filename, 0755); + udev_selinux_resetfscreatecon(udev); + } + add_matching_files(udev, &sort_list, filename, ".rules"); + + /* read default rules */ + add_matching_files(udev, &sort_list, UDEV_PREFIX "/lib/udev/rules.d", ".rules"); + + /* sort all rules files by basename into list of files */ + list_for_each_entry_safe(sort_loop, sort_tmp, &sort_list, node) { + const char *sort_base = strrchr(sort_loop->name, '/'); + + if (sort_base == NULL) + continue; + + list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { + const char *name_base = strrchr(name_loop->name, '/'); + + if (name_base == NULL) + continue; + + if (strcmp(name_base, sort_base) == 0) { + info(udev, "rule file '%s' already added, ignoring '%s'\n", + name_loop->name, sort_loop->name); + list_del(&sort_loop->node); + free(sort_loop); + sort_loop = NULL; + continue; + } + + if (strcmp(name_base, sort_base) > 0) + break; + } + if (sort_loop != NULL) + list_move_tail(&sort_loop->node, &name_loop->node); + } + } + + /* parse list of files */ + list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { + if (stat(name_loop->name, &statbuf) == 0) { + if (statbuf.st_size) + parse_file(rules, name_loop->name); + else + dbg(udev, "empty rules file '%s'\n", name_loop->name); + } else + err(udev, "could not read '%s': %m\n", name_loop->name); + list_del(&name_loop->node); + free(name_loop); + } + + return retval; +} + +void udev_rules_cleanup(struct udev_rules *rules) +{ + if (rules->buf) { + free(rules->buf); + rules->buf = NULL; + } +} + diff --git a/udev/udev-rules.c b/udev/udev-rules.c new file mode 100644 index 0000000000..8b94d40c0b --- /dev/null +++ b/udev/udev-rules.c @@ -0,0 +1,1634 @@ +/* + * Copyright (C) 2003 Greg Kroah-Hartman + * Copyright (C) 2003-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev-rules.h" + +extern char **environ; + +/* extract possible {attr} and move str behind it */ +static char *get_format_attribute(struct udev *udev, char **str) +{ + char *pos; + char *attr = NULL; + + if (*str[0] == '{') { + pos = strchr(*str, '}'); + if (pos == NULL) { + err(udev, "missing closing brace for format\n"); + return NULL; + } + pos[0] = '\0'; + attr = *str+1; + *str = pos+1; + dbg(udev, "attribute='%s', str='%s'\n", attr, *str); + } + return attr; +} + +/* extract possible format length and move str behind it*/ +static int get_format_len(struct udev *udev, char **str) +{ + int num; + char *tail; + + if (isdigit(*str[0])) { + num = (int) strtoul(*str, &tail, 10); + if (num > 0) { + *str = tail; + dbg(udev, "format length=%i\n", num); + return num; + } else { + err(udev, "format parsing error '%s'\n", *str); + } + } + return -1; +} + +static int get_key(char **line, char **key, char **value) +{ + char *linepos; + char *temp; + + linepos = *line; + if (linepos == NULL) + return -1; + + /* skip whitespace */ + while (isspace(linepos[0])) + linepos++; + + /* get the key */ + temp = strchr(linepos, '='); + if (temp == NULL || temp == linepos) + return -1; + temp[0] = '\0'; + *key = linepos; + linepos = &temp[1]; + + /* get a quoted value */ + if (linepos[0] == '"' || linepos[0] == '\'') { + temp = strchr(&linepos[1], linepos[0]); + if (temp != NULL) { + temp[0] = '\0'; + *value = &linepos[1]; + goto out; + } + } + + /* get the value*/ + temp = strchr(linepos, '\n'); + if (temp != NULL) + temp[0] = '\0'; + *value = linepos; +out: + return 0; +} + +static int run_program(struct udev_device *dev, const char *command, + char *result, size_t ressize, size_t *reslen) +{ + struct udev *udev = udev_device_get_udev(dev); + int status; + char **envp; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + pid_t pid; + char arg[UTIL_PATH_SIZE]; + char program[UTIL_PATH_SIZE]; + char *argv[(sizeof(arg) / 2) + 1]; + int devnull; + int i; + int err = 0; + + /* build argv from command */ + util_strlcpy(arg, command, sizeof(arg)); + i = 0; + if (strchr(arg, ' ') != NULL) { + char *pos = arg; + + while (pos != NULL) { + if (pos[0] == '\'') { + /* don't separate if in apostrophes */ + pos++; + argv[i] = strsep(&pos, "\'"); + while (pos != NULL && pos[0] == ' ') + pos++; + } else { + argv[i] = strsep(&pos, " "); + } + dbg(udev, "arg[%i] '%s'\n", i, argv[i]); + i++; + } + argv[i] = NULL; + } else { + argv[0] = arg; + argv[1] = NULL; + } + info(udev, "'%s'\n", command); + + /* prepare pipes from child to parent */ + if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { + if (pipe(outpipe) != 0) { + err(udev, "pipe failed: %m\n"); + return -1; + } + } + if (udev_get_log_priority(udev) >= LOG_INFO) { + if (pipe(errpipe) != 0) { + err(udev, "pipe failed: %m\n"); + return -1; + } + } + + /* allow programs in /lib/udev/ to be called without the path */ + if (strchr(argv[0], '/') == NULL) { + util_strlcpy(program, UDEV_PREFIX "/lib/udev/", sizeof(program)); + util_strlcat(program, argv[0], sizeof(program)); + argv[0] = program; + } + + envp = udev_device_get_properties_envp(dev); + + pid = fork(); + switch(pid) { + case 0: + /* child closes parent ends of pipes */ + if (outpipe[READ_END] > 0) + close(outpipe[READ_END]); + if (errpipe[READ_END] > 0) + close(errpipe[READ_END]); + + /* discard child output or connect to pipe */ + devnull = open("/dev/null", O_RDWR); + if (devnull > 0) { + dup2(devnull, STDIN_FILENO); + if (outpipe[WRITE_END] < 0) + dup2(devnull, STDOUT_FILENO); + if (errpipe[WRITE_END] < 0) + dup2(devnull, STDERR_FILENO); + close(devnull); + } else + err(udev, "open /dev/null failed: %m\n"); + if (outpipe[WRITE_END] > 0) { + dup2(outpipe[WRITE_END], STDOUT_FILENO); + close(outpipe[WRITE_END]); + } + if (errpipe[WRITE_END] > 0) { + dup2(errpipe[WRITE_END], STDERR_FILENO); + close(errpipe[WRITE_END]); + } + execve(argv[0], argv, envp); + if (errno == ENOENT || errno == ENOTDIR) { + /* may be on a filesytem which is not mounted right now */ + info(udev, "program '%s' not found\n", argv[0]); + } else { + /* other problems */ + err(udev, "exec of program '%s' failed\n", argv[0]); + } + _exit(1); + case -1: + err(udev, "fork of '%s' failed: %m\n", argv[0]); + return -1; + default: + /* read from child if requested */ + if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { + ssize_t count; + size_t respos = 0; + + /* parent closes child ends of pipes */ + if (outpipe[WRITE_END] > 0) + close(outpipe[WRITE_END]); + if (errpipe[WRITE_END] > 0) + close(errpipe[WRITE_END]); + + /* read child output */ + while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { + int fdcount; + fd_set readfds; + + FD_ZERO(&readfds); + if (outpipe[READ_END] > 0) + FD_SET(outpipe[READ_END], &readfds); + if (errpipe[READ_END] > 0) + FD_SET(errpipe[READ_END], &readfds); + fdcount = select(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL); + if (fdcount < 0) { + if (errno == EINTR) + continue; + err = -1; + break; + } + + /* get stdout */ + if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) { + char inbuf[1024]; + char *pos; + char *line; + + count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1); + if (count <= 0) { + close(outpipe[READ_END]); + outpipe[READ_END] = -1; + if (count < 0) { + err(udev, "stdin read failed: %m\n"); + err = -1; + } + continue; + } + inbuf[count] = '\0'; + + /* store result for rule processing */ + if (result) { + if (respos + count < ressize) { + memcpy(&result[respos], inbuf, count); + respos += count; + } else { + err(udev, "ressize %ld too short\n", (long)ressize); + err = -1; + } + } + pos = inbuf; + while ((line = strsep(&pos, "\n"))) + if (pos || line[0] != '\0') + info(udev, "'%s' (stdout) '%s'\n", argv[0], line); + } + + /* get stderr */ + if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) { + char errbuf[1024]; + char *pos; + char *line; + + count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1); + if (count <= 0) { + close(errpipe[READ_END]); + errpipe[READ_END] = -1; + if (count < 0) + err(udev, "stderr read failed: %m\n"); + continue; + } + errbuf[count] = '\0'; + pos = errbuf; + while ((line = strsep(&pos, "\n"))) + if (pos || line[0] != '\0') + info(udev, "'%s' (stderr) '%s'\n", argv[0], line); + } + } + if (outpipe[READ_END] > 0) + close(outpipe[READ_END]); + if (errpipe[READ_END] > 0) + close(errpipe[READ_END]); + + /* return the childs stdout string */ + if (result) { + result[respos] = '\0'; + dbg(udev, "result='%s'\n", result); + if (reslen) + *reslen = respos; + } + } + waitpid(pid, &status, 0); + if (WIFEXITED(status)) { + info(udev, "'%s' returned with status %i\n", argv[0], WEXITSTATUS(status)); + if (WEXITSTATUS(status) != 0) + err = -1; + } else { + err(udev, "'%s' abnormal exit\n", argv[0]); + err = -1; + } + } + + return err; +} + +static int import_keys_into_env(struct udev_event *event, const char *buf, size_t bufsize) +{ + struct udev_device *dev = event->dev; + char line[UTIL_LINE_SIZE]; + const char *bufline; + char *linepos; + char *variable; + char *value; + size_t cur; + size_t count; + int lineno; + + /* loop through the whole buffer */ + lineno = 0; + cur = 0; + while (cur < bufsize) { + count = buf_get_line(buf, bufsize, cur); + bufline = &buf[cur]; + cur += count+1; + lineno++; + + /* eat the whitespace */ + while ((count > 0) && isspace(bufline[0])) { + bufline++; + count--; + } + if (count == 0) + continue; + + /* see if this is a comment */ + if (bufline[0] == '#') + continue; + + if (count >= sizeof(line)) { + err(event->udev, "line too long, skipped\n"); + continue; + } + + memcpy(line, bufline, count); + line[count] = '\0'; + + linepos = line; + if (get_key(&linepos, &variable, &value) == 0) { + char syspath[UTIL_PATH_SIZE]; + + dbg(event->udev, "import '%s=%s'\n", variable, value); + /* handle device, renamed by external tool, returning new path */ + if (strcmp(variable, "DEVPATH") == 0) { + info(event->udev, "updating devpath from '%s' to '%s'\n", + udev_device_get_devpath(dev), value); + util_strlcpy(syspath, udev_get_sys_path(event->udev), sizeof(syspath)); + util_strlcat(syspath, value, sizeof(syspath)); + udev_device_set_syspath(dev, syspath); + } else { + struct udev_list_entry *entry; + + entry = udev_device_add_property(dev, variable, value); + /* store in db */ + udev_list_entry_set_flag(entry, 1); + } + } + } + return 0; +} + +static int import_file_into_env(struct udev_event *event, const char *filename) +{ + char *buf; + size_t bufsize; + + if (file_map(filename, &buf, &bufsize) != 0) { + err(event->udev, "can't open '%s': %m\n", filename); + return -1; + } + import_keys_into_env(event, buf, bufsize); + file_unmap(buf, bufsize); + + return 0; +} + +static int import_program_into_env(struct udev_event *event, const char *program) +{ + char result[2048]; + size_t reslen; + + if (run_program(event->dev, program, result, sizeof(result), &reslen) != 0) + return -1; + return import_keys_into_env(event, result, reslen); +} + +static int import_parent_into_env(struct udev_event *event, const char *filter) +{ + struct udev_device *dev_parent; + struct udev_list_entry *list_entry; + + dev_parent = udev_device_get_parent(event->dev); + if (dev_parent == NULL) + return -1; + + dbg(event->udev, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent)); + udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { + const char *key = udev_list_entry_get_name(list_entry); + const char *val = udev_list_entry_get_value(list_entry); + + if (fnmatch(filter, key, 0) == 0) { + struct udev_list_entry *entry; + + dbg(event->udev, "import key '%s=%s'\n", key, val); + entry = udev_device_add_property(event->dev, key, val); + /* store in db */ + udev_list_entry_set_flag(entry, 1); + } + } + return 0; +} + +int udev_rules_run(struct udev_event *event) +{ + struct udev_list_entry *list_entry; + int err = 0; + + dbg(event->udev, "executing run list\n"); + udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { + const char *cmd = udev_list_entry_get_name(list_entry); + + if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { + struct udev_monitor *monitor; + + monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); + if (monitor == NULL) + continue; + udev_monitor_send_device(monitor, event->dev); + udev_monitor_unref(monitor); + } else { + char program[UTIL_PATH_SIZE]; + + util_strlcpy(program, cmd, sizeof(program)); + udev_rules_apply_format(event, program, sizeof(program)); + if (run_program(event->dev, program, NULL, 0, NULL) != 0) { + if (!udev_list_entry_get_flag(list_entry)) + err = -1; + } + } + } + return err; +} + +#define WAIT_LOOP_PER_SECOND 50 +static int wait_for_file(struct udev_event *event, const char *file, int timeout) +{ + char filepath[UTIL_PATH_SIZE]; + char devicepath[UTIL_PATH_SIZE] = ""; + struct stat stats; + int loop = timeout * WAIT_LOOP_PER_SECOND; + + /* a relative path is a device attribute */ + if (file[0] != '/') { + util_strlcpy(devicepath, udev_get_sys_path(event->udev), sizeof(devicepath)); + util_strlcat(devicepath, udev_device_get_devpath(event->dev), sizeof(devicepath)); + + util_strlcpy(filepath, devicepath, sizeof(filepath)); + util_strlcat(filepath, "/", sizeof(filepath)); + util_strlcat(filepath, file, sizeof(filepath)); + file = filepath; + } + + dbg(event->udev, "will wait %i sec for '%s'\n", timeout, file); + while (--loop) { + /* lookup file */ + if (stat(file, &stats) == 0) { + info(event->udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); + return 0; + } + /* make sure, the device did not disappear in the meantime */ + if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { + info(event->udev, "device disappeared while waiting for '%s'\n", file); + return -2; + } + info(event->udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); + usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); + } + info(event->udev, "waiting for '%s' failed\n", file); + return -1; +} + +/* handle "[$SUBSYSTEM/$KERNEL]" lookup */ +static int split_subsys_sysname(struct udev *udev, char *attrstr, char **subsys, char **sysname, char **attr) +{ + char *pos; + + if (attrstr[0] != '[') + return -1; + + *subsys = &attrstr[1]; + pos = strchr(*subsys, ']'); + if (pos == NULL) + return -1; + pos[0] = '\0'; + pos = &pos[1]; + + if (pos[0] == '/') + pos = &pos[1]; + if (pos[0] != '\0') + *attr = pos; + else + *attr = NULL; + + pos = strchr(*subsys, '/'); + if (pos == NULL) + return -1; + pos[0] = '\0'; + *sysname = &pos[1]; + return 0; +} + +static int attr_subst_subdir(char *attr, size_t len) +{ + char *pos; + int found = 0; + + pos = strstr(attr, "/*/"); + if (pos != NULL) { + char str[UTIL_PATH_SIZE]; + DIR *dir; + + pos[1] = '\0'; + util_strlcpy(str, &pos[2], sizeof(str)); + dir = opendir(attr); + if (dir != NULL) { + struct dirent *dent; + + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct stat stats; + + if (dent->d_name[0] == '.') + continue; + util_strlcat(attr, dent->d_name, len); + util_strlcat(attr, str, len); + if (stat(attr, &stats) == 0) { + found = 1; + break; + } + pos[1] = '\0'; + } + closedir(dir); + } + if (!found) + util_strlcat(attr, str, len); + } + + return found; +} + +void udev_rules_apply_format(struct udev_event *event, char *string, size_t maxsize) +{ + struct udev_device *dev = event->dev; + char temp[UTIL_PATH_SIZE]; + char temp2[UTIL_PATH_SIZE]; + char *head, *tail, *cpos, *attr, *rest; + int len; + int i; + int count; + enum subst_type { + SUBST_UNKNOWN, + SUBST_DEVPATH, + SUBST_KERNEL, + SUBST_KERNEL_NUMBER, + SUBST_ID, + SUBST_DRIVER, + SUBST_MAJOR, + SUBST_MINOR, + SUBST_RESULT, + SUBST_ATTR, + SUBST_PARENT, + SUBST_TEMP_NODE, + SUBST_NAME, + SUBST_LINKS, + SUBST_ROOT, + SUBST_SYS, + SUBST_ENV, + }; + static const struct subst_map { + char *name; + char fmt; + enum subst_type type; + } map[] = { + { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, + { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, + { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, + { .name = "id", .fmt = 'b', .type = SUBST_ID }, + { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, + { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, + { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, + { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, + { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, + { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, + { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, + { .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE }, + { .name = "name", .fmt = 'D', .type = SUBST_NAME }, + { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, + { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, + { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, + { .name = "env", .fmt = 'E', .type = SUBST_ENV }, + { NULL, '\0', 0 } + }; + enum subst_type type; + const struct subst_map *subst; + + head = string; + while (1) { + len = -1; + while (head[0] != '\0') { + if (head[0] == '$') { + /* substitute named variable */ + if (head[1] == '\0') + break; + if (head[1] == '$') { + util_strlcpy(temp, head+2, sizeof(temp)); + util_strlcpy(head+1, temp, maxsize); + head++; + continue; + } + head[0] = '\0'; + for (subst = map; subst->name; subst++) { + if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { + type = subst->type; + tail = head + strlen(subst->name)+1; + dbg(event->udev, "will substitute format name '%s'\n", subst->name); + goto found; + } + } + head[0] = '$'; + err(event->udev, "unknown format variable '%s'\n", head); + } else if (head[0] == '%') { + /* substitute format char */ + if (head[1] == '\0') + break; + if (head[1] == '%') { + util_strlcpy(temp, head+2, sizeof(temp)); + util_strlcpy(head+1, temp, maxsize); + head++; + continue; + } + head[0] = '\0'; + tail = head+1; + len = get_format_len(event->udev, &tail); + for (subst = map; subst->name; subst++) { + if (tail[0] == subst->fmt) { + type = subst->type; + tail++; + dbg(event->udev, "will substitute format char '%c'\n", subst->fmt); + goto found; + } + } + head[0] = '%'; + err(event->udev, "unknown format char '%c'\n", tail[0]); + } + head++; + } + break; +found: + attr = get_format_attribute(event->udev, &tail); + util_strlcpy(temp, tail, sizeof(temp)); + dbg(event->udev, "format=%i, string='%s', tail='%s'\n", type ,string, tail); + + switch (type) { + case SUBST_DEVPATH: + util_strlcat(string, udev_device_get_devpath(dev), maxsize); + dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); + break; + case SUBST_KERNEL: + util_strlcat(string, udev_device_get_sysname(dev), maxsize); + dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); + break; + case SUBST_KERNEL_NUMBER: + if (udev_device_get_sysnum(dev) == NULL) + break; + util_strlcat(string, udev_device_get_sysnum(dev), maxsize); + dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); + break; + case SUBST_ID: + if (event->dev_parent != NULL) { + util_strlcat(string, udev_device_get_sysname(event->dev_parent), maxsize); + dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); + } + break; + case SUBST_DRIVER: + if (event->dev_parent != NULL) { + const char *driver = udev_device_get_driver(event->dev_parent); + + if (driver == NULL) + break; + util_strlcat(string, driver, maxsize); + dbg(event->udev, "substitute driver '%s'\n", driver); + } + break; + case SUBST_MAJOR: + sprintf(temp2, "%d", major(udev_device_get_devnum(dev))); + util_strlcat(string, temp2, maxsize); + dbg(event->udev, "substitute major number '%s'\n", temp2); + break; + case SUBST_MINOR: + sprintf(temp2, "%d", minor(udev_device_get_devnum(dev))); + util_strlcat(string, temp2, maxsize); + dbg(event->udev, "substitute minor number '%s'\n", temp2); + break; + case SUBST_RESULT: + if (event->program_result[0] == '\0') + break; + /* get part part of the result string */ + i = 0; + if (attr != NULL) + i = strtoul(attr, &rest, 10); + if (i > 0) { + dbg(event->udev, "request part #%d of result string\n", i); + cpos = event->program_result; + while (--i) { + while (cpos[0] != '\0' && !isspace(cpos[0])) + cpos++; + while (isspace(cpos[0])) + cpos++; + } + if (i > 0) { + err(event->udev, "requested part of result string not found\n"); + break; + } + util_strlcpy(temp2, cpos, sizeof(temp2)); + /* %{2+}c copies the whole string from the second part on */ + if (rest[0] != '+') { + cpos = strchr(temp2, ' '); + if (cpos) + cpos[0] = '\0'; + } + util_strlcat(string, temp2, maxsize); + dbg(event->udev, "substitute part of result string '%s'\n", temp2); + } else { + util_strlcat(string, event->program_result, maxsize); + dbg(event->udev, "substitute result string '%s'\n", event->program_result); + } + break; + case SUBST_ATTR: + if (attr == NULL) + err(event->udev, "missing file parameter for attr\n"); + else { + char *subsys; + char *sysname; + char *attrib; + char value[UTIL_NAME_SIZE] = ""; + size_t size; + + if (split_subsys_sysname(event->udev, attr, &subsys, &sysname, &attrib) == 0) { + struct udev_device *d; + const char *val; + + if (attrib == NULL) + break; + d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); + if (d == NULL) + break; + val = udev_device_get_attr_value(d, attrib); + if (val != NULL) + util_strlcpy(value, val, sizeof(value)); + udev_device_unref(d); + } + + /* try the current device, other matches may have selected */ + if (value[0]=='\0' && event->dev_parent != NULL && event->dev_parent != event->dev) { + const char *val; + + val = udev_device_get_attr_value(event->dev_parent, attr); + if (val != NULL) + util_strlcpy(value, val, sizeof(value)); + } + + /* look at all devices along the chain of parents */ + if (value[0]=='\0') { + struct udev_device *dev_parent = dev; + const char *val; + + do { + dbg(event->udev, "looking at '%s'\n", udev_device_get_syspath(dev_parent)); + val = udev_device_get_attr_value(dev_parent, attr); + if (val != NULL) { + util_strlcpy(value, val, sizeof(value)); + break; + } + dev_parent = udev_device_get_parent(dev_parent); + } while (dev_parent != NULL); + } + + if (value[0]=='\0') + break; + + /* strip trailing whitespace, and replace unwanted characters */ + size = strlen(value); + while (size > 0 && isspace(value[--size])) + value[size] = '\0'; + count = util_replace_chars(value, ALLOWED_CHARS_INPUT); + if (count > 0) + info(event->udev, "%i character(s) replaced\n" , count); + util_strlcat(string, value, maxsize); + dbg(event->udev, "substitute sysfs value '%s'\n", value); + } + break; + case SUBST_PARENT: + { + struct udev_device *dev_parent; + const char *devnode; + + dev_parent = udev_device_get_parent(event->dev); + if (dev_parent == NULL) + break; + devnode = udev_device_get_devnode(dev_parent); + if (devnode != NULL) { + size_t devlen = strlen(udev_get_dev_path(event->udev))+1; + + util_strlcat(string, &devnode[devlen], maxsize); + dbg(event->udev, "found parent '%s', got node name '%s'\n", + udev_device_get_syspath(dev_parent), &devnode[devlen]); + } + } + break; + case SUBST_TEMP_NODE: + if (event->tmp_node[0] == '\0' && major(udev_device_get_devnum(dev)) > 0) { + dbg(event->udev, "create temporary device node for callout\n"); + snprintf(event->tmp_node, sizeof(event->tmp_node), "%s/.tmp-%u-%u", + udev_get_dev_path(event->udev), + major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); + udev_node_mknod(dev, event->tmp_node, makedev(0,0), 0600, 0, 0); + } + util_strlcat(string, event->tmp_node, maxsize); + dbg(event->udev, "substitute temporary device node name '%s'\n", event->tmp_node); + break; + case SUBST_NAME: + if (event->name != NULL) { + util_strlcat(string, event->name, maxsize); + dbg(event->udev, "substitute name '%s'\n", event->name); + } else { + util_strlcat(string, udev_device_get_sysname(dev), maxsize); + dbg(event->udev, "substitute sysname '%s'\n", udev_device_get_sysname(dev)); + } + break; + case SUBST_LINKS: + { + struct udev_list_entry *list_entry; + + list_entry = udev_device_get_properties_list_entry(dev); + util_strlcpy(string, udev_list_entry_get_name(list_entry), maxsize); + udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) { + util_strlcat(string, " ", maxsize); + util_strlcat(string, udev_list_entry_get_name(list_entry), maxsize); + } + } + break; + case SUBST_ROOT: + util_strlcat(string, udev_get_dev_path(event->udev), maxsize); + dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); + break; + case SUBST_SYS: + util_strlcat(string, udev_get_sys_path(event->udev), maxsize); + dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); + break; + case SUBST_ENV: + if (attr == NULL) { + dbg(event->udev, "missing attribute\n"); + break; + } else { + struct udev_list_entry *list_entry; + const char *value; + + list_entry = udev_device_get_properties_list_entry(event->dev); + list_entry = udev_list_entry_get_by_name(list_entry, attr); + if (list_entry == NULL) + break; + value = udev_list_entry_get_value(list_entry); + dbg(event->udev, "substitute env '%s=%s'\n", attr, value); + util_strlcat(string, value, maxsize); + break; + } + default: + err(event->udev, "unknown substitution type=%i\n", type); + break; + } + /* possibly truncate to format-char specified length */ + if (len >= 0 && len < (int)strlen(head)) { + head[len] = '\0'; + dbg(event->udev, "truncate to %i chars, subtitution string becomes '%s'\n", len, head); + } + util_strlcat(string, temp, maxsize); + } +} + +static char *key_val(struct udev_rule *rule, struct key *key) +{ + return rule->buf + key->val_off; +} + +static char *key_pair_name(struct udev_rule *rule, struct key_pair *pair) +{ + return rule->buf + pair->key_name_off; +} + +static int match_key(struct udev *udev, const char *key_name, struct udev_rule *rule, struct key *key, const char *val) +{ + char value[UTIL_PATH_SIZE]; + char *key_value; + char *pos; + int match = 0; + + if (key->operation != KEY_OP_MATCH && + key->operation != KEY_OP_NOMATCH) + return 0; + + if (val == NULL) + val = ""; + + /* look for a matching string, parts are separated by '|' */ + util_strlcpy(value, rule->buf + key->val_off, sizeof(value)); + key_value = value; + dbg(udev, "key %s value='%s'\n", key_name, key_value); + while (key_value != NULL) { + pos = strchr(key_value, '|'); + if (pos != NULL) { + pos[0] = '\0'; + pos = &pos[1]; + } + + dbg(udev, "match %s '%s' <-> '%s'\n", key_name, key_value, val); + match = (fnmatch(key_value, val, 0) == 0); + if (match) + break; + + key_value = pos; + } + + if (match && (key->operation == KEY_OP_MATCH)) { + dbg(udev, "%s is true (matching value)\n", key_name); + return 0; + } + if (!match && (key->operation == KEY_OP_NOMATCH)) { + dbg(udev, "%s is true (non-matching value)\n", key_name); + return 0; + } + return -1; +} + +/* match a single rule against a given device and possibly its parent devices */ +static int match_rule(struct udev_event *event, struct udev_rule *rule) +{ + struct udev_device *dev = event->dev; + int i; + + if (match_key(event->udev, "ACTION", rule, &rule->action, udev_device_get_action(dev))) + goto nomatch; + + if (match_key(event->udev, "KERNEL", rule, &rule->kernel, udev_device_get_sysname(dev))) + goto nomatch; + + if (match_key(event->udev, "SUBSYSTEM", rule, &rule->subsystem, udev_device_get_subsystem(dev))) + goto nomatch; + + if (match_key(event->udev, "DEVPATH", rule, &rule->devpath, udev_device_get_devpath(dev))) + goto nomatch; + + if (match_key(event->udev, "DRIVER", rule, &rule->driver, udev_device_get_driver(dev))) + goto nomatch; + + /* match NAME against a value assigned by an earlier rule */ + if (match_key(event->udev, "NAME", rule, &rule->name, event->name)) + goto nomatch; + + /* match against current list of symlinks */ + if (rule->symlink_match.operation == KEY_OP_MATCH || + rule->symlink_match.operation == KEY_OP_NOMATCH) { + size_t devlen = strlen(udev_get_dev_path(event->udev))+1; + struct udev_list_entry *list_entry; + int match = 0; + + udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { + const char *devlink; + + devlink = &udev_list_entry_get_name(list_entry)[devlen]; + if (match_key(event->udev, "SYMLINK", rule, &rule->symlink_match, devlink) == 0) { + match = 1; + break; + } + } + if (!match) + goto nomatch; + } + + for (i = 0; i < rule->env.count; i++) { + struct key_pair *pair = &rule->env.keys[i]; + + /* we only check for matches, assignments will be handled later */ + if (pair->key.operation == KEY_OP_MATCH || + pair->key.operation == KEY_OP_NOMATCH) { + struct udev_list_entry *list_entry; + const char *key_name = key_pair_name(rule, pair); + const char *value; + + list_entry = udev_device_get_properties_list_entry(event->dev); + list_entry = udev_list_entry_get_by_name(list_entry, key_name); + value = udev_list_entry_get_value(list_entry); + if (value == NULL) { + dbg(event->udev, "ENV{%s} is not set, treat as empty\n", key_name); + value = ""; + } + if (match_key(event->udev, "ENV", rule, &pair->key, value)) + goto nomatch; + } + } + + if (rule->test.operation == KEY_OP_MATCH || + rule->test.operation == KEY_OP_NOMATCH) { + char filename[UTIL_PATH_SIZE]; + char *subsys; + char *sysname; + char *attrib; + struct stat statbuf; + int match; + + util_strlcpy(filename, key_val(rule, &rule->test), sizeof(filename)); + udev_rules_apply_format(event, filename, sizeof(filename)); + + if (split_subsys_sysname(event->udev, filename, &subsys, &sysname, &attrib) == 0) { + struct udev_device *d; + d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); + if (d != NULL) { + util_strlcpy(filename, udev_device_get_syspath(d), sizeof(filename)); + if (attrib != NULL) { + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, attrib, sizeof(filename)); + } + udev_device_unref(d); + } + } else if (filename[0] != '/') { + char tmp[UTIL_PATH_SIZE]; + + util_strlcpy(tmp, udev_device_get_syspath(dev), sizeof(tmp)); + util_strlcat(tmp, "/", sizeof(tmp)); + util_strlcat(tmp, filename, sizeof(tmp)); + util_strlcpy(filename, tmp, sizeof(filename)); + } + + attr_subst_subdir(filename, sizeof(filename)); + + match = (stat(filename, &statbuf) == 0); + info(event->udev, "'%s' %s", filename, match ? "exists\n" : "does not exist\n"); + if (match && rule->test_mode_mask > 0) { + match = ((statbuf.st_mode & rule->test_mode_mask) > 0); + info(event->udev, "'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, + match ? "matches" : "does not match", + rule->test_mode_mask); + } + if (match && rule->test.operation == KEY_OP_NOMATCH) + goto nomatch; + if (!match && rule->test.operation == KEY_OP_MATCH) + goto nomatch; + dbg(event->udev, "TEST key is true\n"); + } + + if (rule->wait_for.operation != KEY_OP_UNSET) { + char filename[UTIL_PATH_SIZE]; + int found; + + util_strlcpy(filename, key_val(rule, &rule->wait_for), sizeof(filename)); + udev_rules_apply_format(event, filename, sizeof(filename)); + found = (wait_for_file(event, filename, 10) == 0); + if (!found && (rule->wait_for.operation != KEY_OP_NOMATCH)) + goto nomatch; + } + + /* check for matching sysfs attribute pairs */ + for (i = 0; i < rule->attr.count; i++) { + struct key_pair *pair = &rule->attr.keys[i]; + + if (pair->key.operation == KEY_OP_MATCH || + pair->key.operation == KEY_OP_NOMATCH) { + char attr[UTIL_PATH_SIZE]; + const char *key_name = key_pair_name(rule, pair); + const char *key_value = key_val(rule, &pair->key); + char *subsys; + char *sysname; + char *attrib; + char value[UTIL_NAME_SIZE] = ""; + size_t len; + + util_strlcpy(attr, key_name, sizeof(attr)); + if (split_subsys_sysname(event->udev, attr, &subsys, &sysname, &attrib) == 0) { + struct udev_device *d; + const char *val; + + if (attrib == NULL) + goto nomatch; + d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); + if (d == NULL) + goto nomatch; + val = udev_device_get_attr_value(d, attrib); + if (val != NULL) + util_strlcpy(value, val, sizeof(value)); + udev_device_unref(d); + } + + if (value[0]=='\0') { + const char *val; + + val = udev_device_get_attr_value(dev, key_name); + if (val != NULL) + util_strlcpy(value, val, sizeof(value)); + } + + if (value[0]=='\0') + goto nomatch; + + /* strip trailing whitespace of value, if not asked to match for it */ + len = strlen(key_value); + if (len > 0 && !isspace(key_value[len-1])) { + len = strlen(value); + while (len > 0 && isspace(value[--len])) + value[len] = '\0'; + dbg(event->udev, "removed trailing whitespace from '%s'\n", value); + } + + if (match_key(event->udev, "ATTR", rule, &pair->key, value)) + goto nomatch; + } + } + + /* walk up the chain of parent devices and find a match */ + event->dev_parent = dev; + while (1) { + /* check for matching kernel device name */ + if (match_key(event->udev, "KERNELS", rule, + &rule->kernels, udev_device_get_sysname(event->dev_parent))) + goto try_parent; + + /* check for matching subsystem value */ + if (match_key(event->udev, "SUBSYSTEMS", rule, + &rule->subsystems, udev_device_get_subsystem(event->dev_parent))) + goto try_parent; + + /* check for matching driver */ + if (match_key(event->udev, "DRIVERS", rule, + &rule->drivers, udev_device_get_driver(event->dev_parent))) + goto try_parent; + + /* check for matching sysfs attribute pairs */ + for (i = 0; i < rule->attrs.count; i++) { + struct key_pair *pair = &rule->attrs.keys[i]; + + if (pair->key.operation == KEY_OP_MATCH || + pair->key.operation == KEY_OP_NOMATCH) { + const char *key_name = key_pair_name(rule, pair); + const char *key_value = key_val(rule, &pair->key); + const char *val; + char value[UTIL_NAME_SIZE]; + size_t len; + + val = udev_device_get_attr_value(event->dev_parent, key_name); + if (val == NULL) + val = udev_device_get_attr_value(dev, key_name); + if (val == NULL) + goto try_parent; + util_strlcpy(value, val, sizeof(value)); + + /* strip trailing whitespace of value, if not asked to match for it */ + len = strlen(key_value); + if (len > 0 && !isspace(key_value[len-1])) { + len = strlen(value); + while (len > 0 && isspace(value[--len])) + value[len] = '\0'; + dbg(event->udev, "removed trailing whitespace from '%s'\n", value); + } + + if (match_key(event->udev, "ATTRS", rule, &pair->key, value)) + goto try_parent; + } + } + + /* found matching device */ + break; +try_parent: + /* move to parent device */ + dbg(event->udev, "try parent sysfs device\n"); + event->dev_parent = udev_device_get_parent(event->dev_parent); + if (event->dev_parent == NULL) + goto nomatch; + dbg(event->udev, "looking at dev_parent->devpath='%s'\n", + udev_device_get_syspath(event->dev_parent)); + } + + /* execute external program */ + if (rule->program.operation != KEY_OP_UNSET) { + char program[UTIL_PATH_SIZE]; + char result[UTIL_PATH_SIZE]; + + util_strlcpy(program, key_val(rule, &rule->program), sizeof(program)); + udev_rules_apply_format(event, program, sizeof(program)); + if (run_program(event->dev, program, result, sizeof(result), NULL) != 0) { + dbg(event->udev, "PROGRAM is false\n"); + event->program_result[0] = '\0'; + if (rule->program.operation != KEY_OP_NOMATCH) + goto nomatch; + } else { + int count; + + dbg(event->udev, "PROGRAM matches\n"); + util_remove_trailing_chars(result, '\n'); + if (rule->string_escape == ESCAPE_UNSET || + rule->string_escape == ESCAPE_REPLACE) { + count = util_replace_chars(result, ALLOWED_CHARS_INPUT); + if (count > 0) + info(event->udev, "%i character(s) replaced\n" , count); + } + dbg(event->udev, "result is '%s'\n", result); + util_strlcpy(event->program_result, result, sizeof(event->program_result)); + dbg(event->udev, "PROGRAM returned successful\n"); + if (rule->program.operation == KEY_OP_NOMATCH) + goto nomatch; + } + dbg(event->udev, "PROGRAM key is true\n"); + } + + /* check for matching result of external program */ + if (match_key(event->udev, "RESULT", rule, &rule->result, event->program_result)) + goto nomatch; + + /* import variables returned from program or or file into environment */ + if (rule->import.operation != KEY_OP_UNSET) { + char import[UTIL_PATH_SIZE]; + int rc = -1; + + util_strlcpy(import, key_val(rule, &rule->import), sizeof(import)); + udev_rules_apply_format(event, import, sizeof(import)); + dbg(event->udev, "check for IMPORT import='%s'\n", import); + if (rule->import_type == IMPORT_PROGRAM) { + rc = import_program_into_env(event, import); + } else if (rule->import_type == IMPORT_FILE) { + dbg(event->udev, "import file import='%s'\n", import); + rc = import_file_into_env(event, import); + } else if (rule->import_type == IMPORT_PARENT) { + dbg(event->udev, "import parent import='%s'\n", import); + rc = import_parent_into_env(event, import); + } + if (rc != 0) { + dbg(event->udev, "IMPORT failed\n"); + if (rule->import.operation != KEY_OP_NOMATCH) + goto nomatch; + } else + dbg(event->udev, "IMPORT '%s' imported\n", key_val(rule, &rule->import)); + dbg(event->udev, "IMPORT key is true\n"); + } + + /* rule matches, if we have ENV assignments export it */ + for (i = 0; i < rule->env.count; i++) { + struct key_pair *pair = &rule->env.keys[i]; + + if (pair->key.operation == KEY_OP_ASSIGN) { + char temp_value[UTIL_NAME_SIZE]; + const char *key_name = key_pair_name(rule, pair); + const char *value = key_val(rule, &pair->key); + + /* make sure we don't write to the same string we possibly read from */ + util_strlcpy(temp_value, value, sizeof(temp_value)); + udev_rules_apply_format(event, temp_value, sizeof(temp_value)); + + if (temp_value[0] != '\0') { + struct udev_list_entry *entry; + + info(event->udev, "set ENV '%s=%s'\n", key_name, temp_value); + entry = udev_device_add_property(dev, key_name, temp_value); + /* store in db */ + udev_list_entry_set_flag(entry, 1); + } + } + } + + /* if we have ATTR assignments, write value to sysfs file */ + for (i = 0; i < rule->attr.count; i++) { + struct key_pair *pair = &rule->attr.keys[i]; + + if (pair->key.operation == KEY_OP_ASSIGN) { + const char *key_name = key_pair_name(rule, pair); + char *subsys; + char *sysname; + char *attrib; + char attr[UTIL_PATH_SIZE]; + char value[UTIL_NAME_SIZE]; + FILE *f; + + util_strlcpy(attr, key_name, sizeof(attr)); + if (split_subsys_sysname(event->udev, attr, &subsys, &sysname, &attrib) == 0) { + struct udev_device *d; + + d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); + if (d != NULL) { + util_strlcpy(attr, udev_device_get_syspath(d), sizeof(attr)); + if (attrib != NULL) { + util_strlcat(attr, "/", sizeof(attr)); + util_strlcat(attr, attrib, sizeof(attr)); + } + udev_device_unref(d); + } + } else { + util_strlcpy(attr, udev_device_get_syspath(dev), sizeof(attr)); + util_strlcat(attr, "/", sizeof(attr)); + util_strlcat(attr, key_name, sizeof(attr)); + } + + attr_subst_subdir(attr, sizeof(attr)); + + util_strlcpy(value, key_val(rule, &pair->key), sizeof(value)); + udev_rules_apply_format(event, value, sizeof(value)); + info(event->udev, "writing '%s' to sysfs file '%s'\n", value, attr); + f = fopen(attr, "w"); + if (f != NULL) { + if (!event->test) + if (fprintf(f, "%s", value) <= 0) + err(event->udev, "error writing ATTR{%s}: %m\n", attr); + fclose(f); + } else + err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr); + } + } + return 0; + +nomatch: + return -1; +} + +int udev_rules_get_name(struct udev_rules *rules, struct udev_event *event) +{ + struct udev_device *dev = event->dev; + struct udev_rules_iter iter; + struct udev_rule *rule; + int name_set = 0; + + dbg(event->udev, "device: '%s'\n", udev_device_get_syspath(dev)); + + /* look for a matching rule to apply */ + udev_rules_iter_init(&iter, rules); + while (1) { + rule = udev_rules_iter_next(&iter); + if (rule == NULL) + break; + + if (name_set && + (rule->name.operation == KEY_OP_ASSIGN || + rule->name.operation == KEY_OP_ASSIGN_FINAL || + rule->name.operation == KEY_OP_ADD)) { + dbg(event->udev, "node name already set, rule ignored\n"); + continue; + } + + dbg(event->udev, "process rule\n"); + if (match_rule(event, rule) == 0) { + /* apply options */ + if (rule->ignore_device) { + info(event->udev, "rule applied, '%s' is ignored\n", udev_device_get_sysname(dev)); + event->ignore_device = 1; + return 0; + } + if (rule->ignore_remove) { + udev_device_set_ignore_remove(dev, 1); + dbg(event->udev, "remove event should be ignored\n"); + } + if (rule->link_priority != 0) { + udev_device_set_devlink_priority(dev, rule->link_priority); + info(event->udev, "devlink_priority=%i\n", rule->link_priority); + } + if (rule->event_timeout >= 0) { + udev_device_set_event_timeout(dev, rule->event_timeout); + info(event->udev, "event_timeout=%i\n", rule->event_timeout); + } + /* apply all_partitions option only at a disk device */ + if (rule->partitions > 0 && + strcmp(udev_device_get_subsystem(dev), "block") == 0 && + udev_device_get_sysnum(dev) == NULL) { + udev_device_set_num_fake_partitions(dev, rule->partitions); + dbg(event->udev, "creation of partition nodes requested\n"); + } + + /* apply permissions */ + if (!event->mode_final && rule->mode.operation != KEY_OP_UNSET) { + if (rule->mode.operation == KEY_OP_ASSIGN_FINAL) + event->mode_final = 1; + char buf[20]; + util_strlcpy(buf, key_val(rule, &rule->mode), sizeof(buf)); + udev_rules_apply_format(event, buf, sizeof(buf)); + event->mode = strtol(buf, NULL, 8); + dbg(event->udev, "applied mode=%#o to '%s'\n", + event->mode, udev_device_get_sysname(dev)); + } + if (!event->owner_final && rule->owner.operation != KEY_OP_UNSET) { + if (rule->owner.operation == KEY_OP_ASSIGN_FINAL) + event->owner_final = 1; + util_strlcpy(event->owner, key_val(rule, &rule->owner), sizeof(event->owner)); + udev_rules_apply_format(event, event->owner, sizeof(event->owner)); + dbg(event->udev, "applied owner='%s' to '%s'\n", + event->owner, udev_device_get_sysname(dev)); + } + if (!event->group_final && rule->group.operation != KEY_OP_UNSET) { + if (rule->group.operation == KEY_OP_ASSIGN_FINAL) + event->group_final = 1; + util_strlcpy(event->group, key_val(rule, &rule->group), sizeof(event->group)); + udev_rules_apply_format(event, event->group, sizeof(event->group)); + dbg(event->udev, "applied group='%s' to '%s'\n", + event->group, udev_device_get_sysname(dev)); + } + + /* collect symlinks */ + if (!event->devlink_final && + (rule->symlink.operation == KEY_OP_ASSIGN || + rule->symlink.operation == KEY_OP_ASSIGN_FINAL || + rule->symlink.operation == KEY_OP_ADD)) { + char temp[UTIL_PATH_SIZE]; + char filename[UTIL_PATH_SIZE]; + char *pos, *next; + int count = 0; + + if (rule->symlink.operation == KEY_OP_ASSIGN_FINAL) + event->devlink_final = 1; + if (rule->symlink.operation == KEY_OP_ASSIGN || + rule->symlink.operation == KEY_OP_ASSIGN_FINAL) { + info(event->udev, "reset symlink list\n"); + udev_device_cleanup_devlinks_list(dev); + } + /* allow multiple symlinks separated by spaces */ + util_strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp)); + udev_rules_apply_format(event, temp, sizeof(temp)); + if (rule->string_escape == ESCAPE_UNSET) + count = util_replace_chars(temp, ALLOWED_CHARS_FILE " "); + else if (rule->string_escape == ESCAPE_REPLACE) + count = util_replace_chars(temp, ALLOWED_CHARS_FILE); + if (count > 0) + info(event->udev, "%i character(s) replaced\n" , count); + dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp); + pos = temp; + while (isspace(pos[0])) + pos++; + next = strchr(pos, ' '); + while (next) { + next[0] = '\0'; + info(event->udev, "add symlink '%s'\n", pos); + util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, pos, sizeof(filename)); + udev_device_add_devlink(dev, filename); + while (isspace(next[1])) + next++; + pos = &next[1]; + next = strchr(pos, ' '); + } + if (pos[0] != '\0') { + info(event->udev, "add symlink '%s'\n", pos); + util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); + util_strlcat(filename, "/", sizeof(filename)); + util_strlcat(filename, pos, sizeof(filename)); + udev_device_add_devlink(dev, filename); + } + } + + /* set name, later rules with name set will be ignored */ + if (rule->name.operation == KEY_OP_ASSIGN || + rule->name.operation == KEY_OP_ASSIGN_FINAL || + rule->name.operation == KEY_OP_ADD) { + int count; + + name_set = 1; + util_strlcpy(event->name, key_val(rule, &rule->name), sizeof(event->name)); + udev_rules_apply_format(event, event->name, sizeof(event->name)); + if (rule->string_escape == ESCAPE_UNSET || + rule->string_escape == ESCAPE_REPLACE) { + count = util_replace_chars(event->name, ALLOWED_CHARS_FILE); + if (count > 0) + info(event->udev, "%i character(s) replaced\n", count); + } + + info(event->udev, "rule applied, '%s' becomes '%s'\n", + udev_device_get_sysname(dev), event->name); + if (strcmp(udev_device_get_subsystem(dev), "net") != 0) + dbg(event->udev, "'%s' owner='%s', group='%s', mode=%#o partitions=%i\n", + event->name, event->owner, event->group, event->mode, + udev_device_get_num_fake_partitions(dev)); + } + + if (!event->run_final && rule->run.operation != KEY_OP_UNSET) { + struct udev_list_entry *list_entry; + + if (rule->run.operation == KEY_OP_ASSIGN_FINAL) + event->run_final = 1; + if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) { + info(event->udev, "reset run list\n"); + udev_list_cleanup(event->udev, &event->run_list); + } + dbg(event->udev, "add run '%s'\n", key_val(rule, &rule->run)); + list_entry = udev_list_entry_add(event->udev, &event->run_list, + key_val(rule, &rule->run), NULL, 1, 0); + if (rule->run_ignore_error && list_entry != NULL) + udev_list_entry_set_flag(list_entry, 1); + } + + if (rule->last_rule) { + dbg(event->udev, "last rule to be applied\n"); + break; + } + + if (rule->goto_label.operation != KEY_OP_UNSET) { + dbg(event->udev, "moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); + udev_rules_iter_goto(&iter, rule->goto_rule_off); + } + } + } + + if (!name_set) { + info(event->udev, "no node name set, will use kernel name '%s'\n", + udev_device_get_sysname(dev)); + util_strlcpy(event->name, udev_device_get_sysname(dev), sizeof(event->name)); + } + + if (event->tmp_node[0] != '\0') { + dbg(event->udev, "removing temporary device node\n"); + unlink_secure(event->udev, event->tmp_node); + event->tmp_node[0] = '\0'; + } + return 0; +} + +int udev_rules_get_run(struct udev_rules *rules, struct udev_event *event) +{ + struct udev_device *dev = event->dev; + struct udev_rules_iter iter; + struct udev_rule *rule; + + dbg(event->udev, "sysname: '%s'\n", udev_device_get_sysname(dev)); + + /* look for a matching rule to apply */ + udev_rules_iter_init(&iter, rules); + while (1) { + rule = udev_rules_iter_next(&iter); + if (rule == NULL) + break; + + dbg(event->udev, "process rule\n"); + if (rule->name.operation == KEY_OP_ASSIGN || + rule->name.operation == KEY_OP_ASSIGN_FINAL || + rule->name.operation == KEY_OP_ADD || + rule->symlink.operation == KEY_OP_ASSIGN || + rule->symlink.operation == KEY_OP_ASSIGN_FINAL || + rule->symlink.operation == KEY_OP_ADD || + rule->mode.operation != KEY_OP_UNSET || + rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) { + dbg(event->udev, "skip rule that names a device\n"); + continue; + } + + if (match_rule(event, rule) == 0) { + if (rule->ignore_device) { + info(event->udev, "rule applied, '%s' is ignored\n", udev_device_get_sysname(dev)); + event->ignore_device = 1; + return 0; + } + if (rule->ignore_remove) { + udev_device_set_ignore_remove(dev, 1); + dbg(event->udev, "remove event should be ignored\n"); + } + + if (!event->run_final && rule->run.operation != KEY_OP_UNSET) { + struct udev_list_entry *list_entry; + + if (rule->run.operation == KEY_OP_ASSIGN || + rule->run.operation == KEY_OP_ASSIGN_FINAL) { + info(event->udev, "reset run list\n"); + udev_list_cleanup(event->udev, &event->run_list); + } + dbg(event->udev, "add run '%s'\n", key_val(rule, &rule->run)); + list_entry = udev_list_entry_add(event->udev, &event->run_list, + key_val(rule, &rule->run), NULL, 1, 0); + if (rule->run_ignore_error && list_entry != NULL) + udev_list_entry_set_flag(list_entry, 1); + if (rule->run.operation == KEY_OP_ASSIGN_FINAL) + break; + } + + if (rule->last_rule) { + dbg(event->udev, "last rule to be applied\n"); + break; + } + + if (rule->goto_label.operation != KEY_OP_UNSET) { + dbg(event->udev, "moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); + udev_rules_iter_goto(&iter, rule->goto_rule_off); + } + } + } + + return 0; +} diff --git a/udev/udev-rules.h b/udev/udev-rules.h new file mode 100644 index 0000000000..d8f2f367c9 --- /dev/null +++ b/udev/udev-rules.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef UDEV_RULES_H +#define UDEV_RULES_H + +#include "udev.h" + +#define PAIRS_MAX 5 + +enum key_operation { + KEY_OP_UNSET, + KEY_OP_MATCH, + KEY_OP_NOMATCH, + KEY_OP_ADD, + KEY_OP_ASSIGN, + KEY_OP_ASSIGN_FINAL, +}; + +struct key { + enum key_operation operation; + size_t val_off; +}; + +struct key_pair { + struct key key; + size_t key_name_off; +}; + +struct key_pairs { + int count; + struct key_pair keys[PAIRS_MAX]; +}; + +enum import_type { + IMPORT_UNSET, + IMPORT_PROGRAM, + IMPORT_FILE, + IMPORT_PARENT, +}; + +enum escape_type { + ESCAPE_UNSET, + ESCAPE_NONE, + ESCAPE_REPLACE, +}; + +struct udev_rule { + struct key action; + struct key devpath; + struct key kernel; + struct key subsystem; + struct key driver; + struct key_pairs attr; + + struct key kernels; + struct key subsystems; + struct key drivers; + struct key_pairs attrs; + + struct key_pairs env; + struct key program; + struct key result; + struct key import; + enum import_type import_type; + struct key test; + mode_t test_mode_mask; + struct key run; + struct key wait_for; + struct key label; + struct key goto_label; + size_t goto_rule_off; + + struct key name; + struct key symlink; + struct key symlink_match; + struct key owner; + struct key group; + struct key mode; + enum escape_type string_escape; + + unsigned int link_priority; + int event_timeout; + unsigned int partitions; + unsigned int last_rule:1, + run_ignore_error:1, + ignore_device:1, + ignore_remove:1; + + size_t bufsize; + char buf[]; +}; + +struct udev_rules { + struct udev *udev; + char *buf; + size_t bufsize; + int resolve_names; +}; + +struct udev_rules_iter { + struct udev_rules *rules; + size_t current; +}; + +extern int udev_rules_init(struct udev *udev, struct udev_rules *rules, int resolve_names); +extern void udev_rules_cleanup(struct udev_rules *rules); + +extern void udev_rules_iter_init(struct udev_rules_iter *iter, struct udev_rules *rules); +extern struct udev_rule *udev_rules_iter_next(struct udev_rules_iter *iter); +extern struct udev_rule *udev_rules_iter_goto(struct udev_rules_iter *iter, size_t rule_off); + +extern int udev_rules_get_name(struct udev_rules *rules, struct udev_event *event); +extern int udev_rules_get_run(struct udev_rules *rules, struct udev_event *event); +extern int udev_rules_run(struct udev_event *event); + +extern void udev_rules_apply_format(struct udev_event *event, char *string, size_t maxsize); + +#endif diff --git a/udev/udev-selinux.c b/udev/udev-selinux.c new file mode 100644 index 0000000000..cf7f43496a --- /dev/null +++ b/udev/udev-selinux.c @@ -0,0 +1,90 @@ +/* + * libudev - interface to udev device information + * + * Copyright (C) 2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static int selinux_enabled; +security_context_t selinux_prev_scontext; + +void selinux_init(struct udev *udev) +{ + /* record the present security context */ + selinux_enabled = (is_selinux_enabled() > 0); + info(udev, "selinux=%i\n", selinux_enabled); + if (!selinux_enabled) + return; + matchpathcon_init_prefix(NULL, udev_get_dev_path(udev)); + if (getfscreatecon(&selinux_prev_scontext) < 0) { + err(udev, "getfscreatecon failed\n"); + selinux_prev_scontext = NULL; + } +} + +void selinux_exit(struct udev *udev) +{ + if (!selinux_enabled) + return; + freecon(selinux_prev_scontext); + selinux_prev_scontext = NULL; +} + +void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) +{ + security_context_t scontext = NULL; + + if (!selinux_enabled) + return; + if (matchpathcon(file, mode, &scontext) < 0) { + err(udev, "matchpathcon(%s) failed\n", file); + return; + } + if (lsetfilecon(file, scontext) < 0) + err(udev, "setfilecon %s failed: %m\n", file); + freecon(scontext); +} + +void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) +{ + security_context_t scontext = NULL; + + if (!selinux_enabled) + return; + if (matchpathcon(file, mode, &scontext) < 0) { + err(udev, "matchpathcon(%s) failed\n", file); + return; + } + if (setfscreatecon(scontext) < 0) + err(udev, "setfscreatecon %s failed: %m\n", file); + freecon(scontext); +} + +void udev_selinux_resetfscreatecon(struct udev *udev) +{ + if (!selinux_enabled) + return; + if (setfscreatecon(selinux_prev_scontext) < 0) + err(udev, "setfscreatecon failed: %m\n"); +} diff --git a/udev/udev-sysdeps.h b/udev/udev-sysdeps.h new file mode 100644 index 0000000000..eaeab86f6b --- /dev/null +++ b/udev/udev-sysdeps.h @@ -0,0 +1,50 @@ +/* + * wrapping of libc features and kernel interfaces + * + * Copyright (C) 2005-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _UDEV_SYSDEPS_H_ +#define _UDEV_SYSDEPS_H_ + +#include + +/* needed for our signal handlers to work */ +#undef asmlinkage +#ifdef __i386__ +#define asmlinkage __attribute__((regparm(0))) +#else +#define asmlinkage +#endif /* __i386__ */ + +#ifndef HAVE_INOTIFY +static inline int inotify_init(void) +{ + return -1; +} + +static inline int inotify_add_watch(int fd, const char *name, uint32_t mask) +{ + return -1; +} + +#define IN_CREATE 0 +#define IN_DELETE 0 +#define IN_MOVE 0 +#define IN_CLOSE_WRITE 0 + +#endif /* HAVE_INOTIFY */ +#endif diff --git a/udev/udev-util-file.c b/udev/udev-util-file.c new file mode 100644 index 0000000000..84ff09e050 --- /dev/null +++ b/udev/udev-util-file.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +int create_path(struct udev *udev, const char *path) +{ + char p[UTIL_PATH_SIZE]; + char *pos; + struct stat stats; + int ret; + + util_strlcpy(p, path, sizeof(p)); + pos = strrchr(p, '/'); + if (pos == p || pos == NULL) + return 0; + + while (pos[-1] == '/') + pos--; + pos[0] = '\0'; + + dbg(udev, "stat '%s'\n", p); + if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) + return 0; + + if (create_path(udev, p) != 0) + return -1; + + dbg(udev, "mkdir '%s'\n", p); + udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755); + ret = mkdir(p, 0755); + udev_selinux_resetfscreatecon(udev); + if (ret == 0) + return 0; + + if (errno == EEXIST) + if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) + return 0; + return -1; +} + +int delete_path(struct udev *udev, const char *path) +{ + char p[UTIL_PATH_SIZE]; + char *pos; + int retval; + + strcpy (p, path); + pos = strrchr(p, '/'); + if (pos == p || pos == NULL) + return 0; + + while (1) { + *pos = '\0'; + pos = strrchr(p, '/'); + + /* don't remove the last one */ + if ((pos == p) || (pos == NULL)) + break; + + /* remove if empty */ + retval = rmdir(p); + if (errno == ENOENT) + retval = 0; + if (retval) { + if (errno == ENOTEMPTY) + return 0; + err(udev, "rmdir(%s) failed: %m\n", p); + break; + } + dbg(udev, "removed '%s'\n", p); + } + return 0; +} + +/* Reset permissions on the device node, before unlinking it to make sure, + * that permisions of possible hard links will be removed too. + */ +int unlink_secure(struct udev *udev, const char *filename) +{ + int retval; + + retval = chown(filename, 0, 0); + if (retval) + err(udev, "chown(%s, 0, 0) failed: %m\n", filename); + + retval = chmod(filename, 0000); + if (retval) + err(udev, "chmod(%s, 0000) failed: %m\n", filename); + + retval = unlink(filename); + if (errno == ENOENT) + retval = 0; + + if (retval) + err(udev, "unlink(%s) failed: %m\n", filename); + + return retval; +} + +int file_map(const char *filename, char **buf, size_t *bufsize) +{ + struct stat stats; + int fd; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + return -1; + } + + if (fstat(fd, &stats) < 0) { + close(fd); + return -1; + } + + *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (*buf == MAP_FAILED) { + close(fd); + return -1; + } + *bufsize = stats.st_size; + + close(fd); + + return 0; +} + +void file_unmap(void *buf, size_t bufsize) +{ + munmap(buf, bufsize); +} + +/* return number of chars until the next newline, skip escaped newline */ +size_t buf_get_line(const char *buf, size_t buflen, size_t cur) +{ + int escape = 0; + size_t count; + + for (count = cur; count < buflen; count++) { + if (!escape && buf[count] == '\n') + break; + + if (buf[count] == '\\') + escape = 1; + else + escape = 0; + } + + return count - cur; +} diff --git a/udev/udev-util.c b/udev/udev-util.c new file mode 100644 index 0000000000..4623c30caf --- /dev/null +++ b/udev/udev-util.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2004-2008 Kay Sievers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +struct name_entry *name_list_add(struct udev *udev, struct list_head *name_list, const char *name, int sort) +{ + struct name_entry *name_loop; + struct name_entry *name_new; + + /* avoid duplicate entries */ + list_for_each_entry(name_loop, name_list, node) { + if (strcmp(name_loop->name, name) == 0) { + dbg(udev, "'%s' is already in the list\n", name); + return name_loop; + } + } + + if (sort) + list_for_each_entry(name_loop, name_list, node) { + if (strcmp(name_loop->name, name) > 0) + break; + } + + name_new = malloc(sizeof(struct name_entry)); + if (name_new == NULL) + return NULL; + memset(name_new, 0x00, sizeof(struct name_entry)); + util_strlcpy(name_new->name, name, sizeof(name_new->name)); + dbg(udev, "adding '%s'\n", name_new->name); + list_add_tail(&name_new->node, &name_loop->node); + + return name_new; +} + +struct name_entry *name_list_key_add(struct udev *udev, struct list_head *name_list, const char *key, const char *value) +{ + struct name_entry *name_loop; + struct name_entry *name_new; + size_t keylen = strlen(key); + + list_for_each_entry(name_loop, name_list, node) { + if (strncmp(name_loop->name, key, keylen) != 0) + continue; + if (name_loop->name[keylen] != '=') + continue; + dbg(udev, "key already present '%s', replace it\n", name_loop->name); + snprintf(name_loop->name, sizeof(name_loop->name), "%s=%s", key, value); + name_loop->name[sizeof(name_loop->name)-1] = '\0'; + return name_loop; + } + + name_new = malloc(sizeof(struct name_entry)); + if (name_new == NULL) + return NULL; + memset(name_new, 0x00, sizeof(struct name_entry)); + snprintf(name_new->name, sizeof(name_new->name), "%s=%s", key, value); + name_new->name[sizeof(name_new->name)-1] = '\0'; + dbg(udev, "adding '%s'\n", name_new->name); + list_add_tail(&name_new->node, &name_loop->node); + + return name_new; +} + +int name_list_key_remove(struct udev *udev, struct list_head *name_list, const char *key) +{ + struct name_entry *name_loop; + struct name_entry *name_tmp; + size_t keylen = strlen(key); + int retval = 0; + + list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { + if (strncmp(name_loop->name, key, keylen) != 0) + continue; + if (name_loop->name[keylen] != '=') + continue; + list_del(&name_loop->node); + free(name_loop); + retval = 1; + break; + } + return retval; +} + +void name_list_cleanup(struct udev *udev, struct list_head *name_list) +{ + struct name_entry *name_loop; + struct name_entry *name_tmp; + + list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { + list_del(&name_loop->node); + free(name_loop); + } +} + +/* calls function for every file found in specified directory */ +int add_matching_files(struct udev *udev, struct list_head *name_list, const char *dirname, const char *suffix) +{ + struct dirent *ent; + DIR *dir; + char filename[UTIL_PATH_SIZE]; + + dbg(udev, "open directory '%s'\n", dirname); + dir = opendir(dirname); + if (dir == NULL) { + err(udev, "unable to open '%s': %m\n", dirname); + return -1; + } + + while (1) { + ent = readdir(dir); + if (ent == NULL || ent->d_name[0] == '\0') + break; + + if ((ent->d_name[0] == '.') || (ent->d_name[0] == '#')) + continue; + + /* look for file matching with specified suffix */ + if (suffix != NULL) { + const char *ext; + + ext = strrchr(ent->d_name, '.'); + if (ext == NULL) + continue; + if (strcmp(ext, suffix) != 0) + continue; + } + dbg(udev, "put file '%s/%s' into list\n", dirname, ent->d_name); + + snprintf(filename, sizeof(filename), "%s/%s", dirname, ent->d_name); + filename[sizeof(filename)-1] = '\0'; + name_list_add(udev, name_list, filename, 1); + } + + closedir(dir); + return 0; +} + +uid_t lookup_user(struct udev *udev, const char *user) +{ + struct passwd *pw; + uid_t uid = 0; + + errno = 0; + pw = getpwnam(user); + if (pw == NULL) { + if (errno == 0 || errno == ENOENT || errno == ESRCH) + err(udev, "specified user '%s' unknown\n", user); + else + err(udev, "error resolving user '%s': %m\n", user); + } else + uid = pw->pw_uid; + + return uid; +} + +extern gid_t lookup_group(struct udev *udev, const char *group) +{ + struct group *gr; + gid_t gid = 0; + + errno = 0; + gr = getgrnam(group); + if (gr == NULL) { + if (errno == 0 || errno == ENOENT || errno == ESRCH) + err(udev, "specified group '%s' unknown\n", group); + else + err(udev, "error resolving group '%s': %m\n", group); + } else + gid = gr->gr_gid; + + return gid; +} + diff --git a/udev/udev.h b/udev/udev.h index e486df3656..d789d443ac 100644 --- a/udev/udev.h +++ b/udev/udev.h @@ -22,7 +22,7 @@ #include #include -#include "udev_sysdeps.h" +#include "udev-sysdeps.h" #include "lib/libudev.h" #include "lib/libudev-private.h" #include "list.h" diff --git a/udev/udev_device_event.c b/udev/udev_device_event.c deleted file mode 100644 index e5d716085a..0000000000 --- a/udev/udev_device_event.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" - -struct udev_event *udev_event_new(struct udev_device *dev) -{ - struct udev_event *event; - - event = malloc(sizeof(struct udev_event)); - if (event == NULL) - return NULL; - memset(event, 0x00, sizeof(struct udev_event)); - - event->dev = dev; - event->udev = udev_device_get_udev(dev); - udev_list_init(&event->run_list); - event->mode = 0660; - util_strlcpy(event->owner, "0", sizeof(event->owner)); - util_strlcpy(event->group, "0", sizeof(event->group)); - - dbg(event->udev, "allocated event %p\n", event); - return event; -} - -void udev_event_unref(struct udev_event *event) -{ - udev_list_cleanup(event->udev, &event->run_list); - dbg(event->udev, "free event %p\n", event); - free(event); -} - -static void kernel_log(struct ifreq ifr) -{ - int klog; - FILE *f; - - klog = open("/dev/kmsg", O_WRONLY); - if (klog < 0) - return; - - f = fdopen(klog, "w"); - if (f == NULL) { - close(klog); - return; - } - - fprintf(f, "<6>udev: renamed network interface %s to %s\n", - ifr.ifr_name, ifr.ifr_newname); - fclose(f); -} - -static int rename_netif(struct udev_event *event) -{ - struct udev_device *dev = event->dev; - int sk; - struct ifreq ifr; - int err; - - info(event->udev, "changing net interface name from '%s' to '%s'\n", - udev_device_get_sysname(dev), event->name); - if (event->test) - return 0; - - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) { - err(event->udev, "error opening socket: %m\n"); - return -1; - } - - memset(&ifr, 0x00, sizeof(struct ifreq)); - util_strlcpy(ifr.ifr_name, udev_device_get_sysname(dev), IFNAMSIZ); - util_strlcpy(ifr.ifr_newname, event->name, IFNAMSIZ); - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) - kernel_log(ifr); - else { - int loop; - - /* see if the destination interface name already exists */ - if (errno != EEXIST) { - err(event->udev, "error changing netif name %s to %s: %m\n", - ifr.ifr_name, ifr.ifr_newname); - goto exit; - } - - /* free our own name, another process may wait for us */ - util_strlcpy(ifr.ifr_newname, udev_device_get_sysname(dev), IFNAMSIZ); - util_strlcat(ifr.ifr_newname, "_rename", IFNAMSIZ); - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err != 0) { - err(event->udev, "error changing netif name %s to %s: %m\n", - ifr.ifr_name, ifr.ifr_newname); - goto exit; - } - - /* wait 30 seconds for our target to become available */ - util_strlcpy(ifr.ifr_name, ifr.ifr_newname, IFNAMSIZ); - util_strlcpy(ifr.ifr_newname, udev_device_get_devnode(dev), IFNAMSIZ); - loop = 30 * 20; - while (loop--) { - err = ioctl(sk, SIOCSIFNAME, &ifr); - if (err == 0) { - kernel_log(ifr); - break; - } - - if (errno != EEXIST) { - err(event->udev, "error changing net interface name %s to %s: %m\n", - ifr.ifr_name, ifr.ifr_newname); - break; - } - dbg(event->udev, "wait for netif '%s' to become free, loop=%i\n", - udev_device_get_devnode(dev), (30 * 20) - loop); - usleep(1000 * 1000 / 20); - } - } - -exit: - close(sk); - return err; -} - -int udev_event_run(struct udev_event *event, struct udev_rules *rules) -{ - struct udev_device *dev = event->dev; - int err = 0; - - if (udev_device_get_devpath_old(dev) != NULL) { - if (udev_device_rename_db(dev, udev_device_get_devpath(dev)) == 0) - info(event->udev, "moved database from '%s' to '%s'\n", - udev_device_get_devpath_old(dev), udev_device_get_devpath(dev)); - } - - /* add device node */ - if (major(udev_device_get_devnum(dev)) != 0 && - (strcmp(udev_device_get_action(dev), "add") == 0 || strcmp(udev_device_get_action(dev), "change") == 0)) { - char filename[UTIL_PATH_SIZE]; - struct udev_device *dev_old; - - dbg(event->udev, "device node add '%s'\n", udev_device_get_devpath(dev)); - - udev_rules_get_name(rules, event); - if (event->ignore_device) { - info(event->udev, "device event will be ignored\n"); - goto exit; - } - - if (event->name[0] == '\0') { - info(event->udev, "device node creation supressed\n"); - goto exit; - } - - /* set device node name */ - util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); - util_strlcat(filename, "/", sizeof(filename)); - util_strlcat(filename, event->name, sizeof(filename)); - udev_device_set_devnode(dev, filename); - - /* read current database entry; cleanup, if it is known device */ - dev_old = udev_device_new_from_syspath(event->udev, udev_device_get_syspath(dev)); - if (dev_old != NULL) { - info(event->udev, "device '%s' already in database, updating\n", - udev_device_get_devpath(dev)); - udev_node_update_old_links(dev, dev_old, event->test); - udev_device_unref(dev_old); - } - - udev_device_update_db(dev); - - err = udev_node_add(dev, event->mode, event->owner, event->group, event->test); - if (err != 0) - goto exit; - - goto exit; - } - - /* add netif */ - if (strcmp(udev_device_get_subsystem(dev), "net") == 0 && strcmp(udev_device_get_action(dev), "add") == 0) { - dbg(event->udev, "netif add '%s'\n", udev_device_get_devpath(dev)); - - udev_rules_get_name(rules, event); - if (event->ignore_device) { - info(event->udev, "device event will be ignored\n"); - goto exit; - } - if (event->name[0] == '\0') { - info(event->udev, "device renaming supressed\n"); - goto exit; - } - - /* look if we want to change the name of the netif */ - if (strcmp(event->name, udev_device_get_sysname(dev)) != 0) { - char syspath[UTIL_PATH_SIZE]; - char *pos; - - err = rename_netif(event); - if (err != 0) - goto exit; - info(event->udev, "renamed netif to '%s'\n", event->name); - - /* remember old name */ - udev_device_add_property(dev, "INTERFACE_OLD", udev_device_get_sysname(dev)); - - /* now change the devpath, because the kernel device name has changed */ - util_strlcpy(syspath, udev_device_get_syspath(dev), sizeof(syspath)); - pos = strrchr(syspath, '/'); - if (pos != NULL) { - pos[1] = '\0'; - util_strlcat(syspath, event->name, sizeof(syspath)); - udev_device_set_syspath(event->dev, syspath); - udev_device_add_property(dev, "INTERFACE", udev_device_get_sysname(dev)); - info(event->udev, "changed devpath to '%s'\n", udev_device_get_devpath(dev)); - } - } - goto exit; - } - - /* remove device node */ - if (major(udev_device_get_devnum(dev)) != 0 && strcmp(udev_device_get_action(dev), "remove") == 0) { - /* import database entry and delete it */ - udev_device_read_db(dev); - if (!event->test) - udev_device_delete_db(dev); - - if (udev_device_get_devnode(dev) == NULL) { - char devnode[UTIL_PATH_SIZE]; - - info(event->udev, "'%s' not found in database, using kernel name '%s'\n", - udev_device_get_syspath(dev), udev_device_get_sysname(dev)); - util_strlcpy(devnode, udev_get_dev_path(event->udev), sizeof(devnode)); - util_strlcat(devnode, "/", sizeof(devnode)); - util_strlcat(devnode, udev_device_get_sysname(dev), sizeof(devnode)); - udev_device_set_devnode(dev, devnode); - } - - udev_rules_get_run(rules, event); - if (event->ignore_device) { - info(event->udev, "device event will be ignored\n"); - goto exit; - } - - if (udev_device_get_ignore_remove(dev)) { - info(event->udev, "ignore_remove for '%s'\n", udev_device_get_devnode(dev)); - goto exit; - } - - err = udev_node_remove(dev, event->test); - goto exit; - } - - /* default devices */ - udev_rules_get_run(rules, event); - if (event->ignore_device) - info(event->udev, "device event will be ignored\n"); -exit: - return err; -} diff --git a/udev/udev_node.c b/udev/udev_node.c deleted file mode 100644 index 425764242a..0000000000 --- a/udev/udev_node.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" - -#define TMP_FILE_EXT ".udev-tmp" - -/* reverse mapping from the device file name to the devpath */ -static int name_index(struct udev *udev, const char *devpath, const char *name, int add, int test) -{ - char device[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE * 2]; - size_t devlen = strlen(udev_get_dev_path(udev))+1; - size_t start; - int fd; - - /* directory with device name */ - util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); - start = util_strlcat(filename, "/.udev/names/", sizeof(filename)); - util_strlcat(filename, &name[devlen], sizeof(filename)); - util_path_encode(&filename[start], sizeof(filename) - start); - /* entry with the devpath */ - util_strlcpy(device, devpath, sizeof(device)); - util_path_encode(device, sizeof(device)); - util_strlcat(filename, "/", sizeof(filename)); - util_strlcat(filename, device, sizeof(filename)); - - if (add) { - info(udev, "creating index: '%s'\n", filename); - create_path(udev, filename); - fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); - if (fd > 0) - close(fd); - } else { - info(udev, "removing index: '%s'\n", filename); - unlink(filename); - delete_path(udev, filename); - } - return 0; -} - -int udev_node_mknod(struct udev_device *dev, const char *file, dev_t devnum, mode_t mode, uid_t uid, gid_t gid) -{ - struct udev *udev = udev_device_get_udev(dev); - char file_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; - struct stat stats; - int preserve = 0; - int err = 0; - - if (major(devnum) == 0) - devnum = udev_device_get_devnum(dev); - - if (strcmp(udev_device_get_subsystem(dev), "block") == 0) - mode |= S_IFBLK; - else - mode |= S_IFCHR; - - if (file == NULL) - file = udev_device_get_devnode(dev); - - if (lstat(file, &stats) == 0) { - if (((stats.st_mode & S_IFMT) == (mode & S_IFMT)) && (stats.st_rdev == devnum)) { - info(udev, "preserve file '%s', because it has correct dev_t\n", file); - preserve = 1; - udev_selinux_lsetfilecon(udev, file, mode); - } else { - info(udev, "atomically replace existing file '%s'\n", file); - util_strlcpy(file_tmp, file, sizeof(file_tmp)); - util_strlcat(file_tmp, TMP_FILE_EXT, sizeof(file_tmp)); - unlink(file_tmp); - udev_selinux_setfscreatecon(udev, file_tmp, mode); - err = mknod(file_tmp, mode, devnum); - udev_selinux_resetfscreatecon(udev); - if (err != 0) { - err(udev, "mknod(%s, %#o, %u, %u) failed: %m\n", - file_tmp, mode, major(devnum), minor(devnum)); - goto exit; - } - err = rename(file_tmp, file); - if (err != 0) { - err(udev, "rename(%s, %s) failed: %m\n", file_tmp, file); - unlink(file_tmp); - } - } - } else { - info(udev, "mknod(%s, %#o, (%u,%u))\n", file, mode, major(devnum), minor(devnum)); - udev_selinux_setfscreatecon(udev, file, mode); - err = mknod(file, mode, devnum); - udev_selinux_resetfscreatecon(udev); - if (err != 0) { - err(udev, "mknod(%s, %#o, (%u,%u) failed: %m\n", file, mode, major(devnum), minor(devnum)); - goto exit; - } - } - - if (!preserve || stats.st_mode != mode) { - info(udev, "chmod(%s, %#o)\n", file, mode); - err = chmod(file, mode); - if (err != 0) { - err(udev, "chmod(%s, %#o) failed: %m\n", file, mode); - goto exit; - } - } - - if (!preserve || stats.st_uid != uid || stats.st_gid != gid) { - info(udev, "chown(%s, %u, %u)\n", file, uid, gid); - err = chown(file, uid, gid); - if (err != 0) { - err(udev, "chown(%s, %u, %u) failed: %m\n", file, uid, gid); - goto exit; - } - } -exit: - return err; -} - -static int node_symlink(struct udev *udev, const char *node, const char *slink) -{ - struct stat stats; - char target[UTIL_PATH_SIZE] = ""; - char slink_tmp[UTIL_PATH_SIZE + sizeof(TMP_FILE_EXT)]; - int i = 0; - int tail = 0; - int len; - int err = 0; - - /* use relative link */ - while (node[i] && (node[i] == slink[i])) { - if (node[i] == '/') - tail = i+1; - i++; - } - while (slink[i] != '\0') { - if (slink[i] == '/') - util_strlcat(target, "../", sizeof(target)); - i++; - } - util_strlcat(target, &node[tail], sizeof(target)); - - /* preserve link with correct target, do not replace node of other device */ - if (lstat(slink, &stats) == 0) { - if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { - struct stat stats2; - - info(udev, "found existing node instead of symlink '%s'\n", slink); - if (lstat(node, &stats2) == 0) { - if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) && - stats.st_rdev == stats2.st_rdev) { - info(udev, "replace device node '%s' with symlink to our node '%s'\n", - slink, node); - } else { - err(udev, "device node '%s' already exists, " - "link to '%s' will not overwrite it\n", - slink, node); - goto exit; - } - } - } else if (S_ISLNK(stats.st_mode)) { - char buf[UTIL_PATH_SIZE]; - - info(udev, "found existing symlink '%s'\n", slink); - len = readlink(slink, buf, sizeof(buf)); - if (len > 0) { - buf[len] = '\0'; - if (strcmp(target, buf) == 0) { - info(udev, "preserve already existing symlink '%s' to '%s'\n", - slink, target); - udev_selinux_lsetfilecon(udev, slink, S_IFLNK); - goto exit; - } - } - } - } else { - info(udev, "creating symlink '%s' to '%s'\n", slink, target); - udev_selinux_setfscreatecon(udev, slink, S_IFLNK); - err = symlink(target, slink); - udev_selinux_resetfscreatecon(udev); - if (err == 0) - goto exit; - } - - info(udev, "atomically replace '%s'\n", slink); - util_strlcpy(slink_tmp, slink, sizeof(slink_tmp)); - util_strlcat(slink_tmp, TMP_FILE_EXT, sizeof(slink_tmp)); - unlink(slink_tmp); - udev_selinux_setfscreatecon(udev, slink, S_IFLNK); - err = symlink(target, slink_tmp); - udev_selinux_resetfscreatecon(udev); - if (err != 0) { - err(udev, "symlink(%s, %s) failed: %m\n", target, slink_tmp); - goto exit; - } - err = rename(slink_tmp, slink); - if (err != 0) { - err(udev, "rename(%s, %s) failed: %m\n", slink_tmp, slink); - unlink(slink_tmp); - goto exit; - } -exit: - return err; -} - -static int get_devices_by_name(struct udev *udev, const char *name, struct list_head *name_list) -{ - char dirname[PATH_MAX]; - size_t devlen = strlen(udev_get_dev_path(udev))+1; - size_t start; - DIR *dir; - int count = 0; - - util_strlcpy(dirname, udev_get_dev_path(udev), sizeof(dirname)); - start = util_strlcat(dirname, "/.udev/names/", sizeof(dirname)); - util_strlcat(dirname, &name[devlen], sizeof(dirname)); - util_path_encode(&dirname[start], sizeof(dirname) - start); - - dir = opendir(dirname); - if (dir == NULL) { - info(udev, "no index directory '%s': %m\n", dirname); - count = -1; - goto out; - } - - info(udev, "found index directory '%s'\n", dirname); - while (1) { - struct dirent *ent; - char device[UTIL_PATH_SIZE]; - - ent = readdir(dir); - if (ent == NULL || ent->d_name[0] == '\0') - break; - if (ent->d_name[0] == '.') - continue; - - util_strlcpy(device, udev_get_sys_path(udev), sizeof(device)); - util_strlcat(device, ent->d_name, sizeof(device)); - util_path_decode(device); - name_list_add(udev, name_list, device, 0); - count++; - } - closedir(dir); -out: - return count; -} - -static int update_link(struct udev_device *dev, const char *slink, int test) -{ - struct udev *udev = udev_device_get_udev(dev); - LIST_HEAD(name_list); - struct name_entry *device; - char target[UTIL_PATH_SIZE] = ""; - int count; - int priority = 0; - int rc = 0; - - info(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev)); - - count = get_devices_by_name(udev, slink, &name_list); - info(udev, "found %i devices with name '%s'\n", count, slink); - - /* if we don't have a reference, delete it */ - if (count <= 0) { - info(udev, "no reference left, remove '%s'\n", slink); - if (!test) { - unlink(slink); - delete_path(udev, slink); - } - goto out; - } - - /* find the device with the highest priority */ - list_for_each_entry(device, &name_list, node) { - struct udev_device *dev_db; - const char *devnode; - - info(udev, "found '%s' for '%s'\n", device->name, slink); - - /* did we find ourself? we win, if we have the same priority */ - if (strcmp(udev_device_get_syspath(dev), device->name) == 0) { - info(udev, "compare (our own) priority of '%s' %i >= %i\n", - udev_device_get_devpath(dev), udev_device_get_devlink_priority(dev), priority); - if (strcmp(udev_device_get_devnode(dev), slink) == 0) { - info(udev, "'%s' is our device node, database inconsistent, skip link update\n", - udev_device_get_devnode(dev)); - } else if (target[0] == '\0' || udev_device_get_devlink_priority(dev) >= priority) { - priority = udev_device_get_devlink_priority(dev); - util_strlcpy(target, udev_device_get_devnode(dev), sizeof(target)); - } - continue; - } - - /* another device, read priority from database */ - dev_db = udev_device_new_from_syspath(udev, device->name); - if (dev_db == NULL) - continue; - devnode = udev_device_get_devnode(dev_db); - if (devnode != NULL) { - if (strcmp(devnode, slink) == 0) { - info(udev, "'%s' is a device node of '%s', skip link update\n", - devnode, device->name); - } else { - info(udev, "compare priority of '%s' %i > %i\n", - udev_device_get_devpath(dev_db), - udev_device_get_devlink_priority(dev_db), - priority); - if (target[0] == '\0' || udev_device_get_devlink_priority(dev_db) > priority) { - priority = udev_device_get_devlink_priority(dev_db); - util_strlcpy(target, devnode, sizeof(target)); - } - } - } - udev_device_unref(dev_db); - } - name_list_cleanup(udev, &name_list); - - if (target[0] == '\0') { - info(udev, "no current target for '%s' found\n", slink); - rc = 1; - goto out; - } - - /* create symlink to the target with the highest priority */ - info(udev, "'%s' with target '%s' has the highest priority %i, create it\n", slink, target, priority); - if (!test) { - create_path(udev, slink); - node_symlink(udev, target, slink); - } -out: - return rc; -} - -void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old, int test) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_list_entry *list_entry; - const char *devnode_old; - - /* update possible left-over symlinks */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev_old)) { - struct udev_list_entry *list_entry_current; - - udev_list_entry_foreach(list_entry_current, udev_device_get_devlinks_list_entry(dev)) { - if (strcmp(udev_list_entry_get_name(list_entry_current), - udev_list_entry_get_name(list_entry)) == 0) - continue; - } - /* link does no longer belong to this device */ - info(udev, "update old symlink '%s' no longer belonging to '%s'\n", - udev_list_entry_get_name(list_entry), udev_device_get_devpath(dev)); - update_link(dev, udev_list_entry_get_name(list_entry), test); - } - - /* - * if the node name has changed, delete the node, - * and possibly restore a symlink of another device - */ - devnode_old = udev_device_get_devnode(dev_old); - if (devnode_old != NULL) { - const char *devnode = udev_device_get_devnode(dev); - - if (devnode != NULL && strcmp(devnode_old, devnode) != 0) - update_link(dev, devnode_old, test); - } -} - -int udev_node_add(struct udev_device *dev, mode_t mode, const char *owner, const char *group, int test) -{ - struct udev *udev = udev_device_get_udev(dev); - uid_t uid; - gid_t gid; - int i; - int num; - struct udev_list_entry *list_entry; - int err = 0; - - create_path(udev, udev_device_get_devnode(dev)); - - if (strcmp(owner, "root") == 0) - uid = 0; - else { - char *endptr; - unsigned long id; - - id = strtoul(owner, &endptr, 10); - if (endptr[0] == '\0') - uid = (uid_t) id; - else - uid = lookup_user(udev, owner); - } - - if (strcmp(group, "root") == 0) - gid = 0; - else { - char *endptr; - unsigned long id; - - id = strtoul(group, &endptr, 10); - if (endptr[0] == '\0') - gid = (gid_t) id; - else - gid = lookup_group(udev, group); - } - - info(udev, "creating device node '%s', devnum=%d:%d, mode=%#o, uid=%d, gid=%d\n", - udev_device_get_devnode(dev), - major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)), - mode, uid, gid); - - if (!test) - if (udev_node_mknod(dev, NULL, makedev(0,0), mode, uid, gid) != 0) { - err = -1; - goto exit; - } - - /* create all_partitions if requested */ - num = udev_device_get_num_fake_partitions(dev); - if (num > 0) { - info(udev, "creating device partition nodes '%s[1-%i]'\n", udev_device_get_devnode(dev), num); - if (!test) { - for (i = 1; i <= num; i++) { - char partitionname[UTIL_PATH_SIZE]; - dev_t part_devnum; - - snprintf(partitionname, sizeof(partitionname), "%s%d", - udev_device_get_devnode(dev), i); - partitionname[sizeof(partitionname)-1] = '\0'; - part_devnum = makedev(major(udev_device_get_devnum(dev)), - minor(udev_device_get_devnum(dev)) + i); - udev_node_mknod(dev, partitionname, part_devnum, mode, uid, gid); - } - } - } - - /* add node and to name index */ - name_index(udev, udev_device_get_devpath(dev), udev_device_get_devnode(dev), 1, test); - - /* create/update symlinks, add symlinks to name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { - name_index(udev, udev_device_get_devpath(dev), udev_list_entry_get_name(list_entry), 1, test); - update_link(dev, udev_list_entry_get_name(list_entry), test); - } -exit: - return err; -} - -extern int udev_node_remove(struct udev_device *dev, int test) -{ - struct udev *udev = udev_device_get_udev(dev); - struct udev_list_entry *list_entry; - const char *devnode; - char partitionname[UTIL_PATH_SIZE]; - struct stat stats; - int err = 0; - int num; - - /* remove node from name index */ - name_index(udev, udev_device_get_devpath(dev), udev_device_get_devnode(dev), 0, test); - - /* remove,update symlinks, remove symlinks from name index */ - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { - name_index(udev, udev_device_get_devpath(dev), udev_list_entry_get_name(list_entry), 0, test); - update_link(dev, udev_list_entry_get_name(list_entry), test); - } - - devnode = udev_device_get_devnode(dev); - if (devnode == NULL) - return 0; - if (stat(devnode, &stats) != 0) { - info(udev, "device node '%s' not found\n", devnode); - return 0; - } - if (stats.st_rdev != udev_device_get_devnum(dev)) { - info(udev, "device node '%s' points to a different device, skip removal\n", devnode); - return -1; - } - - info(udev, "removing device node '%s'\n", devnode); - if (!test) - err = unlink_secure(udev, devnode); - if (err) - return err; - - num = udev_device_get_num_fake_partitions(dev); - if (num > 0) { - int i; - - info(udev, "removing all_partitions '%s[1-%i]'\n", devnode, num); - if (num > 255) - return -1; - for (i = 1; i <= num; i++) { - snprintf(partitionname, sizeof(partitionname), "%s%d", devnode, i); - partitionname[sizeof(partitionname)-1] = '\0'; - if (!test) - unlink_secure(udev, partitionname); - } - } - delete_path(udev, devnode); - return err; -} diff --git a/udev/udev_rules.c b/udev/udev_rules.c deleted file mode 100644 index dfcd87539c..0000000000 --- a/udev/udev_rules.c +++ /dev/null @@ -1,1634 +0,0 @@ -/* - * Copyright (C) 2003 Greg Kroah-Hartman - * Copyright (C) 2003-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" - -extern char **environ; - -/* extract possible {attr} and move str behind it */ -static char *get_format_attribute(struct udev *udev, char **str) -{ - char *pos; - char *attr = NULL; - - if (*str[0] == '{') { - pos = strchr(*str, '}'); - if (pos == NULL) { - err(udev, "missing closing brace for format\n"); - return NULL; - } - pos[0] = '\0'; - attr = *str+1; - *str = pos+1; - dbg(udev, "attribute='%s', str='%s'\n", attr, *str); - } - return attr; -} - -/* extract possible format length and move str behind it*/ -static int get_format_len(struct udev *udev, char **str) -{ - int num; - char *tail; - - if (isdigit(*str[0])) { - num = (int) strtoul(*str, &tail, 10); - if (num > 0) { - *str = tail; - dbg(udev, "format length=%i\n", num); - return num; - } else { - err(udev, "format parsing error '%s'\n", *str); - } - } - return -1; -} - -static int get_key(char **line, char **key, char **value) -{ - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL) - return -1; - - /* skip whitespace */ - while (isspace(linepos[0])) - linepos++; - - /* get the key */ - temp = strchr(linepos, '='); - if (temp == NULL || temp == linepos) - return -1; - temp[0] = '\0'; - *key = linepos; - linepos = &temp[1]; - - /* get a quoted value */ - if (linepos[0] == '"' || linepos[0] == '\'') { - temp = strchr(&linepos[1], linepos[0]); - if (temp != NULL) { - temp[0] = '\0'; - *value = &linepos[1]; - goto out; - } - } - - /* get the value*/ - temp = strchr(linepos, '\n'); - if (temp != NULL) - temp[0] = '\0'; - *value = linepos; -out: - return 0; -} - -static int run_program(struct udev_device *dev, const char *command, - char *result, size_t ressize, size_t *reslen) -{ - struct udev *udev = udev_device_get_udev(dev); - int status; - char **envp; - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; - pid_t pid; - char arg[UTIL_PATH_SIZE]; - char program[UTIL_PATH_SIZE]; - char *argv[(sizeof(arg) / 2) + 1]; - int devnull; - int i; - int err = 0; - - /* build argv from command */ - util_strlcpy(arg, command, sizeof(arg)); - i = 0; - if (strchr(arg, ' ') != NULL) { - char *pos = arg; - - while (pos != NULL) { - if (pos[0] == '\'') { - /* don't separate if in apostrophes */ - pos++; - argv[i] = strsep(&pos, "\'"); - while (pos != NULL && pos[0] == ' ') - pos++; - } else { - argv[i] = strsep(&pos, " "); - } - dbg(udev, "arg[%i] '%s'\n", i, argv[i]); - i++; - } - argv[i] = NULL; - } else { - argv[0] = arg; - argv[1] = NULL; - } - info(udev, "'%s'\n", command); - - /* prepare pipes from child to parent */ - if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { - if (pipe(outpipe) != 0) { - err(udev, "pipe failed: %m\n"); - return -1; - } - } - if (udev_get_log_priority(udev) >= LOG_INFO) { - if (pipe(errpipe) != 0) { - err(udev, "pipe failed: %m\n"); - return -1; - } - } - - /* allow programs in /lib/udev/ to be called without the path */ - if (strchr(argv[0], '/') == NULL) { - util_strlcpy(program, UDEV_PREFIX "/lib/udev/", sizeof(program)); - util_strlcat(program, argv[0], sizeof(program)); - argv[0] = program; - } - - envp = udev_device_get_properties_envp(dev); - - pid = fork(); - switch(pid) { - case 0: - /* child closes parent ends of pipes */ - if (outpipe[READ_END] > 0) - close(outpipe[READ_END]); - if (errpipe[READ_END] > 0) - close(errpipe[READ_END]); - - /* discard child output or connect to pipe */ - devnull = open("/dev/null", O_RDWR); - if (devnull > 0) { - dup2(devnull, STDIN_FILENO); - if (outpipe[WRITE_END] < 0) - dup2(devnull, STDOUT_FILENO); - if (errpipe[WRITE_END] < 0) - dup2(devnull, STDERR_FILENO); - close(devnull); - } else - err(udev, "open /dev/null failed: %m\n"); - if (outpipe[WRITE_END] > 0) { - dup2(outpipe[WRITE_END], STDOUT_FILENO); - close(outpipe[WRITE_END]); - } - if (errpipe[WRITE_END] > 0) { - dup2(errpipe[WRITE_END], STDERR_FILENO); - close(errpipe[WRITE_END]); - } - execve(argv[0], argv, envp); - if (errno == ENOENT || errno == ENOTDIR) { - /* may be on a filesytem which is not mounted right now */ - info(udev, "program '%s' not found\n", argv[0]); - } else { - /* other problems */ - err(udev, "exec of program '%s' failed\n", argv[0]); - } - _exit(1); - case -1: - err(udev, "fork of '%s' failed: %m\n", argv[0]); - return -1; - default: - /* read from child if requested */ - if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { - ssize_t count; - size_t respos = 0; - - /* parent closes child ends of pipes */ - if (outpipe[WRITE_END] > 0) - close(outpipe[WRITE_END]); - if (errpipe[WRITE_END] > 0) - close(errpipe[WRITE_END]); - - /* read child output */ - while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { - int fdcount; - fd_set readfds; - - FD_ZERO(&readfds); - if (outpipe[READ_END] > 0) - FD_SET(outpipe[READ_END], &readfds); - if (errpipe[READ_END] > 0) - FD_SET(errpipe[READ_END], &readfds); - fdcount = select(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL); - if (fdcount < 0) { - if (errno == EINTR) - continue; - err = -1; - break; - } - - /* get stdout */ - if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) { - char inbuf[1024]; - char *pos; - char *line; - - count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1); - if (count <= 0) { - close(outpipe[READ_END]); - outpipe[READ_END] = -1; - if (count < 0) { - err(udev, "stdin read failed: %m\n"); - err = -1; - } - continue; - } - inbuf[count] = '\0'; - - /* store result for rule processing */ - if (result) { - if (respos + count < ressize) { - memcpy(&result[respos], inbuf, count); - respos += count; - } else { - err(udev, "ressize %ld too short\n", (long)ressize); - err = -1; - } - } - pos = inbuf; - while ((line = strsep(&pos, "\n"))) - if (pos || line[0] != '\0') - info(udev, "'%s' (stdout) '%s'\n", argv[0], line); - } - - /* get stderr */ - if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) { - char errbuf[1024]; - char *pos; - char *line; - - count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1); - if (count <= 0) { - close(errpipe[READ_END]); - errpipe[READ_END] = -1; - if (count < 0) - err(udev, "stderr read failed: %m\n"); - continue; - } - errbuf[count] = '\0'; - pos = errbuf; - while ((line = strsep(&pos, "\n"))) - if (pos || line[0] != '\0') - info(udev, "'%s' (stderr) '%s'\n", argv[0], line); - } - } - if (outpipe[READ_END] > 0) - close(outpipe[READ_END]); - if (errpipe[READ_END] > 0) - close(errpipe[READ_END]); - - /* return the childs stdout string */ - if (result) { - result[respos] = '\0'; - dbg(udev, "result='%s'\n", result); - if (reslen) - *reslen = respos; - } - } - waitpid(pid, &status, 0); - if (WIFEXITED(status)) { - info(udev, "'%s' returned with status %i\n", argv[0], WEXITSTATUS(status)); - if (WEXITSTATUS(status) != 0) - err = -1; - } else { - err(udev, "'%s' abnormal exit\n", argv[0]); - err = -1; - } - } - - return err; -} - -static int import_keys_into_env(struct udev_event *event, const char *buf, size_t bufsize) -{ - struct udev_device *dev = event->dev; - char line[UTIL_LINE_SIZE]; - const char *bufline; - char *linepos; - char *variable; - char *value; - size_t cur; - size_t count; - int lineno; - - /* loop through the whole buffer */ - lineno = 0; - cur = 0; - while (cur < bufsize) { - count = buf_get_line(buf, bufsize, cur); - bufline = &buf[cur]; - cur += count+1; - lineno++; - - /* eat the whitespace */ - while ((count > 0) && isspace(bufline[0])) { - bufline++; - count--; - } - if (count == 0) - continue; - - /* see if this is a comment */ - if (bufline[0] == '#') - continue; - - if (count >= sizeof(line)) { - err(event->udev, "line too long, skipped\n"); - continue; - } - - memcpy(line, bufline, count); - line[count] = '\0'; - - linepos = line; - if (get_key(&linepos, &variable, &value) == 0) { - char syspath[UTIL_PATH_SIZE]; - - dbg(event->udev, "import '%s=%s'\n", variable, value); - /* handle device, renamed by external tool, returning new path */ - if (strcmp(variable, "DEVPATH") == 0) { - info(event->udev, "updating devpath from '%s' to '%s'\n", - udev_device_get_devpath(dev), value); - util_strlcpy(syspath, udev_get_sys_path(event->udev), sizeof(syspath)); - util_strlcat(syspath, value, sizeof(syspath)); - udev_device_set_syspath(dev, syspath); - } else { - struct udev_list_entry *entry; - - entry = udev_device_add_property(dev, variable, value); - /* store in db */ - udev_list_entry_set_flag(entry, 1); - } - } - } - return 0; -} - -static int import_file_into_env(struct udev_event *event, const char *filename) -{ - char *buf; - size_t bufsize; - - if (file_map(filename, &buf, &bufsize) != 0) { - err(event->udev, "can't open '%s': %m\n", filename); - return -1; - } - import_keys_into_env(event, buf, bufsize); - file_unmap(buf, bufsize); - - return 0; -} - -static int import_program_into_env(struct udev_event *event, const char *program) -{ - char result[2048]; - size_t reslen; - - if (run_program(event->dev, program, result, sizeof(result), &reslen) != 0) - return -1; - return import_keys_into_env(event, result, reslen); -} - -static int import_parent_into_env(struct udev_event *event, const char *filter) -{ - struct udev_device *dev_parent; - struct udev_list_entry *list_entry; - - dev_parent = udev_device_get_parent(event->dev); - if (dev_parent == NULL) - return -1; - - dbg(event->udev, "found parent '%s', get the node name\n", udev_device_get_syspath(dev_parent)); - udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev_parent)) { - const char *key = udev_list_entry_get_name(list_entry); - const char *val = udev_list_entry_get_value(list_entry); - - if (fnmatch(filter, key, 0) == 0) { - struct udev_list_entry *entry; - - dbg(event->udev, "import key '%s=%s'\n", key, val); - entry = udev_device_add_property(event->dev, key, val); - /* store in db */ - udev_list_entry_set_flag(entry, 1); - } - } - return 0; -} - -int udev_rules_run(struct udev_event *event) -{ - struct udev_list_entry *list_entry; - int err = 0; - - dbg(event->udev, "executing run list\n"); - udev_list_entry_foreach(list_entry, udev_list_get_entry(&event->run_list)) { - const char *cmd = udev_list_entry_get_name(list_entry); - - if (strncmp(cmd, "socket:", strlen("socket:")) == 0) { - struct udev_monitor *monitor; - - monitor = udev_monitor_new_from_socket(event->udev, &cmd[strlen("socket:")]); - if (monitor == NULL) - continue; - udev_monitor_send_device(monitor, event->dev); - udev_monitor_unref(monitor); - } else { - char program[UTIL_PATH_SIZE]; - - util_strlcpy(program, cmd, sizeof(program)); - udev_rules_apply_format(event, program, sizeof(program)); - if (run_program(event->dev, program, NULL, 0, NULL) != 0) { - if (!udev_list_entry_get_flag(list_entry)) - err = -1; - } - } - } - return err; -} - -#define WAIT_LOOP_PER_SECOND 50 -static int wait_for_file(struct udev_event *event, const char *file, int timeout) -{ - char filepath[UTIL_PATH_SIZE]; - char devicepath[UTIL_PATH_SIZE] = ""; - struct stat stats; - int loop = timeout * WAIT_LOOP_PER_SECOND; - - /* a relative path is a device attribute */ - if (file[0] != '/') { - util_strlcpy(devicepath, udev_get_sys_path(event->udev), sizeof(devicepath)); - util_strlcat(devicepath, udev_device_get_devpath(event->dev), sizeof(devicepath)); - - util_strlcpy(filepath, devicepath, sizeof(filepath)); - util_strlcat(filepath, "/", sizeof(filepath)); - util_strlcat(filepath, file, sizeof(filepath)); - file = filepath; - } - - dbg(event->udev, "will wait %i sec for '%s'\n", timeout, file); - while (--loop) { - /* lookup file */ - if (stat(file, &stats) == 0) { - info(event->udev, "file '%s' appeared after %i loops\n", file, (timeout * WAIT_LOOP_PER_SECOND) - loop-1); - return 0; - } - /* make sure, the device did not disappear in the meantime */ - if (devicepath[0] != '\0' && stat(devicepath, &stats) != 0) { - info(event->udev, "device disappeared while waiting for '%s'\n", file); - return -2; - } - info(event->udev, "wait for '%s' for %i mseconds\n", file, 1000 / WAIT_LOOP_PER_SECOND); - usleep(1000 * 1000 / WAIT_LOOP_PER_SECOND); - } - info(event->udev, "waiting for '%s' failed\n", file); - return -1; -} - -/* handle "[$SUBSYSTEM/$KERNEL]" lookup */ -static int split_subsys_sysname(struct udev *udev, char *attrstr, char **subsys, char **sysname, char **attr) -{ - char *pos; - - if (attrstr[0] != '[') - return -1; - - *subsys = &attrstr[1]; - pos = strchr(*subsys, ']'); - if (pos == NULL) - return -1; - pos[0] = '\0'; - pos = &pos[1]; - - if (pos[0] == '/') - pos = &pos[1]; - if (pos[0] != '\0') - *attr = pos; - else - *attr = NULL; - - pos = strchr(*subsys, '/'); - if (pos == NULL) - return -1; - pos[0] = '\0'; - *sysname = &pos[1]; - return 0; -} - -static int attr_subst_subdir(char *attr, size_t len) -{ - char *pos; - int found = 0; - - pos = strstr(attr, "/*/"); - if (pos != NULL) { - char str[UTIL_PATH_SIZE]; - DIR *dir; - - pos[1] = '\0'; - util_strlcpy(str, &pos[2], sizeof(str)); - dir = opendir(attr); - if (dir != NULL) { - struct dirent *dent; - - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; - - if (dent->d_name[0] == '.') - continue; - util_strlcat(attr, dent->d_name, len); - util_strlcat(attr, str, len); - if (stat(attr, &stats) == 0) { - found = 1; - break; - } - pos[1] = '\0'; - } - closedir(dir); - } - if (!found) - util_strlcat(attr, str, len); - } - - return found; -} - -void udev_rules_apply_format(struct udev_event *event, char *string, size_t maxsize) -{ - struct udev_device *dev = event->dev; - char temp[UTIL_PATH_SIZE]; - char temp2[UTIL_PATH_SIZE]; - char *head, *tail, *cpos, *attr, *rest; - int len; - int i; - int count; - enum subst_type { - SUBST_UNKNOWN, - SUBST_DEVPATH, - SUBST_KERNEL, - SUBST_KERNEL_NUMBER, - SUBST_ID, - SUBST_DRIVER, - SUBST_MAJOR, - SUBST_MINOR, - SUBST_RESULT, - SUBST_ATTR, - SUBST_PARENT, - SUBST_TEMP_NODE, - SUBST_NAME, - SUBST_LINKS, - SUBST_ROOT, - SUBST_SYS, - SUBST_ENV, - }; - static const struct subst_map { - char *name; - char fmt; - enum subst_type type; - } map[] = { - { .name = "devpath", .fmt = 'p', .type = SUBST_DEVPATH }, - { .name = "number", .fmt = 'n', .type = SUBST_KERNEL_NUMBER }, - { .name = "kernel", .fmt = 'k', .type = SUBST_KERNEL }, - { .name = "id", .fmt = 'b', .type = SUBST_ID }, - { .name = "driver", .fmt = 'd', .type = SUBST_DRIVER }, - { .name = "major", .fmt = 'M', .type = SUBST_MAJOR }, - { .name = "minor", .fmt = 'm', .type = SUBST_MINOR }, - { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, - { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, - { .name = "sysfs", .fmt = 's', .type = SUBST_ATTR }, - { .name = "parent", .fmt = 'P', .type = SUBST_PARENT }, - { .name = "tempnode", .fmt = 'N', .type = SUBST_TEMP_NODE }, - { .name = "name", .fmt = 'D', .type = SUBST_NAME }, - { .name = "links", .fmt = 'L', .type = SUBST_LINKS }, - { .name = "root", .fmt = 'r', .type = SUBST_ROOT }, - { .name = "sys", .fmt = 'S', .type = SUBST_SYS }, - { .name = "env", .fmt = 'E', .type = SUBST_ENV }, - { NULL, '\0', 0 } - }; - enum subst_type type; - const struct subst_map *subst; - - head = string; - while (1) { - len = -1; - while (head[0] != '\0') { - if (head[0] == '$') { - /* substitute named variable */ - if (head[1] == '\0') - break; - if (head[1] == '$') { - util_strlcpy(temp, head+2, sizeof(temp)); - util_strlcpy(head+1, temp, maxsize); - head++; - continue; - } - head[0] = '\0'; - for (subst = map; subst->name; subst++) { - if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { - type = subst->type; - tail = head + strlen(subst->name)+1; - dbg(event->udev, "will substitute format name '%s'\n", subst->name); - goto found; - } - } - head[0] = '$'; - err(event->udev, "unknown format variable '%s'\n", head); - } else if (head[0] == '%') { - /* substitute format char */ - if (head[1] == '\0') - break; - if (head[1] == '%') { - util_strlcpy(temp, head+2, sizeof(temp)); - util_strlcpy(head+1, temp, maxsize); - head++; - continue; - } - head[0] = '\0'; - tail = head+1; - len = get_format_len(event->udev, &tail); - for (subst = map; subst->name; subst++) { - if (tail[0] == subst->fmt) { - type = subst->type; - tail++; - dbg(event->udev, "will substitute format char '%c'\n", subst->fmt); - goto found; - } - } - head[0] = '%'; - err(event->udev, "unknown format char '%c'\n", tail[0]); - } - head++; - } - break; -found: - attr = get_format_attribute(event->udev, &tail); - util_strlcpy(temp, tail, sizeof(temp)); - dbg(event->udev, "format=%i, string='%s', tail='%s'\n", type ,string, tail); - - switch (type) { - case SUBST_DEVPATH: - util_strlcat(string, udev_device_get_devpath(dev), maxsize); - dbg(event->udev, "substitute devpath '%s'\n", udev_device_get_devpath(dev)); - break; - case SUBST_KERNEL: - util_strlcat(string, udev_device_get_sysname(dev), maxsize); - dbg(event->udev, "substitute kernel name '%s'\n", udev_device_get_sysname(dev)); - break; - case SUBST_KERNEL_NUMBER: - if (udev_device_get_sysnum(dev) == NULL) - break; - util_strlcat(string, udev_device_get_sysnum(dev), maxsize); - dbg(event->udev, "substitute kernel number '%s'\n", udev_device_get_sysnum(dev)); - break; - case SUBST_ID: - if (event->dev_parent != NULL) { - util_strlcat(string, udev_device_get_sysname(event->dev_parent), maxsize); - dbg(event->udev, "substitute id '%s'\n", udev_device_get_sysname(event->dev_parent)); - } - break; - case SUBST_DRIVER: - if (event->dev_parent != NULL) { - const char *driver = udev_device_get_driver(event->dev_parent); - - if (driver == NULL) - break; - util_strlcat(string, driver, maxsize); - dbg(event->udev, "substitute driver '%s'\n", driver); - } - break; - case SUBST_MAJOR: - sprintf(temp2, "%d", major(udev_device_get_devnum(dev))); - util_strlcat(string, temp2, maxsize); - dbg(event->udev, "substitute major number '%s'\n", temp2); - break; - case SUBST_MINOR: - sprintf(temp2, "%d", minor(udev_device_get_devnum(dev))); - util_strlcat(string, temp2, maxsize); - dbg(event->udev, "substitute minor number '%s'\n", temp2); - break; - case SUBST_RESULT: - if (event->program_result[0] == '\0') - break; - /* get part part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - dbg(event->udev, "request part #%d of result string\n", i); - cpos = event->program_result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - } - if (i > 0) { - err(event->udev, "requested part of result string not found\n"); - break; - } - util_strlcpy(temp2, cpos, sizeof(temp2)); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(temp2, ' '); - if (cpos) - cpos[0] = '\0'; - } - util_strlcat(string, temp2, maxsize); - dbg(event->udev, "substitute part of result string '%s'\n", temp2); - } else { - util_strlcat(string, event->program_result, maxsize); - dbg(event->udev, "substitute result string '%s'\n", event->program_result); - } - break; - case SUBST_ATTR: - if (attr == NULL) - err(event->udev, "missing file parameter for attr\n"); - else { - char *subsys; - char *sysname; - char *attrib; - char value[UTIL_NAME_SIZE] = ""; - size_t size; - - if (split_subsys_sysname(event->udev, attr, &subsys, &sysname, &attrib) == 0) { - struct udev_device *d; - const char *val; - - if (attrib == NULL) - break; - d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); - if (d == NULL) - break; - val = udev_device_get_attr_value(d, attrib); - if (val != NULL) - util_strlcpy(value, val, sizeof(value)); - udev_device_unref(d); - } - - /* try the current device, other matches may have selected */ - if (value[0]=='\0' && event->dev_parent != NULL && event->dev_parent != event->dev) { - const char *val; - - val = udev_device_get_attr_value(event->dev_parent, attr); - if (val != NULL) - util_strlcpy(value, val, sizeof(value)); - } - - /* look at all devices along the chain of parents */ - if (value[0]=='\0') { - struct udev_device *dev_parent = dev; - const char *val; - - do { - dbg(event->udev, "looking at '%s'\n", udev_device_get_syspath(dev_parent)); - val = udev_device_get_attr_value(dev_parent, attr); - if (val != NULL) { - util_strlcpy(value, val, sizeof(value)); - break; - } - dev_parent = udev_device_get_parent(dev_parent); - } while (dev_parent != NULL); - } - - if (value[0]=='\0') - break; - - /* strip trailing whitespace, and replace unwanted characters */ - size = strlen(value); - while (size > 0 && isspace(value[--size])) - value[size] = '\0'; - count = util_replace_chars(value, ALLOWED_CHARS_INPUT); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - util_strlcat(string, value, maxsize); - dbg(event->udev, "substitute sysfs value '%s'\n", value); - } - break; - case SUBST_PARENT: - { - struct udev_device *dev_parent; - const char *devnode; - - dev_parent = udev_device_get_parent(event->dev); - if (dev_parent == NULL) - break; - devnode = udev_device_get_devnode(dev_parent); - if (devnode != NULL) { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - - util_strlcat(string, &devnode[devlen], maxsize); - dbg(event->udev, "found parent '%s', got node name '%s'\n", - udev_device_get_syspath(dev_parent), &devnode[devlen]); - } - } - break; - case SUBST_TEMP_NODE: - if (event->tmp_node[0] == '\0' && major(udev_device_get_devnum(dev)) > 0) { - dbg(event->udev, "create temporary device node for callout\n"); - snprintf(event->tmp_node, sizeof(event->tmp_node), "%s/.tmp-%u-%u", - udev_get_dev_path(event->udev), - major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev))); - udev_node_mknod(dev, event->tmp_node, makedev(0,0), 0600, 0, 0); - } - util_strlcat(string, event->tmp_node, maxsize); - dbg(event->udev, "substitute temporary device node name '%s'\n", event->tmp_node); - break; - case SUBST_NAME: - if (event->name != NULL) { - util_strlcat(string, event->name, maxsize); - dbg(event->udev, "substitute name '%s'\n", event->name); - } else { - util_strlcat(string, udev_device_get_sysname(dev), maxsize); - dbg(event->udev, "substitute sysname '%s'\n", udev_device_get_sysname(dev)); - } - break; - case SUBST_LINKS: - { - struct udev_list_entry *list_entry; - - list_entry = udev_device_get_properties_list_entry(dev); - util_strlcpy(string, udev_list_entry_get_name(list_entry), maxsize); - udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) { - util_strlcat(string, " ", maxsize); - util_strlcat(string, udev_list_entry_get_name(list_entry), maxsize); - } - } - break; - case SUBST_ROOT: - util_strlcat(string, udev_get_dev_path(event->udev), maxsize); - dbg(event->udev, "substitute udev_root '%s'\n", udev_get_dev_path(event->udev)); - break; - case SUBST_SYS: - util_strlcat(string, udev_get_sys_path(event->udev), maxsize); - dbg(event->udev, "substitute sys_path '%s'\n", udev_get_sys_path(event->udev)); - break; - case SUBST_ENV: - if (attr == NULL) { - dbg(event->udev, "missing attribute\n"); - break; - } else { - struct udev_list_entry *list_entry; - const char *value; - - list_entry = udev_device_get_properties_list_entry(event->dev); - list_entry = udev_list_entry_get_by_name(list_entry, attr); - if (list_entry == NULL) - break; - value = udev_list_entry_get_value(list_entry); - dbg(event->udev, "substitute env '%s=%s'\n", attr, value); - util_strlcat(string, value, maxsize); - break; - } - default: - err(event->udev, "unknown substitution type=%i\n", type); - break; - } - /* possibly truncate to format-char specified length */ - if (len >= 0 && len < (int)strlen(head)) { - head[len] = '\0'; - dbg(event->udev, "truncate to %i chars, subtitution string becomes '%s'\n", len, head); - } - util_strlcat(string, temp, maxsize); - } -} - -static char *key_val(struct udev_rule *rule, struct key *key) -{ - return rule->buf + key->val_off; -} - -static char *key_pair_name(struct udev_rule *rule, struct key_pair *pair) -{ - return rule->buf + pair->key_name_off; -} - -static int match_key(struct udev *udev, const char *key_name, struct udev_rule *rule, struct key *key, const char *val) -{ - char value[UTIL_PATH_SIZE]; - char *key_value; - char *pos; - int match = 0; - - if (key->operation != KEY_OP_MATCH && - key->operation != KEY_OP_NOMATCH) - return 0; - - if (val == NULL) - val = ""; - - /* look for a matching string, parts are separated by '|' */ - util_strlcpy(value, rule->buf + key->val_off, sizeof(value)); - key_value = value; - dbg(udev, "key %s value='%s'\n", key_name, key_value); - while (key_value != NULL) { - pos = strchr(key_value, '|'); - if (pos != NULL) { - pos[0] = '\0'; - pos = &pos[1]; - } - - dbg(udev, "match %s '%s' <-> '%s'\n", key_name, key_value, val); - match = (fnmatch(key_value, val, 0) == 0); - if (match) - break; - - key_value = pos; - } - - if (match && (key->operation == KEY_OP_MATCH)) { - dbg(udev, "%s is true (matching value)\n", key_name); - return 0; - } - if (!match && (key->operation == KEY_OP_NOMATCH)) { - dbg(udev, "%s is true (non-matching value)\n", key_name); - return 0; - } - return -1; -} - -/* match a single rule against a given device and possibly its parent devices */ -static int match_rule(struct udev_event *event, struct udev_rule *rule) -{ - struct udev_device *dev = event->dev; - int i; - - if (match_key(event->udev, "ACTION", rule, &rule->action, udev_device_get_action(dev))) - goto nomatch; - - if (match_key(event->udev, "KERNEL", rule, &rule->kernel, udev_device_get_sysname(dev))) - goto nomatch; - - if (match_key(event->udev, "SUBSYSTEM", rule, &rule->subsystem, udev_device_get_subsystem(dev))) - goto nomatch; - - if (match_key(event->udev, "DEVPATH", rule, &rule->devpath, udev_device_get_devpath(dev))) - goto nomatch; - - if (match_key(event->udev, "DRIVER", rule, &rule->driver, udev_device_get_driver(dev))) - goto nomatch; - - /* match NAME against a value assigned by an earlier rule */ - if (match_key(event->udev, "NAME", rule, &rule->name, event->name)) - goto nomatch; - - /* match against current list of symlinks */ - if (rule->symlink_match.operation == KEY_OP_MATCH || - rule->symlink_match.operation == KEY_OP_NOMATCH) { - size_t devlen = strlen(udev_get_dev_path(event->udev))+1; - struct udev_list_entry *list_entry; - int match = 0; - - udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { - const char *devlink; - - devlink = &udev_list_entry_get_name(list_entry)[devlen]; - if (match_key(event->udev, "SYMLINK", rule, &rule->symlink_match, devlink) == 0) { - match = 1; - break; - } - } - if (!match) - goto nomatch; - } - - for (i = 0; i < rule->env.count; i++) { - struct key_pair *pair = &rule->env.keys[i]; - - /* we only check for matches, assignments will be handled later */ - if (pair->key.operation == KEY_OP_MATCH || - pair->key.operation == KEY_OP_NOMATCH) { - struct udev_list_entry *list_entry; - const char *key_name = key_pair_name(rule, pair); - const char *value; - - list_entry = udev_device_get_properties_list_entry(event->dev); - list_entry = udev_list_entry_get_by_name(list_entry, key_name); - value = udev_list_entry_get_value(list_entry); - if (value == NULL) { - dbg(event->udev, "ENV{%s} is not set, treat as empty\n", key_name); - value = ""; - } - if (match_key(event->udev, "ENV", rule, &pair->key, value)) - goto nomatch; - } - } - - if (rule->test.operation == KEY_OP_MATCH || - rule->test.operation == KEY_OP_NOMATCH) { - char filename[UTIL_PATH_SIZE]; - char *subsys; - char *sysname; - char *attrib; - struct stat statbuf; - int match; - - util_strlcpy(filename, key_val(rule, &rule->test), sizeof(filename)); - udev_rules_apply_format(event, filename, sizeof(filename)); - - if (split_subsys_sysname(event->udev, filename, &subsys, &sysname, &attrib) == 0) { - struct udev_device *d; - d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); - if (d != NULL) { - util_strlcpy(filename, udev_device_get_syspath(d), sizeof(filename)); - if (attrib != NULL) { - util_strlcat(filename, "/", sizeof(filename)); - util_strlcat(filename, attrib, sizeof(filename)); - } - udev_device_unref(d); - } - } else if (filename[0] != '/') { - char tmp[UTIL_PATH_SIZE]; - - util_strlcpy(tmp, udev_device_get_syspath(dev), sizeof(tmp)); - util_strlcat(tmp, "/", sizeof(tmp)); - util_strlcat(tmp, filename, sizeof(tmp)); - util_strlcpy(filename, tmp, sizeof(filename)); - } - - attr_subst_subdir(filename, sizeof(filename)); - - match = (stat(filename, &statbuf) == 0); - info(event->udev, "'%s' %s", filename, match ? "exists\n" : "does not exist\n"); - if (match && rule->test_mode_mask > 0) { - match = ((statbuf.st_mode & rule->test_mode_mask) > 0); - info(event->udev, "'%s' has mode=%#o and %s %#o\n", filename, statbuf.st_mode, - match ? "matches" : "does not match", - rule->test_mode_mask); - } - if (match && rule->test.operation == KEY_OP_NOMATCH) - goto nomatch; - if (!match && rule->test.operation == KEY_OP_MATCH) - goto nomatch; - dbg(event->udev, "TEST key is true\n"); - } - - if (rule->wait_for.operation != KEY_OP_UNSET) { - char filename[UTIL_PATH_SIZE]; - int found; - - util_strlcpy(filename, key_val(rule, &rule->wait_for), sizeof(filename)); - udev_rules_apply_format(event, filename, sizeof(filename)); - found = (wait_for_file(event, filename, 10) == 0); - if (!found && (rule->wait_for.operation != KEY_OP_NOMATCH)) - goto nomatch; - } - - /* check for matching sysfs attribute pairs */ - for (i = 0; i < rule->attr.count; i++) { - struct key_pair *pair = &rule->attr.keys[i]; - - if (pair->key.operation == KEY_OP_MATCH || - pair->key.operation == KEY_OP_NOMATCH) { - char attr[UTIL_PATH_SIZE]; - const char *key_name = key_pair_name(rule, pair); - const char *key_value = key_val(rule, &pair->key); - char *subsys; - char *sysname; - char *attrib; - char value[UTIL_NAME_SIZE] = ""; - size_t len; - - util_strlcpy(attr, key_name, sizeof(attr)); - if (split_subsys_sysname(event->udev, attr, &subsys, &sysname, &attrib) == 0) { - struct udev_device *d; - const char *val; - - if (attrib == NULL) - goto nomatch; - d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); - if (d == NULL) - goto nomatch; - val = udev_device_get_attr_value(d, attrib); - if (val != NULL) - util_strlcpy(value, val, sizeof(value)); - udev_device_unref(d); - } - - if (value[0]=='\0') { - const char *val; - - val = udev_device_get_attr_value(dev, key_name); - if (val != NULL) - util_strlcpy(value, val, sizeof(value)); - } - - if (value[0]=='\0') - goto nomatch; - - /* strip trailing whitespace of value, if not asked to match for it */ - len = strlen(key_value); - if (len > 0 && !isspace(key_value[len-1])) { - len = strlen(value); - while (len > 0 && isspace(value[--len])) - value[len] = '\0'; - dbg(event->udev, "removed trailing whitespace from '%s'\n", value); - } - - if (match_key(event->udev, "ATTR", rule, &pair->key, value)) - goto nomatch; - } - } - - /* walk up the chain of parent devices and find a match */ - event->dev_parent = dev; - while (1) { - /* check for matching kernel device name */ - if (match_key(event->udev, "KERNELS", rule, - &rule->kernels, udev_device_get_sysname(event->dev_parent))) - goto try_parent; - - /* check for matching subsystem value */ - if (match_key(event->udev, "SUBSYSTEMS", rule, - &rule->subsystems, udev_device_get_subsystem(event->dev_parent))) - goto try_parent; - - /* check for matching driver */ - if (match_key(event->udev, "DRIVERS", rule, - &rule->drivers, udev_device_get_driver(event->dev_parent))) - goto try_parent; - - /* check for matching sysfs attribute pairs */ - for (i = 0; i < rule->attrs.count; i++) { - struct key_pair *pair = &rule->attrs.keys[i]; - - if (pair->key.operation == KEY_OP_MATCH || - pair->key.operation == KEY_OP_NOMATCH) { - const char *key_name = key_pair_name(rule, pair); - const char *key_value = key_val(rule, &pair->key); - const char *val; - char value[UTIL_NAME_SIZE]; - size_t len; - - val = udev_device_get_attr_value(event->dev_parent, key_name); - if (val == NULL) - val = udev_device_get_attr_value(dev, key_name); - if (val == NULL) - goto try_parent; - util_strlcpy(value, val, sizeof(value)); - - /* strip trailing whitespace of value, if not asked to match for it */ - len = strlen(key_value); - if (len > 0 && !isspace(key_value[len-1])) { - len = strlen(value); - while (len > 0 && isspace(value[--len])) - value[len] = '\0'; - dbg(event->udev, "removed trailing whitespace from '%s'\n", value); - } - - if (match_key(event->udev, "ATTRS", rule, &pair->key, value)) - goto try_parent; - } - } - - /* found matching device */ - break; -try_parent: - /* move to parent device */ - dbg(event->udev, "try parent sysfs device\n"); - event->dev_parent = udev_device_get_parent(event->dev_parent); - if (event->dev_parent == NULL) - goto nomatch; - dbg(event->udev, "looking at dev_parent->devpath='%s'\n", - udev_device_get_syspath(event->dev_parent)); - } - - /* execute external program */ - if (rule->program.operation != KEY_OP_UNSET) { - char program[UTIL_PATH_SIZE]; - char result[UTIL_PATH_SIZE]; - - util_strlcpy(program, key_val(rule, &rule->program), sizeof(program)); - udev_rules_apply_format(event, program, sizeof(program)); - if (run_program(event->dev, program, result, sizeof(result), NULL) != 0) { - dbg(event->udev, "PROGRAM is false\n"); - event->program_result[0] = '\0'; - if (rule->program.operation != KEY_OP_NOMATCH) - goto nomatch; - } else { - int count; - - dbg(event->udev, "PROGRAM matches\n"); - util_remove_trailing_chars(result, '\n'); - if (rule->string_escape == ESCAPE_UNSET || - rule->string_escape == ESCAPE_REPLACE) { - count = util_replace_chars(result, ALLOWED_CHARS_INPUT); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - } - dbg(event->udev, "result is '%s'\n", result); - util_strlcpy(event->program_result, result, sizeof(event->program_result)); - dbg(event->udev, "PROGRAM returned successful\n"); - if (rule->program.operation == KEY_OP_NOMATCH) - goto nomatch; - } - dbg(event->udev, "PROGRAM key is true\n"); - } - - /* check for matching result of external program */ - if (match_key(event->udev, "RESULT", rule, &rule->result, event->program_result)) - goto nomatch; - - /* import variables returned from program or or file into environment */ - if (rule->import.operation != KEY_OP_UNSET) { - char import[UTIL_PATH_SIZE]; - int rc = -1; - - util_strlcpy(import, key_val(rule, &rule->import), sizeof(import)); - udev_rules_apply_format(event, import, sizeof(import)); - dbg(event->udev, "check for IMPORT import='%s'\n", import); - if (rule->import_type == IMPORT_PROGRAM) { - rc = import_program_into_env(event, import); - } else if (rule->import_type == IMPORT_FILE) { - dbg(event->udev, "import file import='%s'\n", import); - rc = import_file_into_env(event, import); - } else if (rule->import_type == IMPORT_PARENT) { - dbg(event->udev, "import parent import='%s'\n", import); - rc = import_parent_into_env(event, import); - } - if (rc != 0) { - dbg(event->udev, "IMPORT failed\n"); - if (rule->import.operation != KEY_OP_NOMATCH) - goto nomatch; - } else - dbg(event->udev, "IMPORT '%s' imported\n", key_val(rule, &rule->import)); - dbg(event->udev, "IMPORT key is true\n"); - } - - /* rule matches, if we have ENV assignments export it */ - for (i = 0; i < rule->env.count; i++) { - struct key_pair *pair = &rule->env.keys[i]; - - if (pair->key.operation == KEY_OP_ASSIGN) { - char temp_value[UTIL_NAME_SIZE]; - const char *key_name = key_pair_name(rule, pair); - const char *value = key_val(rule, &pair->key); - - /* make sure we don't write to the same string we possibly read from */ - util_strlcpy(temp_value, value, sizeof(temp_value)); - udev_rules_apply_format(event, temp_value, sizeof(temp_value)); - - if (temp_value[0] != '\0') { - struct udev_list_entry *entry; - - info(event->udev, "set ENV '%s=%s'\n", key_name, temp_value); - entry = udev_device_add_property(dev, key_name, temp_value); - /* store in db */ - udev_list_entry_set_flag(entry, 1); - } - } - } - - /* if we have ATTR assignments, write value to sysfs file */ - for (i = 0; i < rule->attr.count; i++) { - struct key_pair *pair = &rule->attr.keys[i]; - - if (pair->key.operation == KEY_OP_ASSIGN) { - const char *key_name = key_pair_name(rule, pair); - char *subsys; - char *sysname; - char *attrib; - char attr[UTIL_PATH_SIZE]; - char value[UTIL_NAME_SIZE]; - FILE *f; - - util_strlcpy(attr, key_name, sizeof(attr)); - if (split_subsys_sysname(event->udev, attr, &subsys, &sysname, &attrib) == 0) { - struct udev_device *d; - - d = udev_device_new_from_subsystem_sysname(event->udev, subsys, sysname); - if (d != NULL) { - util_strlcpy(attr, udev_device_get_syspath(d), sizeof(attr)); - if (attrib != NULL) { - util_strlcat(attr, "/", sizeof(attr)); - util_strlcat(attr, attrib, sizeof(attr)); - } - udev_device_unref(d); - } - } else { - util_strlcpy(attr, udev_device_get_syspath(dev), sizeof(attr)); - util_strlcat(attr, "/", sizeof(attr)); - util_strlcat(attr, key_name, sizeof(attr)); - } - - attr_subst_subdir(attr, sizeof(attr)); - - util_strlcpy(value, key_val(rule, &pair->key), sizeof(value)); - udev_rules_apply_format(event, value, sizeof(value)); - info(event->udev, "writing '%s' to sysfs file '%s'\n", value, attr); - f = fopen(attr, "w"); - if (f != NULL) { - if (!event->test) - if (fprintf(f, "%s", value) <= 0) - err(event->udev, "error writing ATTR{%s}: %m\n", attr); - fclose(f); - } else - err(event->udev, "error opening ATTR{%s} for writing: %m\n", attr); - } - } - return 0; - -nomatch: - return -1; -} - -int udev_rules_get_name(struct udev_rules *rules, struct udev_event *event) -{ - struct udev_device *dev = event->dev; - struct udev_rules_iter iter; - struct udev_rule *rule; - int name_set = 0; - - dbg(event->udev, "device: '%s'\n", udev_device_get_syspath(dev)); - - /* look for a matching rule to apply */ - udev_rules_iter_init(&iter, rules); - while (1) { - rule = udev_rules_iter_next(&iter); - if (rule == NULL) - break; - - if (name_set && - (rule->name.operation == KEY_OP_ASSIGN || - rule->name.operation == KEY_OP_ASSIGN_FINAL || - rule->name.operation == KEY_OP_ADD)) { - dbg(event->udev, "node name already set, rule ignored\n"); - continue; - } - - dbg(event->udev, "process rule\n"); - if (match_rule(event, rule) == 0) { - /* apply options */ - if (rule->ignore_device) { - info(event->udev, "rule applied, '%s' is ignored\n", udev_device_get_sysname(dev)); - event->ignore_device = 1; - return 0; - } - if (rule->ignore_remove) { - udev_device_set_ignore_remove(dev, 1); - dbg(event->udev, "remove event should be ignored\n"); - } - if (rule->link_priority != 0) { - udev_device_set_devlink_priority(dev, rule->link_priority); - info(event->udev, "devlink_priority=%i\n", rule->link_priority); - } - if (rule->event_timeout >= 0) { - udev_device_set_event_timeout(dev, rule->event_timeout); - info(event->udev, "event_timeout=%i\n", rule->event_timeout); - } - /* apply all_partitions option only at a disk device */ - if (rule->partitions > 0 && - strcmp(udev_device_get_subsystem(dev), "block") == 0 && - udev_device_get_sysnum(dev) == NULL) { - udev_device_set_num_fake_partitions(dev, rule->partitions); - dbg(event->udev, "creation of partition nodes requested\n"); - } - - /* apply permissions */ - if (!event->mode_final && rule->mode.operation != KEY_OP_UNSET) { - if (rule->mode.operation == KEY_OP_ASSIGN_FINAL) - event->mode_final = 1; - char buf[20]; - util_strlcpy(buf, key_val(rule, &rule->mode), sizeof(buf)); - udev_rules_apply_format(event, buf, sizeof(buf)); - event->mode = strtol(buf, NULL, 8); - dbg(event->udev, "applied mode=%#o to '%s'\n", - event->mode, udev_device_get_sysname(dev)); - } - if (!event->owner_final && rule->owner.operation != KEY_OP_UNSET) { - if (rule->owner.operation == KEY_OP_ASSIGN_FINAL) - event->owner_final = 1; - util_strlcpy(event->owner, key_val(rule, &rule->owner), sizeof(event->owner)); - udev_rules_apply_format(event, event->owner, sizeof(event->owner)); - dbg(event->udev, "applied owner='%s' to '%s'\n", - event->owner, udev_device_get_sysname(dev)); - } - if (!event->group_final && rule->group.operation != KEY_OP_UNSET) { - if (rule->group.operation == KEY_OP_ASSIGN_FINAL) - event->group_final = 1; - util_strlcpy(event->group, key_val(rule, &rule->group), sizeof(event->group)); - udev_rules_apply_format(event, event->group, sizeof(event->group)); - dbg(event->udev, "applied group='%s' to '%s'\n", - event->group, udev_device_get_sysname(dev)); - } - - /* collect symlinks */ - if (!event->devlink_final && - (rule->symlink.operation == KEY_OP_ASSIGN || - rule->symlink.operation == KEY_OP_ASSIGN_FINAL || - rule->symlink.operation == KEY_OP_ADD)) { - char temp[UTIL_PATH_SIZE]; - char filename[UTIL_PATH_SIZE]; - char *pos, *next; - int count = 0; - - if (rule->symlink.operation == KEY_OP_ASSIGN_FINAL) - event->devlink_final = 1; - if (rule->symlink.operation == KEY_OP_ASSIGN || - rule->symlink.operation == KEY_OP_ASSIGN_FINAL) { - info(event->udev, "reset symlink list\n"); - udev_device_cleanup_devlinks_list(dev); - } - /* allow multiple symlinks separated by spaces */ - util_strlcpy(temp, key_val(rule, &rule->symlink), sizeof(temp)); - udev_rules_apply_format(event, temp, sizeof(temp)); - if (rule->string_escape == ESCAPE_UNSET) - count = util_replace_chars(temp, ALLOWED_CHARS_FILE " "); - else if (rule->string_escape == ESCAPE_REPLACE) - count = util_replace_chars(temp, ALLOWED_CHARS_FILE); - if (count > 0) - info(event->udev, "%i character(s) replaced\n" , count); - dbg(event->udev, "rule applied, added symlink(s) '%s'\n", temp); - pos = temp; - while (isspace(pos[0])) - pos++; - next = strchr(pos, ' '); - while (next) { - next[0] = '\0'; - info(event->udev, "add symlink '%s'\n", pos); - util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); - util_strlcat(filename, "/", sizeof(filename)); - util_strlcat(filename, pos, sizeof(filename)); - udev_device_add_devlink(dev, filename); - while (isspace(next[1])) - next++; - pos = &next[1]; - next = strchr(pos, ' '); - } - if (pos[0] != '\0') { - info(event->udev, "add symlink '%s'\n", pos); - util_strlcpy(filename, udev_get_dev_path(event->udev), sizeof(filename)); - util_strlcat(filename, "/", sizeof(filename)); - util_strlcat(filename, pos, sizeof(filename)); - udev_device_add_devlink(dev, filename); - } - } - - /* set name, later rules with name set will be ignored */ - if (rule->name.operation == KEY_OP_ASSIGN || - rule->name.operation == KEY_OP_ASSIGN_FINAL || - rule->name.operation == KEY_OP_ADD) { - int count; - - name_set = 1; - util_strlcpy(event->name, key_val(rule, &rule->name), sizeof(event->name)); - udev_rules_apply_format(event, event->name, sizeof(event->name)); - if (rule->string_escape == ESCAPE_UNSET || - rule->string_escape == ESCAPE_REPLACE) { - count = util_replace_chars(event->name, ALLOWED_CHARS_FILE); - if (count > 0) - info(event->udev, "%i character(s) replaced\n", count); - } - - info(event->udev, "rule applied, '%s' becomes '%s'\n", - udev_device_get_sysname(dev), event->name); - if (strcmp(udev_device_get_subsystem(dev), "net") != 0) - dbg(event->udev, "'%s' owner='%s', group='%s', mode=%#o partitions=%i\n", - event->name, event->owner, event->group, event->mode, - udev_device_get_num_fake_partitions(dev)); - } - - if (!event->run_final && rule->run.operation != KEY_OP_UNSET) { - struct udev_list_entry *list_entry; - - if (rule->run.operation == KEY_OP_ASSIGN_FINAL) - event->run_final = 1; - if (rule->run.operation == KEY_OP_ASSIGN || rule->run.operation == KEY_OP_ASSIGN_FINAL) { - info(event->udev, "reset run list\n"); - udev_list_cleanup(event->udev, &event->run_list); - } - dbg(event->udev, "add run '%s'\n", key_val(rule, &rule->run)); - list_entry = udev_list_entry_add(event->udev, &event->run_list, - key_val(rule, &rule->run), NULL, 1, 0); - if (rule->run_ignore_error && list_entry != NULL) - udev_list_entry_set_flag(list_entry, 1); - } - - if (rule->last_rule) { - dbg(event->udev, "last rule to be applied\n"); - break; - } - - if (rule->goto_label.operation != KEY_OP_UNSET) { - dbg(event->udev, "moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); - udev_rules_iter_goto(&iter, rule->goto_rule_off); - } - } - } - - if (!name_set) { - info(event->udev, "no node name set, will use kernel name '%s'\n", - udev_device_get_sysname(dev)); - util_strlcpy(event->name, udev_device_get_sysname(dev), sizeof(event->name)); - } - - if (event->tmp_node[0] != '\0') { - dbg(event->udev, "removing temporary device node\n"); - unlink_secure(event->udev, event->tmp_node); - event->tmp_node[0] = '\0'; - } - return 0; -} - -int udev_rules_get_run(struct udev_rules *rules, struct udev_event *event) -{ - struct udev_device *dev = event->dev; - struct udev_rules_iter iter; - struct udev_rule *rule; - - dbg(event->udev, "sysname: '%s'\n", udev_device_get_sysname(dev)); - - /* look for a matching rule to apply */ - udev_rules_iter_init(&iter, rules); - while (1) { - rule = udev_rules_iter_next(&iter); - if (rule == NULL) - break; - - dbg(event->udev, "process rule\n"); - if (rule->name.operation == KEY_OP_ASSIGN || - rule->name.operation == KEY_OP_ASSIGN_FINAL || - rule->name.operation == KEY_OP_ADD || - rule->symlink.operation == KEY_OP_ASSIGN || - rule->symlink.operation == KEY_OP_ASSIGN_FINAL || - rule->symlink.operation == KEY_OP_ADD || - rule->mode.operation != KEY_OP_UNSET || - rule->owner.operation != KEY_OP_UNSET || rule->group.operation != KEY_OP_UNSET) { - dbg(event->udev, "skip rule that names a device\n"); - continue; - } - - if (match_rule(event, rule) == 0) { - if (rule->ignore_device) { - info(event->udev, "rule applied, '%s' is ignored\n", udev_device_get_sysname(dev)); - event->ignore_device = 1; - return 0; - } - if (rule->ignore_remove) { - udev_device_set_ignore_remove(dev, 1); - dbg(event->udev, "remove event should be ignored\n"); - } - - if (!event->run_final && rule->run.operation != KEY_OP_UNSET) { - struct udev_list_entry *list_entry; - - if (rule->run.operation == KEY_OP_ASSIGN || - rule->run.operation == KEY_OP_ASSIGN_FINAL) { - info(event->udev, "reset run list\n"); - udev_list_cleanup(event->udev, &event->run_list); - } - dbg(event->udev, "add run '%s'\n", key_val(rule, &rule->run)); - list_entry = udev_list_entry_add(event->udev, &event->run_list, - key_val(rule, &rule->run), NULL, 1, 0); - if (rule->run_ignore_error && list_entry != NULL) - udev_list_entry_set_flag(list_entry, 1); - if (rule->run.operation == KEY_OP_ASSIGN_FINAL) - break; - } - - if (rule->last_rule) { - dbg(event->udev, "last rule to be applied\n"); - break; - } - - if (rule->goto_label.operation != KEY_OP_UNSET) { - dbg(event->udev, "moving forward to label '%s'\n", key_val(rule, &rule->goto_label)); - udev_rules_iter_goto(&iter, rule->goto_rule_off); - } - } - } - - return 0; -} diff --git a/udev/udev_rules.h b/udev/udev_rules.h deleted file mode 100644 index d8f2f367c9..0000000000 --- a/udev/udev_rules.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef UDEV_RULES_H -#define UDEV_RULES_H - -#include "udev.h" - -#define PAIRS_MAX 5 - -enum key_operation { - KEY_OP_UNSET, - KEY_OP_MATCH, - KEY_OP_NOMATCH, - KEY_OP_ADD, - KEY_OP_ASSIGN, - KEY_OP_ASSIGN_FINAL, -}; - -struct key { - enum key_operation operation; - size_t val_off; -}; - -struct key_pair { - struct key key; - size_t key_name_off; -}; - -struct key_pairs { - int count; - struct key_pair keys[PAIRS_MAX]; -}; - -enum import_type { - IMPORT_UNSET, - IMPORT_PROGRAM, - IMPORT_FILE, - IMPORT_PARENT, -}; - -enum escape_type { - ESCAPE_UNSET, - ESCAPE_NONE, - ESCAPE_REPLACE, -}; - -struct udev_rule { - struct key action; - struct key devpath; - struct key kernel; - struct key subsystem; - struct key driver; - struct key_pairs attr; - - struct key kernels; - struct key subsystems; - struct key drivers; - struct key_pairs attrs; - - struct key_pairs env; - struct key program; - struct key result; - struct key import; - enum import_type import_type; - struct key test; - mode_t test_mode_mask; - struct key run; - struct key wait_for; - struct key label; - struct key goto_label; - size_t goto_rule_off; - - struct key name; - struct key symlink; - struct key symlink_match; - struct key owner; - struct key group; - struct key mode; - enum escape_type string_escape; - - unsigned int link_priority; - int event_timeout; - unsigned int partitions; - unsigned int last_rule:1, - run_ignore_error:1, - ignore_device:1, - ignore_remove:1; - - size_t bufsize; - char buf[]; -}; - -struct udev_rules { - struct udev *udev; - char *buf; - size_t bufsize; - int resolve_names; -}; - -struct udev_rules_iter { - struct udev_rules *rules; - size_t current; -}; - -extern int udev_rules_init(struct udev *udev, struct udev_rules *rules, int resolve_names); -extern void udev_rules_cleanup(struct udev_rules *rules); - -extern void udev_rules_iter_init(struct udev_rules_iter *iter, struct udev_rules *rules); -extern struct udev_rule *udev_rules_iter_next(struct udev_rules_iter *iter); -extern struct udev_rule *udev_rules_iter_goto(struct udev_rules_iter *iter, size_t rule_off); - -extern int udev_rules_get_name(struct udev_rules *rules, struct udev_event *event); -extern int udev_rules_get_run(struct udev_rules *rules, struct udev_event *event); -extern int udev_rules_run(struct udev_event *event); - -extern void udev_rules_apply_format(struct udev_event *event, char *string, size_t maxsize); - -#endif diff --git a/udev/udev_rules_parse.c b/udev/udev_rules_parse.c deleted file mode 100644 index c42bd08c51..0000000000 --- a/udev/udev_rules_parse.c +++ /dev/null @@ -1,850 +0,0 @@ -/* - * Copyright (C) 2003,2004 Greg Kroah-Hartman - * Copyright (C) 2003-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" - - -void udev_rules_iter_init(struct udev_rules_iter *iter, struct udev_rules *rules) -{ - dbg(rules->udev, "bufsize=%zi\n", rules->bufsize); - iter->rules = rules; - iter->current = 0; -} - -struct udev_rule *udev_rules_iter_next(struct udev_rules_iter *iter) -{ - struct udev_rules *rules; - struct udev_rule *rule; - - rules = iter->rules; - if (!rules) - return NULL; - - dbg(rules->udev, "current=%zi\n", iter->current); - if (iter->current >= rules->bufsize) { - dbg(rules->udev, "no more rules\n"); - return NULL; - } - - /* get next rule */ - rule = (struct udev_rule *) (rules->buf + iter->current); - iter->current += sizeof(struct udev_rule) + rule->bufsize; - - return rule; -} - -struct udev_rule *udev_rules_iter_goto(struct udev_rules_iter *iter, size_t rule_off) -{ - struct udev_rules *rules = iter->rules; - struct udev_rule *rule; - - dbg(rules->udev, "current=%zi\n", iter->current); - iter->current = rule_off; - rule = (struct udev_rule *) (rules->buf + iter->current); - - return rule; -} - -static size_t find_label(const struct udev_rules_iter *iter, const char *label) -{ - struct udev_rule *rule; - struct udev_rules *rules = iter->rules; - size_t current = iter->current; - -next: - dbg(rules->udev, "current=%zi\n", current); - if (current >= rules->bufsize) { - dbg(rules->udev, "LABEL='%s' not found\n", label); - return 0; - } - rule = (struct udev_rule *) (rules->buf + current); - - if (strcmp(&rule->buf[rule->label.val_off], label) != 0) { - dbg(rules->udev, "moving forward, looking for label '%s'\n", label); - current += sizeof(struct udev_rule) + rule->bufsize; - goto next; - } - - dbg(rules->udev, "found label '%s'\n", label); - return current; -} - -static int get_key(struct udev_rules *rules, char **line, char **key, enum key_operation *operation, char **value) -{ - char *linepos; - char *temp; - - linepos = *line; - if (linepos == NULL && linepos[0] == '\0') - return -1; - - /* skip whitespace */ - while (isspace(linepos[0]) || linepos[0] == ',') - linepos++; - - /* get the key */ - if (linepos[0] == '\0') - return -1; - *key = linepos; - - while (1) { - linepos++; - if (linepos[0] == '\0') - return -1; - if (isspace(linepos[0])) - break; - if (linepos[0] == '=') - break; - if ((linepos[0] == '+') || (linepos[0] == '!') || (linepos[0] == ':')) - if (linepos[1] == '=') - break; - } - - /* remember end of key */ - temp = linepos; - - /* skip whitespace after key */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get operation type */ - if (linepos[0] == '=' && linepos[1] == '=') { - *operation = KEY_OP_MATCH; - linepos += 2; - dbg(rules->udev, "operator=match\n"); - } else if (linepos[0] == '!' && linepos[1] == '=') { - *operation = KEY_OP_NOMATCH; - linepos += 2; - dbg(rules->udev, "operator=nomatch\n"); - } else if (linepos[0] == '+' && linepos[1] == '=') { - *operation = KEY_OP_ADD; - linepos += 2; - dbg(rules->udev, "operator=add\n"); - } else if (linepos[0] == '=') { - *operation = KEY_OP_ASSIGN; - linepos++; - dbg(rules->udev, "operator=assign\n"); - } else if (linepos[0] == ':' && linepos[1] == '=') { - *operation = KEY_OP_ASSIGN_FINAL; - linepos += 2; - dbg(rules->udev, "operator=assign_final\n"); - } else - return -1; - - /* terminate key */ - temp[0] = '\0'; - dbg(rules->udev, "key='%s'\n", *key); - - /* skip whitespace after operator */ - while (isspace(linepos[0])) - linepos++; - if (linepos[0] == '\0') - return -1; - - /* get the value*/ - if (linepos[0] == '"') - linepos++; - else - return -1; - *value = linepos; - - temp = strchr(linepos, '"'); - if (!temp) - return -1; - temp[0] = '\0'; - temp++; - dbg(rules->udev, "value='%s'\n", *value); - - /* move line to next key */ - *line = temp; - - return 0; -} - -/* extract possible KEY{attr} */ -static char *get_key_attribute(struct udev_rules *rules, char *str) -{ - char *pos; - char *attr; - - attr = strchr(str, '{'); - if (attr != NULL) { - attr++; - pos = strchr(attr, '}'); - if (pos == NULL) { - err(rules->udev, "missing closing brace for format\n"); - return NULL; - } - pos[0] = '\0'; - dbg(rules->udev, "attribute='%s'\n", attr); - return attr; - } - - return NULL; -} - -static int add_rule_key(struct udev_rule *rule, struct key *key, - enum key_operation operation, const char *value) -{ - size_t val_len = strnlen(value, UTIL_PATH_SIZE); - - key->operation = operation; - - key->val_off = rule->bufsize; - util_strlcpy(rule->buf + rule->bufsize, value, val_len+1); - rule->bufsize += val_len+1; - - return 0; -} - -static int add_rule_key_pair(struct udev_rules *rules, struct udev_rule *rule, struct key_pairs *pairs, - enum key_operation operation, const char *key, const char *value) -{ - size_t key_len = strnlen(key, UTIL_PATH_SIZE); - - if (pairs->count >= PAIRS_MAX) { - err(rules->udev, "skip, too many keys of the same type in a single rule\n"); - return -1; - } - - add_rule_key(rule, &pairs->keys[pairs->count].key, operation, value); - - /* add the key-name of the pair */ - pairs->keys[pairs->count].key_name_off = rule->bufsize; - util_strlcpy(rule->buf + rule->bufsize, key, key_len+1); - rule->bufsize += key_len+1; - - pairs->count++; - - return 0; -} - -static int add_to_rules(struct udev_rules *rules, char *line, const char *filename, unsigned int lineno) -{ - char buf[sizeof(struct udev_rule) + UTIL_LINE_SIZE]; - struct udev_rule *rule; - size_t rule_size; - int valid; - char *linepos; - char *attr; - size_t padding; - int physdev = 0; - int retval; - - memset(buf, 0x00, sizeof(buf)); - rule = (struct udev_rule *) buf; - rule->event_timeout = -1; - linepos = line; - valid = 0; - - /* get all the keys */ - while (1) { - char *key; - char *value; - enum key_operation operation = KEY_OP_UNSET; - - retval = get_key(rules, &linepos, &key, &operation, &value); - if (retval) - break; - - if (strcasecmp(key, "ACTION") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid ACTION operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->action, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "DEVPATH") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid DEVPATH operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->devpath, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "KERNEL") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid KERNEL operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->kernel, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "SUBSYSTEM") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid SUBSYSTEM operation\n"); - goto invalid; - } - /* bus, class, subsystem events should all be the same */ - if (strcmp(value, "subsystem") == 0 || - strcmp(value, "bus") == 0 || - strcmp(value, "class") == 0) { - if (strcmp(value, "bus") == 0 || strcmp(value, "class") == 0) - err(rules->udev, "'%s' must be specified as 'subsystem' \n" - "please fix it in %s:%u", value, filename, lineno); - add_rule_key(rule, &rule->subsystem, operation, "subsystem|class|bus"); - } else - add_rule_key(rule, &rule->subsystem, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "DRIVER") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid DRIVER operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->driver, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "ATTR{", sizeof("ATTR{")-1) == 0) { - attr = get_key_attribute(rules, key + sizeof("ATTR")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ATTR attribute\n"); - goto invalid; - } - if (add_rule_key_pair(rules, rule, &rule->attr, operation, attr, value) != 0) - goto invalid; - valid = 1; - continue; - } - - if (strcasecmp(key, "KERNELS") == 0 || - strcasecmp(key, "ID") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid KERNELS operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->kernels, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "SUBSYSTEMS") == 0 || - strcasecmp(key, "BUS") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid SUBSYSTEMS operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->subsystems, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "DRIVERS") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid DRIVERS operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->drivers, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "ATTRS{", sizeof("ATTRS{")-1) == 0 || - strncasecmp(key, "SYSFS{", sizeof("SYSFS{")-1) == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid ATTRS operation\n"); - goto invalid; - } - attr = get_key_attribute(rules, key + sizeof("ATTRS")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ATTRS attribute\n"); - goto invalid; - } - if (strncmp(attr, "device/", 7) == 0) - err(rules->udev, "the 'device' link is deprecated and will be removed from a future kernel, \n" - "please fix it in %s:%u", filename, lineno); - else if (strstr(attr, "../") != NULL) - err(rules->udev, "do not reference parent sysfs directories directly, that may break with a future kernel, \n" - "please fix it in %s:%u", filename, lineno); - if (add_rule_key_pair(rules, rule, &rule->attrs, operation, attr, value) != 0) - goto invalid; - valid = 1; - continue; - } - - if (strncasecmp(key, "ENV{", sizeof("ENV{")-1) == 0) { - attr = get_key_attribute(rules, key + sizeof("ENV")-1); - if (attr == NULL) { - err(rules->udev, "error parsing ENV attribute\n"); - goto invalid; - } - if (strncmp(attr, "PHYSDEV", 7) == 0) - physdev = 1; - if (add_rule_key_pair(rules, rule, &rule->env, operation, attr, value) != 0) - goto invalid; - valid = 1; - continue; - } - - if (strcasecmp(key, "PROGRAM") == 0) { - add_rule_key(rule, &rule->program, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "RESULT") == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid RESULT operation\n"); - goto invalid; - } - add_rule_key(rule, &rule->result, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "IMPORT", sizeof("IMPORT")-1) == 0) { - attr = get_key_attribute(rules, key + sizeof("IMPORT")-1); - if (attr != NULL && strstr(attr, "program")) { - dbg(rules->udev, "IMPORT will be executed\n"); - rule->import_type = IMPORT_PROGRAM; - } else if (attr != NULL && strstr(attr, "file")) { - dbg(rules->udev, "IMPORT will be included as file\n"); - rule->import_type = IMPORT_FILE; - } else if (attr != NULL && strstr(attr, "parent")) { - dbg(rules->udev, "IMPORT will include the parent values\n"); - rule->import_type = IMPORT_PARENT; - } else { - /* figure it out if it is executable */ - char file[UTIL_PATH_SIZE]; - char *pos; - struct stat statbuf; - - util_strlcpy(file, value, sizeof(file)); - pos = strchr(file, ' '); - if (pos) - pos[0] = '\0'; - - /* allow programs in /lib/udev called without the path */ - if (strchr(file, '/') == NULL) { - util_strlcpy(file, UDEV_PREFIX "/lib/udev/", sizeof(file)); - util_strlcat(file, value, sizeof(file)); - pos = strchr(file, ' '); - if (pos) - pos[0] = '\0'; - } - - dbg(rules->udev, "IMPORT auto mode for '%s'\n", file); - if (!lstat(file, &statbuf) && (statbuf.st_mode & S_IXUSR)) { - dbg(rules->udev, "IMPORT is executable, will be executed (autotype)\n"); - rule->import_type = IMPORT_PROGRAM; - } else { - dbg(rules->udev, "IMPORT is not executable, will be included as file (autotype)\n"); - rule->import_type = IMPORT_FILE; - } - } - add_rule_key(rule, &rule->import, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "TEST", sizeof("TEST")-1) == 0) { - if (operation != KEY_OP_MATCH && - operation != KEY_OP_NOMATCH) { - err(rules->udev, "invalid TEST operation\n"); - goto invalid; - } - attr = get_key_attribute(rules, key + sizeof("TEST")-1); - if (attr != NULL) - rule->test_mode_mask = strtol(attr, NULL, 8); - add_rule_key(rule, &rule->test, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "RUN", sizeof("RUN")-1) == 0) { - attr = get_key_attribute(rules, key + sizeof("RUN")-1); - if (attr != NULL) { - if (strstr(attr, "ignore_error")) - rule->run_ignore_error = 1; - } - add_rule_key(rule, &rule->run, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "WAIT_FOR") == 0 || strcasecmp(key, "WAIT_FOR_SYSFS") == 0) { - add_rule_key(rule, &rule->wait_for, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "LABEL") == 0) { - add_rule_key(rule, &rule->label, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "GOTO") == 0) { - add_rule_key(rule, &rule->goto_label, operation, value); - valid = 1; - continue; - } - - if (strncasecmp(key, "NAME", sizeof("NAME")-1) == 0) { - attr = get_key_attribute(rules, key + sizeof("NAME")-1); - if (attr != NULL) { - if (strstr(attr, "all_partitions") != NULL) { - dbg(rules->udev, "creation of partition nodes requested\n"); - rule->partitions = DEFAULT_PARTITIONS_COUNT; - } - if (strstr(attr, "ignore_remove") != NULL) { - dbg(rules->udev, "remove event should be ignored\n"); - rule->ignore_remove = 1; - } - } - if (value[0] == '\0') - dbg(rules->udev, "name empty, node creation supressed\n"); - add_rule_key(rule, &rule->name, operation, value); - continue; - } - - if (strcasecmp(key, "SYMLINK") == 0) { - if (operation == KEY_OP_MATCH || - operation == KEY_OP_NOMATCH) - add_rule_key(rule, &rule->symlink_match, operation, value); - else - add_rule_key(rule, &rule->symlink, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "OWNER") == 0) { - valid = 1; - if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { - char *endptr; - strtoul(value, &endptr, 10); - if (endptr[0] != '\0') { - char owner[32]; - uid_t uid = lookup_user(rules->udev, value); - dbg(rules->udev, "replacing username='%s' by id=%i\n", value, uid); - sprintf(owner, "%u", (unsigned int) uid); - add_rule_key(rule, &rule->owner, operation, owner); - continue; - } - } - - add_rule_key(rule, &rule->owner, operation, value); - continue; - } - - if (strcasecmp(key, "GROUP") == 0) { - valid = 1; - if (rules->resolve_names && (!strchr(value, '$') && !strchr(value, '%'))) { - char *endptr; - strtoul(value, &endptr, 10); - if (endptr[0] != '\0') { - char group[32]; - gid_t gid = lookup_group(rules->udev, value); - dbg(rules->udev, "replacing groupname='%s' by id=%i\n", value, gid); - sprintf(group, "%u", (unsigned int) gid); - add_rule_key(rule, &rule->group, operation, group); - continue; - } - } - - add_rule_key(rule, &rule->group, operation, value); - continue; - } - - if (strcasecmp(key, "MODE") == 0) { - add_rule_key(rule, &rule->mode, operation, value); - valid = 1; - continue; - } - - if (strcasecmp(key, "OPTIONS") == 0) { - const char *pos; - - if (strstr(value, "last_rule") != NULL) { - dbg(rules->udev, "last rule to be applied\n"); - rule->last_rule = 1; - } - if (strstr(value, "ignore_device") != NULL) { - dbg(rules->udev, "device should be ignored\n"); - rule->ignore_device = 1; - } - if (strstr(value, "ignore_remove") != NULL) { - dbg(rules->udev, "remove event should be ignored\n"); - rule->ignore_remove = 1; - } - pos = strstr(value, "link_priority="); - if (pos != NULL) { - rule->link_priority = atoi(&pos[strlen("link_priority=")]); - dbg(rules->udev, "link priority=%i\n", rule->link_priority); - } - pos = strstr(value, "event_timeout="); - if (pos != NULL) { - rule->event_timeout = atoi(&pos[strlen("event_timeout=")]); - dbg(rules->udev, "event timout=%i\n", rule->event_timeout); - } - pos = strstr(value, "string_escape="); - if (pos != NULL) { - pos = &pos[strlen("string_escape=")]; - if (strncmp(pos, "none", strlen("none")) == 0) - rule->string_escape = ESCAPE_NONE; - else if (strncmp(pos, "replace", strlen("replace")) == 0) - rule->string_escape = ESCAPE_REPLACE; - } - if (strstr(value, "all_partitions") != NULL) { - dbg(rules->udev, "creation of partition nodes requested\n"); - rule->partitions = DEFAULT_PARTITIONS_COUNT; - } - valid = 1; - continue; - } - - err(rules->udev, "unknown key '%s' in %s:%u\n", key, filename, lineno); - } - - if (physdev && rule->wait_for.operation == KEY_OP_UNSET) - err(rules->udev, "PHYSDEV* values are deprecated and will be removed from a future kernel, \n" - "please fix it in %s:%u", filename, lineno); - - /* skip line if not any valid key was found */ - if (!valid) - goto invalid; - - /* grow buffer and add rule */ - rule_size = sizeof(struct udev_rule) + rule->bufsize; - padding = (sizeof(size_t) - rule_size % sizeof(size_t)) % sizeof(size_t); - dbg(rules->udev, "add %zi padding bytes\n", padding); - rule_size += padding; - rule->bufsize += padding; - - rules->buf = realloc(rules->buf, rules->bufsize + rule_size); - if (!rules->buf) { - err(rules->udev, "realloc failed\n"); - goto exit; - } - dbg(rules->udev, "adding rule to offset %zi\n", rules->bufsize); - memcpy(rules->buf + rules->bufsize, rule, rule_size); - rules->bufsize += rule_size; -exit: - return 0; - -invalid: - err(rules->udev, "invalid rule '%s:%u'\n", filename, lineno); - return -1; -} - -static int parse_file(struct udev_rules *rules, const char *filename) -{ - char line[UTIL_LINE_SIZE]; - char *bufline; - unsigned int lineno; - char *buf; - size_t bufsize; - size_t cur; - size_t count; - int retval = 0; - size_t start; - struct udev_rule *rule; - struct udev_rules_iter iter; - - start = rules->bufsize; - - if (file_map(filename, &buf, &bufsize) != 0) { - err(rules->udev, "can't open '%s' as rules file: %m\n", filename); - return -1; - } - info(rules->udev, "reading '%s' as rules file\n", filename); - - /* loop through the whole file */ - cur = 0; - lineno = 0; - while (cur < bufsize) { - unsigned int i, j; - - count = buf_get_line(buf, bufsize, cur); - bufline = &buf[cur]; - cur += count+1; - lineno++; - - /* eat the whitespace */ - while ((count > 0) && isspace(bufline[0])) { - bufline++; - count--; - } - if (count == 0) - continue; - - /* see if this is a comment */ - if (bufline[0] == '#') - continue; - - if (count >= sizeof(line)) { - err(rules->udev, "line too long, rule skipped '%s:%u'\n", filename, lineno); - continue; - } - - /* skip backslash and newline from multiline rules */ - for (i = j = 0; i < count; i++) { - if (bufline[i] == '\\' && bufline[i+1] == '\n') - continue; - - line[j++] = bufline[i]; - } - line[j] = '\0'; - - dbg(rules->udev, "read '%s'\n", line); - add_to_rules(rules, line, filename, lineno); - } - - /* Compute all goto targets within this file */ - udev_rules_iter_init(&iter, rules); - udev_rules_iter_goto(&iter, start); - while((rule = udev_rules_iter_next(&iter))) { - if (rule->goto_label.operation != KEY_OP_UNSET) { - char *goto_label = &rule->buf[rule->goto_label.val_off]; - - dbg(rules->udev, "resolving goto label '%s'\n", goto_label); - rule->goto_rule_off = find_label(&iter, goto_label); - if (rule->goto_rule_off == 0) { - err(rules->udev, "ignore goto to nonexistent label '%s' in '%s'\n", - goto_label, filename); - rule->goto_rule_off = iter.current; - } - } - } - - file_unmap(buf, bufsize); - return retval; -} - -int udev_rules_init(struct udev *udev, struct udev_rules *rules, int resolve_names) -{ - struct stat statbuf; - char filename[PATH_MAX]; - LIST_HEAD(name_list); - LIST_HEAD(sort_list); - struct name_entry *name_loop, *name_tmp; - struct name_entry *sort_loop, *sort_tmp; - int retval = 0; - - memset(rules, 0x00, sizeof(struct udev_rules)); - rules->udev = udev; - rules->resolve_names = resolve_names; - - if (udev_get_rules_path(udev) != NULL) { - /* custom rules location for testing */ - add_matching_files(udev, &name_list, udev_get_rules_path(udev), ".rules"); - } else { - /* read user/custom rules */ - add_matching_files(udev, &name_list, SYSCONFDIR "/udev/rules.d", ".rules"); - - /* read dynamic/temporary rules */ - util_strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); - util_strlcat(filename, "/.udev/rules.d", sizeof(filename)); - if (stat(filename, &statbuf) != 0) { - create_path(udev, filename); - udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755); - mkdir(filename, 0755); - udev_selinux_resetfscreatecon(udev); - } - add_matching_files(udev, &sort_list, filename, ".rules"); - - /* read default rules */ - add_matching_files(udev, &sort_list, UDEV_PREFIX "/lib/udev/rules.d", ".rules"); - - /* sort all rules files by basename into list of files */ - list_for_each_entry_safe(sort_loop, sort_tmp, &sort_list, node) { - const char *sort_base = strrchr(sort_loop->name, '/'); - - if (sort_base == NULL) - continue; - - list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { - const char *name_base = strrchr(name_loop->name, '/'); - - if (name_base == NULL) - continue; - - if (strcmp(name_base, sort_base) == 0) { - info(udev, "rule file '%s' already added, ignoring '%s'\n", - name_loop->name, sort_loop->name); - list_del(&sort_loop->node); - free(sort_loop); - sort_loop = NULL; - continue; - } - - if (strcmp(name_base, sort_base) > 0) - break; - } - if (sort_loop != NULL) - list_move_tail(&sort_loop->node, &name_loop->node); - } - } - - /* parse list of files */ - list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) { - if (stat(name_loop->name, &statbuf) == 0) { - if (statbuf.st_size) - parse_file(rules, name_loop->name); - else - dbg(udev, "empty rules file '%s'\n", name_loop->name); - } else - err(udev, "could not read '%s': %m\n", name_loop->name); - list_del(&name_loop->node); - free(name_loop); - } - - return retval; -} - -void udev_rules_cleanup(struct udev_rules *rules) -{ - if (rules->buf) { - free(rules->buf); - rules->buf = NULL; - } -} - diff --git a/udev/udev_selinux.c b/udev/udev_selinux.c deleted file mode 100644 index cf7f43496a..0000000000 --- a/udev/udev_selinux.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * libudev - interface to udev device information - * - * Copyright (C) 2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static int selinux_enabled; -security_context_t selinux_prev_scontext; - -void selinux_init(struct udev *udev) -{ - /* record the present security context */ - selinux_enabled = (is_selinux_enabled() > 0); - info(udev, "selinux=%i\n", selinux_enabled); - if (!selinux_enabled) - return; - matchpathcon_init_prefix(NULL, udev_get_dev_path(udev)); - if (getfscreatecon(&selinux_prev_scontext) < 0) { - err(udev, "getfscreatecon failed\n"); - selinux_prev_scontext = NULL; - } -} - -void selinux_exit(struct udev *udev) -{ - if (!selinux_enabled) - return; - freecon(selinux_prev_scontext); - selinux_prev_scontext = NULL; -} - -void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) -{ - security_context_t scontext = NULL; - - if (!selinux_enabled) - return; - if (matchpathcon(file, mode, &scontext) < 0) { - err(udev, "matchpathcon(%s) failed\n", file); - return; - } - if (lsetfilecon(file, scontext) < 0) - err(udev, "setfilecon %s failed: %m\n", file); - freecon(scontext); -} - -void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) -{ - security_context_t scontext = NULL; - - if (!selinux_enabled) - return; - if (matchpathcon(file, mode, &scontext) < 0) { - err(udev, "matchpathcon(%s) failed\n", file); - return; - } - if (setfscreatecon(scontext) < 0) - err(udev, "setfscreatecon %s failed: %m\n", file); - freecon(scontext); -} - -void udev_selinux_resetfscreatecon(struct udev *udev) -{ - if (!selinux_enabled) - return; - if (setfscreatecon(selinux_prev_scontext) < 0) - err(udev, "setfscreatecon failed: %m\n"); -} diff --git a/udev/udev_sysdeps.h b/udev/udev_sysdeps.h deleted file mode 100644 index eaeab86f6b..0000000000 --- a/udev/udev_sysdeps.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * wrapping of libc features and kernel interfaces - * - * Copyright (C) 2005-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef _UDEV_SYSDEPS_H_ -#define _UDEV_SYSDEPS_H_ - -#include - -/* needed for our signal handlers to work */ -#undef asmlinkage -#ifdef __i386__ -#define asmlinkage __attribute__((regparm(0))) -#else -#define asmlinkage -#endif /* __i386__ */ - -#ifndef HAVE_INOTIFY -static inline int inotify_init(void) -{ - return -1; -} - -static inline int inotify_add_watch(int fd, const char *name, uint32_t mask) -{ - return -1; -} - -#define IN_CREATE 0 -#define IN_DELETE 0 -#define IN_MOVE 0 -#define IN_CLOSE_WRITE 0 - -#endif /* HAVE_INOTIFY */ -#endif diff --git a/udev/udev_utils.c b/udev/udev_utils.c deleted file mode 100644 index 4623c30caf..0000000000 --- a/udev/udev_utils.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -struct name_entry *name_list_add(struct udev *udev, struct list_head *name_list, const char *name, int sort) -{ - struct name_entry *name_loop; - struct name_entry *name_new; - - /* avoid duplicate entries */ - list_for_each_entry(name_loop, name_list, node) { - if (strcmp(name_loop->name, name) == 0) { - dbg(udev, "'%s' is already in the list\n", name); - return name_loop; - } - } - - if (sort) - list_for_each_entry(name_loop, name_list, node) { - if (strcmp(name_loop->name, name) > 0) - break; - } - - name_new = malloc(sizeof(struct name_entry)); - if (name_new == NULL) - return NULL; - memset(name_new, 0x00, sizeof(struct name_entry)); - util_strlcpy(name_new->name, name, sizeof(name_new->name)); - dbg(udev, "adding '%s'\n", name_new->name); - list_add_tail(&name_new->node, &name_loop->node); - - return name_new; -} - -struct name_entry *name_list_key_add(struct udev *udev, struct list_head *name_list, const char *key, const char *value) -{ - struct name_entry *name_loop; - struct name_entry *name_new; - size_t keylen = strlen(key); - - list_for_each_entry(name_loop, name_list, node) { - if (strncmp(name_loop->name, key, keylen) != 0) - continue; - if (name_loop->name[keylen] != '=') - continue; - dbg(udev, "key already present '%s', replace it\n", name_loop->name); - snprintf(name_loop->name, sizeof(name_loop->name), "%s=%s", key, value); - name_loop->name[sizeof(name_loop->name)-1] = '\0'; - return name_loop; - } - - name_new = malloc(sizeof(struct name_entry)); - if (name_new == NULL) - return NULL; - memset(name_new, 0x00, sizeof(struct name_entry)); - snprintf(name_new->name, sizeof(name_new->name), "%s=%s", key, value); - name_new->name[sizeof(name_new->name)-1] = '\0'; - dbg(udev, "adding '%s'\n", name_new->name); - list_add_tail(&name_new->node, &name_loop->node); - - return name_new; -} - -int name_list_key_remove(struct udev *udev, struct list_head *name_list, const char *key) -{ - struct name_entry *name_loop; - struct name_entry *name_tmp; - size_t keylen = strlen(key); - int retval = 0; - - list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { - if (strncmp(name_loop->name, key, keylen) != 0) - continue; - if (name_loop->name[keylen] != '=') - continue; - list_del(&name_loop->node); - free(name_loop); - retval = 1; - break; - } - return retval; -} - -void name_list_cleanup(struct udev *udev, struct list_head *name_list) -{ - struct name_entry *name_loop; - struct name_entry *name_tmp; - - list_for_each_entry_safe(name_loop, name_tmp, name_list, node) { - list_del(&name_loop->node); - free(name_loop); - } -} - -/* calls function for every file found in specified directory */ -int add_matching_files(struct udev *udev, struct list_head *name_list, const char *dirname, const char *suffix) -{ - struct dirent *ent; - DIR *dir; - char filename[UTIL_PATH_SIZE]; - - dbg(udev, "open directory '%s'\n", dirname); - dir = opendir(dirname); - if (dir == NULL) { - err(udev, "unable to open '%s': %m\n", dirname); - return -1; - } - - while (1) { - ent = readdir(dir); - if (ent == NULL || ent->d_name[0] == '\0') - break; - - if ((ent->d_name[0] == '.') || (ent->d_name[0] == '#')) - continue; - - /* look for file matching with specified suffix */ - if (suffix != NULL) { - const char *ext; - - ext = strrchr(ent->d_name, '.'); - if (ext == NULL) - continue; - if (strcmp(ext, suffix) != 0) - continue; - } - dbg(udev, "put file '%s/%s' into list\n", dirname, ent->d_name); - - snprintf(filename, sizeof(filename), "%s/%s", dirname, ent->d_name); - filename[sizeof(filename)-1] = '\0'; - name_list_add(udev, name_list, filename, 1); - } - - closedir(dir); - return 0; -} - -uid_t lookup_user(struct udev *udev, const char *user) -{ - struct passwd *pw; - uid_t uid = 0; - - errno = 0; - pw = getpwnam(user); - if (pw == NULL) { - if (errno == 0 || errno == ENOENT || errno == ESRCH) - err(udev, "specified user '%s' unknown\n", user); - else - err(udev, "error resolving user '%s': %m\n", user); - } else - uid = pw->pw_uid; - - return uid; -} - -extern gid_t lookup_group(struct udev *udev, const char *group) -{ - struct group *gr; - gid_t gid = 0; - - errno = 0; - gr = getgrnam(group); - if (gr == NULL) { - if (errno == 0 || errno == ENOENT || errno == ESRCH) - err(udev, "specified group '%s' unknown\n", group); - else - err(udev, "error resolving group '%s': %m\n", group); - } else - gid = gr->gr_gid; - - return gid; -} - diff --git a/udev/udev_utils_file.c b/udev/udev_utils_file.c deleted file mode 100644 index 84ff09e050..0000000000 --- a/udev/udev_utils_file.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2004-2008 Kay Sievers - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -int create_path(struct udev *udev, const char *path) -{ - char p[UTIL_PATH_SIZE]; - char *pos; - struct stat stats; - int ret; - - util_strlcpy(p, path, sizeof(p)); - pos = strrchr(p, '/'); - if (pos == p || pos == NULL) - return 0; - - while (pos[-1] == '/') - pos--; - pos[0] = '\0'; - - dbg(udev, "stat '%s'\n", p); - if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) - return 0; - - if (create_path(udev, p) != 0) - return -1; - - dbg(udev, "mkdir '%s'\n", p); - udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755); - ret = mkdir(p, 0755); - udev_selinux_resetfscreatecon(udev); - if (ret == 0) - return 0; - - if (errno == EEXIST) - if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR) - return 0; - return -1; -} - -int delete_path(struct udev *udev, const char *path) -{ - char p[UTIL_PATH_SIZE]; - char *pos; - int retval; - - strcpy (p, path); - pos = strrchr(p, '/'); - if (pos == p || pos == NULL) - return 0; - - while (1) { - *pos = '\0'; - pos = strrchr(p, '/'); - - /* don't remove the last one */ - if ((pos == p) || (pos == NULL)) - break; - - /* remove if empty */ - retval = rmdir(p); - if (errno == ENOENT) - retval = 0; - if (retval) { - if (errno == ENOTEMPTY) - return 0; - err(udev, "rmdir(%s) failed: %m\n", p); - break; - } - dbg(udev, "removed '%s'\n", p); - } - return 0; -} - -/* Reset permissions on the device node, before unlinking it to make sure, - * that permisions of possible hard links will be removed too. - */ -int unlink_secure(struct udev *udev, const char *filename) -{ - int retval; - - retval = chown(filename, 0, 0); - if (retval) - err(udev, "chown(%s, 0, 0) failed: %m\n", filename); - - retval = chmod(filename, 0000); - if (retval) - err(udev, "chmod(%s, 0000) failed: %m\n", filename); - - retval = unlink(filename); - if (errno == ENOENT) - retval = 0; - - if (retval) - err(udev, "unlink(%s) failed: %m\n", filename); - - return retval; -} - -int file_map(const char *filename, char **buf, size_t *bufsize) -{ - struct stat stats; - int fd; - - fd = open(filename, O_RDONLY); - if (fd < 0) { - return -1; - } - - if (fstat(fd, &stats) < 0) { - close(fd); - return -1; - } - - *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (*buf == MAP_FAILED) { - close(fd); - return -1; - } - *bufsize = stats.st_size; - - close(fd); - - return 0; -} - -void file_unmap(void *buf, size_t bufsize) -{ - munmap(buf, bufsize); -} - -/* return number of chars until the next newline, skip escaped newline */ -size_t buf_get_line(const char *buf, size_t buflen, size_t cur) -{ - int escape = 0; - size_t count; - - for (count = cur; count < buflen; count++) { - if (!escape && buf[count] == '\n') - break; - - if (buf[count] == '\\') - escape = 1; - else - escape = 0; - } - - return count - cur; -} diff --git a/udev/udevadm-test.c b/udev/udevadm-test.c index 70da9ebf67..090c524190 100644 --- a/udev/udevadm-test.c +++ b/udev/udevadm-test.c @@ -29,7 +29,7 @@ #include #include "udev.h" -#include "udev_rules.h" +#include "udev-rules.h" int udevadm_test(struct udev *udev, int argc, char *argv[]) { diff --git a/udev/udevd.c b/udev/udevd.c index 15ec0fbb03..1583e22ac9 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -40,7 +40,7 @@ #endif #include "udev.h" -#include "udev_rules.h" +#include "udev-rules.h" #define UDEVD_PRIORITY -4 #define UDEV_PRIORITY -2