chiark / gitweb /
[PATCH] clean up match_place()
[elogind.git] / udevd.c
diff --git a/udevd.c b/udevd.c
index 7ae7a5b232c7bd1264baa35e5ab091e6d927ac0b..7d7fd63332baf664b9ba111e3553115aeca993fc 100644 (file)
--- a/udevd.c
+++ b/udevd.c
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.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>
 
@@ -45,6 +47,7 @@
 
 /* global variables*/
 static int udevsendsock;
+static pid_t sid;
 
 static int pipefds[2];
 static long startup_time;
@@ -144,6 +147,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 +156,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,6 +164,100 @@ 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;
@@ -218,13 +314,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, list) {
+               /* 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;
+                       }
+               }
+
                msg = running_with_devpath(loop_msg);
                if (!msg) {
                        /* move event to run list */
                        list_move_tail(&loop_msg->list, &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",
@@ -450,6 +563,7 @@ static void reap_sigchilds(void)
 static void user_sighandler(void)
 {
        int sig;
+
        while(1) {
                int rc = read(pipefds[0], &sig, sizeof(sig));
                if (rc < 0)
@@ -500,6 +614,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);
@@ -527,24 +642,27 @@ 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 ) {
+       if (fd >= 0)  {
+               dup2(fd, 0);
+               dup2(fd, 1);
+               dup2(fd, 2);
+               if (fd > 2)
+                       close(fd);
+       } else
                dbg("error opening /dev/null %s", strerror(errno));
-               goto exit;
-       }
-       dup2(fd, 0);
-       dup2(fd, 1);
-       dup2(fd, 2);
-       if (fd > 2) 
-               close(fd);
-
-       /* become session leader */
-       setsid();
 
        /* setup signal handler pipe */
        retval = pipe(pipefds);
@@ -559,10 +677,8 @@ int main(int argc, char *argv[], char *envp[])
                goto exit;
        }
        retval = fcntl(pipefds[0], F_SETFD, FD_CLOEXEC);
-       if (retval < 0) {
+       if (retval < 0)
                dbg("error fcntl on read pipe: %s", strerror(errno));
-               goto exit;
-       }
 
        retval = fcntl(pipefds[1], F_SETFL, O_NONBLOCK);
        if (retval < 0) {
@@ -570,12 +686,11 @@ int main(int argc, char *argv[], char *envp[])
                goto exit;
        }
        retval = fcntl(pipefds[1], F_SETFD, FD_CLOEXEC);
-       if (retval < 0) {
+       if (retval < 0)
                dbg("error fcntl on write pipe: %s", strerror(errno));
-               goto exit;
-       }
 
        /* 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;
@@ -586,7 +701,7 @@ int main(int argc, char *argv[], char *envp[])
 
        if (init_udevsend_socket() < 0) {
                if (errno == EADDRINUSE)
-                       dbg("another udevd is running, exit");
+                       dbg("another udevd running, exit");
                else
                        dbg("error initialising udevsend socket: %s", strerror(errno));
 
@@ -600,7 +715,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;