From e4255f115330b949c1caeb561cbe0e39f5225f50 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Wed, 3 Sep 2008 23:33:06 +0200 Subject: udevadm: rename source files --- NEWS | 4 + udev/Makefile.am | 12 +- udev/udevadm-control.c | 304 +++++++++++++++++++++ udev/udevadm-info.c | 507 ++++++++++++++++++++++++++++++++++ udev/udevadm-monitor.c | 289 ++++++++++++++++++++ udev/udevadm-settle.c | 178 ++++++++++++ udev/udevadm-test.c | 207 ++++++++++++++ udev/udevadm-trigger.c | 716 +++++++++++++++++++++++++++++++++++++++++++++++++ udev/udevcontrol.c | 304 --------------------- udev/udevd.h | 2 +- udev/udevinfo.c | 507 ---------------------------------- udev/udevmonitor.c | 289 -------------------- udev/udevsettle.c | 178 ------------ udev/udevtest.c | 207 -------------- udev/udevtrigger.c | 716 ------------------------------------------------- 15 files changed, 2212 insertions(+), 2208 deletions(-) create mode 100644 udev/udevadm-control.c create mode 100644 udev/udevadm-info.c create mode 100644 udev/udevadm-monitor.c create mode 100644 udev/udevadm-settle.c create mode 100644 udev/udevadm-test.c create mode 100644 udev/udevadm-trigger.c delete mode 100644 udev/udevcontrol.c delete mode 100644 udev/udevinfo.c delete mode 100644 udev/udevmonitor.c delete mode 100644 udev/udevsettle.c delete mode 100644 udev/udevtest.c delete mode 100644 udev/udevtrigger.c diff --git a/NEWS b/NEWS index 70cd0debbb..8f7871d566 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ The udevadm info --device-id-of-file= output has changed to use the obvious format. Possible current users should use the --export option which is not affected. +The old udev commands symlinks to udevadm are not installed since +a while, if these symlinks are used, a deprecation warning to stderr +is printed. + udev 127 ======== Bugfixes. diff --git a/udev/Makefile.am b/udev/Makefile.am index 4a7c7855ce..d123c0b3fd 100644 --- a/udev/Makefile.am +++ b/udev/Makefile.am @@ -55,12 +55,12 @@ udevd_LDADD = \ udevadm_SOURCES = \ $(common_files) \ udevadm.c \ - udevinfo.c \ - udevcontrol.c \ - udevtest.c \ - udevmonitor.c \ - udevsettle.c \ - udevtrigger.c + udevadm-info.c \ + udevadm-control.c \ + udevadm-test.c \ + udevadm-monitor.c \ + udevadm-settle.c \ + udevadm-trigger.c udevadm_LDADD = \ $(common_ldadd) diff --git a/udev/udevadm-control.c b/udev/udevadm-control.c new file mode 100644 index 0000000000..5f1952543c --- /dev/null +++ b/udev/udevadm-control.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2005-2006 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 version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" + +static int udev_log = 0; + +struct udev_ctrl; +extern struct udev_ctrl *udev_ctrl_new_from_socket(const char *socket_path); +extern void udev_ctrl_unref(struct udev_ctrl *uctrl); +extern int udev_ctrl_set_log_level(struct udev_ctrl *uctrl, int priority); +extern int udev_ctrl_stop_exec_queue(struct udev_ctrl *uctrl); +extern int udev_ctrl_start_exec_queue(struct udev_ctrl *uctrl); +extern int udev_ctrl_reload_rules(struct udev_ctrl *uctrl); +extern int udev_ctrl_set_env(struct udev_ctrl *uctrl, const char *key); +extern int udev_ctrl_set_max_childs(struct udev_ctrl *uctrl, int count); +extern int udev_ctrl_set_max_childs_running(struct udev_ctrl *uctrl, int count); + +struct udev_ctrl { + int sock; + struct sockaddr_un saddr; + socklen_t addrlen; +}; + +struct udev_ctrl *udev_ctrl_new_from_socket(const char *socket_path) +{ + struct udev_ctrl *uctrl; + + uctrl = malloc(sizeof(struct udev_ctrl)); + if (uctrl == NULL) + return NULL; + memset(uctrl, 0x00, sizeof(struct udev_ctrl)); + + uctrl->sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (uctrl->sock < 0) { + err("error getting socket: %s\n", strerror(errno)); + free(uctrl); + return NULL; + } + + uctrl->saddr.sun_family = AF_LOCAL; + strcpy(uctrl->saddr.sun_path, socket_path); + uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path); + /* translate leading '@' to abstract namespace */ + if (uctrl->saddr.sun_path[0] == '@') + uctrl->saddr.sun_path[0] = '\0'; + return uctrl; +} + +void udev_ctrl_unref(struct udev_ctrl *uctrl) +{ + if (uctrl == NULL) + return; + close(uctrl->sock); +} + +static int ctrl_send(struct udev_ctrl *uctrl, enum udevd_ctrl_msg_type type, int intval, const char *buf) +{ + struct udevd_ctrl_msg ctrl_msg; + int err; + + memset(&ctrl_msg, 0x00, sizeof(struct udevd_ctrl_msg)); + strcpy(ctrl_msg.magic, UDEVD_CTRL_MAGIC); + ctrl_msg.type = type; + + if (buf != NULL) + strlcpy(ctrl_msg.buf, buf, sizeof(ctrl_msg.buf)); + else + ctrl_msg.intval = intval; + + err = sendto(uctrl->sock, &ctrl_msg, sizeof(ctrl_msg), 0, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); + if (err == -1) { + err("error sending message: %s\n", strerror(errno)); + } + return err; +} + +int udev_ctrl_set_log_level(struct udev_ctrl *uctrl, int priority) +{ + ctrl_send(uctrl, UDEVD_CTRL_SET_LOG_LEVEL, priority, NULL); + return 0; +} + +int udev_ctrl_stop_exec_queue(struct udev_ctrl *uctrl) +{ + ctrl_send(uctrl, UDEVD_CTRL_STOP_EXEC_QUEUE, 0, NULL); + return 0; +} + +int udev_ctrl_start_exec_queue(struct udev_ctrl *uctrl) +{ + ctrl_send(uctrl, UDEVD_CTRL_START_EXEC_QUEUE, 0, NULL); + return 0; +} + +int udev_ctrl_reload_rules(struct udev_ctrl *uctrl) +{ + ctrl_send(uctrl, UDEVD_CTRL_RELOAD_RULES, 0, NULL); + return 0; +} + +int udev_ctrl_set_env(struct udev_ctrl *uctrl, const char *key) +{ + ctrl_send(uctrl, UDEVD_CTRL_ENV, 0, optarg); + return 0; +} + +int udev_ctrl_set_max_childs(struct udev_ctrl *uctrl, int count) +{ + ctrl_send(uctrl, UDEVD_CTRL_SET_MAX_CHILDS, count, NULL); + return 0; +} + +int udev_ctrl_set_max_childs_running(struct udev_ctrl *uctrl, int count) +{ + ctrl_send(uctrl, UDEVD_CTRL_SET_MAX_CHILDS_RUNNING, count, NULL); + return 0; +} + +int udevcontrol(int argc, char *argv[]) +{ + struct udev_ctrl *uctrl; + const char *env; + int rc = 1; + + /* compat values with '_' will be removed in a future release */ + static const struct option options[] = { + { "log-priority", 1, NULL, 'l' }, + { "log_priority", 1, NULL, 'l' + 256 }, + { "stop-exec-queue", 0, NULL, 's' }, + { "stop_exec_queue", 0, NULL, 's' + 256 }, + { "start-exec-queue", 0, NULL, 'S' }, + { "start_exec_queue", 0, NULL, 'S' + 256}, + { "reload-rules", 0, NULL, 'R' }, + { "reload_rules", 0, NULL, 'R' + 256}, + { "env", 1, NULL, 'e' }, + { "max-childs", 1, NULL, 'm' }, + { "max_childs", 1, NULL, 'm' + 256}, + { "max-childs-running", 1, NULL, 'M' }, + { "max_childs_running", 1, NULL, 'M' + 256}, + { "help", 0, NULL, 'h' }, + {} + }; + + env = getenv("UDEV_LOG"); + if (env) + udev_log = log_priority(env); + + logging_init("udevcontrol"); + dbg("version %s\n", VERSION); + + if (getuid() != 0) { + fprintf(stderr, "root privileges required\n"); + goto exit; + } + + uctrl = udev_ctrl_new_from_socket(UDEVD_CTRL_SOCK_PATH); + if (uctrl == NULL) + goto exit; + + while (1) { + int option; + int i; + char *endp; + + option = getopt_long(argc, argv, "l:sSRe:m:M:h", options, NULL); + if (option == -1) + break; + + if (option > 255) { + info("udevadm control expects commands without underscore, " + "this will stop working in a future release\n"); + fprintf(stderr, "udevadm control expects commands without underscore, " + "this will stop working in a future release\n"); + } + + switch (option) { + case 'l': + case 'l' + 256: + i = log_priority(optarg); + if (i < 0) { + fprintf(stderr, "invalid number '%s'\n", optarg); + goto exit; + } + udev_ctrl_set_log_level(uctrl, log_priority(optarg)); + break; + case 's': + case 's' + 256: + udev_ctrl_stop_exec_queue(uctrl); + break; + case 'S': + case 'S' + 256: + udev_ctrl_start_exec_queue(uctrl); + break; + case 'R': + case 'R' + 256: + udev_ctrl_reload_rules(uctrl); + break; + case 'e': + if (strchr(optarg, '=') == NULL) { + fprintf(stderr, "expect = instead of '%s'\n", optarg); + goto exit; + } + udev_ctrl_set_env(uctrl, optarg); + break; + case 'm': + case 'm' + 256: + i = strtoul(optarg, &endp, 0); + if (endp[0] != '\0' || i < 1) { + fprintf(stderr, "invalid number '%s'\n", optarg); + goto exit; + } + udev_ctrl_set_max_childs(uctrl, i); + break; + case 'M': + case 'M' + 256: + i = strtoul(optarg, &endp, 0); + if (endp[0] != '\0' || i < 1) { + fprintf(stderr, "invalid number '%s'\n", optarg); + goto exit; + } + udev_ctrl_set_max_childs_running(uctrl, i); + break; + break; + case 'h': + printf("Usage: udevadm control COMMAND\n" + " --log-priority= set the udev log level for the daemon\n" + " --stop-exec-queue keep udevd from executing events, queue only\n" + " --start-exec-queue execute events, flush queue\n" + " --reload-rules reloads the rules files\n" + " --env== set a global environment variable\n" + " --max-childs= maximum number of childs\n" + " --max-childs-running= maximum number of childs running at the same time\n" + " --help print this help text\n\n"); + goto exit; + default: + goto exit; + } + } + + /* compat stuff which will be removed in a future release */ + if (argv[optind] != NULL) { + const char *arg = argv[optind]; + + fprintf(stderr, "udevadm control commands requires the -- format, " + "this will stop working in a future release\n"); + err("udevadm control commands requires the -- format, " + "this will stop working in a future release\n"); + + if (!strncmp(arg, "log_priority=", strlen("log_priority="))) { + udev_ctrl_set_log_level(uctrl, log_priority(&arg[strlen("log_priority=")])); + } else if (!strcmp(arg, "stop_exec_queue")) { + udev_ctrl_stop_exec_queue(uctrl); + } else if (!strcmp(arg, "start_exec_queue")) { + udev_ctrl_start_exec_queue(uctrl); + } else if (!strcmp(arg, "reload_rules")) { + udev_ctrl_reload_rules(uctrl); + } else if (!strncmp(arg, "max_childs=", strlen("max_childs="))) { + udev_ctrl_set_max_childs(uctrl, strtoul(&arg[strlen("max_childs=")], NULL, 0)); + } else if (!strncmp(arg, "max_childs_running=", strlen("max_childs_running="))) { + udev_ctrl_set_max_childs_running(uctrl, strtoul(&arg[strlen("max_childs_running=")], NULL, 0)); + } else if (!strncmp(arg, "env", strlen("env"))) { + udev_ctrl_set_env(uctrl, &arg[strlen("env=")]); + } else { + fprintf(stderr, "unrecognized command '%s'\n", arg); + err("unrecognized command '%s'\n", arg); + } + } +exit: + udev_ctrl_unref(uctrl); + logging_close(); + return rc; +} diff --git a/udev/udevadm-info.c b/udev/udevadm-info.c new file mode 100644 index 0000000000..6501b57d1a --- /dev/null +++ b/udev/udevadm-info.c @@ -0,0 +1,507 @@ +/* + * Copyright (C) 2004-2006 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 version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" + +static void print_all_attributes(const char *devpath, const char *key) +{ + char path[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + + dir = opendir(path); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct stat statbuf; + char filename[PATH_SIZE]; + char *attr_value; + char value[NAME_SIZE]; + size_t len; + + if (dent->d_name[0] == '.') + continue; + + if (strcmp(dent->d_name, "uevent") == 0) + continue; + if (strcmp(dent->d_name, "dev") == 0) + continue; + + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, dent->d_name, sizeof(filename)); + if (lstat(filename, &statbuf) != 0) + continue; + if (S_ISLNK(statbuf.st_mode)) + continue; + + attr_value = sysfs_attr_get_value(devpath, dent->d_name); + if (attr_value == NULL) + continue; + len = strlcpy(value, attr_value, sizeof(value)); + if(len >= sizeof(value)) + len = sizeof(value) - 1; + dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len); + + /* remove trailing newlines */ + while (len && value[len-1] == '\n') + value[--len] = '\0'; + + /* skip nonprintable attributes */ + while (len && isprint(value[len-1])) + len--; + if (len) { + dbg("attribute value of '%s' non-printable, skip\n", dent->d_name); + continue; + } + + printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value); + } + } + printf("\n"); +} + +static int print_device_chain(const char *devpath) +{ + struct sysfs_device *dev; + + dev = sysfs_device_get(devpath); + if (dev == NULL) + return -1; + + printf("\n" + "Udevinfo starts with the device specified by the devpath and then\n" + "walks up the chain of parent devices. It prints for every device\n" + "found, all possible attributes in the udev rules key format.\n" + "A rule to match, can be composed by the attributes of the device\n" + "and the attributes from one single parent device.\n" + "\n"); + + printf(" looking at device '%s':\n", dev->devpath); + printf(" KERNEL==\"%s\"\n", dev->kernel); + printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem); + printf(" DRIVER==\"%s\"\n", dev->driver); + print_all_attributes(dev->devpath, "ATTR"); + + /* walk up the chain of devices */ + while (1) { + dev = sysfs_device_get_parent(dev); + if (dev == NULL) + break; + printf(" looking at parent device '%s':\n", dev->devpath); + printf(" KERNELS==\"%s\"\n", dev->kernel); + printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem); + printf(" DRIVERS==\"%s\"\n", dev->driver); + + print_all_attributes(dev->devpath, "ATTRS"); + } + + return 0; +} + +static void print_record(struct udevice *udev) +{ + struct name_entry *name_loop; + + printf("P: %s\n", udev->dev->devpath); + printf("N: %s\n", udev->name); + list_for_each_entry(name_loop, &udev->symlink_list, node) + printf("S: %s\n", name_loop->name); + if (udev->link_priority != 0) + printf("L: %i\n", udev->link_priority); + if (udev->partitions != 0) + printf("A:%u\n", udev->partitions); + if (udev->ignore_remove) + printf("R:%u\n", udev->ignore_remove); + list_for_each_entry(name_loop, &udev->env_list, node) + printf("E: %s\n", name_loop->name); +} + +static void export_db(void) { + LIST_HEAD(name_list); + struct name_entry *name_loop; + + udev_db_get_all_entries(&name_list); + list_for_each_entry(name_loop, &name_list, node) { + struct udevice *udev_db; + + udev_db = udev_device_init(); + if (udev_db == NULL) + continue; + if (udev_db_get_device(udev_db, name_loop->name) == 0) + print_record(udev_db); + printf("\n"); + udev_device_cleanup(udev_db); + } + name_list_cleanup(&name_list); +} + +static int lookup_device_by_name(struct udevice **udev, const char *name) +{ + LIST_HEAD(name_list); + int count; + struct name_entry *device; + int rc = -1; + + count = udev_db_get_devices_by_name(name, &name_list); + if (count <= 0) + goto out; + + info("found %i devices for '%s'\n", count, name); + + /* select the device that seems to match */ + list_for_each_entry(device, &name_list, node) { + struct udevice *udev_loop; + char filename[PATH_SIZE]; + struct stat statbuf; + + udev_loop = udev_device_init(); + if (udev_loop == NULL) + break; + if (udev_db_get_device(udev_loop, device->name) != 0) + goto next; + info("found db entry '%s'\n", device->name); + + /* make sure, we don't get a link of a different device */ + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/", sizeof(filename)); + strlcat(filename, name, sizeof(filename)); + if (stat(filename, &statbuf) != 0) + goto next; + if (major(udev_loop->devt) > 0 && udev_loop->devt != statbuf.st_rdev) { + info("skip '%s', dev_t doesn't match\n", udev_loop->name); + goto next; + } + rc = 0; + *udev = udev_loop; + break; +next: + udev_device_cleanup(udev_loop); + } +out: + name_list_cleanup(&name_list); + return rc; +} + +static int stat_device(const char *name, int export, const char *prefix) +{ + struct stat statbuf; + + if (stat(name, &statbuf) != 0) + return -1; + + if (export) { + if (prefix == NULL) + prefix = "INFO_"; + printf("%sMAJOR=%d\n" + "%sMINOR=%d\n", + prefix, major(statbuf.st_dev), + prefix, minor(statbuf.st_dev)); + } else + printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); + return 0; +} + +int udevinfo(int argc, char *argv[]) +{ + int option; + struct udevice *udev = NULL; + int root = 0; + int export = 0; + const char *export_prefix = NULL; + + static const struct option options[] = { + { "name", 1, NULL, 'n' }, + { "path", 1, NULL, 'p' }, + { "query", 1, NULL, 'q' }, + { "attribute-walk", 0, NULL, 'a' }, + { "export-db", 0, NULL, 'e' }, + { "root", 0, NULL, 'r' }, + { "device-id-of-file", 1, NULL, 'd' }, + { "export", 0, NULL, 'x' }, + { "export-prefix", 1, NULL, 'P' }, + { "version", 0, NULL, 1 }, /* -V outputs braindead format */ + { "help", 0, NULL, 'h' }, + {} + }; + + enum action_type { + ACTION_NONE, + ACTION_QUERY, + ACTION_ATTRIBUTE_WALK, + ACTION_ROOT, + ACTION_DEVICE_ID_FILE, + } action = ACTION_NONE; + + enum query_type { + QUERY_NONE, + QUERY_NAME, + QUERY_PATH, + QUERY_SYMLINK, + QUERY_ENV, + QUERY_ALL, + } query = QUERY_NONE; + + char path[PATH_SIZE] = ""; + char name[PATH_SIZE] = ""; + struct name_entry *name_loop; + int rc = 0; + + logging_init("udevinfo"); + udev_config_init(); + sysfs_init(); + + while (1) { + option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL); + if (option == -1) + break; + + dbg("option '%c'\n", option); + switch (option) { + case 'n': + /* remove /dev if given */ + if (strncmp(optarg, udev_root, strlen(udev_root)) == 0) + strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name)); + else + strlcpy(name, optarg, sizeof(name)); + remove_trailing_chars(name, '/'); + dbg("name: %s\n", name); + break; + case 'p': + /* remove /sys if given */ + if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0) + strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path)); + else + strlcpy(path, optarg, sizeof(path)); + remove_trailing_chars(path, '/'); + + /* possibly resolve to real devpath */ + if (sysfs_resolve_link(path, sizeof(path)) != 0) { + char temp[PATH_SIZE]; + char *pos; + + /* also check if the parent is a link */ + strlcpy(temp, path, sizeof(temp)); + pos = strrchr(temp, '/'); + if (pos != 0) { + char tail[PATH_SIZE]; + + strlcpy(tail, pos, sizeof(tail)); + pos[0] = '\0'; + if (sysfs_resolve_link(temp, sizeof(temp)) == 0) { + strlcpy(path, temp, sizeof(path)); + strlcat(path, tail, sizeof(path)); + } + } + } + dbg("path: %s\n", path); + break; + case 'q': + action = ACTION_QUERY; + if (strcmp(optarg, "name") == 0) { + query = QUERY_NAME; + break; + } + if (strcmp(optarg, "symlink") == 0) { + query = QUERY_SYMLINK; + break; + } + if (strcmp(optarg, "path") == 0) { + query = QUERY_PATH; + break; + } + if (strcmp(optarg, "env") == 0) { + query = QUERY_ENV; + break; + } + if (strcmp(optarg, "all") == 0) { + query = QUERY_ALL; + break; + } + fprintf(stderr, "unknown query type\n"); + rc = 2; + goto exit; + case 'r': + if (action == ACTION_NONE) + action = ACTION_ROOT; + root = 1; + break; + case 'd': + action = ACTION_DEVICE_ID_FILE; + strlcpy(name, optarg, sizeof(name)); + break; + case 'a': + action = ACTION_ATTRIBUTE_WALK; + break; + case 'e': + export_db(); + goto exit; + case 'x': + export = 1; + break; + case 'P': + export_prefix = optarg; + break; + case 1: + printf("%s\n", VERSION); + goto exit; + case 'V': + printf("udevinfo, version %s\n", VERSION); + goto exit; + case 'h': + printf("Usage: udevadm info OPTIONS\n" + " --query= query database for the specified value:\n" + " name name of device node\n" + " symlink pointing to node\n" + " path sysfs device path\n" + " env the device related imported environment\n" + " all all values\n" + " --path= sysfs device path used for query or chain\n" + " --name= node or symlink name used for query\n" + " --root prepend to query result or print udev_root\n" + " --attribute-walk print all key matches while walking along chain\n" + " of parent devices\n" + " --device-id-of-file= print major/minor of underlying device\n" + " --export-db export the content of the udev database\n" + " --help print this text\n" + "\n"); + goto exit; + default: + goto exit; + } + } + + /* run action */ + switch (action) { + case ACTION_QUERY: + /* needs devpath or node/symlink name for query */ + if (path[0] != '\0') { + udev = udev_device_init(); + if (udev == NULL) { + rc = 1; + goto exit; + } + if (udev_db_get_device(udev, path) != 0) { + fprintf(stderr, "no record for '%s' in database\n", path); + rc = 3; + goto exit; + } + } else if (name[0] != '\0') { + if (lookup_device_by_name(&udev, name) != 0) { + fprintf(stderr, "node name not found\n"); + rc = 4; + goto exit; + } + } else { + fprintf(stderr, "query needs --path or node --name specified\n"); + rc = 4; + goto exit; + } + + switch(query) { + case QUERY_NAME: + if (root) + printf("%s/%s\n", udev_root, udev->name); + else + printf("%s\n", udev->name); + break; + case QUERY_SYMLINK: + list_for_each_entry(name_loop, &udev->symlink_list, node) { + char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n'; + + if (root) + printf("%s/%s%c", udev_root, name_loop->name, c); + else + printf("%s%c", name_loop->name, c); + } + break; + case QUERY_PATH: + printf("%s\n", udev->dev->devpath); + goto exit; + case QUERY_ENV: + list_for_each_entry(name_loop, &udev->env_list, node) + printf("%s\n", name_loop->name); + break; + case QUERY_ALL: + print_record(udev); + break; + default: + fprintf(stderr, "unknown query type\n"); + break; + } + break; + case ACTION_ATTRIBUTE_WALK: + if (path[0] != '\0') { + if (print_device_chain(path) != 0) { + fprintf(stderr, "no valid sysfs device found\n"); + rc = 4; + goto exit; + } + } else if (name[0] != '\0') { + if (lookup_device_by_name(&udev, name) != 0) { + fprintf(stderr, "node name not found\n"); + rc = 4; + goto exit; + } + if (print_device_chain(udev->dev->devpath) != 0) { + fprintf(stderr, "no valid sysfs device found\n"); + rc = 4; + goto exit; + } + } else { + fprintf(stderr, "attribute walk needs --path or node --name specified\n"); + rc = 5; + goto exit; + } + break; + case ACTION_DEVICE_ID_FILE: + if (stat_device(name, export, export_prefix) != 0) + rc = 6; + break; + case ACTION_ROOT: + printf("%s\n", udev_root); + break; + default: + fprintf(stderr, "missing option\n"); + rc = 1; + break; + } + +exit: + udev_device_cleanup(udev); + sysfs_cleanup(); + logging_close(); + return rc; +} diff --git a/udev/udevadm-monitor.c b/udev/udevadm-monitor.c new file mode 100644 index 0000000000..3b65bcae9b --- /dev/null +++ b/udev/udevadm-monitor.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2004-2006 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 version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" + +static int uevent_netlink_sock = -1; +static int udev_monitor_sock = -1; +static volatile int udev_exit; + +static int init_udev_monitor_socket(void) +{ + struct sockaddr_un saddr; + socklen_t addrlen; + int retval; + + memset(&saddr, 0x00, sizeof(saddr)); + saddr.sun_family = AF_LOCAL; + /* use abstract namespace for socket path */ + strcpy(&saddr.sun_path[1], "/org/kernel/udev/monitor"); + addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + + udev_monitor_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (udev_monitor_sock == -1) { + fprintf(stderr, "error getting socket: %s\n", strerror(errno)); + return -1; + } + + /* the bind takes care of ensuring only one copy running */ + retval = bind(udev_monitor_sock, (struct sockaddr *) &saddr, addrlen); + if (retval < 0) { + fprintf(stderr, "bind failed: %s\n", strerror(errno)); + close(udev_monitor_sock); + udev_monitor_sock = -1; + return -1; + } + + return 0; +} + +static int init_uevent_netlink_sock(void) +{ + struct sockaddr_nl snl; + int retval; + + memset(&snl, 0x00, sizeof(struct sockaddr_nl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); + snl.nl_groups = 1; + + uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if (uevent_netlink_sock == -1) { + fprintf(stderr, "error getting socket: %s\n", strerror(errno)); + return -1; + } + + retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, + sizeof(struct sockaddr_nl)); + if (retval < 0) { + fprintf(stderr, "bind failed: %s\n", strerror(errno)); + close(uevent_netlink_sock); + uevent_netlink_sock = -1; + return -1; + } + + return 0; +} + +static void asmlinkage sig_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) + udev_exit = 1; +} + +static const char *search_key(const char *searchkey, const char *buf, size_t buflen) +{ + size_t bufpos = 0; + size_t searchkeylen = strlen(searchkey); + + while (bufpos < buflen) { + const char *key; + int keylen; + + key = &buf[bufpos]; + keylen = strlen(key); + if (keylen == 0) + break; + if ((strncmp(searchkey, key, searchkeylen) == 0) && key[searchkeylen] == '=') + return &key[searchkeylen + 1]; + bufpos += keylen + 1; + } + return NULL; +} + +int udevmonitor(int argc, char *argv[]) +{ + struct sigaction act; + int option; + int env = 0; + int kernel = 0; + int udev = 0; + fd_set readfds; + int retval = 0; + + static const struct option options[] = { + { "environment", 0, NULL, 'e' }, + { "kernel", 0, NULL, 'k' }, + { "udev", 0, NULL, 'u' }, + { "help", 0, NULL, 'h' }, + {} + }; + + while (1) { + option = getopt_long(argc, argv, "ekuh", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'e': + env = 1; + break; + case 'k': + kernel = 1; + break; + case 'u': + udev = 1; + break; + case 'h': + printf("Usage: udevadm monitor [--environment] [--kernel] [--udev] [--help]\n" + " --env print the whole event environment\n" + " --kernel print kernel uevents\n" + " --udev print udev events\n" + " --help print this help text\n\n"); + default: + goto out; + } + } + + if (!kernel && !udev) { + kernel = 1; + udev =1; + } + + if (getuid() != 0 && kernel) { + fprintf(stderr, "root privileges needed to subscribe to kernel events\n"); + goto out; + } + + /* set signal handlers */ + memset(&act, 0x00, sizeof(struct sigaction)); + act.sa_handler = (void (*)(int)) sig_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + + printf("udevmonitor will print the received events for:\n"); + if (udev) { + retval = init_udev_monitor_socket(); + if (retval) + goto out; + printf("UDEV the event which udev sends out after rule processing\n"); + } + if (kernel) { + retval = init_uevent_netlink_sock(); + if (retval) + goto out; + printf("UEVENT the kernel uevent\n"); + } + printf("\n"); + + while (!udev_exit) { + char buf[UEVENT_BUFFER_SIZE*2]; + ssize_t buflen; + ssize_t bufpos; + ssize_t keys; + int fdcount; + struct timeval tv; + struct timezone tz; + char timestr[64]; + const char *source = NULL; + const char *devpath, *action, *subsys; + + buflen = 0; + FD_ZERO(&readfds); + if (uevent_netlink_sock >= 0) + FD_SET(uevent_netlink_sock, &readfds); + if (udev_monitor_sock >= 0) + FD_SET(udev_monitor_sock, &readfds); + + fdcount = select(UDEV_MAX(uevent_netlink_sock, udev_monitor_sock)+1, &readfds, NULL, NULL, NULL); + if (fdcount < 0) { + if (errno != EINTR) + fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); + continue; + } + + if (gettimeofday(&tv, &tz) == 0) { + snprintf(timestr, sizeof(timestr), "%llu.%06u", + (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec); + } else + timestr[0] = '\0'; + + if ((uevent_netlink_sock >= 0) && FD_ISSET(uevent_netlink_sock, &readfds)) { + buflen = recv(uevent_netlink_sock, &buf, sizeof(buf), 0); + if (buflen <= 0) { + fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); + continue; + } + source = "UEVENT"; + } + + if ((udev_monitor_sock >= 0) && FD_ISSET(udev_monitor_sock, &readfds)) { + buflen = recv(udev_monitor_sock, &buf, sizeof(buf), 0); + if (buflen <= 0) { + fprintf(stderr, "error receiving udev message: %s\n", strerror(errno)); + continue; + } + source = "UDEV "; + } + + if (buflen == 0) + continue; + + keys = strlen(buf) + 1; /* start of payload */ + devpath = search_key("DEVPATH", &buf[keys], buflen); + action = search_key("ACTION", &buf[keys], buflen); + subsys = search_key("SUBSYSTEM", &buf[keys], buflen); + printf("%s[%s] %-8s %s (%s)\n", source, timestr, action, devpath, subsys); + + /* print environment */ + bufpos = keys; + if (env) { + while (bufpos < buflen) { + int keylen; + char *key; + + key = &buf[bufpos]; + keylen = strlen(key); + if (keylen == 0) + break; + printf("%s\n", key); + bufpos += keylen + 1; + } + printf("\n"); + } + } + +out: + if (uevent_netlink_sock >= 0) + close(uevent_netlink_sock); + if (udev_monitor_sock >= 0) + close(udev_monitor_sock); + + if (retval) + return 1; + return 0; +} diff --git a/udev/udevadm-settle.c b/udev/udevadm-settle.c new file mode 100644 index 0000000000..11277f5a43 --- /dev/null +++ b/udev/udevadm-settle.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2006 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 version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" + +#define DEFAULT_TIMEOUT 180 +#define LOOP_PER_SECOND 20 + +static void print_queue(const char *dir) +{ + LIST_HEAD(files); + struct name_entry *item; + + if (add_matching_files(&files, dir, NULL) < 0) + return; + + printf("\n\nAfter the udevadm settle timeout, the events queue contains:\n\n"); + + list_for_each_entry(item, &files, node) { + char target[NAME_SIZE]; + size_t len; + const char *filename = strrchr(item->name, '/'); + + if (filename == NULL) + continue; + filename++; + if (*filename == '\0') + continue; + + len = readlink(item->name, target, sizeof(target)); + if (len < 0) + continue; + target[len] = '\0'; + + printf("%s: %s\n", filename, target); + } + + printf("\n\n"); +} + +int udevsettle(int argc, char *argv[]) +{ + char queuename[PATH_SIZE]; + char filename[PATH_SIZE]; + unsigned long long seq_kernel; + unsigned long long seq_udev; + char seqnum[32]; + int fd; + ssize_t len; + int timeout = DEFAULT_TIMEOUT; + int loop; + static const struct option options[] = { + { "timeout", 1, NULL, 't' }, + { "help", 0, NULL, 'h' }, + {} + }; + int option; + int rc = 1; + int seconds; + + logging_init("udevsettle"); + udev_config_init(); + dbg("version %s\n", VERSION); + sysfs_init(); + + while (1) { + option = getopt_long(argc, argv, "t:h", options, NULL); + if (option == -1) + break; + + switch (option) { + case 't': + seconds = atoi(optarg); + if (seconds > 0) + timeout = seconds; + else + fprintf(stderr, "invalid timeout value\n"); + dbg("timeout=%i\n", timeout); + break; + case 'h': + printf("Usage: udevadm settle [--help] [--timeout=]\n\n"); + goto exit; + } + } + + strlcpy(queuename, udev_root, sizeof(queuename)); + strlcat(queuename, "/.udev/queue", sizeof(queuename)); + + loop = timeout * LOOP_PER_SECOND; + while (loop--) { + /* wait for events in queue to finish */ + while (loop--) { + struct stat statbuf; + + if (stat(queuename, &statbuf) < 0) { + info("queue is empty\n"); + break; + } + usleep(1000 * 1000 / LOOP_PER_SECOND); + } + if (loop <= 0) { + info("timeout waiting for queue\n"); + print_queue(queuename); + goto exit; + } + + /* read current udev seqnum */ + strlcpy(filename, udev_root, sizeof(filename)); + strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); + fd = open(filename, O_RDONLY); + if (fd < 0) + goto exit; + len = read(fd, seqnum, sizeof(seqnum)-1); + close(fd); + if (len <= 0) + goto exit; + seqnum[len] = '\0'; + seq_udev = strtoull(seqnum, NULL, 10); + info("udev seqnum = %llu\n", seq_udev); + + /* read current kernel seqnum */ + strlcpy(filename, sysfs_path, sizeof(filename)); + strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); + fd = open(filename, O_RDONLY); + if (fd < 0) + goto exit; + len = read(fd, seqnum, sizeof(seqnum)-1); + close(fd); + if (len <= 0) + goto exit; + seqnum[len] = '\0'; + seq_kernel = strtoull(seqnum, NULL, 10); + info("kernel seqnum = %llu\n", seq_kernel); + + /* make sure all kernel events have arrived in the queue */ + if (seq_udev >= seq_kernel) { + info("queue is empty and no pending events left\n"); + rc = 0; + goto exit; + } + usleep(1000 * 1000 / LOOP_PER_SECOND); + info("queue is empty, but events still pending\n"); + } + +exit: + sysfs_cleanup(); + logging_close(); + return rc; +} diff --git a/udev/udevadm-test.c b/udev/udevadm-test.c new file mode 100644 index 0000000000..63603aad97 --- /dev/null +++ b/udev/udevadm-test.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2003-2004 Greg Kroah-Hartman + * Copyright (C) 2004-2006 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 version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udev_rules.h" + +static int import_uevent_var(const char *devpath) +{ + char path[PATH_SIZE]; + static char value[4096]; /* must stay, used with putenv */ + ssize_t size; + int fd; + char *key; + char *next; + int rc = -1; + + /* read uevent file */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + strlcat(path, "/uevent", sizeof(path)); + fd = open(path, O_RDONLY); + if (fd < 0) + goto out; + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + goto out; + value[size] = '\0'; + + /* import keys into environment */ + key = value; + while (key[0] != '\0') { + next = strchr(key, '\n'); + if (next == NULL) + goto out; + next[0] = '\0'; + info("import into environment: '%s'\n", key); + putenv(key); + key = &next[1]; + } + rc = 0; +out: + return rc; +} + +int udevtest(int argc, char *argv[]) +{ + int force = 0; + const char *action = "add"; + const char *subsystem = NULL; + const char *devpath = NULL; + struct udevice *udev; + struct sysfs_device *dev; + struct udev_rules rules = {}; + int retval; + int rc = 0; + + static const struct option options[] = { + { "action", 1, NULL, 'a' }, + { "subsystem", 1, NULL, 's' }, + { "force", 0, NULL, 'f' }, + { "help", 0, NULL, 'h' }, + {} + }; + + info("version %s\n", VERSION); + udev_config_init(); + if (udev_log_priority < LOG_INFO) { + char priority[32]; + + udev_log_priority = LOG_INFO; + sprintf(priority, "%i", udev_log_priority); + setenv("UDEV_LOG", priority, 1); + } + + while (1) { + int option; + + option = getopt_long(argc, argv, "a:s:fh", options, NULL); + if (option == -1) + break; + + dbg("option '%c'\n", option); + switch (option) { + case 'a': + action = optarg; + break; + case 's': + subsystem = optarg; + break; + case 'f': + force = 1; + break; + case 'h': + printf("Usage: udevadm test OPTIONS \n" + " --action= set action string\n" + " --subsystem= set subsystem string\n" + " --force don't skip node/link creation\n" + " --help print this help text\n\n"); + exit(0); + default: + exit(1); + } + } + devpath = argv[optind]; + + if (devpath == NULL) { + fprintf(stderr, "devpath parameter missing\n"); + rc = 1; + goto exit; + } + + printf("This program is for debugging only, it does not run any program,\n" + "specified by a RUN key. It may show incorrect results, because\n" + "some values may be different, or not available at a simulation run.\n" + "\n"); + + sysfs_init(); + udev_rules_init(&rules, 0); + + /* remove /sys if given */ + if (strncmp(devpath, sysfs_path, strlen(sysfs_path)) == 0) + devpath = &devpath[strlen(sysfs_path)]; + + dev = sysfs_device_get(devpath); + if (dev == NULL) { + fprintf(stderr, "unable to open device '%s'\n", devpath); + rc = 2; + goto exit; + } + + udev = udev_device_init(); + if (udev == NULL) { + fprintf(stderr, "error initializing device\n"); + rc = 3; + goto exit; + } + + if (subsystem != NULL) + strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); + + /* override built-in sysfs device */ + udev->dev = dev; + strlcpy(udev->action, action, sizeof(udev->action)); + udev->devt = udev_device_get_devt(udev); + + /* simulate node creation with test flag */ + if (!force) + udev->test_run = 1; + + setenv("DEVPATH", udev->dev->devpath, 1); + setenv("SUBSYSTEM", udev->dev->subsystem, 1); + setenv("ACTION", udev->action, 1); + import_uevent_var(udev->dev->devpath); + + info("looking at device '%s' from subsystem '%s'\n", udev->dev->devpath, udev->dev->subsystem); + retval = udev_device_event(&rules, udev); + + if (udev->event_timeout >= 0) + info("custom event timeout: %i\n", udev->event_timeout); + + if (retval == 0 && !udev->ignore_device && udev_run) { + struct name_entry *name_loop; + + list_for_each_entry(name_loop, &udev->run_list, node) { + char program[PATH_SIZE]; + + strlcpy(program, name_loop->name, sizeof(program)); + udev_rules_apply_format(udev, program, sizeof(program)); + info("run: '%s'\n", program); + } + } + udev_device_cleanup(udev); + +exit: + udev_rules_cleanup(&rules); + sysfs_cleanup(); + return rc; +} diff --git a/udev/udevadm-trigger.c b/udev/udevadm-trigger.c new file mode 100644 index 0000000000..4a8f62ab5c --- /dev/null +++ b/udev/udevadm-trigger.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2004-2006 Kay Sievers + * Copyright (C) 2006 Hannes Reinecke + * + * 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 version 2 of the License. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "udev.h" +#include "udevd.h" +#include "udev_rules.h" + +static int verbose; +static int dry_run; +LIST_HEAD(device_list); +LIST_HEAD(filter_subsystem_match_list); +LIST_HEAD(filter_subsystem_nomatch_list); +LIST_HEAD(filter_attr_match_list); +LIST_HEAD(filter_attr_nomatch_list); +static int sock = -1; +static struct sockaddr_un saddr; +static socklen_t saddrlen; + +/* devices that should run last cause of their dependencies */ +static int delay_device(const char *devpath) +{ + static const char *delay_device_list[] = { + "*/md*", + "*/dm-*", + NULL + }; + int i; + + for (i = 0; delay_device_list[i] != NULL; i++) + if (fnmatch(delay_device_list[i], devpath, 0) == 0) + return 1; + return 0; +} + +static int device_list_insert(const char *path) +{ + char filename[PATH_SIZE]; + char devpath[PATH_SIZE]; + struct stat statbuf; + + dbg("add '%s'\n" , path); + + /* we only have a device, if we have an uevent file */ + strlcpy(filename, path, sizeof(filename)); + strlcat(filename, "/uevent", sizeof(filename)); + if (stat(filename, &statbuf) < 0) + return -1; + if (!(statbuf.st_mode & S_IWUSR)) + return -1; + + strlcpy(devpath, &path[strlen(sysfs_path)], sizeof(devpath)); + + /* resolve possible link to real target */ + if (lstat(path, &statbuf) < 0) + return -1; + if (S_ISLNK(statbuf.st_mode)) + if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) + return -1; + + name_list_add(&device_list, devpath, 1); + return 0; +} + +static void trigger_uevent(const char *devpath, const char *action) +{ + char filename[PATH_SIZE]; + int fd; + + strlcpy(filename, sysfs_path, sizeof(filename)); + strlcat(filename, devpath, sizeof(filename)); + strlcat(filename, "/uevent", sizeof(filename)); + + if (verbose) + printf("%s\n", devpath); + + if (dry_run) + return; + + fd = open(filename, O_WRONLY); + if (fd < 0) { + dbg("error on opening %s: %s\n", filename, strerror(errno)); + return; + } + + if (write(fd, action, strlen(action)) < 0) + info("error writing '%s' to '%s': %s\n", action, filename, strerror(errno)); + + close(fd); +} + +static int pass_to_socket(const char *devpath, const char *action, const char *env) +{ + struct udevice *udev; + struct name_entry *name_loop; + char buf[4096]; + size_t bufpos = 0; + ssize_t count; + char path[PATH_SIZE]; + int fd; + char link_target[PATH_SIZE]; + int len; + int err = 0; + + if (verbose) + printf("%s\n", devpath); + + udev = udev_device_init(); + if (udev == NULL) + return -1; + udev_db_get_device(udev, devpath); + + /* add header */ + bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath); + bufpos++; + + /* add cookie */ + if (env != NULL) { + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "%s", env); + bufpos++; + } + + /* add standard keys */ + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVPATH=%s", devpath); + bufpos++; + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "ACTION=%s", action); + bufpos++; + + /* add subsystem */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + strlcat(path, "/subsystem", sizeof(path)); + len = readlink(path, link_target, sizeof(link_target)); + if (len > 0) { + char *pos; + + link_target[len] = '\0'; + pos = strrchr(link_target, '/'); + if (pos != NULL) { + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "SUBSYSTEM=%s", &pos[1]); + bufpos++; + } + } + + /* add symlinks and node name */ + path[0] = '\0'; + list_for_each_entry(name_loop, &udev->symlink_list, node) { + strlcat(path, udev_root, sizeof(path)); + strlcat(path, "/", sizeof(path)); + strlcat(path, name_loop->name, sizeof(path)); + strlcat(path, " ", sizeof(path)); + } + remove_trailing_chars(path, ' '); + if (path[0] != '\0') { + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVLINKS=%s", path); + bufpos++; + } + if (udev->name[0] != '\0') { + strlcpy(path, udev_root, sizeof(path)); + strlcat(path, "/", sizeof(path)); + strlcat(path, udev->name, sizeof(path)); + bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVNAME=%s", path); + bufpos++; + } + + /* add keys from device "uevent" file */ + strlcpy(path, sysfs_path, sizeof(path)); + strlcat(path, devpath, sizeof(path)); + strlcat(path, "/uevent", sizeof(path)); + fd = open(path, O_RDONLY); + if (fd >= 0) { + char value[4096]; + + count = read(fd, value, sizeof(value)); + close(fd); + if (count > 0) { + char *key; + + value[count] = '\0'; + key = value; + while (key[0] != '\0') { + char *next; + + next = strchr(key, '\n'); + if (next == NULL) + break; + next[0] = '\0'; + bufpos += strlcpy(&buf[bufpos], key, sizeof(buf) - bufpos-1); + bufpos++; + key = &next[1]; + } + } + } + + /* add keys from database */ + list_for_each_entry(name_loop, &udev->env_list, node) { + bufpos += strlcpy(&buf[bufpos], name_loop->name, sizeof(buf) - bufpos-1); + bufpos++; + } + if (bufpos > sizeof(buf)) + bufpos = sizeof(buf); + + count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, saddrlen); + if (count < 0) + err = -1; + + return err; +} + +static void exec_list(const char *action, const char *env) +{ + struct name_entry *loop_device; + struct name_entry *tmp_device; + + list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { + if (delay_device(loop_device->name)) + continue; + if (sock >= 0) + pass_to_socket(loop_device->name, action, env); + else + trigger_uevent(loop_device->name, action); + list_del(&loop_device->node); + free(loop_device); + } + + /* trigger remaining delayed devices */ + list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { + if (sock >= 0) + pass_to_socket(loop_device->name, action, env); + else + trigger_uevent(loop_device->name, action); + list_del(&loop_device->node); + free(loop_device); + } +} + +static int subsystem_filtered(const char *subsystem) +{ + struct name_entry *loop_name; + + /* skip devices matching the listed subsystems */ + list_for_each_entry(loop_name, &filter_subsystem_nomatch_list, node) + if (fnmatch(loop_name->name, subsystem, 0) == 0) + return 1; + + /* skip devices not matching the listed subsystems */ + if (!list_empty(&filter_subsystem_match_list)) { + list_for_each_entry(loop_name, &filter_subsystem_match_list, node) + if (fnmatch(loop_name->name, subsystem, 0) == 0) + return 0; + return 1; + } + + return 0; +} + +static int attr_match(const char *path, const char *attr_value) +{ + char attr[NAME_SIZE]; + char file[PATH_SIZE]; + char *match_value; + + strlcpy(attr, attr_value, sizeof(attr)); + + /* separate attr and match value */ + match_value = strchr(attr, '='); + if (match_value != NULL) { + match_value[0] = '\0'; + match_value = &match_value[1]; + } + + strlcpy(file, path, sizeof(file)); + strlcat(file, "/", sizeof(file)); + strlcat(file, attr, sizeof(file)); + + if (match_value != NULL) { + /* match file content */ + char value[NAME_SIZE]; + int fd; + ssize_t size; + + fd = open(file, O_RDONLY); + if (fd < 0) + return 0; + size = read(fd, value, sizeof(value)); + close(fd); + if (size < 0) + return 0; + value[size] = '\0'; + remove_trailing_chars(value, '\n'); + + /* match if attribute value matches */ + if (fnmatch(match_value, value, 0) == 0) + return 1; + } else { + /* match if attribute exists */ + struct stat statbuf; + + if (stat(file, &statbuf) == 0) + return 1; + } + return 0; +} + +static int attr_filtered(const char *path) +{ + struct name_entry *loop_name; + + /* skip devices matching the listed sysfs attributes */ + list_for_each_entry(loop_name, &filter_attr_nomatch_list, node) + if (attr_match(path, loop_name->name)) + return 1; + + /* skip devices not matching the listed sysfs attributes */ + if (!list_empty(&filter_attr_match_list)) { + list_for_each_entry(loop_name, &filter_attr_match_list, node) + if (attr_match(path, loop_name->name)) + return 0; + return 1; + } + return 0; +} + +enum scan_type { + SCAN_DEVICES, + SCAN_SUBSYSTEM, +}; + +static void scan_subsystem(const char *subsys, enum scan_type scan) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + const char *subdir; + + if (scan == SCAN_DEVICES) + subdir = "/devices"; + else if (scan == SCAN_SUBSYSTEM) + subdir = "/drivers"; + else + return; + + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/", sizeof(base)); + strlcat(base, subsys, sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char dirname[PATH_SIZE]; + DIR *dir2; + struct dirent *dent2; + + if (dent->d_name[0] == '.') + continue; + + if (scan == SCAN_DEVICES) + if (subsystem_filtered(dent->d_name)) + continue; + + strlcpy(dirname, base, sizeof(dirname)); + strlcat(dirname, "/", sizeof(dirname)); + strlcat(dirname, dent->d_name, sizeof(dirname)); + + if (scan == SCAN_SUBSYSTEM) { + if (attr_filtered(dirname)) + continue; + if (!subsystem_filtered("subsystem")) + device_list_insert(dirname); + if (subsystem_filtered("drivers")) + continue; + } + + strlcat(dirname, subdir, sizeof(dirname)); + + /* look for devices/drivers */ + dir2 = opendir(dirname); + if (dir2 != NULL) { + for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { + char dirname2[PATH_SIZE]; + + if (dent2->d_name[0] == '.') + continue; + + strlcpy(dirname2, dirname, sizeof(dirname2)); + strlcat(dirname2, "/", sizeof(dirname2)); + strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; + device_list_insert(dirname2); + } + closedir(dir2); + } + } + closedir(dir); + } +} + +static void scan_block(void) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + if (subsystem_filtered("block")) + return; + + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/block", sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char dirname[PATH_SIZE]; + DIR *dir2; + struct dirent *dent2; + + if (dent->d_name[0] == '.') + continue; + + strlcpy(dirname, base, sizeof(dirname)); + strlcat(dirname, "/", sizeof(dirname)); + strlcat(dirname, dent->d_name, sizeof(dirname)); + if (attr_filtered(dirname)) + continue; + if (device_list_insert(dirname) != 0) + continue; + + /* look for partitions */ + dir2 = opendir(dirname); + if (dir2 != NULL) { + for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { + char dirname2[PATH_SIZE]; + + if (dent2->d_name[0] == '.') + continue; + + if (!strcmp(dent2->d_name,"device")) + continue; + + strlcpy(dirname2, dirname, sizeof(dirname2)); + strlcat(dirname2, "/", sizeof(dirname2)); + strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; + device_list_insert(dirname2); + } + closedir(dir2); + } + } + closedir(dir); + } +} + +static void scan_class(void) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/class", sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char dirname[PATH_SIZE]; + DIR *dir2; + struct dirent *dent2; + + if (dent->d_name[0] == '.') + continue; + + if (subsystem_filtered(dent->d_name)) + continue; + + strlcpy(dirname, base, sizeof(dirname)); + strlcat(dirname, "/", sizeof(dirname)); + strlcat(dirname, dent->d_name, sizeof(dirname)); + dir2 = opendir(dirname); + if (dir2 != NULL) { + for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { + char dirname2[PATH_SIZE]; + + if (dent2->d_name[0] == '.') + continue; + + if (!strcmp(dent2->d_name, "device")) + continue; + + strlcpy(dirname2, dirname, sizeof(dirname2)); + strlcat(dirname2, "/", sizeof(dirname2)); + strlcat(dirname2, dent2->d_name, sizeof(dirname2)); + if (attr_filtered(dirname2)) + continue; + device_list_insert(dirname2); + } + closedir(dir2); + } + } + closedir(dir); + } +} + +static void scan_failed(void) +{ + char base[PATH_SIZE]; + DIR *dir; + struct dirent *dent; + + strlcpy(base, udev_root, sizeof(base)); + strlcat(base, "/.udev/failed", sizeof(base)); + + dir = opendir(base); + if (dir != NULL) { + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + char device[PATH_SIZE]; + size_t start; + + if (dent->d_name[0] == '.') + continue; + + start = strlcpy(device, sysfs_path, sizeof(device)); + if(start >= sizeof(device)) + start = sizeof(device) - 1; + strlcat(device, dent->d_name, sizeof(device)); + path_decode(&device[start]); + device_list_insert(device); + } + closedir(dir); + } +} + +int udevtrigger(int argc, char *argv[]) +{ + int failed = 0; + const char *sockpath = NULL; + int option; + const char *action = "add"; + const char *env = NULL; + static const struct option options[] = { + { "verbose", 0, NULL, 'v' }, + { "dry-run", 0, NULL, 'n' }, + { "retry-failed", 0, NULL, 'F' }, + { "socket", 1, NULL, 'o' }, + { "help", 0, NULL, 'h' }, + { "action", 1, NULL, 'c' }, + { "subsystem-match", 1, NULL, 's' }, + { "subsystem-nomatch", 1, NULL, 'S' }, + { "attr-match", 1, NULL, 'a' }, + { "attr-nomatch", 1, NULL, 'A' }, + { "env", 1, NULL, 'e' }, + {} + }; + + logging_init("udevtrigger"); + udev_config_init(); + dbg("version %s\n", VERSION); + sysfs_init(); + + while (1) { + option = getopt_long(argc, argv, "vnFo:hce::s:S:a:A:", options, NULL); + if (option == -1) + break; + + switch (option) { + case 'v': + verbose = 1; + break; + case 'n': + dry_run = 1; + break; + case 'F': + failed = 1; + break; + case 'o': + sockpath = optarg; + break; + case 'c': + action = optarg; + break; + case 'e': + if (strchr(optarg, '=') != NULL) + env = optarg; + break; + case 's': + name_list_add(&filter_subsystem_match_list, optarg, 0); + break; + case 'S': + name_list_add(&filter_subsystem_nomatch_list, optarg, 0); + break; + case 'a': + name_list_add(&filter_attr_match_list, optarg, 0); + break; + case 'A': + name_list_add(&filter_attr_nomatch_list, optarg, 0); + break; + case 'h': + printf("Usage: udevadm trigger OPTIONS\n" + " --verbose print the list of devices while running\n" + " --dry-run do not actually trigger the events\n" + " --retry-failed trigger only the events which have been\n" + " marked as failed during a previous run\n" + " --socket= pass events to socket instead of triggering kernel events\n" + " --env== pass an additional key (works only with --socket=)\n" + " --subsystem-match= trigger devices from a matching subystem\n" + " --subsystem-nomatch= exclude devices from a matching subystem\n" + " --attr-match=]> trigger devices with a matching sysfs\n" + " attribute\n" + " --attr-nomatch=]> exclude devices with a matching sysfs\n" + " attribute\n" + " --help print this text\n" + "\n"); + goto exit; + default: + goto exit; + } + } + + if (sockpath != NULL) { + struct stat stats; + + sock = socket(AF_LOCAL, SOCK_DGRAM, 0); + memset(&saddr, 0x00, sizeof(struct sockaddr_un)); + saddr.sun_family = AF_LOCAL; + if (sockpath[0] == '@') { + /* abstract namespace socket requested */ + strlcpy(&saddr.sun_path[1], &sockpath[1], sizeof(saddr.sun_path)-1); + saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + } else if (stat(sockpath, &stats) == 0 && S_ISSOCK(stats.st_mode)) { + /* existing socket file */ + strlcpy(saddr.sun_path, sockpath, sizeof(saddr.sun_path)); + saddrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path); + } else { + /* no socket file, assume abstract namespace socket */ + strlcpy(&saddr.sun_path[1], sockpath, sizeof(saddr.sun_path)-1); + saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); + } + } else if (env != NULL) { + fprintf(stderr, "error: --env= only valid with --socket= option\n"); + goto exit; + } + + if (failed) { + scan_failed(); + exec_list(action, env); + } else { + char base[PATH_SIZE]; + struct stat statbuf; + + /* if we have /sys/subsystem, forget all the old stuff */ + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/subsystem", sizeof(base)); + if (stat(base, &statbuf) == 0) { + scan_subsystem("subsystem", SCAN_SUBSYSTEM); + exec_list(action, env); + scan_subsystem("subsystem", SCAN_DEVICES); + exec_list(action, env); + } else { + scan_subsystem("bus", SCAN_SUBSYSTEM); + exec_list(action, env); + scan_subsystem("bus", SCAN_DEVICES); + scan_class(); + + /* scan "block" if it isn't a "class" */ + strlcpy(base, sysfs_path, sizeof(base)); + strlcat(base, "/class/block", sizeof(base)); + if (stat(base, &statbuf) != 0) + scan_block(); + exec_list(action, env); + } + } + +exit: + name_list_cleanup(&filter_subsystem_match_list); + name_list_cleanup(&filter_subsystem_nomatch_list); + name_list_cleanup(&filter_attr_match_list); + name_list_cleanup(&filter_attr_nomatch_list); + + if (sock >= 0) + close(sock); + sysfs_cleanup(); + logging_close(); + return 0; +} diff --git a/udev/udevcontrol.c b/udev/udevcontrol.c deleted file mode 100644 index 5f1952543c..0000000000 --- a/udev/udevcontrol.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2005-2006 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 version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" - -static int udev_log = 0; - -struct udev_ctrl; -extern struct udev_ctrl *udev_ctrl_new_from_socket(const char *socket_path); -extern void udev_ctrl_unref(struct udev_ctrl *uctrl); -extern int udev_ctrl_set_log_level(struct udev_ctrl *uctrl, int priority); -extern int udev_ctrl_stop_exec_queue(struct udev_ctrl *uctrl); -extern int udev_ctrl_start_exec_queue(struct udev_ctrl *uctrl); -extern int udev_ctrl_reload_rules(struct udev_ctrl *uctrl); -extern int udev_ctrl_set_env(struct udev_ctrl *uctrl, const char *key); -extern int udev_ctrl_set_max_childs(struct udev_ctrl *uctrl, int count); -extern int udev_ctrl_set_max_childs_running(struct udev_ctrl *uctrl, int count); - -struct udev_ctrl { - int sock; - struct sockaddr_un saddr; - socklen_t addrlen; -}; - -struct udev_ctrl *udev_ctrl_new_from_socket(const char *socket_path) -{ - struct udev_ctrl *uctrl; - - uctrl = malloc(sizeof(struct udev_ctrl)); - if (uctrl == NULL) - return NULL; - memset(uctrl, 0x00, sizeof(struct udev_ctrl)); - - uctrl->sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (uctrl->sock < 0) { - err("error getting socket: %s\n", strerror(errno)); - free(uctrl); - return NULL; - } - - uctrl->saddr.sun_family = AF_LOCAL; - strcpy(uctrl->saddr.sun_path, socket_path); - uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.sun_path); - /* translate leading '@' to abstract namespace */ - if (uctrl->saddr.sun_path[0] == '@') - uctrl->saddr.sun_path[0] = '\0'; - return uctrl; -} - -void udev_ctrl_unref(struct udev_ctrl *uctrl) -{ - if (uctrl == NULL) - return; - close(uctrl->sock); -} - -static int ctrl_send(struct udev_ctrl *uctrl, enum udevd_ctrl_msg_type type, int intval, const char *buf) -{ - struct udevd_ctrl_msg ctrl_msg; - int err; - - memset(&ctrl_msg, 0x00, sizeof(struct udevd_ctrl_msg)); - strcpy(ctrl_msg.magic, UDEVD_CTRL_MAGIC); - ctrl_msg.type = type; - - if (buf != NULL) - strlcpy(ctrl_msg.buf, buf, sizeof(ctrl_msg.buf)); - else - ctrl_msg.intval = intval; - - err = sendto(uctrl->sock, &ctrl_msg, sizeof(ctrl_msg), 0, (struct sockaddr *)&uctrl->saddr, uctrl->addrlen); - if (err == -1) { - err("error sending message: %s\n", strerror(errno)); - } - return err; -} - -int udev_ctrl_set_log_level(struct udev_ctrl *uctrl, int priority) -{ - ctrl_send(uctrl, UDEVD_CTRL_SET_LOG_LEVEL, priority, NULL); - return 0; -} - -int udev_ctrl_stop_exec_queue(struct udev_ctrl *uctrl) -{ - ctrl_send(uctrl, UDEVD_CTRL_STOP_EXEC_QUEUE, 0, NULL); - return 0; -} - -int udev_ctrl_start_exec_queue(struct udev_ctrl *uctrl) -{ - ctrl_send(uctrl, UDEVD_CTRL_START_EXEC_QUEUE, 0, NULL); - return 0; -} - -int udev_ctrl_reload_rules(struct udev_ctrl *uctrl) -{ - ctrl_send(uctrl, UDEVD_CTRL_RELOAD_RULES, 0, NULL); - return 0; -} - -int udev_ctrl_set_env(struct udev_ctrl *uctrl, const char *key) -{ - ctrl_send(uctrl, UDEVD_CTRL_ENV, 0, optarg); - return 0; -} - -int udev_ctrl_set_max_childs(struct udev_ctrl *uctrl, int count) -{ - ctrl_send(uctrl, UDEVD_CTRL_SET_MAX_CHILDS, count, NULL); - return 0; -} - -int udev_ctrl_set_max_childs_running(struct udev_ctrl *uctrl, int count) -{ - ctrl_send(uctrl, UDEVD_CTRL_SET_MAX_CHILDS_RUNNING, count, NULL); - return 0; -} - -int udevcontrol(int argc, char *argv[]) -{ - struct udev_ctrl *uctrl; - const char *env; - int rc = 1; - - /* compat values with '_' will be removed in a future release */ - static const struct option options[] = { - { "log-priority", 1, NULL, 'l' }, - { "log_priority", 1, NULL, 'l' + 256 }, - { "stop-exec-queue", 0, NULL, 's' }, - { "stop_exec_queue", 0, NULL, 's' + 256 }, - { "start-exec-queue", 0, NULL, 'S' }, - { "start_exec_queue", 0, NULL, 'S' + 256}, - { "reload-rules", 0, NULL, 'R' }, - { "reload_rules", 0, NULL, 'R' + 256}, - { "env", 1, NULL, 'e' }, - { "max-childs", 1, NULL, 'm' }, - { "max_childs", 1, NULL, 'm' + 256}, - { "max-childs-running", 1, NULL, 'M' }, - { "max_childs_running", 1, NULL, 'M' + 256}, - { "help", 0, NULL, 'h' }, - {} - }; - - env = getenv("UDEV_LOG"); - if (env) - udev_log = log_priority(env); - - logging_init("udevcontrol"); - dbg("version %s\n", VERSION); - - if (getuid() != 0) { - fprintf(stderr, "root privileges required\n"); - goto exit; - } - - uctrl = udev_ctrl_new_from_socket(UDEVD_CTRL_SOCK_PATH); - if (uctrl == NULL) - goto exit; - - while (1) { - int option; - int i; - char *endp; - - option = getopt_long(argc, argv, "l:sSRe:m:M:h", options, NULL); - if (option == -1) - break; - - if (option > 255) { - info("udevadm control expects commands without underscore, " - "this will stop working in a future release\n"); - fprintf(stderr, "udevadm control expects commands without underscore, " - "this will stop working in a future release\n"); - } - - switch (option) { - case 'l': - case 'l' + 256: - i = log_priority(optarg); - if (i < 0) { - fprintf(stderr, "invalid number '%s'\n", optarg); - goto exit; - } - udev_ctrl_set_log_level(uctrl, log_priority(optarg)); - break; - case 's': - case 's' + 256: - udev_ctrl_stop_exec_queue(uctrl); - break; - case 'S': - case 'S' + 256: - udev_ctrl_start_exec_queue(uctrl); - break; - case 'R': - case 'R' + 256: - udev_ctrl_reload_rules(uctrl); - break; - case 'e': - if (strchr(optarg, '=') == NULL) { - fprintf(stderr, "expect = instead of '%s'\n", optarg); - goto exit; - } - udev_ctrl_set_env(uctrl, optarg); - break; - case 'm': - case 'm' + 256: - i = strtoul(optarg, &endp, 0); - if (endp[0] != '\0' || i < 1) { - fprintf(stderr, "invalid number '%s'\n", optarg); - goto exit; - } - udev_ctrl_set_max_childs(uctrl, i); - break; - case 'M': - case 'M' + 256: - i = strtoul(optarg, &endp, 0); - if (endp[0] != '\0' || i < 1) { - fprintf(stderr, "invalid number '%s'\n", optarg); - goto exit; - } - udev_ctrl_set_max_childs_running(uctrl, i); - break; - break; - case 'h': - printf("Usage: udevadm control COMMAND\n" - " --log-priority= set the udev log level for the daemon\n" - " --stop-exec-queue keep udevd from executing events, queue only\n" - " --start-exec-queue execute events, flush queue\n" - " --reload-rules reloads the rules files\n" - " --env== set a global environment variable\n" - " --max-childs= maximum number of childs\n" - " --max-childs-running= maximum number of childs running at the same time\n" - " --help print this help text\n\n"); - goto exit; - default: - goto exit; - } - } - - /* compat stuff which will be removed in a future release */ - if (argv[optind] != NULL) { - const char *arg = argv[optind]; - - fprintf(stderr, "udevadm control commands requires the -- format, " - "this will stop working in a future release\n"); - err("udevadm control commands requires the -- format, " - "this will stop working in a future release\n"); - - if (!strncmp(arg, "log_priority=", strlen("log_priority="))) { - udev_ctrl_set_log_level(uctrl, log_priority(&arg[strlen("log_priority=")])); - } else if (!strcmp(arg, "stop_exec_queue")) { - udev_ctrl_stop_exec_queue(uctrl); - } else if (!strcmp(arg, "start_exec_queue")) { - udev_ctrl_start_exec_queue(uctrl); - } else if (!strcmp(arg, "reload_rules")) { - udev_ctrl_reload_rules(uctrl); - } else if (!strncmp(arg, "max_childs=", strlen("max_childs="))) { - udev_ctrl_set_max_childs(uctrl, strtoul(&arg[strlen("max_childs=")], NULL, 0)); - } else if (!strncmp(arg, "max_childs_running=", strlen("max_childs_running="))) { - udev_ctrl_set_max_childs_running(uctrl, strtoul(&arg[strlen("max_childs_running=")], NULL, 0)); - } else if (!strncmp(arg, "env", strlen("env"))) { - udev_ctrl_set_env(uctrl, &arg[strlen("env=")]); - } else { - fprintf(stderr, "unrecognized command '%s'\n", arg); - err("unrecognized command '%s'\n", arg); - } - } -exit: - udev_ctrl_unref(uctrl); - logging_close(); - return rc; -} diff --git a/udev/udevd.h b/udev/udevd.h index 7405704c7a..a22bc96e5d 100644 --- a/udev/udevd.h +++ b/udev/udevd.h @@ -32,7 +32,7 @@ #define UEVENT_NUM_ENVP 32 #define UDEVD_CTRL_SOCK_PATH "@" UDEV_PREFIX "/org/kernel/udev/udevd" -#define UDEVD_CTRL_MAGIC "udevd_" VERSION +#define UDEVD_CTRL_MAGIC "udevd-128" enum udevd_ctrl_msg_type { UDEVD_CTRL_UNKNOWN, diff --git a/udev/udevinfo.c b/udev/udevinfo.c deleted file mode 100644 index 6501b57d1a..0000000000 --- a/udev/udevinfo.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright (C) 2004-2006 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 version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" - -static void print_all_attributes(const char *devpath, const char *key) -{ - char path[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - - dir = opendir(path); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat statbuf; - char filename[PATH_SIZE]; - char *attr_value; - char value[NAME_SIZE]; - size_t len; - - if (dent->d_name[0] == '.') - continue; - - if (strcmp(dent->d_name, "uevent") == 0) - continue; - if (strcmp(dent->d_name, "dev") == 0) - continue; - - strlcpy(filename, path, sizeof(filename)); - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, dent->d_name, sizeof(filename)); - if (lstat(filename, &statbuf) != 0) - continue; - if (S_ISLNK(statbuf.st_mode)) - continue; - - attr_value = sysfs_attr_get_value(devpath, dent->d_name); - if (attr_value == NULL) - continue; - len = strlcpy(value, attr_value, sizeof(value)); - if(len >= sizeof(value)) - len = sizeof(value) - 1; - dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len); - - /* remove trailing newlines */ - while (len && value[len-1] == '\n') - value[--len] = '\0'; - - /* skip nonprintable attributes */ - while (len && isprint(value[len-1])) - len--; - if (len) { - dbg("attribute value of '%s' non-printable, skip\n", dent->d_name); - continue; - } - - printf(" %s{%s}==\"%s\"\n", key, dent->d_name, value); - } - } - printf("\n"); -} - -static int print_device_chain(const char *devpath) -{ - struct sysfs_device *dev; - - dev = sysfs_device_get(devpath); - if (dev == NULL) - return -1; - - printf("\n" - "Udevinfo starts with the device specified by the devpath and then\n" - "walks up the chain of parent devices. It prints for every device\n" - "found, all possible attributes in the udev rules key format.\n" - "A rule to match, can be composed by the attributes of the device\n" - "and the attributes from one single parent device.\n" - "\n"); - - printf(" looking at device '%s':\n", dev->devpath); - printf(" KERNEL==\"%s\"\n", dev->kernel); - printf(" SUBSYSTEM==\"%s\"\n", dev->subsystem); - printf(" DRIVER==\"%s\"\n", dev->driver); - print_all_attributes(dev->devpath, "ATTR"); - - /* walk up the chain of devices */ - while (1) { - dev = sysfs_device_get_parent(dev); - if (dev == NULL) - break; - printf(" looking at parent device '%s':\n", dev->devpath); - printf(" KERNELS==\"%s\"\n", dev->kernel); - printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem); - printf(" DRIVERS==\"%s\"\n", dev->driver); - - print_all_attributes(dev->devpath, "ATTRS"); - } - - return 0; -} - -static void print_record(struct udevice *udev) -{ - struct name_entry *name_loop; - - printf("P: %s\n", udev->dev->devpath); - printf("N: %s\n", udev->name); - list_for_each_entry(name_loop, &udev->symlink_list, node) - printf("S: %s\n", name_loop->name); - if (udev->link_priority != 0) - printf("L: %i\n", udev->link_priority); - if (udev->partitions != 0) - printf("A:%u\n", udev->partitions); - if (udev->ignore_remove) - printf("R:%u\n", udev->ignore_remove); - list_for_each_entry(name_loop, &udev->env_list, node) - printf("E: %s\n", name_loop->name); -} - -static void export_db(void) { - LIST_HEAD(name_list); - struct name_entry *name_loop; - - udev_db_get_all_entries(&name_list); - list_for_each_entry(name_loop, &name_list, node) { - struct udevice *udev_db; - - udev_db = udev_device_init(); - if (udev_db == NULL) - continue; - if (udev_db_get_device(udev_db, name_loop->name) == 0) - print_record(udev_db); - printf("\n"); - udev_device_cleanup(udev_db); - } - name_list_cleanup(&name_list); -} - -static int lookup_device_by_name(struct udevice **udev, const char *name) -{ - LIST_HEAD(name_list); - int count; - struct name_entry *device; - int rc = -1; - - count = udev_db_get_devices_by_name(name, &name_list); - if (count <= 0) - goto out; - - info("found %i devices for '%s'\n", count, name); - - /* select the device that seems to match */ - list_for_each_entry(device, &name_list, node) { - struct udevice *udev_loop; - char filename[PATH_SIZE]; - struct stat statbuf; - - udev_loop = udev_device_init(); - if (udev_loop == NULL) - break; - if (udev_db_get_device(udev_loop, device->name) != 0) - goto next; - info("found db entry '%s'\n", device->name); - - /* make sure, we don't get a link of a different device */ - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/", sizeof(filename)); - strlcat(filename, name, sizeof(filename)); - if (stat(filename, &statbuf) != 0) - goto next; - if (major(udev_loop->devt) > 0 && udev_loop->devt != statbuf.st_rdev) { - info("skip '%s', dev_t doesn't match\n", udev_loop->name); - goto next; - } - rc = 0; - *udev = udev_loop; - break; -next: - udev_device_cleanup(udev_loop); - } -out: - name_list_cleanup(&name_list); - return rc; -} - -static int stat_device(const char *name, int export, const char *prefix) -{ - struct stat statbuf; - - if (stat(name, &statbuf) != 0) - return -1; - - if (export) { - if (prefix == NULL) - prefix = "INFO_"; - printf("%sMAJOR=%d\n" - "%sMINOR=%d\n", - prefix, major(statbuf.st_dev), - prefix, minor(statbuf.st_dev)); - } else - printf("%d:%d\n", major(statbuf.st_dev), minor(statbuf.st_dev)); - return 0; -} - -int udevinfo(int argc, char *argv[]) -{ - int option; - struct udevice *udev = NULL; - int root = 0; - int export = 0; - const char *export_prefix = NULL; - - static const struct option options[] = { - { "name", 1, NULL, 'n' }, - { "path", 1, NULL, 'p' }, - { "query", 1, NULL, 'q' }, - { "attribute-walk", 0, NULL, 'a' }, - { "export-db", 0, NULL, 'e' }, - { "root", 0, NULL, 'r' }, - { "device-id-of-file", 1, NULL, 'd' }, - { "export", 0, NULL, 'x' }, - { "export-prefix", 1, NULL, 'P' }, - { "version", 0, NULL, 1 }, /* -V outputs braindead format */ - { "help", 0, NULL, 'h' }, - {} - }; - - enum action_type { - ACTION_NONE, - ACTION_QUERY, - ACTION_ATTRIBUTE_WALK, - ACTION_ROOT, - ACTION_DEVICE_ID_FILE, - } action = ACTION_NONE; - - enum query_type { - QUERY_NONE, - QUERY_NAME, - QUERY_PATH, - QUERY_SYMLINK, - QUERY_ENV, - QUERY_ALL, - } query = QUERY_NONE; - - char path[PATH_SIZE] = ""; - char name[PATH_SIZE] = ""; - struct name_entry *name_loop; - int rc = 0; - - logging_init("udevinfo"); - udev_config_init(); - sysfs_init(); - - while (1) { - option = getopt_long(argc, argv, "aed:n:p:q:rxPVh", options, NULL); - if (option == -1) - break; - - dbg("option '%c'\n", option); - switch (option) { - case 'n': - /* remove /dev if given */ - if (strncmp(optarg, udev_root, strlen(udev_root)) == 0) - strlcpy(name, &optarg[strlen(udev_root)+1], sizeof(name)); - else - strlcpy(name, optarg, sizeof(name)); - remove_trailing_chars(name, '/'); - dbg("name: %s\n", name); - break; - case 'p': - /* remove /sys if given */ - if (strncmp(optarg, sysfs_path, strlen(sysfs_path)) == 0) - strlcpy(path, &optarg[strlen(sysfs_path)], sizeof(path)); - else - strlcpy(path, optarg, sizeof(path)); - remove_trailing_chars(path, '/'); - - /* possibly resolve to real devpath */ - if (sysfs_resolve_link(path, sizeof(path)) != 0) { - char temp[PATH_SIZE]; - char *pos; - - /* also check if the parent is a link */ - strlcpy(temp, path, sizeof(temp)); - pos = strrchr(temp, '/'); - if (pos != 0) { - char tail[PATH_SIZE]; - - strlcpy(tail, pos, sizeof(tail)); - pos[0] = '\0'; - if (sysfs_resolve_link(temp, sizeof(temp)) == 0) { - strlcpy(path, temp, sizeof(path)); - strlcat(path, tail, sizeof(path)); - } - } - } - dbg("path: %s\n", path); - break; - case 'q': - action = ACTION_QUERY; - if (strcmp(optarg, "name") == 0) { - query = QUERY_NAME; - break; - } - if (strcmp(optarg, "symlink") == 0) { - query = QUERY_SYMLINK; - break; - } - if (strcmp(optarg, "path") == 0) { - query = QUERY_PATH; - break; - } - if (strcmp(optarg, "env") == 0) { - query = QUERY_ENV; - break; - } - if (strcmp(optarg, "all") == 0) { - query = QUERY_ALL; - break; - } - fprintf(stderr, "unknown query type\n"); - rc = 2; - goto exit; - case 'r': - if (action == ACTION_NONE) - action = ACTION_ROOT; - root = 1; - break; - case 'd': - action = ACTION_DEVICE_ID_FILE; - strlcpy(name, optarg, sizeof(name)); - break; - case 'a': - action = ACTION_ATTRIBUTE_WALK; - break; - case 'e': - export_db(); - goto exit; - case 'x': - export = 1; - break; - case 'P': - export_prefix = optarg; - break; - case 1: - printf("%s\n", VERSION); - goto exit; - case 'V': - printf("udevinfo, version %s\n", VERSION); - goto exit; - case 'h': - printf("Usage: udevadm info OPTIONS\n" - " --query= query database for the specified value:\n" - " name name of device node\n" - " symlink pointing to node\n" - " path sysfs device path\n" - " env the device related imported environment\n" - " all all values\n" - " --path= sysfs device path used for query or chain\n" - " --name= node or symlink name used for query\n" - " --root prepend to query result or print udev_root\n" - " --attribute-walk print all key matches while walking along chain\n" - " of parent devices\n" - " --device-id-of-file= print major/minor of underlying device\n" - " --export-db export the content of the udev database\n" - " --help print this text\n" - "\n"); - goto exit; - default: - goto exit; - } - } - - /* run action */ - switch (action) { - case ACTION_QUERY: - /* needs devpath or node/symlink name for query */ - if (path[0] != '\0') { - udev = udev_device_init(); - if (udev == NULL) { - rc = 1; - goto exit; - } - if (udev_db_get_device(udev, path) != 0) { - fprintf(stderr, "no record for '%s' in database\n", path); - rc = 3; - goto exit; - } - } else if (name[0] != '\0') { - if (lookup_device_by_name(&udev, name) != 0) { - fprintf(stderr, "node name not found\n"); - rc = 4; - goto exit; - } - } else { - fprintf(stderr, "query needs --path or node --name specified\n"); - rc = 4; - goto exit; - } - - switch(query) { - case QUERY_NAME: - if (root) - printf("%s/%s\n", udev_root, udev->name); - else - printf("%s\n", udev->name); - break; - case QUERY_SYMLINK: - list_for_each_entry(name_loop, &udev->symlink_list, node) { - char c = name_loop->node.next != &udev->symlink_list ? ' ' : '\n'; - - if (root) - printf("%s/%s%c", udev_root, name_loop->name, c); - else - printf("%s%c", name_loop->name, c); - } - break; - case QUERY_PATH: - printf("%s\n", udev->dev->devpath); - goto exit; - case QUERY_ENV: - list_for_each_entry(name_loop, &udev->env_list, node) - printf("%s\n", name_loop->name); - break; - case QUERY_ALL: - print_record(udev); - break; - default: - fprintf(stderr, "unknown query type\n"); - break; - } - break; - case ACTION_ATTRIBUTE_WALK: - if (path[0] != '\0') { - if (print_device_chain(path) != 0) { - fprintf(stderr, "no valid sysfs device found\n"); - rc = 4; - goto exit; - } - } else if (name[0] != '\0') { - if (lookup_device_by_name(&udev, name) != 0) { - fprintf(stderr, "node name not found\n"); - rc = 4; - goto exit; - } - if (print_device_chain(udev->dev->devpath) != 0) { - fprintf(stderr, "no valid sysfs device found\n"); - rc = 4; - goto exit; - } - } else { - fprintf(stderr, "attribute walk needs --path or node --name specified\n"); - rc = 5; - goto exit; - } - break; - case ACTION_DEVICE_ID_FILE: - if (stat_device(name, export, export_prefix) != 0) - rc = 6; - break; - case ACTION_ROOT: - printf("%s\n", udev_root); - break; - default: - fprintf(stderr, "missing option\n"); - rc = 1; - break; - } - -exit: - udev_device_cleanup(udev); - sysfs_cleanup(); - logging_close(); - return rc; -} diff --git a/udev/udevmonitor.c b/udev/udevmonitor.c deleted file mode 100644 index 3b65bcae9b..0000000000 --- a/udev/udevmonitor.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2004-2006 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 version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" - -static int uevent_netlink_sock = -1; -static int udev_monitor_sock = -1; -static volatile int udev_exit; - -static int init_udev_monitor_socket(void) -{ - struct sockaddr_un saddr; - socklen_t addrlen; - int retval; - - memset(&saddr, 0x00, sizeof(saddr)); - saddr.sun_family = AF_LOCAL; - /* use abstract namespace for socket path */ - strcpy(&saddr.sun_path[1], "/org/kernel/udev/monitor"); - addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - - udev_monitor_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - if (udev_monitor_sock == -1) { - fprintf(stderr, "error getting socket: %s\n", strerror(errno)); - return -1; - } - - /* the bind takes care of ensuring only one copy running */ - retval = bind(udev_monitor_sock, (struct sockaddr *) &saddr, addrlen); - if (retval < 0) { - fprintf(stderr, "bind failed: %s\n", strerror(errno)); - close(udev_monitor_sock); - udev_monitor_sock = -1; - return -1; - } - - return 0; -} - -static int init_uevent_netlink_sock(void) -{ - struct sockaddr_nl snl; - int retval; - - memset(&snl, 0x00, sizeof(struct sockaddr_nl)); - snl.nl_family = AF_NETLINK; - snl.nl_pid = getpid(); - snl.nl_groups = 1; - - uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); - if (uevent_netlink_sock == -1) { - fprintf(stderr, "error getting socket: %s\n", strerror(errno)); - return -1; - } - - retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, - sizeof(struct sockaddr_nl)); - if (retval < 0) { - fprintf(stderr, "bind failed: %s\n", strerror(errno)); - close(uevent_netlink_sock); - uevent_netlink_sock = -1; - return -1; - } - - return 0; -} - -static void asmlinkage sig_handler(int signum) -{ - if (signum == SIGINT || signum == SIGTERM) - udev_exit = 1; -} - -static const char *search_key(const char *searchkey, const char *buf, size_t buflen) -{ - size_t bufpos = 0; - size_t searchkeylen = strlen(searchkey); - - while (bufpos < buflen) { - const char *key; - int keylen; - - key = &buf[bufpos]; - keylen = strlen(key); - if (keylen == 0) - break; - if ((strncmp(searchkey, key, searchkeylen) == 0) && key[searchkeylen] == '=') - return &key[searchkeylen + 1]; - bufpos += keylen + 1; - } - return NULL; -} - -int udevmonitor(int argc, char *argv[]) -{ - struct sigaction act; - int option; - int env = 0; - int kernel = 0; - int udev = 0; - fd_set readfds; - int retval = 0; - - static const struct option options[] = { - { "environment", 0, NULL, 'e' }, - { "kernel", 0, NULL, 'k' }, - { "udev", 0, NULL, 'u' }, - { "help", 0, NULL, 'h' }, - {} - }; - - while (1) { - option = getopt_long(argc, argv, "ekuh", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'e': - env = 1; - break; - case 'k': - kernel = 1; - break; - case 'u': - udev = 1; - break; - case 'h': - printf("Usage: udevadm monitor [--environment] [--kernel] [--udev] [--help]\n" - " --env print the whole event environment\n" - " --kernel print kernel uevents\n" - " --udev print udev events\n" - " --help print this help text\n\n"); - default: - goto out; - } - } - - if (!kernel && !udev) { - kernel = 1; - udev =1; - } - - if (getuid() != 0 && kernel) { - fprintf(stderr, "root privileges needed to subscribe to kernel events\n"); - goto out; - } - - /* set signal handlers */ - memset(&act, 0x00, sizeof(struct sigaction)); - act.sa_handler = (void (*)(int)) sig_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART; - sigaction(SIGINT, &act, NULL); - sigaction(SIGTERM, &act, NULL); - - printf("udevmonitor will print the received events for:\n"); - if (udev) { - retval = init_udev_monitor_socket(); - if (retval) - goto out; - printf("UDEV the event which udev sends out after rule processing\n"); - } - if (kernel) { - retval = init_uevent_netlink_sock(); - if (retval) - goto out; - printf("UEVENT the kernel uevent\n"); - } - printf("\n"); - - while (!udev_exit) { - char buf[UEVENT_BUFFER_SIZE*2]; - ssize_t buflen; - ssize_t bufpos; - ssize_t keys; - int fdcount; - struct timeval tv; - struct timezone tz; - char timestr[64]; - const char *source = NULL; - const char *devpath, *action, *subsys; - - buflen = 0; - FD_ZERO(&readfds); - if (uevent_netlink_sock >= 0) - FD_SET(uevent_netlink_sock, &readfds); - if (udev_monitor_sock >= 0) - FD_SET(udev_monitor_sock, &readfds); - - fdcount = select(UDEV_MAX(uevent_netlink_sock, udev_monitor_sock)+1, &readfds, NULL, NULL, NULL); - if (fdcount < 0) { - if (errno != EINTR) - fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); - continue; - } - - if (gettimeofday(&tv, &tz) == 0) { - snprintf(timestr, sizeof(timestr), "%llu.%06u", - (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec); - } else - timestr[0] = '\0'; - - if ((uevent_netlink_sock >= 0) && FD_ISSET(uevent_netlink_sock, &readfds)) { - buflen = recv(uevent_netlink_sock, &buf, sizeof(buf), 0); - if (buflen <= 0) { - fprintf(stderr, "error receiving uevent message: %s\n", strerror(errno)); - continue; - } - source = "UEVENT"; - } - - if ((udev_monitor_sock >= 0) && FD_ISSET(udev_monitor_sock, &readfds)) { - buflen = recv(udev_monitor_sock, &buf, sizeof(buf), 0); - if (buflen <= 0) { - fprintf(stderr, "error receiving udev message: %s\n", strerror(errno)); - continue; - } - source = "UDEV "; - } - - if (buflen == 0) - continue; - - keys = strlen(buf) + 1; /* start of payload */ - devpath = search_key("DEVPATH", &buf[keys], buflen); - action = search_key("ACTION", &buf[keys], buflen); - subsys = search_key("SUBSYSTEM", &buf[keys], buflen); - printf("%s[%s] %-8s %s (%s)\n", source, timestr, action, devpath, subsys); - - /* print environment */ - bufpos = keys; - if (env) { - while (bufpos < buflen) { - int keylen; - char *key; - - key = &buf[bufpos]; - keylen = strlen(key); - if (keylen == 0) - break; - printf("%s\n", key); - bufpos += keylen + 1; - } - printf("\n"); - } - } - -out: - if (uevent_netlink_sock >= 0) - close(uevent_netlink_sock); - if (udev_monitor_sock >= 0) - close(udev_monitor_sock); - - if (retval) - return 1; - return 0; -} diff --git a/udev/udevsettle.c b/udev/udevsettle.c deleted file mode 100644 index 11277f5a43..0000000000 --- a/udev/udevsettle.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2006 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 version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" - -#define DEFAULT_TIMEOUT 180 -#define LOOP_PER_SECOND 20 - -static void print_queue(const char *dir) -{ - LIST_HEAD(files); - struct name_entry *item; - - if (add_matching_files(&files, dir, NULL) < 0) - return; - - printf("\n\nAfter the udevadm settle timeout, the events queue contains:\n\n"); - - list_for_each_entry(item, &files, node) { - char target[NAME_SIZE]; - size_t len; - const char *filename = strrchr(item->name, '/'); - - if (filename == NULL) - continue; - filename++; - if (*filename == '\0') - continue; - - len = readlink(item->name, target, sizeof(target)); - if (len < 0) - continue; - target[len] = '\0'; - - printf("%s: %s\n", filename, target); - } - - printf("\n\n"); -} - -int udevsettle(int argc, char *argv[]) -{ - char queuename[PATH_SIZE]; - char filename[PATH_SIZE]; - unsigned long long seq_kernel; - unsigned long long seq_udev; - char seqnum[32]; - int fd; - ssize_t len; - int timeout = DEFAULT_TIMEOUT; - int loop; - static const struct option options[] = { - { "timeout", 1, NULL, 't' }, - { "help", 0, NULL, 'h' }, - {} - }; - int option; - int rc = 1; - int seconds; - - logging_init("udevsettle"); - udev_config_init(); - dbg("version %s\n", VERSION); - sysfs_init(); - - while (1) { - option = getopt_long(argc, argv, "t:h", options, NULL); - if (option == -1) - break; - - switch (option) { - case 't': - seconds = atoi(optarg); - if (seconds > 0) - timeout = seconds; - else - fprintf(stderr, "invalid timeout value\n"); - dbg("timeout=%i\n", timeout); - break; - case 'h': - printf("Usage: udevadm settle [--help] [--timeout=]\n\n"); - goto exit; - } - } - - strlcpy(queuename, udev_root, sizeof(queuename)); - strlcat(queuename, "/.udev/queue", sizeof(queuename)); - - loop = timeout * LOOP_PER_SECOND; - while (loop--) { - /* wait for events in queue to finish */ - while (loop--) { - struct stat statbuf; - - if (stat(queuename, &statbuf) < 0) { - info("queue is empty\n"); - break; - } - usleep(1000 * 1000 / LOOP_PER_SECOND); - } - if (loop <= 0) { - info("timeout waiting for queue\n"); - print_queue(queuename); - goto exit; - } - - /* read current udev seqnum */ - strlcpy(filename, udev_root, sizeof(filename)); - strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); - fd = open(filename, O_RDONLY); - if (fd < 0) - goto exit; - len = read(fd, seqnum, sizeof(seqnum)-1); - close(fd); - if (len <= 0) - goto exit; - seqnum[len] = '\0'; - seq_udev = strtoull(seqnum, NULL, 10); - info("udev seqnum = %llu\n", seq_udev); - - /* read current kernel seqnum */ - strlcpy(filename, sysfs_path, sizeof(filename)); - strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); - fd = open(filename, O_RDONLY); - if (fd < 0) - goto exit; - len = read(fd, seqnum, sizeof(seqnum)-1); - close(fd); - if (len <= 0) - goto exit; - seqnum[len] = '\0'; - seq_kernel = strtoull(seqnum, NULL, 10); - info("kernel seqnum = %llu\n", seq_kernel); - - /* make sure all kernel events have arrived in the queue */ - if (seq_udev >= seq_kernel) { - info("queue is empty and no pending events left\n"); - rc = 0; - goto exit; - } - usleep(1000 * 1000 / LOOP_PER_SECOND); - info("queue is empty, but events still pending\n"); - } - -exit: - sysfs_cleanup(); - logging_close(); - return rc; -} diff --git a/udev/udevtest.c b/udev/udevtest.c deleted file mode 100644 index 63603aad97..0000000000 --- a/udev/udevtest.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2003-2004 Greg Kroah-Hartman - * Copyright (C) 2004-2006 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 version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udev_rules.h" - -static int import_uevent_var(const char *devpath) -{ - char path[PATH_SIZE]; - static char value[4096]; /* must stay, used with putenv */ - ssize_t size; - int fd; - char *key; - char *next; - int rc = -1; - - /* read uevent file */ - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - strlcat(path, "/uevent", sizeof(path)); - fd = open(path, O_RDONLY); - if (fd < 0) - goto out; - size = read(fd, value, sizeof(value)); - close(fd); - if (size < 0) - goto out; - value[size] = '\0'; - - /* import keys into environment */ - key = value; - while (key[0] != '\0') { - next = strchr(key, '\n'); - if (next == NULL) - goto out; - next[0] = '\0'; - info("import into environment: '%s'\n", key); - putenv(key); - key = &next[1]; - } - rc = 0; -out: - return rc; -} - -int udevtest(int argc, char *argv[]) -{ - int force = 0; - const char *action = "add"; - const char *subsystem = NULL; - const char *devpath = NULL; - struct udevice *udev; - struct sysfs_device *dev; - struct udev_rules rules = {}; - int retval; - int rc = 0; - - static const struct option options[] = { - { "action", 1, NULL, 'a' }, - { "subsystem", 1, NULL, 's' }, - { "force", 0, NULL, 'f' }, - { "help", 0, NULL, 'h' }, - {} - }; - - info("version %s\n", VERSION); - udev_config_init(); - if (udev_log_priority < LOG_INFO) { - char priority[32]; - - udev_log_priority = LOG_INFO; - sprintf(priority, "%i", udev_log_priority); - setenv("UDEV_LOG", priority, 1); - } - - while (1) { - int option; - - option = getopt_long(argc, argv, "a:s:fh", options, NULL); - if (option == -1) - break; - - dbg("option '%c'\n", option); - switch (option) { - case 'a': - action = optarg; - break; - case 's': - subsystem = optarg; - break; - case 'f': - force = 1; - break; - case 'h': - printf("Usage: udevadm test OPTIONS \n" - " --action= set action string\n" - " --subsystem= set subsystem string\n" - " --force don't skip node/link creation\n" - " --help print this help text\n\n"); - exit(0); - default: - exit(1); - } - } - devpath = argv[optind]; - - if (devpath == NULL) { - fprintf(stderr, "devpath parameter missing\n"); - rc = 1; - goto exit; - } - - printf("This program is for debugging only, it does not run any program,\n" - "specified by a RUN key. It may show incorrect results, because\n" - "some values may be different, or not available at a simulation run.\n" - "\n"); - - sysfs_init(); - udev_rules_init(&rules, 0); - - /* remove /sys if given */ - if (strncmp(devpath, sysfs_path, strlen(sysfs_path)) == 0) - devpath = &devpath[strlen(sysfs_path)]; - - dev = sysfs_device_get(devpath); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n", devpath); - rc = 2; - goto exit; - } - - udev = udev_device_init(); - if (udev == NULL) { - fprintf(stderr, "error initializing device\n"); - rc = 3; - goto exit; - } - - if (subsystem != NULL) - strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); - - /* override built-in sysfs device */ - udev->dev = dev; - strlcpy(udev->action, action, sizeof(udev->action)); - udev->devt = udev_device_get_devt(udev); - - /* simulate node creation with test flag */ - if (!force) - udev->test_run = 1; - - setenv("DEVPATH", udev->dev->devpath, 1); - setenv("SUBSYSTEM", udev->dev->subsystem, 1); - setenv("ACTION", udev->action, 1); - import_uevent_var(udev->dev->devpath); - - info("looking at device '%s' from subsystem '%s'\n", udev->dev->devpath, udev->dev->subsystem); - retval = udev_device_event(&rules, udev); - - if (udev->event_timeout >= 0) - info("custom event timeout: %i\n", udev->event_timeout); - - if (retval == 0 && !udev->ignore_device && udev_run) { - struct name_entry *name_loop; - - list_for_each_entry(name_loop, &udev->run_list, node) { - char program[PATH_SIZE]; - - strlcpy(program, name_loop->name, sizeof(program)); - udev_rules_apply_format(udev, program, sizeof(program)); - info("run: '%s'\n", program); - } - } - udev_device_cleanup(udev); - -exit: - udev_rules_cleanup(&rules); - sysfs_cleanup(); - return rc; -} diff --git a/udev/udevtrigger.c b/udev/udevtrigger.c deleted file mode 100644 index 4a8f62ab5c..0000000000 --- a/udev/udevtrigger.c +++ /dev/null @@ -1,716 +0,0 @@ -/* - * Copyright (C) 2004-2006 Kay Sievers - * Copyright (C) 2006 Hannes Reinecke - * - * 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 version 2 of the License. - * - * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "udev.h" -#include "udevd.h" -#include "udev_rules.h" - -static int verbose; -static int dry_run; -LIST_HEAD(device_list); -LIST_HEAD(filter_subsystem_match_list); -LIST_HEAD(filter_subsystem_nomatch_list); -LIST_HEAD(filter_attr_match_list); -LIST_HEAD(filter_attr_nomatch_list); -static int sock = -1; -static struct sockaddr_un saddr; -static socklen_t saddrlen; - -/* devices that should run last cause of their dependencies */ -static int delay_device(const char *devpath) -{ - static const char *delay_device_list[] = { - "*/md*", - "*/dm-*", - NULL - }; - int i; - - for (i = 0; delay_device_list[i] != NULL; i++) - if (fnmatch(delay_device_list[i], devpath, 0) == 0) - return 1; - return 0; -} - -static int device_list_insert(const char *path) -{ - char filename[PATH_SIZE]; - char devpath[PATH_SIZE]; - struct stat statbuf; - - dbg("add '%s'\n" , path); - - /* we only have a device, if we have an uevent file */ - strlcpy(filename, path, sizeof(filename)); - strlcat(filename, "/uevent", sizeof(filename)); - if (stat(filename, &statbuf) < 0) - return -1; - if (!(statbuf.st_mode & S_IWUSR)) - return -1; - - strlcpy(devpath, &path[strlen(sysfs_path)], sizeof(devpath)); - - /* resolve possible link to real target */ - if (lstat(path, &statbuf) < 0) - return -1; - if (S_ISLNK(statbuf.st_mode)) - if (sysfs_resolve_link(devpath, sizeof(devpath)) != 0) - return -1; - - name_list_add(&device_list, devpath, 1); - return 0; -} - -static void trigger_uevent(const char *devpath, const char *action) -{ - char filename[PATH_SIZE]; - int fd; - - strlcpy(filename, sysfs_path, sizeof(filename)); - strlcat(filename, devpath, sizeof(filename)); - strlcat(filename, "/uevent", sizeof(filename)); - - if (verbose) - printf("%s\n", devpath); - - if (dry_run) - return; - - fd = open(filename, O_WRONLY); - if (fd < 0) { - dbg("error on opening %s: %s\n", filename, strerror(errno)); - return; - } - - if (write(fd, action, strlen(action)) < 0) - info("error writing '%s' to '%s': %s\n", action, filename, strerror(errno)); - - close(fd); -} - -static int pass_to_socket(const char *devpath, const char *action, const char *env) -{ - struct udevice *udev; - struct name_entry *name_loop; - char buf[4096]; - size_t bufpos = 0; - ssize_t count; - char path[PATH_SIZE]; - int fd; - char link_target[PATH_SIZE]; - int len; - int err = 0; - - if (verbose) - printf("%s\n", devpath); - - udev = udev_device_init(); - if (udev == NULL) - return -1; - udev_db_get_device(udev, devpath); - - /* add header */ - bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath); - bufpos++; - - /* add cookie */ - if (env != NULL) { - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "%s", env); - bufpos++; - } - - /* add standard keys */ - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVPATH=%s", devpath); - bufpos++; - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "ACTION=%s", action); - bufpos++; - - /* add subsystem */ - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - strlcat(path, "/subsystem", sizeof(path)); - len = readlink(path, link_target, sizeof(link_target)); - if (len > 0) { - char *pos; - - link_target[len] = '\0'; - pos = strrchr(link_target, '/'); - if (pos != NULL) { - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "SUBSYSTEM=%s", &pos[1]); - bufpos++; - } - } - - /* add symlinks and node name */ - path[0] = '\0'; - list_for_each_entry(name_loop, &udev->symlink_list, node) { - strlcat(path, udev_root, sizeof(path)); - strlcat(path, "/", sizeof(path)); - strlcat(path, name_loop->name, sizeof(path)); - strlcat(path, " ", sizeof(path)); - } - remove_trailing_chars(path, ' '); - if (path[0] != '\0') { - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVLINKS=%s", path); - bufpos++; - } - if (udev->name[0] != '\0') { - strlcpy(path, udev_root, sizeof(path)); - strlcat(path, "/", sizeof(path)); - strlcat(path, udev->name, sizeof(path)); - bufpos += snprintf(&buf[bufpos], sizeof(buf)-1, "DEVNAME=%s", path); - bufpos++; - } - - /* add keys from device "uevent" file */ - strlcpy(path, sysfs_path, sizeof(path)); - strlcat(path, devpath, sizeof(path)); - strlcat(path, "/uevent", sizeof(path)); - fd = open(path, O_RDONLY); - if (fd >= 0) { - char value[4096]; - - count = read(fd, value, sizeof(value)); - close(fd); - if (count > 0) { - char *key; - - value[count] = '\0'; - key = value; - while (key[0] != '\0') { - char *next; - - next = strchr(key, '\n'); - if (next == NULL) - break; - next[0] = '\0'; - bufpos += strlcpy(&buf[bufpos], key, sizeof(buf) - bufpos-1); - bufpos++; - key = &next[1]; - } - } - } - - /* add keys from database */ - list_for_each_entry(name_loop, &udev->env_list, node) { - bufpos += strlcpy(&buf[bufpos], name_loop->name, sizeof(buf) - bufpos-1); - bufpos++; - } - if (bufpos > sizeof(buf)) - bufpos = sizeof(buf); - - count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, saddrlen); - if (count < 0) - err = -1; - - return err; -} - -static void exec_list(const char *action, const char *env) -{ - struct name_entry *loop_device; - struct name_entry *tmp_device; - - list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { - if (delay_device(loop_device->name)) - continue; - if (sock >= 0) - pass_to_socket(loop_device->name, action, env); - else - trigger_uevent(loop_device->name, action); - list_del(&loop_device->node); - free(loop_device); - } - - /* trigger remaining delayed devices */ - list_for_each_entry_safe(loop_device, tmp_device, &device_list, node) { - if (sock >= 0) - pass_to_socket(loop_device->name, action, env); - else - trigger_uevent(loop_device->name, action); - list_del(&loop_device->node); - free(loop_device); - } -} - -static int subsystem_filtered(const char *subsystem) -{ - struct name_entry *loop_name; - - /* skip devices matching the listed subsystems */ - list_for_each_entry(loop_name, &filter_subsystem_nomatch_list, node) - if (fnmatch(loop_name->name, subsystem, 0) == 0) - return 1; - - /* skip devices not matching the listed subsystems */ - if (!list_empty(&filter_subsystem_match_list)) { - list_for_each_entry(loop_name, &filter_subsystem_match_list, node) - if (fnmatch(loop_name->name, subsystem, 0) == 0) - return 0; - return 1; - } - - return 0; -} - -static int attr_match(const char *path, const char *attr_value) -{ - char attr[NAME_SIZE]; - char file[PATH_SIZE]; - char *match_value; - - strlcpy(attr, attr_value, sizeof(attr)); - - /* separate attr and match value */ - match_value = strchr(attr, '='); - if (match_value != NULL) { - match_value[0] = '\0'; - match_value = &match_value[1]; - } - - strlcpy(file, path, sizeof(file)); - strlcat(file, "/", sizeof(file)); - strlcat(file, attr, sizeof(file)); - - if (match_value != NULL) { - /* match file content */ - char value[NAME_SIZE]; - int fd; - ssize_t size; - - fd = open(file, O_RDONLY); - if (fd < 0) - return 0; - size = read(fd, value, sizeof(value)); - close(fd); - if (size < 0) - return 0; - value[size] = '\0'; - remove_trailing_chars(value, '\n'); - - /* match if attribute value matches */ - if (fnmatch(match_value, value, 0) == 0) - return 1; - } else { - /* match if attribute exists */ - struct stat statbuf; - - if (stat(file, &statbuf) == 0) - return 1; - } - return 0; -} - -static int attr_filtered(const char *path) -{ - struct name_entry *loop_name; - - /* skip devices matching the listed sysfs attributes */ - list_for_each_entry(loop_name, &filter_attr_nomatch_list, node) - if (attr_match(path, loop_name->name)) - return 1; - - /* skip devices not matching the listed sysfs attributes */ - if (!list_empty(&filter_attr_match_list)) { - list_for_each_entry(loop_name, &filter_attr_match_list, node) - if (attr_match(path, loop_name->name)) - return 0; - return 1; - } - return 0; -} - -enum scan_type { - SCAN_DEVICES, - SCAN_SUBSYSTEM, -}; - -static void scan_subsystem(const char *subsys, enum scan_type scan) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - const char *subdir; - - if (scan == SCAN_DEVICES) - subdir = "/devices"; - else if (scan == SCAN_SUBSYSTEM) - subdir = "/drivers"; - else - return; - - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/", sizeof(base)); - strlcat(base, subsys, sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char dirname[PATH_SIZE]; - DIR *dir2; - struct dirent *dent2; - - if (dent->d_name[0] == '.') - continue; - - if (scan == SCAN_DEVICES) - if (subsystem_filtered(dent->d_name)) - continue; - - strlcpy(dirname, base, sizeof(dirname)); - strlcat(dirname, "/", sizeof(dirname)); - strlcat(dirname, dent->d_name, sizeof(dirname)); - - if (scan == SCAN_SUBSYSTEM) { - if (attr_filtered(dirname)) - continue; - if (!subsystem_filtered("subsystem")) - device_list_insert(dirname); - if (subsystem_filtered("drivers")) - continue; - } - - strlcat(dirname, subdir, sizeof(dirname)); - - /* look for devices/drivers */ - dir2 = opendir(dirname); - if (dir2 != NULL) { - for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { - char dirname2[PATH_SIZE]; - - if (dent2->d_name[0] == '.') - continue; - - strlcpy(dirname2, dirname, sizeof(dirname2)); - strlcat(dirname2, "/", sizeof(dirname2)); - strlcat(dirname2, dent2->d_name, sizeof(dirname2)); - if (attr_filtered(dirname2)) - continue; - device_list_insert(dirname2); - } - closedir(dir2); - } - } - closedir(dir); - } -} - -static void scan_block(void) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - if (subsystem_filtered("block")) - return; - - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/block", sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char dirname[PATH_SIZE]; - DIR *dir2; - struct dirent *dent2; - - if (dent->d_name[0] == '.') - continue; - - strlcpy(dirname, base, sizeof(dirname)); - strlcat(dirname, "/", sizeof(dirname)); - strlcat(dirname, dent->d_name, sizeof(dirname)); - if (attr_filtered(dirname)) - continue; - if (device_list_insert(dirname) != 0) - continue; - - /* look for partitions */ - dir2 = opendir(dirname); - if (dir2 != NULL) { - for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { - char dirname2[PATH_SIZE]; - - if (dent2->d_name[0] == '.') - continue; - - if (!strcmp(dent2->d_name,"device")) - continue; - - strlcpy(dirname2, dirname, sizeof(dirname2)); - strlcat(dirname2, "/", sizeof(dirname2)); - strlcat(dirname2, dent2->d_name, sizeof(dirname2)); - if (attr_filtered(dirname2)) - continue; - device_list_insert(dirname2); - } - closedir(dir2); - } - } - closedir(dir); - } -} - -static void scan_class(void) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/class", sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char dirname[PATH_SIZE]; - DIR *dir2; - struct dirent *dent2; - - if (dent->d_name[0] == '.') - continue; - - if (subsystem_filtered(dent->d_name)) - continue; - - strlcpy(dirname, base, sizeof(dirname)); - strlcat(dirname, "/", sizeof(dirname)); - strlcat(dirname, dent->d_name, sizeof(dirname)); - dir2 = opendir(dirname); - if (dir2 != NULL) { - for (dent2 = readdir(dir2); dent2 != NULL; dent2 = readdir(dir2)) { - char dirname2[PATH_SIZE]; - - if (dent2->d_name[0] == '.') - continue; - - if (!strcmp(dent2->d_name, "device")) - continue; - - strlcpy(dirname2, dirname, sizeof(dirname2)); - strlcat(dirname2, "/", sizeof(dirname2)); - strlcat(dirname2, dent2->d_name, sizeof(dirname2)); - if (attr_filtered(dirname2)) - continue; - device_list_insert(dirname2); - } - closedir(dir2); - } - } - closedir(dir); - } -} - -static void scan_failed(void) -{ - char base[PATH_SIZE]; - DIR *dir; - struct dirent *dent; - - strlcpy(base, udev_root, sizeof(base)); - strlcat(base, "/.udev/failed", sizeof(base)); - - dir = opendir(base); - if (dir != NULL) { - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - char device[PATH_SIZE]; - size_t start; - - if (dent->d_name[0] == '.') - continue; - - start = strlcpy(device, sysfs_path, sizeof(device)); - if(start >= sizeof(device)) - start = sizeof(device) - 1; - strlcat(device, dent->d_name, sizeof(device)); - path_decode(&device[start]); - device_list_insert(device); - } - closedir(dir); - } -} - -int udevtrigger(int argc, char *argv[]) -{ - int failed = 0; - const char *sockpath = NULL; - int option; - const char *action = "add"; - const char *env = NULL; - static const struct option options[] = { - { "verbose", 0, NULL, 'v' }, - { "dry-run", 0, NULL, 'n' }, - { "retry-failed", 0, NULL, 'F' }, - { "socket", 1, NULL, 'o' }, - { "help", 0, NULL, 'h' }, - { "action", 1, NULL, 'c' }, - { "subsystem-match", 1, NULL, 's' }, - { "subsystem-nomatch", 1, NULL, 'S' }, - { "attr-match", 1, NULL, 'a' }, - { "attr-nomatch", 1, NULL, 'A' }, - { "env", 1, NULL, 'e' }, - {} - }; - - logging_init("udevtrigger"); - udev_config_init(); - dbg("version %s\n", VERSION); - sysfs_init(); - - while (1) { - option = getopt_long(argc, argv, "vnFo:hce::s:S:a:A:", options, NULL); - if (option == -1) - break; - - switch (option) { - case 'v': - verbose = 1; - break; - case 'n': - dry_run = 1; - break; - case 'F': - failed = 1; - break; - case 'o': - sockpath = optarg; - break; - case 'c': - action = optarg; - break; - case 'e': - if (strchr(optarg, '=') != NULL) - env = optarg; - break; - case 's': - name_list_add(&filter_subsystem_match_list, optarg, 0); - break; - case 'S': - name_list_add(&filter_subsystem_nomatch_list, optarg, 0); - break; - case 'a': - name_list_add(&filter_attr_match_list, optarg, 0); - break; - case 'A': - name_list_add(&filter_attr_nomatch_list, optarg, 0); - break; - case 'h': - printf("Usage: udevadm trigger OPTIONS\n" - " --verbose print the list of devices while running\n" - " --dry-run do not actually trigger the events\n" - " --retry-failed trigger only the events which have been\n" - " marked as failed during a previous run\n" - " --socket= pass events to socket instead of triggering kernel events\n" - " --env== pass an additional key (works only with --socket=)\n" - " --subsystem-match= trigger devices from a matching subystem\n" - " --subsystem-nomatch= exclude devices from a matching subystem\n" - " --attr-match=]> trigger devices with a matching sysfs\n" - " attribute\n" - " --attr-nomatch=]> exclude devices with a matching sysfs\n" - " attribute\n" - " --help print this text\n" - "\n"); - goto exit; - default: - goto exit; - } - } - - if (sockpath != NULL) { - struct stat stats; - - sock = socket(AF_LOCAL, SOCK_DGRAM, 0); - memset(&saddr, 0x00, sizeof(struct sockaddr_un)); - saddr.sun_family = AF_LOCAL; - if (sockpath[0] == '@') { - /* abstract namespace socket requested */ - strlcpy(&saddr.sun_path[1], &sockpath[1], sizeof(saddr.sun_path)-1); - saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - } else if (stat(sockpath, &stats) == 0 && S_ISSOCK(stats.st_mode)) { - /* existing socket file */ - strlcpy(saddr.sun_path, sockpath, sizeof(saddr.sun_path)); - saddrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path); - } else { - /* no socket file, assume abstract namespace socket */ - strlcpy(&saddr.sun_path[1], sockpath, sizeof(saddr.sun_path)-1); - saddrlen = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&saddr.sun_path[1]); - } - } else if (env != NULL) { - fprintf(stderr, "error: --env= only valid with --socket= option\n"); - goto exit; - } - - if (failed) { - scan_failed(); - exec_list(action, env); - } else { - char base[PATH_SIZE]; - struct stat statbuf; - - /* if we have /sys/subsystem, forget all the old stuff */ - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/subsystem", sizeof(base)); - if (stat(base, &statbuf) == 0) { - scan_subsystem("subsystem", SCAN_SUBSYSTEM); - exec_list(action, env); - scan_subsystem("subsystem", SCAN_DEVICES); - exec_list(action, env); - } else { - scan_subsystem("bus", SCAN_SUBSYSTEM); - exec_list(action, env); - scan_subsystem("bus", SCAN_DEVICES); - scan_class(); - - /* scan "block" if it isn't a "class" */ - strlcpy(base, sysfs_path, sizeof(base)); - strlcat(base, "/class/block", sizeof(base)); - if (stat(base, &statbuf) != 0) - scan_block(); - exec_list(action, env); - } - } - -exit: - name_list_cleanup(&filter_subsystem_match_list); - name_list_cleanup(&filter_subsystem_nomatch_list); - name_list_cleanup(&filter_attr_match_list); - name_list_cleanup(&filter_attr_nomatch_list); - - if (sock >= 0) - close(sock); - sysfs_cleanup(); - logging_close(); - return 0; -} -- cgit v1.2.3-54-g00ecf