summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKay Sievers <kay.sievers@vrfy.org>2009-07-13 03:09:05 +0200
committerKay Sievers <kay.sievers@vrfy.org>2009-07-13 03:09:05 +0200
commitbc113de9a4dc1229f7533acd41310a56d60fbe7e (patch)
tree8d29f70ea1a7770fbc14cad3f71c6881341be703
parentadda4c682ad2c56fc091222be3bd94fa817013b9 (diff)
udevd: handle SIGCHLD before the worker event message
We may need to handle SIGCHLD before the queued worker message. The last reference, from the SIGCHLD or the worker message will clean up the worker context. In case we receive an unexpected SIGCHLD with an error, we let the event fail and clean up the worker context.
-rw-r--r--udev/udevd.c47
1 files changed, 33 insertions, 14 deletions
diff --git a/udev/udevd.c b/udev/udevd.c
index 876d2ec1df..14736366d0 100644
--- a/udev/udevd.c
+++ b/udev/udevd.c
@@ -128,6 +128,8 @@ enum worker_state {
struct worker {
struct udev_list_node node;
+ struct udev *udev;
+ int refcount;
pid_t pid;
struct udev_monitor *monitor;
enum worker_state state;
@@ -176,9 +178,22 @@ static void event_sig_handler(int signum)
}
}
+static struct worker *worker_ref(struct worker *worker)
+{
+ worker->refcount++;
+ return worker;
+}
+
static void worker_unref(struct worker *worker)
{
+ worker->refcount--;
+ if (worker->refcount > 0)
+ return;
+
+ udev_list_node_remove(&worker->node);
udev_monitor_unref(worker->monitor);
+ childs--;
+ info(worker->udev, "worker [%u] cleaned up\n", worker->pid);
free(worker);
}
@@ -201,6 +216,9 @@ static void worker_new(struct event *event)
worker = calloc(1, sizeof(struct worker));
if (worker == NULL)
return;
+ /* worker + event reference */
+ worker->refcount = 2;
+ worker->udev = event->udev;
pid = fork();
switch (pid) {
@@ -339,18 +357,17 @@ static void event_run(struct event *event)
if (worker->state != WORKER_IDLE)
continue;
- worker->event = event;
- worker->state = WORKER_RUNNING;
- event->state = EVENT_RUNNING;
count = udev_monitor_send_device(monitor, worker->monitor, event->dev);
if (count < 0) {
- event->state = EVENT_QUEUED;
- worker->event = NULL;
err(event->udev, "worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count);
- worker->state = WORKER_KILLED;
kill(worker->pid, SIGKILL);
+ worker->state = WORKER_KILLED;
continue;
}
+ worker_ref(worker);
+ worker->event = event;
+ worker->state = WORKER_RUNNING;
+ event->state = EVENT_RUNNING;
return;
}
@@ -544,6 +561,7 @@ static void worker_returned(void)
worker->event = NULL;
if (worker->state != WORKER_KILLED)
worker->state = WORKER_IDLE;
+ worker_unref(worker);
break;
}
}
@@ -702,17 +720,18 @@ static void handle_signal(struct udev *udev, int signo)
if (worker->pid != pid)
continue;
- /* fail event, if worker died unexpectedly */
- if (worker->event != NULL) {
- worker->event->exitcode = status;
+ info(udev, "worker [%u] exit\n", pid);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
err(udev, "worker [%u] unexpectedly returned with status 0x%04x\n", pid, status);
- event_queue_delete(worker->event);
+ if (worker->event != NULL) {
+ err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath);
+ worker->event->exitcode = -32;
+ event_queue_delete(worker->event);
+ /* drop reference from running event */
+ worker_unref(worker);
+ }
}
-
- udev_list_node_remove(&worker->node);
worker_unref(worker);
- childs--;
- info(udev, "worker [%u] exit\n", pid);
break;
}
}