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