chiark / gitweb /
[PATCH] spilt udev into pieces
authorkay.sievers@vrfy.org <kay.sievers@vrfy.org>
Fri, 23 Jan 2004 08:28:57 +0000 (00:28 -0800)
committerGreg KH <gregkh@suse.de>
Wed, 27 Apr 2005 04:13:17 +0000 (21:13 -0700)
On Thu, Jan 22, 2004 at 01:27:45AM +0100, Kay Sievers wrote:
> On Wed, Jan 21, 2004 at 02:38:25PM +0100, Kay Sievers wrote:
> > On Thu, Jan 15, 2004 at 01:45:10PM -0800, Greg KH wrote:
> > > On Thu, Jan 15, 2004 at 10:36:25PM +0800, Ling, Xiaofeng wrote:
> > > > Hi, Greg
> > > >    I wrote a simple implementation for the two pieces
> > > > of send and receive hotplug event,
> > > > use a message queue and a list for the out of order
> > > >  hotplug event. It also has a timeout timer of 3 seconds.
> > > >  They are now separate program. the file nseq is the test script.
> > > >  Could you have a look to see wether it is feasible?
> > > > If so, I'll continue to merge with udev.
> > >
> > > Yes, very nice start.  Please continue on.
> > >
> > > One minor comment, please stick with the kernel coding style when you
> > > are writing new code for udev.
> >
> > I took the code from Xiaofeng, cleaned the whitespace, renamed some bits,
> > tweaked the debugging, added the udev exec and created a patch for the current tree.
> >
> > It seems functional now, by simply executing our current udev (dirty hack).
> > It reorders the incoming events and if one is missing it delays the
> > execution of the following ones up to a maximum of 10 seconds.
> >
> > Test script is included, but you can't mix hotplug sequence numbers and
> > test script numbers, it will result in waiting for the missing numbers :)
>
> Hey, nobody want's to play with me?
> So here I'm chatting with myself :)
>
> This is the next version with signal handling for resetting the expected
> signal number. I changed the behaviour of the timeout to skip all
> missing events at once and to proceed with the next event in the queue.
>
> So it's now possible to use the test script at any time, cause it resets
> the daemon, if real hotplug event coming in later all missing nimbers will
> be skipped after a timeout of 10 seconds and the queued events are applied.

Here is the next updated updated version to apply to the lastet udev.
I've added infrastructure for getting the state of the IPC queue in the
sender and set the program to exec by the daemon. Also the magic key id
is replaced by the usual key generation by path/nr.

It looks promising, I use it on my machine and my 4in1 USB-flash-reader
connect/disconnect emits the events "randomly" but udevd is able to
reorder it and calls our normal udev in the right order.

Makefile
test/udevd_test.sh [new file with mode: 0644]
udevd.c [new file with mode: 0644]
udevd.h [new file with mode: 0644]
udevsend.c [new file with mode: 0644]

index 8ff4dd1..0fd158a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,8 @@ USE_DBUS = false
 
 
 ROOT =         udev
+DAEMON =       udevd
+SENDER =       udevsend
 VERSION =      014_bk
 INSTALL_DIR =  /usr/local/bin
 RELEASE_NAME = $(ROOT)-$(VERSION)
@@ -155,7 +157,7 @@ endif
 
 CFLAGS += -I$(PWD)/libsysfs
 
-all: $(ROOT)
+all: $(ROOT) $(DAEMON) $(SENDER)
        @extras="$(EXTRAS)" ; for target in $$extras ; do \
                echo $$target ; \
                $(MAKE) prefix=$(prefix) LD="$(LD)" SYSFS="$(SYSFS)" \
@@ -229,10 +231,18 @@ $(ROOT): $(OBJS) udev.h namedev.h udev_version.h udev_dbus.h udevdb.h klibc_fixu
        $(LD) $(LDFLAGS) -o $(ROOT) $(CRT0) $(OBJS) $(LIB_OBJS) $(ARCH_LIB_OBJS)
        $(STRIPCMD) $(ROOT)
 
+$(DAEMON): $(ROOT) udevd.h udevd.o
+       $(LD) $(LDFLAGS) -o $(DAEMON) $(CRT0) udevd.o logging.o $(LIB_OBJS) $(ARCH_LIB_OBJS)
+       $(STRIPCMD) $(ROOT)
+
+$(SENDER): $(ROOT) udevd.h udevsend.o
+       $(LD) $(LDFLAGS) -o $(SENDER) $(CRT0) udevsend.o logging.o $(LIB_OBJS) $(ARCH_LIB_OBJS)
+       $(STRIPCMD) $(ROOT)
+
 clean:
        -find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \
         | xargs rm -f 
-       -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS)
+       -rm -f core $(ROOT) $(GEN_HEADERS) $(GEN_CONFIGS) $(DAEMON) $(SENDER)
        $(MAKE) -C klibc clean
        @extras="$(EXTRAS)" ; for target in $$extras ; do \
                echo $$target ; \
diff --git a/test/udevd_test.sh b/test/udevd_test.sh
new file mode 100644 (file)
index 0000000..013cdb9
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# reset udevd, expected sequence number and empty queue
+killall -HUP udevd
+
+export ACTION=add
+export DEVPATH=/block/sda
+
+export SEQNUM=1
+./udevsend block
+
+export SEQNUM=2
+./udevsend block
+
+export SEQNUM=3
+./udevsend block
+
+export SEQNUM=5
+./udevsend block
+
+export SEQNUM=4
+./udevsend block
+
+export SEQNUM=6
+./udevsend block
+
+export SEQNUM=7
+./udevsend block
+
+export SEQNUM=10
+./udevsend block
+
+export SEQNUM=9
+#./udevsend block
+
+export SEQNUM=8
+#./udevsend block
+
+export SEQNUM=13
+./udevsend block
+
+export SEQNUM=12
+./udevsend block
+
+export SEQNUM=11
+./udevsend block
+
diff --git a/udevd.c b/udevd.c
new file mode 100644 (file)
index 0000000..08dede3
--- /dev/null
+++ b/udevd.c
@@ -0,0 +1,255 @@
+/*
+ * udevd.c
+ *
+ * Userspace devfs
+ *
+ * Copyright (C) 2004 Ling, Xiaofeng <xiaofeng.ling@intel.com>
+ * Copyright (C) 2004 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.,
+ *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "udev.h"
+#include "udevd.h"
+#include "logging.h"
+
+#define BUFFER_SIZE            1024
+#define TIMEOUT_SECONDS                10
+
+static void reset_timer(void);
+static void reset_queue(void);
+
+
+static int expect_seqnum = 0;
+static int timeout_value = TIMEOUT_SECONDS;
+static int timeout = 0;
+static struct hotplug_msg *head = NULL;
+static char exec_program[100];
+
+static void sig_handler(int signum)
+{
+       dbg("caught signal %d", signum);
+       switch (signum) {
+               case SIGHUP:
+                       dbg("reset requested, all waiting events killed");
+                       reset_timer();
+                       reset_queue();
+                       timeout = 0;
+                       expect_seqnum = 0;
+                       break;
+
+               case SIGINT:
+               case SIGTERM:
+               case SIGKILL:
+                       exit(20 + signum);
+                       break;
+
+               default:
+                       dbg("unhandled signal");
+       }
+}
+
+static void sig_alarmhandler(int signum)
+{
+       dbg("caught signal %d", signum);
+       switch (signum) {
+       case SIGALRM:
+               timeout = 1;
+               dbg("event timeout reached");
+               break;
+
+       default:
+               dbg("unhandled signal");
+       }
+}
+
+static void dump_queue(void)
+{
+       struct hotplug_msg *p;
+       p = head;
+
+       dbg("next expected sequence is %d", expect_seqnum);
+       while(p) {
+               dbg("sequence %d in queue", p->seqnum);
+               p=p->next;
+       }
+}
+
+static void dump_msg(struct hotplug_msg *pmsg)
+{
+       dbg("sequence %d, '%s', '%s', '%s'",
+           pmsg->seqnum, pmsg->action, pmsg->devpath, pmsg->subsystem);
+}
+
+static void dispatch_msg(struct hotplug_msg *pmsg)
+{
+       dump_msg(pmsg);
+       dbg("exec '%s'", exec_program);
+       setenv("ACTION", pmsg->action, 1);
+       setenv("DEVPATH", pmsg->devpath, 1);
+       execl(exec_program, pmsg->subsystem);
+}
+
+static void reset_timer(void)
+{
+       alarm(0);
+}
+
+static void set_timer(void)
+{
+       signal(SIGALRM, sig_alarmhandler);
+       alarm(timeout_value);
+}
+
+static void reset_queue(void)
+{
+       struct hotplug_msg *p;
+       p = head;
+
+       while(head) {
+               p = head;
+               head = head->next;
+               free(p);
+       }
+}
+
+static void check_queue(void)
+{
+       struct hotplug_msg *p;
+       p = head;
+
+       dump_queue();
+       while(head && head->seqnum == expect_seqnum) {
+               dispatch_msg(head);
+               expect_seqnum++;
+               p = head;
+               head = head->next;
+               free(p);
+       }
+       if (head != NULL)
+               set_timer();
+       else
+               reset_timer();
+}
+
+static void add_queue(struct hotplug_msg *pmsg)
+{
+       struct hotplug_msg *pnewmsg;
+       struct hotplug_msg *p;
+       struct hotplug_msg *p1;
+
+       p = head;
+       p1 = NULL;
+       pnewmsg = malloc(sizeof(struct hotplug_msg));
+       *pnewmsg = *pmsg;
+       pnewmsg->next = NULL;
+       while(p && pmsg->seqnum > p->seqnum) {
+               p1 = p;
+               p = p->next;
+       }
+       pnewmsg->next = p;
+       if (p1 == NULL) {
+               head = pnewmsg;
+       } else {
+               p1->next = pnewmsg;
+       }
+       dump_queue();
+}
+
+static int process_queue(void)
+{
+       int msgid;
+       key_t key;
+       struct hotplug_msg *pmsg;
+       char buf[BUFFER_SIZE];
+       int ret;
+
+       key = ftok(DEFAULT_EXEC_PROGRAM, IPC_KEY_ID);
+       pmsg = (struct hotplug_msg *) buf;
+       msgid = msgget(key, IPC_CREAT);
+       if (msgid == -1) {
+               dbg("open message queue error");
+               goto exit;
+       }
+       while (1) {
+               ret = msgrcv(msgid, (struct msgbuf *) buf, BUFFER_SIZE-4, HOTPLUGMSGTYPE, 0);
+               if (ret != -1) {
+                       dbg("current sequence %d, expected sequence %d", pmsg->seqnum, expect_seqnum);
+
+                       /* init expected sequence with value from first call */
+                       if (expect_seqnum == 0) {
+                               expect_seqnum = pmsg->seqnum;
+                               dbg("init next expected sequence number to %d", expect_seqnum);
+                       }
+
+                       if (pmsg->seqnum > expect_seqnum) {
+                               add_queue(pmsg);
+                               set_timer();
+                       } else {
+                               if (pmsg->seqnum == expect_seqnum) {
+                                       dispatch_msg(pmsg);
+                                       expect_seqnum++;
+                                       check_queue();
+                               } else {
+                                       dbg("timeout event for unexpected sequence number %d", pmsg->seqnum);
+                               }
+                       }
+               } else
+                       if (errno == EINTR) {
+                               if (head != NULL) {
+                                       /* timeout, skip all missing, proceed with next queued event */
+                                       dbg("timeout reached, skip events %d - %d", expect_seqnum, head->seqnum-1);
+                                       expect_seqnum = head->seqnum;
+                               }
+                               check_queue();
+                               timeout = 0;
+                       } else {
+                               dbg("ipc message receive error '%s'", strerror(errno));
+                       }
+       }
+       return 0;
+exit:
+       return -1;
+}
+
+int main(int argc, char *argv[])
+{
+       /* get program to exec on events */
+       if (argc == 2)
+               strncpy(exec_program, argv[1], sizeof(exec_program));
+       else
+               strcpy(exec_program, DEFAULT_EXEC_PROGRAM);
+
+       /* set up signal handler */
+       signal(SIGINT, sig_handler);
+       signal(SIGTERM, sig_handler);
+       signal(SIGKILL, sig_handler);
+       signal(SIGHUP, sig_handler);
+
+       /* main loop */
+       process_queue();
+       return 0;
+}
diff --git a/udevd.h b/udevd.h
new file mode 100644 (file)
index 0000000..eb516ab
--- /dev/null
+++ b/udevd.h
@@ -0,0 +1,36 @@
+/*
+ * udevd.h
+ *
+ * Userspace devfs
+ *
+ * Copyright (C) 2004 Ling, Xiaofeng <xiaofeng.ling@intel.com>
+ *
+ *
+ *     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.
+ *
+ */
+
+#define HOTPLUGMSGTYPE         44
+#define DEFAULT_EXEC_PROGRAM   "/sbin/udev"
+#define IPC_KEY_ID             0
+
+
+struct hotplug_msg {
+       long mtype;
+       struct hotplug_msg *next;
+       int seqnum;
+       char action[8];
+       char devpath[128];
+       char subsystem[16];
+};
diff --git a/udevsend.c b/udevsend.c
new file mode 100644 (file)
index 0000000..37afaf5
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * udevsend.c
+ *
+ * Userspace devfs
+ *
+ * Copyright (C) 2004 Ling, Xiaofeng <xiaofeng.ling@intel.com>
+ * Copyright (C) 2004 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.,
+ *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "udev.h"
+#include "udevd.h"
+#include "logging.h"
+
+static inline char *get_action(void)
+{
+       char *action;
+
+       action = getenv("ACTION");
+       return action;
+}
+
+static inline char *get_devpath(void)
+{
+       char *devpath;
+
+       devpath = getenv("DEVPATH");
+       return devpath;
+}
+
+static inline char *get_seqnum(void)
+{
+       char *seqnum;
+
+       seqnum = getenv("SEQNUM");
+       return seqnum;
+}
+
+static int build_hotplugmsg(struct hotplug_msg **ppmsg, char *action,
+                           char *devpath, char *subsystem, int seqnum)
+{
+       struct hotplug_msg *pmsg;
+
+       pmsg = malloc(sizeof(struct hotplug_msg));
+       pmsg->mtype = HOTPLUGMSGTYPE;
+       pmsg->seqnum = seqnum;
+       strncpy(pmsg->action, action, 8);
+       strncpy(pmsg->devpath, devpath, 128);
+       strncpy(pmsg->subsystem, subsystem, 16);
+       *ppmsg = pmsg;
+       return sizeof(struct hotplug_msg);
+}
+
+static void free_hotplugmsg(struct hotplug_msg *pmsg)
+{
+       free(pmsg);
+}
+
+int main(int argc, char* argv[])
+{
+       int msgid;
+       key_t key;
+       struct msqid_ds  msg_queue;
+       struct msgbuf *pmsg;
+       char *action;
+       char *devpath;
+       char *subsystem;
+       char *seqnum;
+       int seq;
+       int retval = -EINVAL;
+       int size;
+
+       subsystem = argv[1];
+       if (subsystem == NULL) {
+               dbg("no subsystem");
+               goto exit;
+       }
+
+       devpath = get_devpath();
+       if (devpath == NULL) {
+               dbg("no devpath");
+               goto exit;
+       }
+
+       action = get_action();
+       if (action == NULL) {
+               dbg("no action");
+               goto exit;
+       }
+
+       seqnum = get_seqnum();
+       if (seqnum == NULL) {
+               dbg("no seqnum");
+               goto exit;
+       }
+
+       seq = atoi(seqnum);
+       key = ftok(DEFAULT_EXEC_PROGRAM, IPC_KEY_ID);
+       size =  build_hotplugmsg( (struct hotplug_msg**) &pmsg, action, devpath, subsystem, seq);
+       msgid = msgget(key, IPC_CREAT);
+       if (msgid == -1)
+       {
+               dbg("open ipc queue error");
+               goto exit;
+       }
+
+       /* get state of queue */
+       retval = msgctl(msgid, IPC_STAT, &msg_queue);
+       if (retval == -1) {
+               dbg("error getting info on ipc queue");
+               goto exit;
+       }
+       if (msg_queue.msg_qnum > 0)
+               dbg("%li messages already in the ipc queue", msg_queue.msg_qnum);
+
+       retval = msgsnd(msgid, pmsg, size, 0);
+       free_hotplugmsg( (struct hotplug_msg*) pmsg);
+       if (retval == -1)
+       {
+               dbg("send ipc message error");
+               goto exit;
+       }
+       return 0;
+
+exit:
+       if (retval > 0)
+               retval = 0;
+
+       return retval;
+}