Hi all,

following one of our bug opened on systemd ( 
https://bugzilla.novell.com/show_bug.cgi?id=744818 ) and after discussing the 
issue on irc,
I found some time to do a initial implementation of systemd wide rlimit support.

Idea is simple :
- admin can set system wide limits for all services in 
/etc/systemd/system.conf, with a syntax similar to the one from systemd.exec(5) 
(LimitCPU ... LimitRTTIME).
- those limits, when set, override the default one (set by kernel) to all 
services started, except those which might have their own limits specified in 
their .unit file

Current limitations / Not done yet:
- currently, the limits are set on both rlim_cur and rlim_max (due to reuse of 
config_parse_limit code). I'd like to extend the implementation to support 
separate values for rlim_max and rlim_cur.
But I'm not 100% sure how we should express that in configuration file. Should 
we use a additional keyword (LimitCPU / LimitSoftCPU ) or a dual value 
(LimitCPU=100;10) ?
- for openSUSE, we will ship a generator which will transform 
/etc/sysconfig/ulimit into a /run/systemd/system.conf.

Comments and code review welcome.

-- 
Frederic Crozat <fcro...@suse.com>
SUSE

>From cc66b23fb2d240f674af9795897de95305c29b9b Mon Sep 17 00:00:00 2001
From: Frederic Crozat <fcro...@suse.com>
Date: Wed, 21 Mar 2012 18:03:40 +0100
Subject: [PATCH] allow system wide limits for services

---
 src/main.c    |   35 ++++++++++++++++++++++++++++++++++-
 src/manager.h |    2 ++
 src/service.c |   10 ++++++++++
 3 files changed, 46 insertions(+), 1 deletions(-)

diff --git a/src/main.c b/src/main.c
index ed317b4..c8701aa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -79,6 +79,7 @@ static char **arg_default_controllers = NULL;
 static char ***arg_join_controllers = NULL;
 static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
 static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
+static struct rlimit *default_rlimit[RLIMIT_NLIMITS] = {};
 
 static FILE* serialization = NULL;
 
@@ -659,12 +660,35 @@ static int parse_config_file(void) {
                 { "Manager", "DefaultStandardOutput", config_parse_output,       0, &arg_default_std_output  },
                 { "Manager", "DefaultStandardError",  config_parse_output,       0, &arg_default_std_error   },
                 { "Manager", "JoinControllers",       config_parse_join_controllers, 0, &arg_join_controllers },
+                { "Manager", "LimitCPU",              config_parse_limit,        0, &default_rlimit[RLIMIT_CPU]},
+                { "Manager", "LimitFSIZE",            config_parse_limit,        0, &default_rlimit[RLIMIT_FSIZE]},
+                { "Manager", "LimitDATA",             config_parse_limit,        0, &default_rlimit[RLIMIT_DATA]},
+                { "Manager", "LimitSTACK",            config_parse_limit,        0, &default_rlimit[RLIMIT_STACK]},
+                { "Manager", "LimitCORE",             config_parse_limit,        0, &default_rlimit[RLIMIT_CORE]},
+                { "Manager", "LimitRSS",              config_parse_limit,        0, &default_rlimit[RLIMIT_RSS]},
+                { "Manager", "LimitNOFILE",           config_parse_limit,        0, &default_rlimit[RLIMIT_NOFILE]},
+                { "Manager", "LimitAS",               config_parse_limit,        0, &default_rlimit[RLIMIT_AS]},
+                { "Manager", "LimitNPROC",            config_parse_limit,        0, &default_rlimit[RLIMIT_NPROC]},
+                { "Manager", "LimitMEMLOCK",          config_parse_limit,        0, &default_rlimit[RLIMIT_MEMLOCK]},
+                { "Manager", "LimitLOCKS",            config_parse_limit,        0, &default_rlimit[RLIMIT_LOCKS]},
+                { "Manager", "LimitSIGPENDING",       config_parse_limit,        0, &default_rlimit[RLIMIT_SIGPENDING]},
+                { "Manager", "LimitMSGQUEUE",         config_parse_limit,        0, &default_rlimit[RLIMIT_MSGQUEUE]},
+                { "Manager", "LimitNICE",             config_parse_limit,        0, &default_rlimit[RLIMIT_NICE]},
+                { "Manager", "LimitRTPRIO",           config_parse_limit,        0, &default_rlimit[RLIMIT_RTPRIO]},
+                { "Manager", "LimitRTTIME",           config_parse_limit,        0, &default_rlimit[RLIMIT_RTTIME]},
                 { NULL, NULL, NULL, 0, NULL }
         };
 
         FILE *f;
         const char *fn;
-        int r;
+        int r, i;
+
+        for (i = 0; i < RLIMIT_NLIMITS; i++) {
+                if (!default_rlimit[i])
+                        if (!(default_rlimit[i]= new(struct rlimit, 1)))
+                                break;
+                getrlimit(i,default_rlimit[i]);
+        }
 
         fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
         f = fopen(fn, "re");
@@ -1400,6 +1424,15 @@ int main(int argc, char *argv[]) {
         m->swap_auto = arg_swap_auto;
         m->default_std_output = arg_default_std_output;
         m->default_std_error = arg_default_std_error;
+        for (j = 0; j < RLIMIT_NLIMITS; j++) {
+                if (!m->rlimit[j])
+                        if (!(m->rlimit[j]= new(struct rlimit, 1)))
+                                break;
+
+                (m->rlimit[j])->rlim_cur = default_rlimit[j]->rlim_cur;
+                (m->rlimit[j])->rlim_max = default_rlimit[j]->rlim_max;
+        }
+
 
         if (dual_timestamp_is_set(&initrd_timestamp))
                 m->initrd_timestamp = initrd_timestamp;
diff --git a/src/manager.h b/src/manager.h
index a9d08f0..5dc7e29 100644
--- a/src/manager.h
+++ b/src/manager.h
@@ -225,6 +225,8 @@ struct Manager {
 
         ExecOutput default_std_output, default_std_error;
 
+        struct rlimit *rlimit[RLIMIT_NLIMITS];
+
         /* non-zero if we are reloading or reexecuting, */
         int n_reloading;
 
diff --git a/src/service.c b/src/service.c
index 8b5c0b0..c755230 100644
--- a/src/service.c
+++ b/src/service.c
@@ -24,6 +24,7 @@
 #include <dirent.h>
 #include <unistd.h>
 #include <sys/reboot.h>
+#include <string.h>
 
 #include "manager.h"
 #include "unit.h"
@@ -109,6 +110,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
 
 static void service_init(Unit *u) {
         Service *s = SERVICE(u);
+        int i;
 
         assert(u);
         assert(u->load_state == UNIT_STUB);
@@ -127,6 +129,14 @@ static void service_init(Unit *u) {
         s->guess_main_pid = true;
 
         exec_context_init(&s->exec_context);
+        for (i = 0; i < RLIMIT_NLIMITS; i++) {
+                if (!s->exec_context.rlimit[i])
+                        if (!(s->exec_context.rlimit[i]= new(struct rlimit, 1)))
+                                break;
+
+                (s->exec_context.rlimit[i])->rlim_cur = UNIT(s)->manager->rlimit[i]->rlim_cur;
+                (s->exec_context.rlimit[i])->rlim_max = UNIT(s)->manager->rlimit[i]->rlim_max;
+        }
 
         RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5);
 
-- 
1.7.7

_______________________________________________
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel

Reply via email to