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.
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)
--- /dev/null
+/*
+ * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * 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 <time.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+
+#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 <KEY>=<valaue> 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=<level> 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=<KEY>=<value> set a global environment variable\n"
+ " --max-childs=<N> maximum number of childs\n"
+ " --max-childs-running=<N> 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 --<command> format, "
+ "this will stop working in a future release\n");
+ err("udevadm control commands requires the --<command> 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#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=<type> 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=<devpath> sysfs device path used for query or chain\n"
+ " --name=<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=<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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * 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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2006 Kay Sievers <kay@vrfy.org>
+ *
+ * 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 <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#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=<seconds>]\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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <syslog.h>
+#include <getopt.h>
+
+#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 <devpath>\n"
+ " --action=<string> set action string\n"
+ " --subsystem=<string> 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
+ * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
+ *
+ * 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 <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <fnmatch.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#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=<socket path> pass events to socket instead of triggering kernel events\n"
+ " --env=<KEY>=<value> pass an additional key (works only with --socket=)\n"
+ " --subsystem-match=<subsystem> trigger devices from a matching subystem\n"
+ " --subsystem-nomatch=<subsystem> exclude devices from a matching subystem\n"
+ " --attr-match=<file[=<value>]> trigger devices with a matching sysfs\n"
+ " attribute\n"
+ " --attr-nomatch=<file[=<value>]> 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;
+}
+++ /dev/null
-/*
- * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
- *
- * 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 <time.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/un.h>
-
-#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 <KEY>=<valaue> 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=<level> 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=<KEY>=<value> set a global environment variable\n"
- " --max-childs=<N> maximum number of childs\n"
- " --max-childs-running=<N> 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 --<command> format, "
- "this will stop working in a future release\n");
- err("udevadm control commands requires the --<command> 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;
-}
#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,
+++ /dev/null
-/*
- * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <errno.h>
-#include <getopt.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#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=<type> 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=<devpath> sysfs device path used for query or chain\n"
- " --name=<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=<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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
- *
- * 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 <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <signal.h>
-#include <getopt.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/select.h>
-#include <linux/types.h>
-#include <linux/netlink.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2006 Kay Sievers <kay@vrfy.org>
- *
- * 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 <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <syslog.h>
-#include <getopt.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#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=<seconds>]\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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2003-2004 Greg Kroah-Hartman <greg@kroah.com>
- * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
- *
- * 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 <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <errno.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <syslog.h>
-#include <getopt.h>
-
-#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 <devpath>\n"
- " --action=<string> set action string\n"
- " --subsystem=<string> 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2004-2006 Kay Sievers <kay@vrfy.org>
- * Copyright (C) 2006 Hannes Reinecke <hare@suse.de>
- *
- * 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 <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <syslog.h>
-#include <fnmatch.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#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=<socket path> pass events to socket instead of triggering kernel events\n"
- " --env=<KEY>=<value> pass an additional key (works only with --socket=)\n"
- " --subsystem-match=<subsystem> trigger devices from a matching subystem\n"
- " --subsystem-nomatch=<subsystem> exclude devices from a matching subystem\n"
- " --attr-match=<file[=<value>]> trigger devices with a matching sysfs\n"
- " attribute\n"
- " --attr-nomatch=<file[=<value>]> 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;
-}