Systemd/udevd.c
kay.sievers@vrfy.org 35b7d88c0d [PATCH] udevd - next round of fixes
Here is the next round. We have three queues now. All incoming messages
are queued in msg_list and if nothing is missing we move it to the
running_list and exec in the background.
If the exec comes back, it removes the message from the running_list and
frees the message.

Before we exec, we check the running_list if there is a udev running on
the same device path. If yes, we move the message to the delay_list. If
the former exec comes back, we move the message to the running_list and
exec it.

The very first event is delayed now to catch possible earlier sequences,
every following event is executed without delay if no sequence is missing.

The daemon doesn't exit by itself any longer, cause we don't want to
delay every first exec.

I've put a $(PWD) for now in the Makefile for testing this beast. Only
the local binaries are executed, not the /sbin/udev. We can change it
if we are ready for real testing.

And SIGKILL can't be cought, so I removed it from the handler :)


06:58:36 sig_handler: caught signal 15
06:58:36 main: using ipc queue 0x2d548
06:58:37 message is still in the ipc queue, starting daemon...
06:58:37 work: received sequence 3, expected sequence 0
06:58:37 msg_dump_queue: sequence 3 in queue
06:58:37 set_timeout: set timeout in 1 seconds
06:58:37 main: using ipc queue 0x2d548
06:58:37 main: using ipc queue 0x2d548
06:58:37 work: received sequence 1, expected sequence 1
06:58:37 msg_dump_queue: sequence 1 in queue
06:58:37 msg_dump_queue: sequence 3 in queue
06:58:37 msg_dump: sequence 1, 'add', '/block/sda', 'block'
06:58:37 msg_exec: child [8038] created
06:58:37 running_moveto_queue: move sequence 1 [8038] to running queue '/block/sda'
06:58:37 set_timeout: set timeout in 5 seconds
06:58:37 work: received sequence 2, expected sequence 2
06:58:37 msg_dump_queue: sequence 2 in queue
06:58:37 msg_dump_queue: sequence 3 in queue
06:58:37 msg_dump: sequence 2, 'add', '/block/sdb', 'block'
06:58:37 msg_exec: child [8039] created
06:58:37 running_moveto_queue: move sequence 2 [8039] to running queue '/block/sdb'
06:58:37 msg_dump: sequence 3, 'add', '/block/sdc', 'block'
06:58:37 msg_exec: child [8040] created
06:58:37 running_moveto_queue: move sequence 3 [8040] to running queue '/block/sdc'
06:58:37 main: using ipc queue 0x2d548
06:58:37 main: using ipc queue 0x2d548
06:58:37 work: received sequence 4, expected sequence 4
06:58:37 msg_dump_queue: sequence 4 in queue
06:58:37 msg_dump: sequence 4, 'remove', '/block/sdc', 'block'
06:58:37 msg_exec: delay exec of sequence 4, [8040] already working on '/block/sdc'
06:58:37 delayed_moveto_queue: move event to delayed queue '/block/sdc'
06:58:37 msg_exec: child [8043] created
06:58:37 running_moveto_queue: move sequence 4 [8043] to running queue '/block/sdc'
06:58:37 work: received sequence 5, expected sequence 5
06:58:37 msg_dump_queue: sequence 5 in queue
06:58:37 msg_dump: sequence 5, 'remove', '/block/sdb', 'block'
06:58:37 msg_exec: delay exec of sequence 5, [8039] already working on '/block/sdb'
06:58:37 delayed_moveto_queue: move event to delayed queue '/block/sdb'
06:58:37 msg_exec: child [8044] created
06:58:37 running_moveto_queue: move sequence 5 [8044] to running queue '/block/sdb'
06:58:37 main: using ipc queue 0x2d548
06:58:37 main: using ipc queue 0x2d548
06:58:37 work: received sequence 8, expected sequence 6
06:58:37 msg_dump_queue: sequence 8 in queue
06:58:37 set_timeout: set timeout in 5 seconds
06:58:37 work: received sequence 6, expected sequence 6
06:58:37 msg_dump_queue: sequence 6 in queue
06:58:37 msg_dump_queue: sequence 8 in queue
06:58:37 msg_dump: sequence 6, 'remove', '/block/sda', 'block'
06:58:37 msg_exec: delay exec of sequence 6, [8038] already working on '/block/sda'
06:58:37 delayed_moveto_queue: move event to delayed queue '/block/sda'
06:58:37 msg_exec: child [8047] created
06:58:37 running_moveto_queue: move sequence 6 [8047] to running queue '/block/sda'
06:58:37 set_timeout: set timeout in 5 seconds
06:58:38 sig_handler: caught signal 17
06:58:38 sig_handler: exec finished, pid 8038
06:58:38 set_timeout: set timeout in 4 seconds
06:58:38 msg_dump_queue: sequence 8 in queue
06:58:38 sig_handler: caught signal 17
06:58:38 sig_handler: exec finished, pid 8039
06:58:38 set_timeout: set timeout in 4 seconds
06:58:38 msg_dump_queue: sequence 8 in queue
06:58:38 sig_handler: caught signal 17
06:58:38 sig_handler: exec finished, pid 8040
06:58:38 set_timeout: set timeout in 4 seconds
06:58:38 msg_dump_queue: sequence 8 in queue
06:58:38 sig_handler: caught signal 17
06:58:38 sig_handler: exec finished, pid 8043
06:58:38 set_timeout: set timeout in 4 seconds
06:58:38 msg_dump_queue: sequence 8 in queue
06:58:38 sig_handler: caught signal 17
06:58:38 sig_handler: exec finished, pid 8044
06:58:38 set_timeout: set timeout in 4 seconds
06:58:38 msg_dump_queue: sequence 8 in queue
06:58:38 sig_handler: caught signal 17
06:58:38 sig_handler: exec finished, pid 8047
06:58:38 set_timeout: set timeout in 4 seconds
06:58:38 msg_dump_queue: sequence 8 in queue
06:58:39 main: using ipc queue 0x2d548
06:58:39 main: using ipc queue 0x2d548
06:58:39 work: received sequence 9, expected sequence 7
06:58:39 msg_dump_queue: sequence 8 in queue
06:58:39 msg_dump_queue: sequence 9 in queue
06:58:39 set_timeout: set timeout in 3 seconds
06:58:39 work: received sequence 11, expected sequence 7
06:58:39 msg_dump_queue: sequence 8 in queue
06:58:39 msg_dump_queue: sequence 9 in queue
06:58:39 msg_dump_queue: sequence 11 in queue
06:58:39 set_timeout: set timeout in 3 seconds
06:58:39 main: using ipc queue 0x2d548
06:58:39 work: received sequence 10, expected sequence 7
06:58:39 msg_dump_queue: sequence 8 in queue
06:58:39 msg_dump_queue: sequence 9 in queue
06:58:39 msg_dump_queue: sequence 10 in queue
06:58:39 msg_dump_queue: sequence 11 in queue
06:58:39 set_timeout: set timeout in 3 seconds
06:58:39 main: using ipc queue 0x2d548
06:58:39 work: received sequence 13, expected sequence 7
06:58:39 msg_dump_queue: sequence 8 in queue
06:58:39 msg_dump_queue: sequence 9 in queue
06:58:39 msg_dump_queue: sequence 10 in queue
06:58:39 msg_dump_queue: sequence 11 in queue
06:58:39 msg_dump_queue: sequence 13 in queue
06:58:39 set_timeout: set timeout in 3 seconds
06:58:39 main: using ipc queue 0x2d548
06:58:39 work: received sequence 14, expected sequence 7
06:58:39 msg_dump_queue: sequence 8 in queue
06:58:39 msg_dump_queue: sequence 9 in queue
06:58:39 msg_dump_queue: sequence 10 in queue
06:58:39 msg_dump_queue: sequence 11 in queue
06:58:39 msg_dump_queue: sequence 13 in queue
06:58:39 msg_dump_queue: sequence 14 in queue
06:58:39 set_timeout: set timeout in 3 seconds
06:58:39 main: using ipc queue 0x2d548
06:58:39 work: received sequence 15, expected sequence 7
06:58:39 msg_dump_queue: sequence 8 in queue
06:58:39 msg_dump_queue: sequence 9 in queue
06:58:39 msg_dump_queue: sequence 10 in queue
06:58:39 msg_dump_queue: sequence 11 in queue
06:58:39 msg_dump_queue: sequence 13 in queue
06:58:39 msg_dump_queue: sequence 14 in queue
06:58:39 msg_dump_queue: sequence 15 in queue
06:58:39 set_timeout: set timeout in 3 seconds
06:58:41 main: using ipc queue 0x2d548
06:58:41 work: received sequence 12, expected sequence 7
06:58:41 msg_dump_queue: sequence 8 in queue
06:58:41 msg_dump_queue: sequence 9 in queue
06:58:41 msg_dump_queue: sequence 10 in queue
06:58:41 msg_dump_queue: sequence 11 in queue
06:58:41 msg_dump_queue: sequence 12 in queue
06:58:41 msg_dump_queue: sequence 13 in queue
06:58:41 msg_dump_queue: sequence 14 in queue
06:58:41 msg_dump_queue: sequence 15 in queue
06:58:41 set_timeout: set timeout in 1 seconds
06:58:42 sig_handler: caught signal 14
06:58:42 sig_handler: event timeout reached
06:58:42 event 8, age 5 seconds, skip event 7-7
06:58:42 msg_dump: sequence 8, 'add', '/block/sdb', 'block'
06:58:42 msg_exec: child [8057] created
06:58:42 running_moveto_queue: move sequence 8 [8057] to running queue '/block/sdb'
06:58:42 msg_dump: sequence 9, 'add', '/block/sdc', 'block'
06:58:42 msg_exec: child [8058] created
06:58:42 running_moveto_queue: move sequence 9 [8058] to running queue '/block/sdc'
06:58:42 msg_dump: sequence 10, 'remove', '/block/sdc', 'block'
06:58:42 msg_exec: delay exec of sequence 10, [8058] already working on '/block/sdc'
06:58:42 delayed_moveto_queue: move event to delayed queue '/block/sdc'
06:58:42 msg_exec: child [8059] created
06:58:42 running_moveto_queue: move sequence 10 [8059] to running queue '/block/sdc'
06:58:42 msg_dump: sequence 11, 'remove', '/block/sdb', 'block'
06:58:42 msg_exec: delay exec of sequence 11, [8057] already working on '/block/sdb'
06:58:42 delayed_moveto_queue: move event to delayed queue '/block/sdb'
06:58:42 msg_exec: child [8060] created
06:58:42 running_moveto_queue: move sequence 11 [8060] to running queue '/block/sdb'
06:58:42 msg_dump: sequence 12, 'remove', '/block/sda', 'block'
06:58:42 msg_exec: child [8061] created
06:58:42 running_moveto_queue: move sequence 12 [8061] to running queue '/block/sda'
06:58:42 msg_dump: sequence 13, 'add', '/block/sda', 'block'
06:58:42 msg_exec: delay exec of sequence 13, [8061] already working on '/block/sda'
06:58:42 delayed_moveto_queue: move event to delayed queue '/block/sda'
06:58:42 msg_exec: child [8062] created
06:58:42 running_moveto_queue: move sequence 13 [8062] to running queue '/block/sda'
06:58:42 msg_dump: sequence 14, 'add', '/block/sdb', 'block'
06:58:42 msg_exec: delay exec of sequence 14, [8057] already working on '/block/sdb'
06:58:42 delayed_moveto_queue: move event to delayed queue '/block/sdb'
06:58:42 msg_exec: child [8063] created
06:58:42 running_moveto_queue: move sequence 14 [8063] to running queue '/block/sdb'
06:58:42 msg_dump: sequence 15, 'add', '/block/sdc', 'block'
06:58:42 msg_exec: delay exec of sequence 15, [8058] already working on '/block/sdc'
06:58:42 delayed_moveto_queue: move event to delayed queue '/block/sdc'
06:58:42 msg_exec: child [8064] created
06:58:42 running_moveto_queue: move sequence 15 [8064] to running queue '/block/sdc'
06:58:43 sig_handler: caught signal 17
06:58:43 sig_handler: exec finished, pid 8057
06:58:43 sig_handler: exec finished, pid 8058
06:58:43 sig_handler: caught signal 17
06:58:43 sig_handler: exec finished, pid 8059
06:58:43 sig_handler: caught signal 17
06:58:43 sig_handler: exec finished, pid 8060
06:58:43 sig_handler: exec finished, pid 8061
06:58:43 sig_handler: caught signal 17
06:58:43 sig_handler: exec finished, pid 8062
06:58:43 sig_handler: caught signal 17
06:58:43 sig_handler: exec finished, pid 8063
06:58:43 sig_handler: caught signal 17
06:58:43 sig_handler: exec finished, pid 8064
2005-04-26 21:13:19 -07:00

346 lines
7.7 KiB
C

/*
* 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 <stddef.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/wait.h>
#include <sys/msg.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include "list.h"
#include "udev.h"
#include "udev_version.h"
#include "udevd.h"
#include "logging.h"
#define BUFFER_SIZE 1024
static int running_remove_queue(pid_t pid);
static int msg_exec(struct hotplug_msg *msg);
static int expect_seqnum = 0;
static int lock_file = -1;
static char *lock_filename = ".udevd_lock";
LIST_HEAD(msg_list);
LIST_HEAD(running_list);
LIST_HEAD(delayed_list);
static void sig_handler(int signum)
{
pid_t pid;
dbg("caught signal %d", signum);
switch (signum) {
case SIGALRM:
dbg("event timeout reached");
break;
case SIGCHLD:
/* catch signals from exiting childs */
while ( (pid = waitpid(-1, NULL, WNOHANG)) > 0) {
dbg("exec finished, pid %d", pid);
running_remove_queue(pid);
}
break;
case SIGINT:
case SIGTERM:
if (lock_file >= 0) {
close(lock_file);
unlink(lock_filename);
}
exit(20 + signum);
break;
default:
dbg("unhandled signal");
}
}
static void set_timeout(int seconds)
{
alarm(seconds);
dbg("set timeout in %d seconds", seconds);
}
static int running_moveto_queue(struct hotplug_msg *msg)
{
dbg("move sequence %d [%d] to running queue '%s'",
msg->seqnum, msg->pid, msg->devpath);
list_move_tail(&msg->list, &running_list);
return 0;
}
static int running_remove_queue(pid_t pid)
{
struct hotplug_msg *child;
struct hotplug_msg *tmp_child;
list_for_each_entry_safe(child, tmp_child, &running_list, list)
if (child->pid == pid) {
list_del_init(&child->list);
free(child);
return 0;
}
return -EINVAL;
}
static pid_t running_getpid_by_devpath(struct hotplug_msg *msg)
{
struct hotplug_msg *child;
struct hotplug_msg *tmp_child;
list_for_each_entry_safe(child, tmp_child, &running_list, list)
if (strncmp(child->devpath, msg->devpath, sizeof(child->devpath)) == 0)
return child->pid;
return 0;
}
static void delayed_dump_queue(void)
{
struct hotplug_msg *child;
list_for_each_entry(child, &delayed_list, list)
dbg("event for '%s' in queue", child->devpath);
}
static int delayed_moveto_queue(struct hotplug_msg *msg)
{
dbg("move event to delayed queue '%s'", msg->devpath);
list_move_tail(&msg->list, &delayed_list);
return 0;
}
static void delayed_check_queue(void)
{
struct hotplug_msg *delayed_child;
struct hotplug_msg *running_child;
struct hotplug_msg *tmp_child;
/* see if we have delayed exec's that can run now */
list_for_each_entry_safe(delayed_child, tmp_child, &delayed_list, list)
list_for_each_entry_safe(running_child, tmp_child, &running_list, list)
if (strncmp(delayed_child->devpath, running_child->devpath,
sizeof(running_child->devpath)) == 0) {
dbg("delayed exec for '%s' can run now", delayed_child->devpath);
msg_exec(delayed_child);
}
}
static void msg_dump(struct hotplug_msg *msg)
{
dbg("sequence %d, '%s', '%s', '%s'",
msg->seqnum, msg->action, msg->devpath, msg->subsystem);
}
static int msg_exec(struct hotplug_msg *msg)
{
pid_t pid;
msg_dump(msg);
setenv("ACTION", msg->action, 1);
setenv("DEVPATH", msg->devpath, 1);
/* delay exec, if we already have a udev working on the same devpath */
pid = running_getpid_by_devpath(msg);
if (pid != 0) {
dbg("delay exec of sequence %d, [%d] already working on '%s'",
msg->seqnum, pid, msg->devpath);
delayed_moveto_queue(msg);
}
pid = fork();
switch (pid) {
case 0:
/* child */
execl(UDEV_BIN, "udev", msg->subsystem, NULL);
dbg("exec of child failed");
exit(1);
break;
case -1:
dbg("fork of child failed");
return -1;
default:
/* exec in background, get the SIGCHLD with the sig handler */
msg->pid = pid;
running_moveto_queue(msg);
break;
}
return 0;
}
static void msg_dump_queue(void)
{
struct hotplug_msg *msg;
list_for_each_entry(msg, &msg_list, list)
dbg("sequence %d in queue", msg->seqnum);
}
static void msg_check_queue(void)
{
struct hotplug_msg *msg;
struct hotplug_msg *tmp_msg;
time_t msg_age;
recheck:
/* dispatch events until one is missing */
list_for_each_entry_safe(msg, tmp_msg, &msg_list, list) {
if (msg->seqnum != expect_seqnum)
break;
msg_exec(msg);
expect_seqnum++;
}
/* recalculate next timeout */
if (list_empty(&msg_list) == 0) {
msg_age = time(NULL) - msg->queue_time;
if (msg_age > EVENT_TIMEOUT_SEC-1) {
info("event %d, age %li seconds, skip event %d-%d",
msg->seqnum, msg_age, expect_seqnum, msg->seqnum-1);
expect_seqnum = msg->seqnum;
goto recheck;
}
/* the first sequence gets its own timeout */
if (expect_seqnum == 0) {
msg_age = EVENT_TIMEOUT_SEC - FIRST_EVENT_TIMEOUT_SEC;
expect_seqnum = 1;
}
set_timeout(EVENT_TIMEOUT_SEC - msg_age);
return;
}
}
static int msg_add_queue(struct hotplug_msg *msg)
{
struct hotplug_msg *new_msg;
struct hotplug_msg *tmp_msg;
new_msg = malloc(sizeof(*new_msg));
if (new_msg == NULL) {
dbg("error malloc");
return -ENOMEM;
}
memcpy(new_msg, msg, sizeof(*new_msg));
/* store timestamp of queuing */
new_msg->queue_time = time(NULL);
/* sort message by sequence number into list*/
list_for_each_entry(tmp_msg, &msg_list, list)
if (tmp_msg->seqnum > new_msg->seqnum)
break;
list_add_tail(&new_msg->list, &tmp_msg->list);
return 0;
}
static void work(void)
{
struct hotplug_msg *msg;
int msgid;
key_t key;
char buf[BUFFER_SIZE];
int ret;
key = ftok(UDEVD_BIN, IPC_KEY_ID);
msg = (struct hotplug_msg *) buf;
msgid = msgget(key, IPC_CREAT);
if (msgid == -1) {
dbg("open message queue error");
exit(1);
}
while (1) {
ret = msgrcv(msgid, (struct msgbuf *) buf, BUFFER_SIZE-4, HOTPLUGMSGTYPE, 0);
if (ret != -1) {
dbg("received sequence %d, expected sequence %d", msg->seqnum, expect_seqnum);
if (msg->seqnum >= expect_seqnum) {
msg_add_queue(msg);
msg_dump_queue();
msg_check_queue();
continue;
}
dbg("too late for event with sequence %d, event skipped ", msg->seqnum);
} else {
if (errno == EINTR) {
msg_check_queue();
msg_dump_queue();
delayed_check_queue();
delayed_dump_queue();
continue;
}
dbg("ipc message receive error '%s'", strerror(errno));
}
}
}
static int one_and_only(void)
{
char string[100];
lock_file = open(lock_filename, O_RDWR | O_CREAT, 0x640);
/* see if we can open */
if (lock_file < 0)
return -1;
/* see if we can lock */
if (lockf(lock_file, F_TLOCK, 0) < 0) {
close(lock_file);
unlink(lock_filename);
return -1;
}
snprintf(string, sizeof(string), "%d\n", getpid());
write(lock_file, string, strlen(string));
return 0;
}
int main(int argc, char *argv[])
{
/* only let one version of the daemon run at any one time */
if (one_and_only() != 0)
exit(0);
/* set up signal handler */
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
signal(SIGALRM, sig_handler);
signal(SIGCHLD, sig_handler);
work();
exit(0);
}