diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 1948b0a802..9a0e02187f 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -373,6 +373,22 @@ instead. + + + + Controls the hostname to set within the container, if different from the machine name. Expects + a valid hostname as argument. If this option is used, the kernel hostname of the container will be set to this + value, otherwise it will be initialized to the machine name as controlled by the + option described above. The machine name is used for various aspect of identification of the container from the + outside, the kernel hostname configurable with this option is useful for the container to identify itself from + the inside. It is usually a good idea to keep both forms of identification synchronized, in order to avoid + confusion. It is hence recommended to avoid usage of this option, and use + exclusively. Note that regardless whether the container's hostname is initialized from the name set with + or the one set with , the container can later override + its kernel hostname freely on its own as well. + + + @@ -717,13 +733,24 @@ above). + + + + Takes a boolean argument. Specifies the value of the PR_SET_NO_NEW_PRIVS + flag for the container payload. Defaults to off. When turned on the payload code of the container cannot + acquire new privileges, i.e. the "setuid" file bit as well as file system capabilities will not have an effect + anymore. See prctl2 for details + about this flag. + + Alter the system call filter applied to containers. Takes a space-separated list of system call names or group names (the latter prefixed with @, as listed by the - syscall-filter command of systemd-analyze1). Passed + syscall-filter command of + systemd-analyze1). Passed system calls will be permitted. The list may optionally be prefixed by ~, in which case all listed system calls are prohibited. If this command line option is used multiple times the configured lists are combined. If both a positive and a negative list (that is one system call list without and one with the @@ -734,16 +761,60 @@ capabilities are passed using the --capabilities=. + + + + Sets the specified POSIX resource limit for the container payload. Expects an assignment of the + form + LIMIT=SOFT:HARD + or LIMIT=VALUE, where + LIMIT should refer to a resource limit type, such as + RLIMIT_NOFILE or RLIMIT_NICE. The SOFT and + HARD fields should refer to the numeric soft and hard resource limit values. If the + second form is used, VALUE may specifiy a value that is used both as soft and hard + limit. In place of a numeric value the special string infinity may be used to turn off + resource limiting for the specific type of resource. This command line option may be used multiple times to + control limits on multiple limit types. If used multiple times for the same limit type, the last last use + wins. For details about resource limits see setrlimit2. By default + resource limits for the container's init process (PID 1) are set to the same values the Linux kernel originally + passed to the host init system. Note that some resource limits are enforced on resources counted per user, in + particular RLIMIT_NPROC. This means that unless user namespacing is deployed + (i.e. is used, see above), any limits set will be applied to the resource + usage of the same user on all local containers as well as the host. This means particular care needs to be + taken with these limits as they might be triggered by possibly less trusted code. Example: + --rlimit=RLIMIT_NOFILE=8192:16384. + + + + + + Changes the OOM ("Out Of Memory") score adjustment value for the container payload. This controls + /proc/self/oom_score_adj which influences the preference with which this container is + terminated when memory becomes scarce. For details see proc5. Takes an + integer in the range -1000…1000. + + + + + + Controls the CPU affinity of the container payload. Takes a comma separated list of CPU numbers + or number ranges (the latter's start and end value separated by dashes). See sched_setaffinity2 for + details. + + - Specify the process signal to send to the - container's PID 1 when nspawn itself receives SIGTERM, in - order to trigger an orderly shutdown of the - container. Defaults to SIGRTMIN+3 if - is used (on systemd-compatible init systems SIGRTMIN+3 - triggers an orderly shutdown). For a list of valid signals, see - signal7. + Specify the process signal to send to the container's PID 1 when nspawn itself receives + SIGTERM, in order to trigger an orderly shutdown of the container. Defaults to + SIGRTMIN+3 if is used (on systemd-compatible init systems + SIGRTMIN+3 triggers an orderly shutdown). If is not used and this + option is not specified the container's processes are terminated abrubtly via SIGKILL. For + a list of valid signals, see signal7. diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index b5c60a33e0..1780bfd79a 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -222,6 +222,17 @@ all cases. + + NoNewPrivileges= + + Takes a boolean argument that controls the PR_SET_NO_NEW_PRIVS flag for + the container payload. This is equivalent to the + command line switch. See + systemd-nspawn1 for + details. + + + KillSignal= @@ -278,6 +289,57 @@ details. + + LimitCPU= + LimitFSIZE= + LimitDATA= + LimitSTACK= + LimitCORE= + LimitRSS= + LimitNOFILE= + LimitAS= + LimitNPROC= + LimitMEMLOCK= + LimitLOCKS= + LimitSIGPENDING= + LimitMSGQUEUE= + LimitNICE= + LimitRTPRIO= + LimitRTTIME= + + Configures various types of resource limits applied to containers. This is equivalent to the + command line switch, and takes the same arguments. See + systemd-nspawn1 for + details. + + + + OOMScoreAdjust= + + Configures the OOM score adjustment value. This is equivalent to the + command line switch, and takes the same argument. See + systemd-nspawn1 for + details. + + + + CPUAffinity= + + Configures the CPU affinity. This is equivalent to the command + line switch, and takes the same argument. See + systemd-nspawn1 for + details. + + + + Hostname= + + Configures the kernel hostname set for the container. This is equivalent to the + command line switch, and takes the same argument. See + systemd-nspawn1 for + details. + + diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 4b3e7ed557..9536a50b74 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -199,15 +199,22 @@ int close_all_fds(const int except[], size_t n_except) { d = opendir("/proc/self/fd"); if (!d) { - int fd; struct rlimit rl; + int fd, max_fd; - /* When /proc isn't available (for example in chroots) - * the fallback is brute forcing through the fd + /* When /proc isn't available (for example in chroots) the fallback is brute forcing through the fd * table */ assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); - for (fd = 3; fd < (int) rl.rlim_max; fd ++) { + + if (rl.rlim_max == 0) + return -EINVAL; + + /* Let's take special care if the resource limit is set to unlimited, or actually larger than the range + * of 'int'. Let's avoid implicit overflows. */ + max_fd = (rl.rlim_max == RLIM_INFINITY || rl.rlim_max > INT_MAX) ? INT_MAX : (int) (rl.rlim_max - 1); + + for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { int q; if (fd_in_set(fd, except, n_except)) diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index fbb32f3dc5..07f43b94e5 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -676,3 +676,20 @@ int parse_dev(const char *s, dev_t *ret) { *ret = d; return 0; } + +int parse_oom_score_adjust(const char *s, int *ret) { + int r, v; + + assert(s); + assert(ret); + + r = safe_atoi(s, &v); + if (r < 0) + return r; + + if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX) + return -ERANGE; + + *ret = v; + return 0; +} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 742063c9a4..2b75b938c7 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -118,3 +118,5 @@ int parse_percent(const char *p); int parse_nice(const char *p, int *ret); int parse_ip_port(const char *s, uint16_t *ret); + +int parse_oom_score_adjust(const char *s, int *ret); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 2ecb143014..460b7929bc 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -685,11 +685,12 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar return log_error_errno(r, "Failed to parse path \"%s\" and make it absolute: %m", path); path_kill_slashes(p); - if (suppress_root && path_equal(p, "/")) + if (suppress_root && empty_or_root(p)) p = mfree(p); free(*arg); *arg = p; + return 0; } diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 4180769ee8..ce56ce0735 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1445,6 +1445,15 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret _exit(EXIT_FAILURE); } +int set_oom_score_adjust(int value) { + char t[DECIMAL_STR_MAX(int)]; + + sprintf(t, "%i", value); + + return write_string_file("/proc/self/oom_score_adj", t, + WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index f8d1b5e3e8..0cec7c4c5c 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -173,6 +173,8 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...); +int set_oom_score_adjust(int value); + #if SIZEOF_PID_T == 4 /* The highest possibly (theoretic) pid_t value on this architecture. */ #define PID_T_MAX ((pid_t) INT32_MAX) diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index d371f0a550..57f90474d8 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -33,6 +33,11 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) { if (getrlimit(resource, &highest) < 0) return -errno; + /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM + * then */ + if (highest.rlim_max == RLIM_INFINITY) + return -EPERM; + fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max); fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max); @@ -42,6 +47,32 @@ int setrlimit_closest(int resource, const struct rlimit *rlim) { return 0; } +int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) { + int i, r; + + assert(rlim); + + /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */ + + for (i = 0; i < _RLIMIT_MAX; i++) { + if (!rlim[i]) + continue; + + r = setrlimit_closest(i, rlim[i]); + if (r < 0) { + if (which_failed) + *which_failed = i; + + return r; + } + } + + if (which_failed) + *which_failed = -1; + + return 0; +} + static int rlimit_parse_u64(const char *val, rlim_t *ret) { uint64_t u; int r; @@ -289,22 +320,48 @@ int rlimit_format(const struct rlimit *rl, char **ret) { } static const char* const rlimit_table[_RLIMIT_MAX] = { - [RLIMIT_CPU] = "LimitCPU", - [RLIMIT_FSIZE] = "LimitFSIZE", - [RLIMIT_DATA] = "LimitDATA", - [RLIMIT_STACK] = "LimitSTACK", - [RLIMIT_CORE] = "LimitCORE", - [RLIMIT_RSS] = "LimitRSS", - [RLIMIT_NOFILE] = "LimitNOFILE", - [RLIMIT_AS] = "LimitAS", - [RLIMIT_NPROC] = "LimitNPROC", - [RLIMIT_MEMLOCK] = "LimitMEMLOCK", - [RLIMIT_LOCKS] = "LimitLOCKS", - [RLIMIT_SIGPENDING] = "LimitSIGPENDING", - [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE", - [RLIMIT_NICE] = "LimitNICE", - [RLIMIT_RTPRIO] = "LimitRTPRIO", - [RLIMIT_RTTIME] = "LimitRTTIME" + [RLIMIT_AS] = "AS", + [RLIMIT_CORE] = "CORE", + [RLIMIT_CPU] = "CPU", + [RLIMIT_DATA] = "DATA", + [RLIMIT_FSIZE] = "FSIZE", + [RLIMIT_LOCKS] = "LOCKS", + [RLIMIT_MEMLOCK] = "MEMLOCK", + [RLIMIT_MSGQUEUE] = "MSGQUEUE", + [RLIMIT_NICE] = "NICE", + [RLIMIT_NOFILE] = "NOFILE", + [RLIMIT_NPROC] = "NPROC", + [RLIMIT_RSS] = "RSS", + [RLIMIT_RTPRIO] = "RTPRIO", + [RLIMIT_RTTIME] = "RTTIME", + [RLIMIT_SIGPENDING] = "SIGPENDING", + [RLIMIT_STACK] = "STACK", }; DEFINE_STRING_TABLE_LOOKUP(rlimit, int); + +int rlimit_from_string_harder(const char *s) { + const char *suffix; + + /* The official prefix */ + suffix = startswith(s, "RLIMIT_"); + if (suffix) + return rlimit_from_string(suffix); + + /* Our own unit file setting prefix */ + suffix = startswith(s, "Limit"); + if (suffix) + return rlimit_from_string(suffix); + + return rlimit_from_string(s); +} + +void rlimit_free_all(struct rlimit **rl) { + int i; + + if (!rl) + return; + + for (i = 0; i < _RLIMIT_MAX; i++) + rl[i] = mfree(rl[i]); +} diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h index 3766f78d58..d50fd38c2e 100644 --- a/src/basic/rlimit-util.h +++ b/src/basic/rlimit-util.h @@ -13,12 +13,16 @@ const char *rlimit_to_string(int i) _const_; int rlimit_from_string(const char *s) _pure_; +int rlimit_from_string_harder(const char *s) _pure_; int setrlimit_closest(int resource, const struct rlimit *rlim); +int setrlimit_closest_all(const struct rlimit * const *rlim, int *which_failed); int rlimit_parse_one(int resource, const char *val, rlim_t *ret); int rlimit_parse(int resource, const char *val, struct rlimit *ret); int rlimit_format(const struct rlimit *rl, char **ret); +void rlimit_free_all(struct rlimit **rl); + #define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 3b1c77e967..143fca71b0 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1091,8 +1091,8 @@ int bus_exec_context_set_transient_property( UnitWriteFlags flags, sd_bus_error *error) { - const char *soft = NULL; - int r, ri; + const char *suffix; + int r; assert(u); assert(c); @@ -2313,73 +2313,77 @@ int bus_exec_context_set_transient_property( } return 1; - } - ri = rlimit_from_string(name); - if (ri < 0) { - soft = endswith(name, "Soft"); - if (soft) { - const char *n; + } else if ((suffix = startswith(name, "Limit"))) { + const char *soft = NULL; + int ri; - n = strndupa(name, soft - name); - ri = rlimit_from_string(n); - if (ri >= 0) - name = n; + ri = rlimit_from_string(suffix); + if (ri < 0) { + soft = endswith(suffix, "Soft"); + if (soft) { + const char *n; - } - } - - if (ri >= 0) { - uint64_t rl; - rlim_t x; - - r = sd_bus_message_read(message, "t", &rl); - if (r < 0) - return r; - - if (rl == (uint64_t) -1) - x = RLIM_INFINITY; - else { - x = (rlim_t) rl; - - if ((uint64_t) x != rl) - return -ERANGE; + n = strndupa(suffix, soft - suffix); + ri = rlimit_from_string(n); + if (ri >= 0) + name = strjoina("Limit", n); + } } - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *f = NULL; - struct rlimit nl; + if (ri >= 0) { + uint64_t rl; + rlim_t x; - if (c->rlimit[ri]) { - nl = *c->rlimit[ri]; - - if (soft) - nl.rlim_cur = x; - else - nl.rlim_max = x; - } else - /* When the resource limit is not initialized yet, then assign the value to both fields */ - nl = (struct rlimit) { - .rlim_cur = x, - .rlim_max = x, - }; - - r = rlimit_format(&nl, &f); + r = sd_bus_message_read(message, "t", &rl); if (r < 0) return r; - if (c->rlimit[ri]) - *c->rlimit[ri] = nl; + if (rl == (uint64_t) -1) + x = RLIM_INFINITY; else { - c->rlimit[ri] = newdup(struct rlimit, &nl, 1); - if (!c->rlimit[ri]) - return -ENOMEM; + x = (rlim_t) rl; + + if ((uint64_t) x != rl) + return -ERANGE; } - unit_write_settingf(u, flags, name, "%s=%s", name, f); + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + _cleanup_free_ char *f = NULL; + struct rlimit nl; + + if (c->rlimit[ri]) { + nl = *c->rlimit[ri]; + + if (soft) + nl.rlim_cur = x; + else + nl.rlim_max = x; + } else + /* When the resource limit is not initialized yet, then assign the value to both fields */ + nl = (struct rlimit) { + .rlim_cur = x, + .rlim_max = x, + }; + + r = rlimit_format(&nl, &f); + if (r < 0) + return r; + + if (c->rlimit[ri]) + *c->rlimit[ri] = nl; + else { + c->rlimit[ri] = newdup(struct rlimit, &nl, 1); + if (!c->rlimit[ri]) + return -ENOMEM; + } + + unit_write_settingf(u, flags, name, "%s=%s", name, f); + } + + return 1; } - return 1; } return 0; diff --git a/src/core/execute.c b/src/core/execute.c index fca20c5567..9634ef98b1 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2727,7 +2727,7 @@ static int exec_child( #endif uid_t uid = UID_INVALID; gid_t gid = GID_INVALID; - int i, r, ngids = 0; + int r, ngids = 0; size_t n_fds; ExecDirectoryType dt; int secure_bits; @@ -2916,15 +2916,9 @@ static int exec_child( } if (context->oom_score_adjust_set) { - char t[DECIMAL_STR_MAX(context->oom_score_adjust)]; - - /* When we can't make this change due to EPERM, then - * let's silently skip over it. User namespaces - * prohibit write access to this file, and we - * shouldn't trip up over that. */ - - sprintf(t, "%i", context->oom_score_adjust); - r = write_string_file("/proc/self/oom_score_adj", t, 0); + /* When we can't make this change due to EPERM, then let's silently skip over it. User namespaces + * prohibit write access to this file, and we shouldn't trip up over that. */ + r = set_oom_score_adjust(context->oom_score_adjust); if (IN_SET(r, -EPERM, -EACCES)) log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m"); else if (r < 0) { @@ -3167,17 +3161,12 @@ static int exec_child( if (needs_sandboxing) { uint64_t bset; + int which_failed; - for (i = 0; i < _RLIMIT_MAX; i++) { - - if (!context->rlimit[i]) - continue; - - r = setrlimit_closest(i, context->rlimit[i]); - if (r < 0) { - *exit_status = EXIT_LIMITS; - return log_unit_error_errno(unit, r, "Failed to adjust resource limit %s: %m", rlimit_to_string(i)); - } + r = setrlimit_closest_all((const struct rlimit* const *) context->rlimit, &which_failed); + if (r < 0) { + *exit_status = EXIT_LIMITS; + return log_unit_error_errno(unit, r, "Failed to adjust resource limit RLIMIT_%s: %m", rlimit_to_string(which_failed)); } /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly requested. */ @@ -3574,8 +3563,7 @@ void exec_context_done(ExecContext *c) { c->pass_environment = strv_free(c->pass_environment); c->unset_environment = strv_free(c->unset_environment); - for (l = 0; l < ELEMENTSOF(c->rlimit); l++) - c->rlimit[l] = mfree(c->rlimit[l]); + rlimit_free_all(c->rlimit); for (l = 0; l < 3; l++) { c->stdio_fdname[l] = mfree(c->stdio_fdname[l]); @@ -3975,9 +3963,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { for (i = 0; i < RLIM_NLIMITS; i++) if (c->rlimit[i]) { - fprintf(f, "%s%s: " RLIM_FMT "\n", + fprintf(f, "Limit%s%s: " RLIM_FMT "\n", prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max); - fprintf(f, "%s%sSoft: " RLIM_FMT "\n", + fprintf(f, "Limit%s%sSoft: " RLIM_FMT "\n", prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur); } diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 001837463b..3757a651b2 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -82,22 +82,22 @@ $1.RestrictNamespaces, config_parse_warn_compat, DISABLED_CO $1.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.LockPersonality, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') -$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) -$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) -$1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) -$1.LimitSTACK, config_parse_limit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) -$1.LimitCORE, config_parse_limit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) -$1.LimitRSS, config_parse_limit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) -$1.LimitNOFILE, config_parse_limit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) -$1.LimitAS, config_parse_limit, RLIMIT_AS, offsetof($1, exec_context.rlimit) -$1.LimitNPROC, config_parse_limit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) -$1.LimitMEMLOCK, config_parse_limit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) -$1.LimitLOCKS, config_parse_limit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) -$1.LimitSIGPENDING, config_parse_limit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) -$1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) -$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) -$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) -$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) +$1.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) +$1.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) +$1.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) +$1.LimitSTACK, config_parse_rlimit, RLIMIT_STACK, offsetof($1, exec_context.rlimit) +$1.LimitCORE, config_parse_rlimit, RLIMIT_CORE, offsetof($1, exec_context.rlimit) +$1.LimitRSS, config_parse_rlimit, RLIMIT_RSS, offsetof($1, exec_context.rlimit) +$1.LimitNOFILE, config_parse_rlimit, RLIMIT_NOFILE, offsetof($1, exec_context.rlimit) +$1.LimitAS, config_parse_rlimit, RLIMIT_AS, offsetof($1, exec_context.rlimit) +$1.LimitNPROC, config_parse_rlimit, RLIMIT_NPROC, offsetof($1, exec_context.rlimit) +$1.LimitMEMLOCK, config_parse_rlimit, RLIMIT_MEMLOCK, offsetof($1, exec_context.rlimit) +$1.LimitLOCKS, config_parse_rlimit, RLIMIT_LOCKS, offsetof($1, exec_context.rlimit) +$1.LimitSIGPENDING, config_parse_rlimit, RLIMIT_SIGPENDING, offsetof($1, exec_context.rlimit) +$1.LimitMSGQUEUE, config_parse_rlimit, RLIMIT_MSGQUEUE, offsetof($1, exec_context.rlimit) +$1.LimitNICE, config_parse_rlimit, RLIMIT_NICE, offsetof($1, exec_context.rlimit) +$1.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit) +$1.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit) $1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths) $1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths) $1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 72c6e10008..71d5b335fc 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -45,7 +45,6 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" -#include "rlimit-util.h" #if HAVE_SECCOMP #include "seccomp-util.h" #endif @@ -507,16 +506,17 @@ int config_parse_exec_nice( return 0; } -int config_parse_exec_oom_score_adjust(const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_oom_score_adjust( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; int oa, r; @@ -526,14 +526,18 @@ int config_parse_exec_oom_score_adjust(const char* unit, assert(rvalue); assert(data); - r = safe_atoi(rvalue, &oa); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); + if (isempty(rvalue)) { + c->oom_score_adjust_set = false; return 0; } - if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue); + r = parse_oom_score_adjust(rvalue, &oa); + if (r == -ERANGE) { + log_syntax(unit, LOG_ERR, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue); + return 0; + } + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); return 0; } @@ -1370,47 +1374,6 @@ int config_parse_capability_set( return 0; } -int config_parse_limit( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - struct rlimit **rl = data, d = {}; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = rlimit_parse(ltype, rvalue, &d); - if (r == -EILSEQ) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue); - return 0; - } - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); - return 0; - } - - if (rl[ltype]) - *rl[ltype] = d; - else { - rl[ltype] = newdup(struct rlimit, &d, 1); - if (!rl[ltype]) - return log_oom(); - } - - return 0; -} - #if HAVE_SYSV_COMPAT int config_parse_sysv_priority(const char *unit, const char *filename, @@ -4926,7 +4889,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_log_level, "LEVEL" }, { config_parse_exec_secure_bits, "SECUREBITS" }, { config_parse_capability_set, "BOUNDINGSET" }, - { config_parse_limit, "LIMIT" }, + { config_parse_rlimit, "LIMIT" }, { config_parse_unit_deps, "UNIT [...]" }, { config_parse_exec, "PATH [ARGUMENT [...]]" }, { config_parse_service_type, "SERVICETYPE" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 6d9b13ce37..4af2898b44 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -43,7 +43,6 @@ int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, uns int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_capability_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_sysv_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_kill_signal(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_mount_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/core/main.c b/src/core/main.c index 978434038e..30dd4a23e8 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -675,22 +675,22 @@ static int parse_config_file(void) { { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval }, { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, - { "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit }, - { "Manager", "DefaultLimitFSIZE", config_parse_limit, RLIMIT_FSIZE, arg_default_rlimit }, - { "Manager", "DefaultLimitDATA", config_parse_limit, RLIMIT_DATA, arg_default_rlimit }, - { "Manager", "DefaultLimitSTACK", config_parse_limit, RLIMIT_STACK, arg_default_rlimit }, - { "Manager", "DefaultLimitCORE", config_parse_limit, RLIMIT_CORE, arg_default_rlimit }, - { "Manager", "DefaultLimitRSS", config_parse_limit, RLIMIT_RSS, arg_default_rlimit }, - { "Manager", "DefaultLimitNOFILE", config_parse_limit, RLIMIT_NOFILE, arg_default_rlimit }, - { "Manager", "DefaultLimitAS", config_parse_limit, RLIMIT_AS, arg_default_rlimit }, - { "Manager", "DefaultLimitNPROC", config_parse_limit, RLIMIT_NPROC, arg_default_rlimit }, - { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, RLIMIT_MEMLOCK, arg_default_rlimit }, - { "Manager", "DefaultLimitLOCKS", config_parse_limit, RLIMIT_LOCKS, arg_default_rlimit }, - { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, RLIMIT_SIGPENDING, arg_default_rlimit }, - { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, RLIMIT_MSGQUEUE, arg_default_rlimit }, - { "Manager", "DefaultLimitNICE", config_parse_limit, RLIMIT_NICE, arg_default_rlimit }, - { "Manager", "DefaultLimitRTPRIO", config_parse_limit, RLIMIT_RTPRIO, arg_default_rlimit }, - { "Manager", "DefaultLimitRTTIME", config_parse_limit, RLIMIT_RTTIME, arg_default_rlimit }, + { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_default_rlimit }, + { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_default_rlimit }, + { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_default_rlimit }, + { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_default_rlimit }, + { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_default_rlimit }, + { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_default_rlimit }, + { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_default_rlimit }, + { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_default_rlimit }, + { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_default_rlimit }, + { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_default_rlimit }, + { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_default_rlimit }, + { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_default_rlimit }, + { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_default_rlimit }, + { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_default_rlimit }, + { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_default_rlimit }, + { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_default_rlimit }, { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting }, { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_default_ip_accounting }, diff --git a/src/core/manager.c b/src/core/manager.c index a4cea85136..4a2f68b567 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -64,6 +64,7 @@ #include "path-util.h" #include "process-util.h" #include "ratelimit.h" +#include "rlimit-util.h" #include "rm-rf.h" #include "signal-util.h" #include "socket-util.h" @@ -1174,7 +1175,6 @@ static void manager_clear_jobs_and_units(Manager *m) { Manager* manager_free(Manager *m) { UnitType c; - int i; ExecDirectoryType dt; if (!m) @@ -1242,8 +1242,7 @@ Manager* manager_free(Manager *m) { free(m->switch_root); free(m->switch_root_init); - for (i = 0; i < _RLIMIT_MAX; i++) - m->rlimit[i] = mfree(m->rlimit[i]); + rlimit_free_all(m->rlimit); assert(hashmap_isempty(m->units_requiring_mounts_for)); hashmap_free(m->units_requiring_mounts_for); diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index ea66971fac..f8234e75d4 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -18,35 +18,55 @@ struct ConfigPerfItem; %struct-type %includes %% -Exec.Boot, config_parse_boot, 0, 0 -Exec.ProcessTwo, config_parse_pid2, 0, 0 -Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) -Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) -Exec.User, config_parse_string, 0, offsetof(Settings, user) -Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) -Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) -Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) -Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) -Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) -Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) -Exec.PivotRoot, config_parse_pivot_root, 0, 0 -Exec.PrivateUsers, config_parse_private_users, 0, 0 -Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready) -Exec.SystemCallFilter, config_parse_syscall_filter,0, 0, -Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) -Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) -Files.Bind, config_parse_bind, 0, 0 -Files.BindReadOnly, config_parse_bind, 1, 0 -Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 -Files.Overlay, config_parse_overlay, 0, 0 -Files.OverlayReadOnly, config_parse_overlay, 1, 0 -Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) -Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) -Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) -Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) -Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) -Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) -Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 -Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge) -Network.Zone, config_parse_network_zone, 0, 0 -Network.Port, config_parse_expose_port, 0, 0 +Exec.Boot, config_parse_boot, 0, 0 +Exec.ProcessTwo, config_parse_pid2, 0, 0 +Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) +Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) +Exec.User, config_parse_string, 0, offsetof(Settings, user) +Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) +Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) +Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) +Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) +Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) +Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) +Exec.PivotRoot, config_parse_pivot_root, 0, 0 +Exec.PrivateUsers, config_parse_private_users, 0, 0 +Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready) +Exec.SystemCallFilter, config_parse_syscall_filter, 0, 0, +Exec.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof(Settings, rlimit) +Exec.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof(Settings, rlimit) +Exec.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof(Settings, rlimit) +Exec.LimitSTACK, config_parse_rlimit, RLIMIT_STACK, offsetof(Settings, rlimit) +Exec.LimitCORE, config_parse_rlimit, RLIMIT_CORE, offsetof(Settings, rlimit) +Exec.LimitRSS, config_parse_rlimit, RLIMIT_RSS, offsetof(Settings, rlimit) +Exec.LimitNOFILE, config_parse_rlimit, RLIMIT_NOFILE, offsetof(Settings, rlimit) +Exec.LimitAS, config_parse_rlimit, RLIMIT_AS, offsetof(Settings, rlimit) +Exec.LimitNPROC, config_parse_rlimit, RLIMIT_NPROC, offsetof(Settings, rlimit) +Exec.LimitMEMLOCK, config_parse_rlimit, RLIMIT_MEMLOCK, offsetof(Settings, rlimit) +Exec.LimitLOCKS, config_parse_rlimit, RLIMIT_LOCKS, offsetof(Settings, rlimit) +Exec.LimitSIGPENDING, config_parse_rlimit, RLIMIT_SIGPENDING, offsetof(Settings, rlimit) +Exec.LimitMSGQUEUE, config_parse_rlimit, RLIMIT_MSGQUEUE, offsetof(Settings, rlimit) +Exec.LimitNICE, config_parse_rlimit, RLIMIT_NICE, offsetof(Settings, rlimit) +Exec.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof(Settings, rlimit) +Exec.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof(Settings, rlimit) +Exec.Hostname, config_parse_hostname, 0, offsetof(Settings, hostname) +Exec.NoNewPrivileges, config_parse_tristate, 0, offsetof(Settings, no_new_privileges) +Exec.OOMScoreAdjust, config_parse_oom_score_adjust, 0, 0 +Exec.CPUAffinity, config_parse_cpu_affinity, 0, 0 +Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) +Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) +Files.Bind, config_parse_bind, 0, 0 +Files.BindReadOnly, config_parse_bind, 1, 0 +Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Files.Overlay, config_parse_overlay, 0, 0 +Files.OverlayReadOnly, config_parse_overlay, 1, 0 +Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) +Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) +Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) +Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) +Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) +Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) +Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 +Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge) +Network.Zone, config_parse_network_zone, 0, 0 +Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 487514d40a..0acf718456 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -8,10 +8,13 @@ #include "alloc-util.h" #include "cap-list.h" #include "conf-parser.h" +#include "cpu-set-util.h" +#include "hostname-util.h" #include "nspawn-network.h" #include "nspawn-settings.h" #include "parse-util.h" #include "process-util.h" +#include "rlimit-util.h" #include "socket-util.h" #include "string-util.h" #include "strv.h" @@ -34,6 +37,7 @@ int settings_load(FILE *f, const char *path, Settings **ret) { s->userns_mode = _USER_NAMESPACE_MODE_INVALID; s->uid_shift = UID_INVALID; s->uid_range = UID_INVALID; + s->no_new_privileges = -1; s->read_only = -1; s->volatile_mode = _VOLATILE_MODE_INVALID; @@ -80,6 +84,9 @@ Settings* settings_free(Settings *s) { free(s->working_directory); strv_free(s->syscall_whitelist); strv_free(s->syscall_blacklist); + rlimit_free_all(s->rlimit); + free(s->hostname); + s->cpuset = cpu_set_mfree(s->cpuset); strv_free(s->network_interfaces); strv_free(s->network_macvlan); @@ -601,3 +608,119 @@ int config_parse_syscall_filter( return 0; } + +int config_parse_hostname( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **s = data; + + assert(rvalue); + assert(s); + + if (!hostname_is_valid(rvalue, false)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid hostname, ignoring: %s", rvalue); + return 0; + } + + if (free_and_strdup(s, empty_to_null(rvalue)) < 0) + return log_oom(); + + return 0; +} + +int config_parse_oom_score_adjust( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + int oa, r; + + assert(rvalue); + assert(settings); + + if (isempty(rvalue)) { + settings->oom_score_adjust_set = false; + return 0; + } + + r = parse_oom_score_adjust(rvalue, &oa); + if (r == -ERANGE) { + log_syntax(unit, LOG_ERR, filename, line, r, "OOM score adjust value out of range, ignoring: %s", rvalue); + return 0; + } + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue); + return 0; + } + + settings->oom_score_adjust = oa; + settings->oom_score_adjust_set = true; + + return 0; +} + +int config_parse_cpu_affinity( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; + Settings *settings = data; + int ncpus; + + assert(rvalue); + assert(settings); + + ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue); + if (ncpus < 0) + return ncpus; + + if (ncpus == 0) { + /* An empty assignment resets the CPU list */ + settings->cpuset = cpu_set_mfree(settings->cpuset); + settings->cpuset_ncpus = 0; + return 0; + } + + if (!settings->cpuset) { + settings->cpuset = TAKE_PTR(cpuset); + settings->cpuset_ncpus = (unsigned) ncpus; + return 0; + } + + if (settings->cpuset_ncpus < (unsigned) ncpus) { + CPU_OR_S(CPU_ALLOC_SIZE(settings->cpuset_ncpus), cpuset, settings->cpuset, cpuset); + CPU_FREE(settings->cpuset); + settings->cpuset = TAKE_PTR(cpuset); + settings->cpuset_ncpus = (unsigned) ncpus; + return 0; + } + + CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), settings->cpuset, settings->cpuset, cpuset); + + return 0; +} diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 731db87260..c786bf8c86 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -7,6 +7,7 @@ Copyright 2015 Lennart Poettering ***/ +#include #include #include "sd-id128.h" @@ -32,24 +33,30 @@ typedef enum UserNamespaceMode { } UserNamespaceMode; typedef enum SettingsMask { - SETTING_START_MODE = 1 << 0, - SETTING_ENVIRONMENT = 1 << 1, - SETTING_USER = 1 << 2, - SETTING_CAPABILITY = 1 << 3, - SETTING_KILL_SIGNAL = 1 << 4, - SETTING_PERSONALITY = 1 << 5, - SETTING_MACHINE_ID = 1 << 6, - SETTING_NETWORK = 1 << 7, - SETTING_EXPOSE_PORTS = 1 << 8, - SETTING_READ_ONLY = 1 << 9, - SETTING_VOLATILE_MODE = 1 << 10, - SETTING_CUSTOM_MOUNTS = 1 << 11, - SETTING_WORKING_DIRECTORY = 1 << 12, - SETTING_USERNS = 1 << 13, - SETTING_NOTIFY_READY = 1 << 14, - SETTING_PIVOT_ROOT = 1 << 15, - SETTING_SYSCALL_FILTER = 1 << 16, - _SETTINGS_MASK_ALL = (1 << 17) -1 + SETTING_START_MODE = UINT64_C(1) << 0, + SETTING_ENVIRONMENT = UINT64_C(1) << 1, + SETTING_USER = UINT64_C(1) << 2, + SETTING_CAPABILITY = UINT64_C(1) << 3, + SETTING_KILL_SIGNAL = UINT64_C(1) << 4, + SETTING_PERSONALITY = UINT64_C(1) << 5, + SETTING_MACHINE_ID = UINT64_C(1) << 6, + SETTING_NETWORK = UINT64_C(1) << 7, + SETTING_EXPOSE_PORTS = UINT64_C(1) << 8, + SETTING_READ_ONLY = UINT64_C(1) << 9, + SETTING_VOLATILE_MODE = UINT64_C(1) << 10, + SETTING_CUSTOM_MOUNTS = UINT64_C(1) << 11, + SETTING_WORKING_DIRECTORY = UINT64_C(1) << 12, + SETTING_USERNS = UINT64_C(1) << 13, + SETTING_NOTIFY_READY = UINT64_C(1) << 14, + SETTING_PIVOT_ROOT = UINT64_C(1) << 15, + SETTING_SYSCALL_FILTER = UINT64_C(1) << 16, + SETTING_HOSTNAME = UINT64_C(1) << 17, + SETTING_NO_NEW_PRIVILEGES = UINT64_C(1) << 18, + SETTING_OOM_SCORE_ADJUST = UINT64_C(1) << 19, + SETTING_CPU_AFFINITY = UINT64_C(1) << 20, + SETTING_RLIMIT_FIRST = UINT64_C(1) << 21, /* we define one bit per resource limit here */ + SETTING_RLIMIT_LAST = UINT64_C(1) << (21 + _RLIMIT_MAX - 1), + _SETTINGS_MASK_ALL = (UINT64_C(1) << (21 + _RLIMIT_MAX)) - 1 } SettingsMask; typedef struct Settings { @@ -71,6 +78,13 @@ typedef struct Settings { bool notify_ready; char **syscall_whitelist; char **syscall_blacklist; + struct rlimit *rlimit[_RLIMIT_MAX]; + char *hostname; + int no_new_privileges; + int oom_score_adjust; + bool oom_score_adjust_set; + cpu_set_t *cpuset; + unsigned cpuset_ncpus; /* [Image] */ int read_only; @@ -115,3 +129,6 @@ int config_parse_boot(const char *unit, const char *filename, unsigned line, con int config_parse_pid2(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_private_users(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_syscall_filter(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_hostname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 12eaa6c0d7..b5d9de8da0 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -43,6 +43,7 @@ #include "capability-util.h" #include "cgroup-util.h" #include "copy.h" +#include "cpu-set-util.h" #include "dev-setup.h" #include "dissect-image.h" #include "env-util.h" @@ -75,12 +76,14 @@ #include "nspawn-settings.h" #include "nspawn-setuid.h" #include "nspawn-stub-pid1.h" +#include "pager.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "ptyfwd.h" #include "random-util.h" #include "raw-clone.h" +#include "rlimit-util.h" #include "rm-rf.h" #include "selinux-util.h" #include "signal-util.h" @@ -127,7 +130,8 @@ static char *arg_pivot_root_new = NULL; static char *arg_pivot_root_old = NULL; static char *arg_user = NULL; static sd_id128_t arg_uuid = {}; -static char *arg_machine = NULL; +static char *arg_machine = NULL; /* The name used by the host to refer to this */ +static char *arg_hostname = NULL; /* The name the payload sees by default */ static const char *arg_selinux_context = NULL; static const char *arg_selinux_apifs_context = NULL; static const char *arg_slice = NULL; @@ -200,8 +204,17 @@ static void *arg_root_hash = NULL; static size_t arg_root_hash_size = 0; static char **arg_syscall_whitelist = NULL; static char **arg_syscall_blacklist = NULL; +static struct rlimit *arg_rlimit[_RLIMIT_MAX] = {}; +static bool arg_no_new_privileges = false; +static int arg_oom_score_adjust = 0; +static bool arg_oom_score_adjust_set = false; +static cpu_set_t *arg_cpuset = NULL; +static unsigned arg_cpuset_ncpus = 0; static void help(void) { + + (void) pager_open(false, false); + printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" "Spawn a minimal namespace container for debugging, testing and building.\n\n" " -h --help Show this help\n" @@ -221,6 +234,7 @@ static void help(void) { " Pivot root to given directory in the container\n" " -u --user=USER Run the command under specified user or uid\n" " -M --machine=NAME Set the machine name for the container\n" + " --hostname=NAME Override the hostname for the container\n" " --uuid=UUID Set a specific machine UUID for the container\n" " -S --slice=SLICE Place the container in the specified slice\n" " --property=NAME=VALUE Set scope unit property\n" @@ -264,6 +278,10 @@ static void help(void) { " --drop-capability=CAP Drop the specified capability from the default set\n" " --system-call-filter=LIST|~LIST\n" " Permit/prohibit specific system calls\n" + " --rlimit=NAME=LIMIT Set a resource limit for the payload\n" + " --oom-score-adjust=VALUE\n" + " Adjust the OOM score value for the payload\n" + " --cpu-affinity=CPUS Adjust the CPU affinity of the container\n" " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" " --link-journal=MODE Link up guest journal, one of no, auto, guest, \n" " host, try-guest, try-host\n" @@ -439,6 +457,11 @@ static int parse_argv(int argc, char *argv[]) { ARG_NOTIFY_READY, ARG_ROOT_HASH, ARG_SYSTEM_CALL_FILTER, + ARG_RLIMIT, + ARG_HOSTNAME, + ARG_NO_NEW_PRIVILEGES, + ARG_OOM_SCORE_ADJUST, + ARG_CPU_AFFINITY, }; static const struct option options[] = { @@ -455,6 +478,7 @@ static int parse_argv(int argc, char *argv[]) { { "read-only", no_argument, NULL, ARG_READ_ONLY }, { "capability", required_argument, NULL, ARG_CAPABILITY }, { "drop-capability", required_argument, NULL, ARG_DROP_CAPABILITY }, + { "no-new-privileges", required_argument, NULL, ARG_NO_NEW_PRIVILEGES }, { "link-journal", required_argument, NULL, ARG_LINK_JOURNAL }, { "bind", required_argument, NULL, ARG_BIND }, { "bind-ro", required_argument, NULL, ARG_BIND_RO }, @@ -462,6 +486,7 @@ static int parse_argv(int argc, char *argv[]) { { "overlay", required_argument, NULL, ARG_OVERLAY }, { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, { "machine", required_argument, NULL, 'M' }, + { "hostname", required_argument, NULL, ARG_HOSTNAME }, { "slice", required_argument, NULL, 'S' }, { "setenv", required_argument, NULL, 'E' }, { "selinux-context", required_argument, NULL, 'Z' }, @@ -492,6 +517,9 @@ static int parse_argv(int argc, char *argv[]) { { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, { "root-hash", required_argument, NULL, ARG_ROOT_HASH }, { "system-call-filter", required_argument, NULL, ARG_SYSTEM_CALL_FILTER }, + { "rlimit", required_argument, NULL, ARG_RLIMIT }, + { "oom-score-adjust", required_argument, NULL, ARG_OOM_SCORE_ADJUST }, + { "cpu-affinity", required_argument, NULL, ARG_CPU_AFFINITY }, {} }; @@ -696,6 +724,23 @@ static int parse_argv(int argc, char *argv[]) { } break; + case ARG_HOSTNAME: + if (isempty(optarg)) + arg_hostname = mfree(arg_hostname); + else { + if (!hostname_is_valid(optarg, false)) { + log_error("Invalid hostname: %s", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_hostname, optarg); + if (r < 0) + return log_oom(); + } + + arg_settings_mask |= SETTING_HOSTNAME; + break; + case 'Z': arg_selinux_context = optarg; break; @@ -747,6 +792,15 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_NO_NEW_PRIVILEGES: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --no-new-privileges= argument: %s", optarg); + + arg_no_new_privileges = r; + arg_settings_mask |= SETTING_NO_NEW_PRIVILEGES; + break; + case 'j': arg_link_journal = LINK_GUEST; arg_link_journal_try = true; @@ -1094,6 +1148,66 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_RLIMIT: { + const char *eq; + char *name; + int rl; + + eq = strchr(optarg, '='); + if (!eq) { + log_error("--rlimit= expects an '=' assignment."); + return -EINVAL; + } + + name = strndup(optarg, eq - optarg); + if (!name) + return log_oom(); + + rl = rlimit_from_string_harder(name); + if (rl < 0) { + log_error("Unknown resource limit: %s", name); + return -EINVAL; + } + + if (!arg_rlimit[rl]) { + arg_rlimit[rl] = new0(struct rlimit, 1); + if (!arg_rlimit[rl]) + return log_oom(); + } + + r = rlimit_parse(rl, eq + 1, arg_rlimit[rl]); + if (r < 0) + return log_error_errno(r, "Failed to parse resource limit: %s", eq + 1); + + arg_settings_mask |= SETTING_RLIMIT_FIRST << rl; + break; + } + + case ARG_OOM_SCORE_ADJUST: + r = parse_oom_score_adjust(optarg, &arg_oom_score_adjust); + if (r < 0) + return log_error_errno(r, "Failed to parse --oom-score-adjust= parameter: %s", optarg); + + arg_oom_score_adjust_set = true; + arg_settings_mask |= SETTING_OOM_SCORE_ADJUST; + break; + + case ARG_CPU_AFFINITY: { + _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; + + r = parse_cpu_set(optarg, &cpuset); + if (r < 0) + return log_error_errno(r, "Failed to parse CPU affinity mask: %s", optarg); + + if (arg_cpuset) + CPU_FREE(arg_cpuset); + + arg_cpuset = TAKE_PTR(cpuset); + arg_cpuset_ncpus = r; + arg_settings_mask |= SETTING_CPU_AFFINITY; + break; + } + case '?': return -EINVAL; @@ -1718,12 +1832,14 @@ static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *user } static int setup_hostname(void) { + int r; if ((arg_clone_ns_flags & CLONE_NEWUTS) == 0) return 0; - if (sethostname_idempotent(arg_machine) < 0) - return -errno; + r = sethostname_idempotent(arg_hostname ?: arg_machine); + if (r < 0) + return log_error_errno(r, "Failed to set hostname: %m"); return 0; } @@ -2282,7 +2398,6 @@ static int inner_child( NULL }; const char *exec_target; - _cleanup_strv_free_ char **env_use = NULL; int r; @@ -2377,11 +2492,21 @@ static int inner_child( rtnl_socket = safe_close(rtnl_socket); } + if (arg_oom_score_adjust_set) { + r = set_oom_score_adjust(arg_oom_score_adjust); + if (r < 0) + return log_error_errno(r, "Failed to adjust OOM score: %m"); + } + + if (arg_cpuset) + if (sched_setaffinity(0, CPU_ALLOC_SIZE(arg_cpuset_ncpus), arg_cpuset) < 0) + return log_error_errno(errno, "Failed to set CPU affinity: %m"); + r = drop_capabilities(); if (r < 0) return log_error_errno(r, "drop_capabilities() failed: %m"); - setup_hostname(); + (void) setup_hostname(); if (arg_personality != PERSONALITY_INVALID) { r = safe_personality(arg_personality); @@ -2403,6 +2528,10 @@ static int inner_child( if (r < 0) return r; + if (arg_no_new_privileges) + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) + return log_error_errno(errno, "Failed to disable new privileges: %m"); + /* LXC sets container=lxc, so follow the scheme here */ envp[n_env++] = strjoina("container=", arg_container_service_name); @@ -2559,10 +2688,10 @@ static int outer_child( FDSet *fds, int netns_fd) { + _cleanup_close_ int fd = -1; + int r, which_failed; pid_t pid; ssize_t l; - int r; - _cleanup_close_ int fd = -1; assert(barrier); assert(directory); @@ -2805,6 +2934,10 @@ static int outer_child( if (fd < 0) return fd; + r = setrlimit_closest_all((const struct rlimit *const*) arg_rlimit, &which_failed); + if (r < 0) + return log_error_errno(r, "Failed to apply resource limit RLIMIT_%s: %m", rlimit_to_string(which_failed)); + pid = raw_clone(SIGCHLD|CLONE_NEWNS| arg_clone_ns_flags | (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0)); @@ -3041,6 +3174,206 @@ static int setup_sd_notify_parent(sd_event *event, int fd, pid_t *inner_child_pi return 0; } +static int merge_settings(Settings *settings, const char *path) { + int rl; + + assert(settings); + assert(path); + + /* Copy over bits from the settings, unless they have been explicitly masked by command line switches. Note + * that this steals the fields of the Settings* structure, and hence modifies it. */ + + if ((arg_settings_mask & SETTING_START_MODE) == 0 && + settings->start_mode >= 0) { + arg_start_mode = settings->start_mode; + strv_free_and_replace(arg_parameters, settings->parameters); + } + + if ((arg_settings_mask & SETTING_PIVOT_ROOT) == 0 && + settings->pivot_root_new) { + free_and_replace(arg_pivot_root_new, settings->pivot_root_new); + free_and_replace(arg_pivot_root_old, settings->pivot_root_old); + } + + if ((arg_settings_mask & SETTING_WORKING_DIRECTORY) == 0 && + settings->working_directory) + free_and_replace(arg_chdir, settings->working_directory); + + if ((arg_settings_mask & SETTING_ENVIRONMENT) == 0 && + settings->environment) + strv_free_and_replace(arg_setenv, settings->environment); + + if ((arg_settings_mask & SETTING_USER) == 0 && + settings->user) + free_and_replace(arg_user, settings->user); + + if ((arg_settings_mask & SETTING_CAPABILITY) == 0) { + uint64_t plus; + + plus = settings->capability; + if (settings_private_network(settings)) + plus |= (1ULL << CAP_NET_ADMIN); + + if (!arg_settings_trusted && plus != 0) { + if (settings->capability != 0) + log_warning("Ignoring Capability= setting, file %s is not trusted.", path); + } else + arg_caps_retain |= plus; + + arg_caps_retain &= ~settings->drop_capability; + } + + if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 && + settings->kill_signal > 0) + arg_kill_signal = settings->kill_signal; + + if ((arg_settings_mask & SETTING_PERSONALITY) == 0 && + settings->personality != PERSONALITY_INVALID) + arg_personality = settings->personality; + + if ((arg_settings_mask & SETTING_MACHINE_ID) == 0 && + !sd_id128_is_null(settings->machine_id)) { + + if (!arg_settings_trusted) + log_warning("Ignoring MachineID= setting, file %s is not trusted.", path); + else + arg_uuid = settings->machine_id; + } + + if ((arg_settings_mask & SETTING_READ_ONLY) == 0 && + settings->read_only >= 0) + arg_read_only = settings->read_only; + + if ((arg_settings_mask & SETTING_VOLATILE_MODE) == 0 && + settings->volatile_mode != _VOLATILE_MODE_INVALID) + arg_volatile_mode = settings->volatile_mode; + + if ((arg_settings_mask & SETTING_CUSTOM_MOUNTS) == 0 && + settings->n_custom_mounts > 0) { + + if (!arg_settings_trusted) + log_warning("Ignoring TemporaryFileSystem=, Bind= and BindReadOnly= settings, file %s is not trusted.", path); + else { + custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); + arg_custom_mounts = TAKE_PTR(settings->custom_mounts); + arg_n_custom_mounts = settings->n_custom_mounts; + settings->n_custom_mounts = 0; + } + } + + if ((arg_settings_mask & SETTING_NETWORK) == 0 && + (settings->private_network >= 0 || + settings->network_veth >= 0 || + settings->network_bridge || + settings->network_zone || + settings->network_interfaces || + settings->network_macvlan || + settings->network_ipvlan || + settings->network_veth_extra)) { + + if (!arg_settings_trusted) + log_warning("Ignoring network settings, file %s is not trusted.", path); + else { + arg_network_veth = settings_network_veth(settings); + arg_private_network = settings_private_network(settings); + + strv_free_and_replace(arg_network_interfaces, settings->network_interfaces); + strv_free_and_replace(arg_network_macvlan, settings->network_macvlan); + strv_free_and_replace(arg_network_ipvlan, settings->network_ipvlan); + strv_free_and_replace(arg_network_veth_extra, settings->network_veth_extra); + + free_and_replace(arg_network_bridge, settings->network_bridge); + free_and_replace(arg_network_zone, settings->network_zone); + } + } + + if ((arg_settings_mask & SETTING_EXPOSE_PORTS) == 0 && + settings->expose_ports) { + + if (!arg_settings_trusted) + log_warning("Ignoring Port= setting, file %s is not trusted.", path); + else { + expose_port_free_all(arg_expose_ports); + arg_expose_ports = TAKE_PTR(settings->expose_ports); + } + } + + if ((arg_settings_mask & SETTING_USERNS) == 0 && + settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) { + + if (!arg_settings_trusted) + log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", path); + else { + arg_userns_mode = settings->userns_mode; + arg_uid_shift = settings->uid_shift; + arg_uid_range = settings->uid_range; + arg_userns_chown = settings->userns_chown; + } + } + + if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0) + arg_notify_ready = settings->notify_ready; + + if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) { + + if (!arg_settings_trusted && !strv_isempty(arg_syscall_whitelist)) + log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", path); + else { + strv_free_and_replace(arg_syscall_whitelist, settings->syscall_whitelist); + strv_free_and_replace(arg_syscall_blacklist, settings->syscall_blacklist); + } + } + + for (rl = 0; rl < _RLIMIT_MAX; rl ++) { + if ((arg_settings_mask & (SETTING_RLIMIT_FIRST << rl))) + continue; + + if (!settings->rlimit[rl]) + continue; + + if (!arg_settings_trusted) { + log_warning("Ignoring Limit%s= setting, file '%s' is not trusted.", rlimit_to_string(rl), path); + continue; + } + + free_and_replace(arg_rlimit[rl], settings->rlimit[rl]); + } + + if ((arg_settings_mask & SETTING_HOSTNAME) == 0 && + settings->hostname) + free_and_replace(arg_hostname, settings->hostname); + + if ((arg_settings_mask & SETTING_NO_NEW_PRIVILEGES) == 0 && + settings->no_new_privileges >= 0) + arg_no_new_privileges = settings->no_new_privileges; + + if ((arg_settings_mask & SETTING_OOM_SCORE_ADJUST) == 0 && + settings->oom_score_adjust_set) { + + if (!arg_settings_trusted) + log_warning("Ignoring OOMScoreAdjust= setting, file '%s' is not trusted.", path); + else { + arg_oom_score_adjust = settings->oom_score_adjust; + arg_oom_score_adjust_set = true; + } + } + + if ((arg_settings_mask & SETTING_CPU_AFFINITY) == 0 && + settings->cpuset) { + + if (!arg_settings_trusted) + log_warning("Ignoring CPUAffinity= setting, file '%s' is not trusted.", path); + else { + if (arg_cpuset) + CPU_FREE(arg_cpuset); + arg_cpuset = TAKE_PTR(settings->cpuset); + arg_cpuset_ncpus = settings->cpuset_ncpus; + } + } + + return 0; +} + static int load_settings(void) { _cleanup_(settings_freep) Settings *settings = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -3112,151 +3445,7 @@ static int load_settings(void) { if (r < 0) return r; - /* Copy over bits from the settings, unless they have been - * explicitly masked by command line switches. */ - - if ((arg_settings_mask & SETTING_START_MODE) == 0 && - settings->start_mode >= 0) { - arg_start_mode = settings->start_mode; - strv_free_and_replace(arg_parameters, settings->parameters); - } - - if ((arg_settings_mask & SETTING_PIVOT_ROOT) == 0 && - settings->pivot_root_new) { - free_and_replace(arg_pivot_root_new, settings->pivot_root_new); - free_and_replace(arg_pivot_root_old, settings->pivot_root_old); - } - - if ((arg_settings_mask & SETTING_WORKING_DIRECTORY) == 0 && - settings->working_directory) - free_and_replace(arg_chdir, settings->working_directory); - - if ((arg_settings_mask & SETTING_ENVIRONMENT) == 0 && - settings->environment) - strv_free_and_replace(arg_setenv, settings->environment); - - if ((arg_settings_mask & SETTING_USER) == 0 && - settings->user) - free_and_replace(arg_user, settings->user); - - if ((arg_settings_mask & SETTING_CAPABILITY) == 0) { - uint64_t plus; - - plus = settings->capability; - if (settings_private_network(settings)) - plus |= (1ULL << CAP_NET_ADMIN); - - if (!arg_settings_trusted && plus != 0) { - if (settings->capability != 0) - log_warning("Ignoring Capability= setting, file %s is not trusted.", p); - } else - arg_caps_retain |= plus; - - arg_caps_retain &= ~settings->drop_capability; - } - - if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 && - settings->kill_signal > 0) - arg_kill_signal = settings->kill_signal; - - if ((arg_settings_mask & SETTING_PERSONALITY) == 0 && - settings->personality != PERSONALITY_INVALID) - arg_personality = settings->personality; - - if ((arg_settings_mask & SETTING_MACHINE_ID) == 0 && - !sd_id128_is_null(settings->machine_id)) { - - if (!arg_settings_trusted) - log_warning("Ignoring MachineID= setting, file %s is not trusted.", p); - else - arg_uuid = settings->machine_id; - } - - if ((arg_settings_mask & SETTING_READ_ONLY) == 0 && - settings->read_only >= 0) - arg_read_only = settings->read_only; - - if ((arg_settings_mask & SETTING_VOLATILE_MODE) == 0 && - settings->volatile_mode != _VOLATILE_MODE_INVALID) - arg_volatile_mode = settings->volatile_mode; - - if ((arg_settings_mask & SETTING_CUSTOM_MOUNTS) == 0 && - settings->n_custom_mounts > 0) { - - if (!arg_settings_trusted) - log_warning("Ignoring TemporaryFileSystem=, Bind= and BindReadOnly= settings, file %s is not trusted.", p); - else { - custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); - arg_custom_mounts = TAKE_PTR(settings->custom_mounts); - arg_n_custom_mounts = settings->n_custom_mounts; - settings->n_custom_mounts = 0; - } - } - - if ((arg_settings_mask & SETTING_NETWORK) == 0 && - (settings->private_network >= 0 || - settings->network_veth >= 0 || - settings->network_bridge || - settings->network_zone || - settings->network_interfaces || - settings->network_macvlan || - settings->network_ipvlan || - settings->network_veth_extra)) { - - if (!arg_settings_trusted) - log_warning("Ignoring network settings, file %s is not trusted.", p); - else { - arg_network_veth = settings_network_veth(settings); - arg_private_network = settings_private_network(settings); - - strv_free_and_replace(arg_network_interfaces, settings->network_interfaces); - strv_free_and_replace(arg_network_macvlan, settings->network_macvlan); - strv_free_and_replace(arg_network_ipvlan, settings->network_ipvlan); - strv_free_and_replace(arg_network_veth_extra, settings->network_veth_extra); - - free_and_replace(arg_network_bridge, settings->network_bridge); - free_and_replace(arg_network_zone, settings->network_zone); - } - } - - if ((arg_settings_mask & SETTING_EXPOSE_PORTS) == 0 && - settings->expose_ports) { - - if (!arg_settings_trusted) - log_warning("Ignoring Port= setting, file %s is not trusted.", p); - else { - expose_port_free_all(arg_expose_ports); - arg_expose_ports = TAKE_PTR(settings->expose_ports); - } - } - - if ((arg_settings_mask & SETTING_USERNS) == 0 && - settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) { - - if (!arg_settings_trusted) - log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", p); - else { - arg_userns_mode = settings->userns_mode; - arg_uid_shift = settings->uid_shift; - arg_uid_range = settings->uid_range; - arg_userns_chown = settings->userns_chown; - } - } - - if ((arg_settings_mask & SETTING_NOTIFY_READY) == 0) - arg_notify_ready = settings->notify_ready; - - if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) { - - if (!arg_settings_trusted && !strv_isempty(arg_syscall_whitelist)) - log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", p); - else { - strv_free_and_replace(arg_syscall_whitelist, settings->syscall_whitelist); - strv_free_and_replace(arg_syscall_blacklist, settings->syscall_blacklist); - } - } - - return 0; + return merge_settings(settings, p); } static int run(int master, @@ -3680,20 +3869,20 @@ static int run(int master, "STATUS=Container running.\n" "X_NSPAWN_LEADER_PID=" PID_FMT, *pid); if (!arg_notify_ready) - sd_notify(false, "READY=1\n"); + (void) sd_notify(false, "READY=1\n"); if (arg_kill_signal > 0) { /* Try to kill the init system on SIGINT or SIGTERM */ - sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(*pid)); - sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(*pid)); + (void) sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(*pid)); + (void) sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, PID_TO_PTR(*pid)); } else { /* Immediately exit */ - sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + (void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); } /* Exit when the child exits */ - sd_event_add_signal(event, NULL, SIGCHLD, on_sigchld, PID_TO_PTR(*pid)); + (void) sd_event_add_signal(event, NULL, SIGCHLD, on_sigchld, PID_TO_PTR(*pid)); if (arg_expose_ports) { r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, exposed, &rtnl); @@ -3767,6 +3956,71 @@ static int run(int master, return 1; /* loop again */ } +static int initialize_rlimits(void) { + + /* The default resource limits the kernel passes to PID 1, as per kernel 4.16. Let's pass our container payload + * the same values as the kernel originally passed to PID 1, in order to minimize differences between host and + * container execution environments. */ + + static const struct rlimit kernel_defaults[_RLIMIT_MAX] = { + [RLIMIT_AS] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_CORE] = { 0, RLIM_INFINITY }, + [RLIMIT_CPU] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_DATA] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_FSIZE] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_LOCKS] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_MEMLOCK] = { 65536, 65536 }, + [RLIMIT_MSGQUEUE] = { 819200, 819200 }, + [RLIMIT_NICE] = { 0, 0 }, + [RLIMIT_NOFILE] = { 1024, 4096 }, + [RLIMIT_RSS] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_RTPRIO] = { 0, 0 }, + [RLIMIT_RTTIME] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_STACK] = { 8388608, RLIM_INFINITY }, + + /* The kernel scales the default for RLIMIT_NPROC and RLIMIT_SIGPENDING based on the system's amount of + * RAM. To provide best compatibility we'll read these limits off PID 1 instead of hardcoding them + * here. This is safe as we know that PID 1 doesn't change these two limits and thus the original + * kernel's initialization should still be valid during runtime — at least if PID 1 is systemd. Note + * that PID 1 changes a number of other resource limits during early initialization which is why we + * don't read the other limits from PID 1 but prefer the static table above. */ + }; + + int rl; + + for (rl = 0; rl < _RLIMIT_MAX; rl++) { + + /* Let's only fill in what the user hasn't explicitly configured anyway */ + if ((arg_settings_mask & (SETTING_RLIMIT_FIRST << rl)) == 0) { + const struct rlimit *v; + struct rlimit buffer; + + if (IN_SET(rl, RLIMIT_NPROC, RLIMIT_SIGPENDING)) { + /* For these two let's read the limits off PID 1. See above for an explanation. */ + + if (prlimit(1, rl, NULL, &buffer) < 0) + return log_error_errno(errno, "Failed to read resource limit RLIMIT_%s of PID 1: %m", rlimit_to_string(rl)); + + v = &buffer; + } else + v = kernel_defaults + rl; + + arg_rlimit[rl] = newdup(struct rlimit, v, 1); + if (!arg_rlimit[rl]) + return log_oom(); + } + + if (DEBUG_LOGGING) { + _cleanup_free_ char *k = NULL; + + (void) rlimit_format(arg_rlimit[rl], &k); + log_debug("Setting RLIMIT_%s to %s.", rlimit_to_string(rl), k); + } + } + + return 0; +} + int main(int argc, char *argv[]) { _cleanup_free_ char *console = NULL; @@ -3799,6 +4053,10 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; + r = initialize_rlimits(); + if (r < 0) + goto finish; + r = determine_names(); if (r < 0) goto finish; @@ -4077,7 +4335,7 @@ int main(int argc, char *argv[]) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { + if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) < 0) { r = log_error_errno(errno, "Failed to become subreaper: %m"); goto finish; } @@ -4112,6 +4370,8 @@ finish: if (pid > 0) (void) wait_for_terminate(pid, NULL); + pager_close(); + if (remove_directory && arg_directory) { int k; @@ -4147,6 +4407,7 @@ finish: free(arg_template); free(arg_image); free(arg_machine); + free(arg_hostname); free(arg_user); free(arg_pivot_root_new); free(arg_pivot_root_old); @@ -4161,6 +4422,8 @@ finish: custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); expose_port_free_all(arg_expose_ports); free(arg_root_hash); + rlimit_free_all(arg_rlimit); + arg_cpuset = cpu_set_mfree(arg_cpuset); return r < 0 ? EXIT_FAILURE : ret; } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 78d5141053..fc035e796e 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -684,7 +684,8 @@ static int bus_append_automount_property(sd_bus_message *m, const char *field, c } static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) { - int r, rl; + const char *suffix; + int r; if (STR_IN_SET(field, "User", "Group", @@ -863,25 +864,29 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con return bus_append_byte_array(m, field, decoded, sz); } - rl = rlimit_from_string(field); - if (rl >= 0) { - const char *sn; - struct rlimit l; + if ((suffix = startswith(field, "Limit"))) { + int rl; - r = rlimit_parse(rl, eq, &l); - if (r < 0) - return log_error_errno(r, "Failed to parse resource limit: %s", eq); + rl = rlimit_from_string(suffix); + if (rl >= 0) { + const char *sn; + struct rlimit l; - r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max); - if (r < 0) - return bus_log_create_error(r); + r = rlimit_parse(rl, eq, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse resource limit: %s", eq); - sn = strjoina(field, "Soft"); - r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur); - if (r < 0) - return bus_log_create_error(r); + r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max); + if (r < 0) + return bus_log_create_error(r); - return 1; + sn = strjoina(field, "Soft"); + r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } } if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 66724d0e78..c80fc4fcbb 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1613,36 +1613,40 @@ int bus_property_get_rlimit( void *userdata, sd_bus_error *error) { + const char *is_soft; struct rlimit *rl; uint64_t u; rlim_t x; - const char *is_soft; assert(bus); assert(reply); assert(userdata); is_soft = endswith(property, "Soft"); + rl = *(struct rlimit**) userdata; if (rl) x = is_soft ? rl->rlim_cur : rl->rlim_max; else { struct rlimit buf = {}; + const char *s, *p; int z; - const char *s; + /* Chop off "Soft" suffix */ s = is_soft ? strndupa(property, is_soft - property) : property; - z = rlimit_from_string(strstr(s, "Limit")); + /* Skip over any prefix, such as "Default" */ + assert_se(p = strstr(s, "Limit")); + + z = rlimit_from_string(p + 5); assert(z >= 0); - getrlimit(z, &buf); + (void) getrlimit(z, &buf); x = is_soft ? buf.rlim_cur : buf.rlim_max; } - /* rlim_t might have different sizes, let's map - * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on - * all archs */ + /* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all + * archs */ u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; return sd_bus_message_append(reply, "t", u); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 9b822dd49a..84d0c16996 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -33,6 +33,7 @@ #include "syslog-util.h" #include "time-util.h" #include "utf8.h" +#include "rlimit-util.h" int config_item_table_lookup( const void *table, @@ -707,7 +708,7 @@ int config_parse_string( void *data, void *userdata) { - char **s = data, *n; + char **s = data; assert(filename); assert(lvalue); @@ -719,16 +720,8 @@ int config_parse_string( return 0; } - if (isempty(rvalue)) - n = NULL; - else { - n = strdup(rvalue); - if (!n) - return log_oom(); - } - - free(*s); - *s = n; + if (free_and_strdup(s, empty_to_null(rvalue)) < 0) + return log_oom(); return 0; } @@ -1214,3 +1207,42 @@ int config_parse_mtu( return 0; } + +int config_parse_rlimit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + struct rlimit **rl = data, d = {}; + int r; + + assert(rvalue); + assert(rl); + + r = rlimit_parse(ltype, rvalue, &d); + if (r == -EILSEQ) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue); + return 0; + } + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); + return 0; + } + + if (rl[ltype]) + *rl[ltype] = d; + else { + rl[ltype] = newdup(struct rlimit, &d, 1); + if (!rl[ltype]) + return log_oom(); + } + + return 0; +} diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 094b9cbc44..76adc9a88e 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -147,6 +147,7 @@ int config_parse_ifname(GENERIC_PARSER_ARGS); int config_parse_ip_port(GENERIC_PARSER_ARGS); int config_parse_join_controllers(GENERIC_PARSER_ARGS); int config_parse_mtu(GENERIC_PARSER_ARGS); +int config_parse_rlimit(GENERIC_PARSER_ARGS); typedef enum Disabled { DISABLED_CONFIGURATION, diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c index 09c35b4f8b..38de758cb6 100644 --- a/src/test/test-rlimit-util.c +++ b/src/test/test-rlimit-util.c @@ -42,6 +42,7 @@ int main(int argc, char *argv[]) { .rlim_cur = 10, .rlim_max = 5, }; + int i; log_parse_environment(); log_open(); @@ -53,10 +54,41 @@ int main(int argc, char *argv[]) { new.rlim_max = old.rlim_max; assert_se(setrlimit(RLIMIT_NOFILE, &new) >= 0); - assert_se(rlimit_from_string("LimitNOFILE") == RLIMIT_NOFILE); + assert_se(rlimit_from_string("NOFILE") == RLIMIT_NOFILE); + assert_se(rlimit_from_string("LimitNOFILE") == -1); + assert_se(rlimit_from_string("RLIMIT_NOFILE") == -1); + assert_se(rlimit_from_string("xxxNOFILE") == -1); assert_se(rlimit_from_string("DefaultLimitNOFILE") == -1); - assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "LimitNOFILE")); + assert_se(rlimit_from_string_harder("NOFILE") == RLIMIT_NOFILE); + assert_se(rlimit_from_string_harder("LimitNOFILE") == RLIMIT_NOFILE); + assert_se(rlimit_from_string_harder("RLIMIT_NOFILE") == RLIMIT_NOFILE); + assert_se(rlimit_from_string_harder("xxxNOFILE") == -1); + assert_se(rlimit_from_string_harder("DefaultLimitNOFILE") == -1); + + for (i = 0; i < _RLIMIT_MAX; i++) { + _cleanup_free_ char *prefixed = NULL; + const char *p; + + assert_se(p = rlimit_to_string(i)); + log_info("%i = %s", i, p); + + assert_se(rlimit_from_string(p) == i); + assert_se(rlimit_from_string_harder(p) == i); + + assert_se(prefixed = strjoin("Limit", p)); + + assert_se(rlimit_from_string(prefixed) < 0); + assert_se(rlimit_from_string_harder(prefixed) == i); + + prefixed = mfree(prefixed); + assert_se(prefixed = strjoin("RLIMIT_", p)); + + assert_se(rlimit_from_string(prefixed) < 0); + assert_se(rlimit_from_string_harder(prefixed) == i); + } + + assert_se(streq_ptr(rlimit_to_string(RLIMIT_NOFILE), "NOFILE")); assert_se(rlimit_to_string(-1) == NULL); assert_se(getrlimit(RLIMIT_NOFILE, &old) == 0); diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 45849ef87e..d6dec64fb8 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -16,6 +16,7 @@ #include "alloc-util.h" #include "all-units.h" #include "capability-util.h" +#include "conf-parser.h" #include "fd-util.h" #include "fileio.h" #include "hashmap.h" @@ -748,22 +749,22 @@ static void test_config_parse_capability_set(void) { static void test_config_parse_rlimit(void) { struct rlimit * rl[_RLIMIT_MAX] = {}; - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55); assert_se(rl[RLIMIT_NOFILE]->rlim_max == 66); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max); @@ -772,86 +773,86 @@ static void test_config_parse_rlimit(void) { rl[RLIMIT_NOFILE]->rlim_max = 20; /* Invalid values don't change rl */ - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "10:20:30", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "10:20:30", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "wat:wat", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "wat:wat", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "66:wat", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "66:wat", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "200:100", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "200:100", rl, NULL) >= 0); assert_se(rl[RLIMIT_NOFILE]); assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 10); assert_se(rl[RLIMIT_NOFILE]->rlim_max == 20); rl[RLIMIT_NOFILE] = mfree(rl[RLIMIT_NOFILE]); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0); assert_se(rl[RLIMIT_CPU]); assert_se(rl[RLIMIT_CPU]->rlim_cur == 56); assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "57s", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "57s", rl, NULL) >= 0); assert_se(rl[RLIMIT_CPU]); assert_se(rl[RLIMIT_CPU]->rlim_cur == 57); assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0); assert_se(rl[RLIMIT_CPU]); assert_se(rl[RLIMIT_CPU]->rlim_cur == 40); assert_se(rl[RLIMIT_CPU]->rlim_max == 60); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_CPU]); assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY); assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "1234ms", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "1234ms", rl, NULL) >= 0); assert_se(rl[RLIMIT_CPU]); assert_se(rl[RLIMIT_CPU]->rlim_cur == 2); assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max); rl[RLIMIT_CPU] = mfree(rl[RLIMIT_CPU]); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58); assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC); assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max); - assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0); + assert_se(config_parse_rlimit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0); assert_se(rl[RLIMIT_RTTIME]); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC); assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);