diff --git a/Makefile b/Makefile index 8ff4dd1384..0fd158a2c7 100644 --- 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 index 0000000000..013cdb9d6c --- /dev/null +++ b/test/udevd_test.sh @@ -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 index 0000000000..08dede36ee --- /dev/null +++ b/udevd.c @@ -0,0 +1,255 @@ +/* + * udevd.c + * + * Userspace devfs + * + * Copyright (C) 2004 Ling, Xiaofeng + * Copyright (C) 2004 Kay Sievers + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..eb516ab65d --- /dev/null +++ b/udevd.h @@ -0,0 +1,36 @@ +/* + * udevd.h + * + * Userspace devfs + * + * Copyright (C) 2004 Ling, Xiaofeng + * + * + * 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 index 0000000000..37afaf55fa --- /dev/null +++ b/udevsend.c @@ -0,0 +1,152 @@ +/* + * udevsend.c + * + * Userspace devfs + * + * Copyright (C) 2004 Ling, Xiaofeng + * Copyright (C) 2004 Kay Sievers + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#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; +}