chiark / gitweb /
add udevtrigger to request events for coldplug
authorKay Sievers <kay.sievers@suse.de>
Fri, 17 Mar 2006 00:52:15 +0000 (01:52 +0100)
committerKay Sievers <kay.sievers@suse.de>
Fri, 17 Mar 2006 00:52:15 +0000 (01:52 +0100)
The shell script can't handle 1000's of devices.

Makefile
udevtrigger.8 [new file with mode: 0644]
udevtrigger.c [new file with mode: 0644]
udevtrigger.xml [new file with mode: 0644]

index 9158550..ddb7417 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -49,6 +49,7 @@ V =
 PROGRAMS = \
        udev                            \
        udevd                           \
+       udevtrigger                     \
        udevsend                        \
        udevcontrol                     \
        udevmonitor                     \
@@ -84,6 +85,7 @@ MAN_PAGES = \
        udev.7                          \
        udevmonitor.8                   \
        udevd.8                         \
+       udevtrigger.8                   \
        udevsend.8                      \
        udevtest.8                      \
        udevinfo.8                      \
@@ -265,6 +267,7 @@ install-man:
        $(INSTALL_DATA) -D udevinfo.8 $(DESTDIR)$(mandir)/man8/udevinfo.8
        $(INSTALL_DATA) -D udevtest.8 $(DESTDIR)$(mandir)/man8/udevtest.8
        $(INSTALL_DATA) -D udevd.8 $(DESTDIR)$(mandir)/man8/udevd.8
+       $(INSTALL_DATA) -D udevtrigger.8 $(DESTDIR)$(mandir)/man8/udevtrigger.8
        $(INSTALL_DATA) -D udevmonitor.8 $(DESTDIR)$(mandir)/man8/udevmonitor.8
        - ln -f -s udevd.8 $(DESTDIR)$(mandir)/man8/udevcontrol.8
        @extras="$(EXTRAS)"; for target in $$extras; do \
@@ -277,6 +280,7 @@ uninstall-man:
        - rm -f $(DESTDIR)$(mandir)/man8/udevinfo.8
        - rm -f $(DESTDIR)$(mandir)/man8/udevtest.8
        - rm -f $(DESTDIR)$(mandir)/man8/udevd.8
+       - rm -f $(DESTDIR)$(mandir)/man8/udevtrigger.8
        - rm -f $(DESTDIR)$(mandir)/man8/udevmonitor.8
        - rm -f $(DESTDIR)$(mandir)/man8/udevcontrol.8
        @ extras="$(EXTRAS)"; for target in $$extras; do \
@@ -287,6 +291,7 @@ uninstall-man:
 install-bin:
        $(INSTALL) -d $(DESTDIR)$(udevdir)
        $(INSTALL_PROGRAM) -D udevd $(DESTDIR)$(sbindir)/udevd
+       $(INSTALL_PROGRAM) -D udevtrigger $(DESTDIR)$(sbindir)/udevtrigger
        $(INSTALL_PROGRAM) -D udevcontrol $(DESTDIR)$(sbindir)/udevcontrol
        $(INSTALL_PROGRAM) -D udevmonitor $(DESTDIR)$(usrsbindir)/udevmonitor
        $(INSTALL_PROGRAM) -D udevinfo $(DESTDIR)$(usrbindir)/udevinfo
@@ -303,6 +308,7 @@ endif
 
 uninstall-bin:
        - rm -f $(DESTDIR)$(sbindir)/udevd
+       - rm -f $(DESTDIR)$(sbindir)/udevtrigger
        - rm -f $(DESTDIR)$(sbindir)/udevcontrol
        - rm -f $(DESTDIR)$(usrsbindir)/udevmonitor
        - rm -f $(DESTDIR)$(usrbindir)/udevinfo
diff --git a/udevtrigger.8 b/udevtrigger.8
new file mode 100644 (file)
index 0000000..a792eac
--- /dev/null
@@ -0,0 +1,37 @@
+.\" ** You probably do not want to edit this file directly **
+.\" It was generated using the DocBook XSL Stylesheets (version 1.69.1).
+.\" Instead of manually editing it, you probably should edit the DocBook XML
+.\" source for it and then use the DocBook XSL Stylesheets to regenerate it.
+.TH "UDEVTRIGGER" "8" "March 2006" "udev" "udevtrigger"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+udevtrigger \- request kernel devices events for coldplug
+.SH "SYNOPSIS"
+.HP 12
+\fBudevtrigger\fR [\fB\-\-verbose\fR] [\fB\-\-dry\-run\fR]
+.SH "DESCRIPTION"
+.PP
+Trigger kernel device uevents to replay missing events at system coldplug.
+.SH "OPTIONS"
+.TP
+\fB\-\-verbose\fR
+print out the devices found in sysfs.
+.TP
+\fB\-\-dry\-run\fR
+don't actually trigger the event.
+.SH "ENVIRONMENT"
+.TP
+\fBUDEV_LOG\fR
+Overrides the syslog priority specified in the config file.
+.SH "AUTHOR"
+.PP
+Written by Kay Sievers
+<kay.sievers@vrfy.org>
+and Hannes Reinecke
+<hare@suse.de>.
+.SH "SEE ALSO"
+.PP
+\fBudev\fR(8)
diff --git a/udevtrigger.c b/udevtrigger.c
new file mode 100644 (file)
index 0000000..bc8453a
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * udevtrigger.c
+ *
+ * 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.,
+ *     675 Mass Ave, Cambridge, MA 02139, 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 <sys/stat.h>
+#include <sys/types.h>
+
+#include "udev.h"
+
+static const char *udev_log_str;
+static int verbose;
+static int dry_run;
+
+#ifdef USE_LOG
+void log_message(int priority, const char *format, ...)
+{
+       va_list args;
+
+       if (priority > udev_log_priority)
+               return;
+
+       va_start(args, format);
+       vsyslog(priority, format, args);
+       va_end(args);
+}
+#endif
+
+/* list of devices that we should run last due to any one of a number of reasons */
+static char *last_list[] = {
+       "/class/block/md",
+       "/class/block/dm-",
+       "/block/md",
+       "/block/dm-",
+       NULL
+};
+
+/* list of devices that we should run first due to any one of a number of reasons */
+static char *first_list[] = {
+       "/class/mem",
+       "/class/tty",
+       NULL
+};
+
+LIST_HEAD(device_first_list);
+LIST_HEAD(device_default_list);
+LIST_HEAD(device_last_list);
+
+static int device_list_insert(const char *path)
+{
+       struct list_head *device_list = &device_default_list;
+       int i;
+
+       for (i = 0; first_list[i] != NULL; i++) {
+               if (strncmp(path, first_list[i], strlen(first_list[i])) == 0) {
+                       device_list = &device_first_list;
+                       break;
+               }
+       }
+       for (i = 0; last_list[i] != NULL; i++) {
+               if (strncmp(path, last_list[i], strlen(last_list[i])) == 0) {
+                       device_list = &device_last_list;
+                       break;
+               }
+       }
+
+       dbg("add '%s'" , path);
+       /* double entries will be ignored */
+       name_list_add(device_list, path, 0);
+       return 0;
+}
+
+static void trigger_uevent(const char *path)
+{
+       char filename[PATH_SIZE];
+       int fd;
+
+       strlcpy(filename, path, sizeof(filename));
+       strlcat(filename, "/uevent", sizeof(filename));
+
+       if (verbose)
+               printf("%s\n", path);
+
+       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, "add", 3) < 0)
+               info("error on triggering %s: %s\n", filename, strerror(errno));
+
+       close(fd);
+}
+
+static void exec_lists(void)
+{
+       struct name_entry *loop_device;
+       struct name_entry *tmp_device;
+
+       /* handle the devices on the "first" list first */
+       list_for_each_entry_safe(loop_device, tmp_device, &device_first_list, node) {
+               trigger_uevent(loop_device->name);
+               list_del(&loop_device->node);
+               free(loop_device);
+       }
+
+       /* handle the devices on the "default" list next */
+       list_for_each_entry_safe(loop_device, tmp_device, &device_default_list, node) {
+               trigger_uevent(loop_device->name);
+               list_del(&loop_device->node);
+               free(loop_device);
+       }
+
+       /* handle devices on the "last" list, if any */
+       list_for_each_entry_safe(loop_device, tmp_device, &device_last_list, node) {
+               trigger_uevent(loop_device->name);
+               list_del(&loop_device->node);
+               free(loop_device);
+       }
+}
+
+static int is_device(const char *path)
+{
+       char filename[PATH_SIZE];
+       struct stat statbuf;
+
+       /* look for the uevent file of the kobject */
+       strlcpy(filename, path, sizeof(filename));
+       strlcat(filename, "/uevent", sizeof(filename));
+       if (stat(filename, &statbuf) < 0)
+               return 0;
+
+       if (!(statbuf.st_mode & S_IWUSR))
+               return 0;
+
+       return 1;
+}
+
+static void udev_scan_bus(void)
+{
+       char base[PATH_SIZE];
+       DIR *dir;
+       struct dirent *dent;
+
+       strlcpy(base, sysfs_path, sizeof(base));
+       strlcat(base, "/bus", 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));
+                       strlcat(dirname, "/devices", sizeof(dirname));
+
+                       /* look for devices */
+                       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 (is_device(dirname2))
+                                               device_list_insert(dirname2);
+                               }
+                               closedir(dir2);
+                       }
+               }
+               closedir(dir);
+       }
+}
+
+static void udev_scan_block(void)
+{
+       char base[PATH_SIZE];
+       DIR *dir;
+       struct dirent *dent;
+       struct stat statbuf;
+
+       /* skip if "block" is already a "class" */
+       strlcpy(base, sysfs_path, sizeof(base));
+       strlcat(base, "/class/block", sizeof(base));
+       if (stat(base, &statbuf) == 0)
+               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 (is_device(dirname))
+                               device_list_insert(dirname);
+                       else
+                               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 (is_device(dirname2))
+                                               device_list_insert(dirname2);
+                               }
+                               closedir(dir2);
+                       }
+               }
+               closedir(dir);
+       }
+}
+
+static void udev_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;
+
+                       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 (is_device(dirname2))
+                                               device_list_insert(dirname2);
+                               }
+                               closedir(dir2);
+                       }
+               }
+               closedir(dir);
+       }
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+       int i;
+
+       logging_init("udevtrigger");
+       udev_config_init();
+       dbg("version %s", UDEV_VERSION);
+
+       udev_log_str = getenv("UDEV_LOG");
+
+       for (i = 1 ; i < argc; i++) {
+               char *arg = argv[i];
+
+               if (strcmp(arg, "--verbose") == 0 || strcmp(arg, "-v") == 0)
+                       verbose = 1;
+               else if (strcmp(arg, "--dry-run") == 0 || strcmp(arg, "-n") == 0)
+                       dry_run = 1;
+               else {
+                       fprintf(stderr, "Usage: udevtrigger [--verbose] [--dry-run]\n");
+                       goto exit;
+               }
+       }
+
+       sysfs_init();
+
+       udev_scan_bus();
+       udev_scan_class();
+       udev_scan_block();
+       exec_lists();
+
+       sysfs_cleanup();
+exit:
+       logging_close();
+       return 0;
+}
diff --git a/udevtrigger.xml b/udevtrigger.xml
new file mode 100644 (file)
index 0000000..da83d56
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version='1.0'?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<article>
+  <articleinfo>
+    <title>xmlto</title>
+    <author>
+      <firstname>Kay</firstname>
+      <surname>Sievers</surname>
+      <email>kay.sievers@vrfy.org</email>
+    </author>
+    <copyright>
+      <year>2006</year>
+      <holder>Kay Sievers</holder>
+    </copyright>
+  </articleinfo>
+
+  <section>
+    <title>udevtrigger</title>
+    <refentry>
+      <refentryinfo>
+        <title>udevtrigger</title>
+        <date>March 2006</date>
+        <productname>udev</productname>
+      </refentryinfo>
+
+      <refmeta>
+        <refentrytitle>udevtrigger</refentrytitle>
+        <manvolnum>8</manvolnum>
+      </refmeta>
+
+      <refnamediv>
+        <refname>udevtrigger</refname><refpurpose>request kernel devices events for coldplug</refpurpose>
+      </refnamediv>
+
+      <refsynopsisdiv>
+        <cmdsynopsis>
+          <command>udevtrigger</command>
+          <arg><option>--verbose</option></arg>
+          <arg><option>--dry-run</option></arg>
+        </cmdsynopsis>
+      </refsynopsisdiv>
+
+      <refsect1><title>DESCRIPTION</title>
+        <para>Trigger kernel device uevents to replay missing events at system coldplug.</para>
+      </refsect1>
+
+      <refsect1><title>OPTIONS</title>
+        <variablelist>
+          <varlistentry>
+            <term><option>--verbose</option></term>
+            <listitem>
+              <para>print out the devices found in sysfs.</para>
+            </listitem>
+          </varlistentry>
+          <varlistentry>
+            <term><option>--dry-run</option></term>
+            <listitem>
+              <para>don't actually trigger the event.</para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+      </refsect1>
+
+      <refsect1><title>ENVIRONMENT</title>
+        <variablelist>
+          <varlistentry>
+            <term><option>UDEV_LOG</option></term>
+            <listitem>
+              <para>Overrides the syslog priority specified in the config file.</para>
+            </listitem>
+          </varlistentry>
+        </variablelist>
+     </refsect1>
+
+      <refsect1><title>AUTHOR</title>
+        <para>Written by Kay Sievers <email>kay.sievers@vrfy.org</email> and
+        Hannes Reinecke <email>hare@suse.de</email>.</para>
+      </refsect1>
+
+      <refsect1>
+        <title>SEE ALSO</title>
+        <para><citerefentry>
+            <refentrytitle>udev</refentrytitle><manvolnum>8</manvolnum>
+        </citerefentry></para>
+      </refsect1>
+    </refentry>
+  </section>
+</article>