]> git.itanic.dy.fi Git - rrdd/commitdiff
jobcontrol: Fork bomb prevental
authorTimo Kokkonen <timo.t.kokkonen@iki.fi>
Sun, 28 Oct 2012 16:12:08 +0000 (18:12 +0200)
committerTimo Kokkonen <timo.t.kokkonen@iki.fi>
Sun, 28 Oct 2012 16:19:56 +0000 (18:19 +0200)
If something happens that prevents existing jobs from terminating, it
is generally bad practice to keep on forking more processes
indefinitely. To alleviate the problem, implement a somewhat trivial
process limitation feature. This will define a limit of maximum number
for the processes pending execution. If the number exceeds the global
limit, new processes are to be terminate itself immediately.

This however prevents only "limited forks" for creating too many
processes for waiting execution. Normal forks still have no
limit. This should still make the system to stay alive longer during
situations where something prevents (temporarily) executing processes
to finish in time.

Signed-off-by: Timo Kokkonen <timo.t.kokkonen@iki.fi>
process.c

index 070b5f0920091ac393908e3fbd7e516f2f90f0fd..27e940050aa6804d10c2cb74bb903779b1ccefa4 100644 (file)
--- a/process.c
+++ b/process.c
@@ -19,6 +19,7 @@ static int epoll_fd;
 static unsigned int max_jobs;
 static unsigned int job_count;
 static unsigned int jobs_pending;
+static unsigned int max_jobs_pending;
 
 int get_child_count(void)
 {
@@ -70,6 +71,24 @@ static int grant_new_job(void)
        return 0;
 }
 
+static int deny_job(void)
+{
+       int ret;
+       char byte = -1;
+
+       pr_info("Denying new job. %d jobs currently and %d pending, "
+               "limit of pending jobs is %d\n",
+               job_count, jobs_pending, max_jobs_pending);
+
+       ret = write(job_get_permission_fd[1], &byte, 1);
+       if (ret != 1) {
+               pr_err("Failed to write 1 byte: %m\n");
+               return -1;
+       }
+
+       return 0;
+}
+
 static int handle_job_request(struct event_handler *h)
 {
        int ret, pid;
@@ -87,7 +106,10 @@ static int handle_job_request(struct event_handler *h)
 
        if (pid > 0) {
                if (job_count >= max_jobs) {
-                       jobs_pending++;
+                       if (jobs_pending < max_jobs_pending)
+                               jobs_pending++;
+                       else
+                               deny_job();
                } else {
                        ret = grant_new_job();
                        return 0;
@@ -208,6 +230,9 @@ read_fail:
 no_count_cpus:
        pr_info("Set maximum number of parallel jobs to %d\n", max_jobs);
 
+       max_jobs_pending = max_jobs * 50 + 25;
+       pr_info("Set maximum number of pending jobs to %d\n", max_jobs_pending);
+
        return 0;
 }
 
@@ -370,6 +395,17 @@ int do_fork_limited(void)
        if (ret < 0)
                pr_err("Job control request failure: %m\n");
 
+       if (byte < 0) {
+               pr_info("Did not get permission to execute. Terminating\n");
+
+               /*
+                * Avoid running exit handler, that would tell the
+                * parent we died normally and decrement the job
+                * counters.
+                */
+               raise(SIGKILL);
+       }
+
        pr_info("Continuing\n");
        return child;
 }