chiark / gitweb /
[PATCH] klibc: use klcc wrapper instead of our own Makefile
[elogind.git] / udevd.c
diff --git a/udevd.c b/udevd.c
index 64abbe59a37254003231bbd649d5cb0d0ca9c3bd..2cd3622ce011a5a04630ff8d05595dbd6d53fc81 100644 (file)
--- a/udevd.c
+++ b/udevd.c
@@ -1,7 +1,7 @@
 /*
  * udevd.c - hotplug event serializer
  *
- * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
  * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca>
  *
  *
  */
 
 #include <stddef.h>
-#include <sys/wait.h>
 #include <signal.h>
 #include <unistd.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <sys/wait.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <fcntl.h>
 #include <sys/sysinfo.h>
 #include <sys/stat.h>
 
 #include "list.h"
+#include "udev_libc_wrapper.h"
 #include "udev.h"
 #include "udev_version.h"
 #include "udev_utils.h"
@@ -45,6 +49,7 @@
 
 /* global variables*/
 static int udevsendsock;
+static pid_t sid;
 
 static int pipefds[2];
 static long startup_time;
@@ -64,7 +69,7 @@ static void user_sighandler(void);
 static void reap_sigchilds(void);
 char *udev_bin;
 
-#ifdef LOG
+#ifdef USE_LOG
 void log_message (int level, const char *format, ...)
 {
        va_list args;
@@ -84,14 +89,14 @@ static void msg_dump_queue(void)
 #ifdef DEBUG
        struct hotplug_msg *msg;
 
-       list_for_each_entry(msg, &msg_list, list)
+       list_for_each_entry(msg, &msg_list, node)
                dbg("sequence %llu in queue", msg->seqnum);
 #endif
 }
 
 static void run_queue_delete(struct hotplug_msg *msg)
 {
-       list_del(&msg->list);
+       list_del(&msg->node);
        free(msg);
 }
 
@@ -103,13 +108,13 @@ static void msg_queue_insert(struct hotplug_msg *msg)
 
        if (msg->seqnum == 0) {
                dbg("no SEQNUM, move straight to the exec queue");
-               list_add(&msg->list, &exec_list);
+               list_add(&msg->node, &exec_list);
                run_exec_q = 1;
                return;
        }
 
        /* sort message by sequence number into list */
-       list_for_each_entry_reverse(loop_msg, &msg_list, list) {
+       list_for_each_entry_reverse(loop_msg, &msg_list, node) {
                if (loop_msg->seqnum < msg->seqnum)
                        break;
 
@@ -123,7 +128,7 @@ static void msg_queue_insert(struct hotplug_msg *msg)
        sysinfo(&info);
        msg->queue_time = info.uptime;
 
-       list_add(&msg->list, &loop_msg->list);
+       list_add(&msg->node, &loop_msg->node);
        dbg("queued message seq %llu", msg->seqnum);
 
        /* run msg queue manager */
@@ -144,6 +149,8 @@ static void udev_run(struct hotplug_msg *msg)
                /* child */
                close(udevsendsock);
                logging_close();
+
+               setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY);
                execve(udev_bin, argv, msg->envp);
                dbg("exec of child failed");
                _exit(1);
@@ -151,9 +158,6 @@ static void udev_run(struct hotplug_msg *msg)
        case -1:
                dbg("fork of child failed");
                run_queue_delete(msg);
-               /* note: we never managed to run, so we had no impact on 
-                * running_with_devpath(), so don't bother setting run_exec_q
-                */
                break;
        default:
                /* get SIGCHLD in main loop */
@@ -162,11 +166,105 @@ static void udev_run(struct hotplug_msg *msg)
        }
 }
 
+static int running_processes(void)
+{
+       int f;
+       static char buf[4096];
+       int len;
+       int running;
+       const char *pos;
+
+       f = open("/proc/stat", O_RDONLY);
+       if (f == -1)
+               return -1;
+
+       len = read(f, buf, sizeof(buf));
+       close(f);
+
+       if (len <= 0)
+               return -1;
+       else
+               buf[len] = '\0';
+
+       pos = strstr(buf, "procs_running ");
+       if (pos == NULL)
+               return -1;
+
+       if (sscanf(pos, "procs_running %u", &running) != 1)
+               return -1;
+
+       return running;
+}
+
+/* return the number of process es in our session, count only until limit */
+static int running_processes_in_session(pid_t session, int limit)
+{
+       DIR *dir;
+       struct dirent *dent;
+       int running = 0;
+
+       dir = opendir("/proc");
+       if (!dir)
+               return -1;
+
+       /* read process info from /proc */
+       for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+               int f;
+               char procdir[64];
+               char line[256];
+               const char *pos;
+               char state;
+               pid_t ppid, pgrp, sess;
+               int len;
+
+               if (!isdigit(dent->d_name[0]))
+                       continue;
+
+               snprintf(procdir, sizeof(procdir), "/proc/%s/stat", dent->d_name);
+               procdir[sizeof(procdir)-1] = '\0';
+
+               f = open(procdir, O_RDONLY);
+               if (f == -1)
+                       continue;
+
+               len = read(f, line, sizeof(line));
+               close(f);
+
+               if (len <= 0)
+                       continue;
+               else
+                       line[len] = '\0';
+
+               /* skip ugly program name */
+               pos = strrchr(line, ')') + 2;
+               if (pos == NULL)
+                       continue;
+
+               if (sscanf(pos, "%c %d %d %d ", &state, &ppid, &pgrp, &sess) != 4)
+                       continue;
+
+               /* count only processes in our session */
+               if (sess != session)
+                       continue;
+
+               /* count only running, no sleeping processes */
+               if (state != 'R')
+                       continue;
+
+               running++;
+               if (limit > 0 && running >= limit)
+                       break;
+       }
+       closedir(dir);
+
+       return running;
+}
+
 static int compare_devpath(const char *running, const char *waiting)
 {
        int i;
 
-       for (i = 0; i < DEVPATH_SIZE; i++) {
+       for (i = 0; i < PATH_SIZE; i++) {
                /* identical device event found */
                if (running[i] == '\0' && waiting[i] == '\0')
                        return 1;
@@ -195,7 +293,7 @@ static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg)
        if (msg->devpath == NULL)
                return NULL;
 
-       list_for_each_entry(loop_msg, &running_list, list) {
+       list_for_each_entry(loop_msg, &running_list, node) {
                if (loop_msg->devpath == NULL)
                        continue;
 
@@ -218,13 +316,30 @@ static void exec_queue_manager(void)
        struct hotplug_msg *loop_msg;
        struct hotplug_msg *tmp_msg;
        struct hotplug_msg *msg;
+       int running;
+
+       running = running_processes();
+       dbg("%d processes runnning on system", running);
+       if (running < 0)
+               running = THROTTLE_MAX_RUNNING_CHILDS;
+
+       list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, node) {
+               /* check running processes in our session and possibly throttle */
+               if (running >= THROTTLE_MAX_RUNNING_CHILDS) {
+                       running = running_processes_in_session(sid, THROTTLE_MAX_RUNNING_CHILDS+10);
+                       dbg("%d processes running in session", running);
+                       if (running >= THROTTLE_MAX_RUNNING_CHILDS) {
+                               dbg("delay seq %llu, cause too many processes already running", loop_msg->seqnum);
+                               return;
+                       }
+               }
 
-       list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, list) {
                msg = running_with_devpath(loop_msg);
                if (!msg) {
                        /* move event to run list */
-                       list_move_tail(&loop_msg->list, &running_list);
+                       list_move_tail(&loop_msg->node, &running_list);
                        udev_run(loop_msg);
+                       running++;
                        dbg("moved seq %llu to running list", loop_msg->seqnum);
                } else {
                        dbg("delay seq %llu (%s), cause seq %llu (%s) is still running",
@@ -235,7 +350,7 @@ static void exec_queue_manager(void)
 
 static void msg_move_exec(struct hotplug_msg *msg)
 {
-       list_move_tail(&msg->list, &exec_list);
+       list_move_tail(&msg->node, &exec_list);
        run_exec_q = 1;
        expected_seqnum = msg->seqnum+1;
        dbg("moved seq %llu to exec, next expected is %llu",
@@ -254,7 +369,7 @@ static void msg_queue_manager(void)
 
        dbg("msg queue manager, next expected is %llu", expected_seqnum);
 recheck:
-       list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, list) {
+       list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, node) {
                /* move event with expected sequence to the exec list */
                if (loop_msg->seqnum == expected_seqnum) {
                        msg_move_exec(loop_msg);
@@ -419,7 +534,7 @@ static void udev_done(int pid)
        /* find msg associated with pid and delete it */
        struct hotplug_msg *msg;
 
-       list_for_each_entry(msg, &running_list, list) {
+       list_for_each_entry(msg, &running_list, node) {
                if (msg->pid == pid) {
                        dbg("<== exec seq %llu came back", msg->seqnum);
                        run_queue_delete(msg);
@@ -501,6 +616,7 @@ int main(int argc, char *argv[], char *envp[])
        int fd;
        struct sigaction act;
        fd_set readfds;
+       const char *udevd_expected_seqnum;
 
        logging_init("udevd");
        dbg("version %s", UDEV_VERSION);
@@ -528,10 +644,17 @@ int main(int argc, char *argv[], char *envp[])
                }
        }
 
+       /* become session leader */
+       sid = setsid();
+       dbg("our session is %d", sid);
+
        /* make sure we don't lock any path */
        chdir("/");
        umask(umask(077) | 022);
 
+       /*set a reasonable scheduling priority for the daemon */
+       setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY);
+
        /* Set fds to dev/null */
        fd = open( "/dev/null", O_RDWR );
        if (fd >= 0)  {
@@ -543,9 +666,6 @@ int main(int argc, char *argv[], char *envp[])
        } else
                dbg("error opening /dev/null %s", strerror(errno));
 
-       /* become session leader */
-       setsid();
-
        /* setup signal handler pipe */
        retval = pipe(pipefds);
        if (retval < 0) {
@@ -572,6 +692,7 @@ int main(int argc, char *argv[], char *envp[])
                dbg("error fcntl on write pipe: %s", strerror(errno));
 
        /* set signal handlers */
+       memset(&act, 0x00, sizeof(struct sigaction));
        act.sa_handler = (void (*) (int))sig_handler;
        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_RESTART;
@@ -596,7 +717,14 @@ int main(int argc, char *argv[], char *envp[])
        else
                udev_bin = UDEV_BIN;
 
-       /* handle special startup timeout*/
+       /* possible init of expected_seqnum value */
+       udevd_expected_seqnum = getenv("UDEVD_EXPECTED_SEQNUM");
+       if (udevd_expected_seqnum != NULL) {
+               expected_seqnum = strtoull(udevd_expected_seqnum, NULL, 10);
+               dbg("initialize expected_seqnum to %llu", expected_seqnum);
+       }
+
+       /* get current time to provide shorter timeout on startup */
        sysinfo(&info);
        startup_time = info.uptime;