#include <sched.h>
#include <sys/resource.h>
+#include "sd-messages.h"
+
#include "af-list.h"
#include "alloc-util.h"
#include "all-units.h"
#include "errno-list.h"
#include "escape.h"
#include "fd-util.h"
+#include "fileio.h"
#include "fs-util.h"
#include "hexdecoct.h"
#include "io-util.h"
DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy");
DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
if (m == KILL_NONE)
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Unit configured to use KillMode=none. "
- "This is unsafe, as it disables systemd's process life-cycle management for the service. "
+ "This is unsafe, as it disables systemd's process lifecycle management for the service. "
"Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
"Support for KillMode=none is deprecated and will eventually be removed.");
return 0;
}
+int config_parse_root_image_options(
+ 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_(mount_options_free_allp) MountOptions *options = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ char **first = NULL, **second = NULL;
+ ExecContext *c = data;
+ const Unit *u = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ c->root_image_options = mount_options_free_all(c->root_image_options);
+ return 0;
+ }
+
+ r = strv_split_colon_pairs(&l, rvalue);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ STRV_FOREACH_PAIR(first, second, l) {
+ _cleanup_free_ char *mount_options_resolved = NULL;
+ const char *mount_options = NULL;
+ MountOptions *o = NULL;
+ unsigned int partition_number = 0;
+
+ /* Format is either '0:foo' or 'foo' (0 is implied) */
+ if (!isempty(*second)) {
+ mount_options = *second;
+ r = safe_atou(*first, &partition_number);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse partition number from \"%s\", ignoring: %m", *first);
+ continue;
+ }
+ } else
+ mount_options = *first;
+
+ r = unit_full_printf(u, mount_options, &mount_options_resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", mount_options);
+ continue;
+ }
+
+ o = new(MountOptions, 1);
+ if (!o)
+ return log_oom();
+ *o = (MountOptions) {
+ .partition_number = partition_number,
+ .options = TAKE_PTR(mount_options_resolved),
+ };
+ LIST_APPEND(mount_options, options, o);
+ }
+
+ /* empty spaces/separators only */
+ if (LIST_IS_EMPTY(options))
+ c->root_image_options = mount_options_free_all(c->root_image_options);
+ else
+ LIST_JOIN(mount_options, c->root_image_options, options);
+
+ return 0;
+}
+
+int config_parse_exec_root_hash(
+ 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_free_ void *roothash_decoded = NULL;
+ ExecContext *c = data;
+ size_t roothash_decoded_size = 0;
+ int r;
+
+ assert(data);
+ assert(filename);
+ assert(line);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ /* Reset if the empty string is assigned */
+ c->root_hash_path = mfree(c->root_hash_path);
+ c->root_hash = mfree(c->root_hash);
+ c->root_hash_size = 0;
+ return 0;
+ }
+
+ if (path_is_absolute(rvalue)) {
+ /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
+ _cleanup_free_ char *p = NULL;
+
+ p = strdup(rvalue);
+ if (!p)
+ return -ENOMEM;
+
+ free_and_replace(c->root_hash_path, p);
+ c->root_hash = mfree(c->root_hash);
+ c->root_hash_size = 0;
+ return 0;
+ }
+
+ /* We have a roothash to decode, eg: RootHash=012345789abcdef */
+ r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
+ if (roothash_decoded_size < sizeof(sd_id128_t))
+ return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "RootHash= is too short, ignoring: %s", rvalue);
+
+ free_and_replace(c->root_hash, roothash_decoded);
+ c->root_hash_size = roothash_decoded_size;
+ c->root_hash_path = mfree(c->root_hash_path);
+
+ return 0;
+}
+
+int config_parse_exec_root_hash_sig(
+ 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_free_ void *roothash_sig_decoded = NULL;
+ char *value;
+ ExecContext *c = data;
+ size_t roothash_sig_decoded_size = 0;
+ int r;
+
+ assert(data);
+ assert(filename);
+ assert(line);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ /* Reset if the empty string is assigned */
+ c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+ c->root_hash_sig = mfree(c->root_hash_sig);
+ c->root_hash_sig_size = 0;
+ return 0;
+ }
+
+ if (path_is_absolute(rvalue)) {
+ /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */
+ _cleanup_free_ char *p = NULL;
+
+ p = strdup(rvalue);
+ if (!p)
+ return -ENOMEM;
+
+ free_and_replace(c->root_hash_sig_path, p);
+ c->root_hash_sig = mfree(c->root_hash_sig);
+ c->root_hash_sig_size = 0;
+ return 0;
+ }
+
+ if (!(value = startswith(rvalue, "base64:")))
+ return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue);
+
+ /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
+ r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
+
+ free_and_replace(c->root_hash_sig, roothash_sig_decoded);
+ c->root_hash_sig_size = roothash_sig_decoded_size;
+ c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+
+ return 0;
+}
+
int config_parse_exec_cpu_affinity(const char *unit,
const char *filename,
unsigned line,
return -ENOEXEC;
}
+ if (strstr(lvalue, "User") && streq(k, NOBODY_USER_NAME))
+ log_struct(LOG_NOTICE,
+ "MESSAGE=%s:%u: Special user %s configured, this is not safe!", filename, line, k,
+ "UNIT=%s", unit,
+ "MESSAGE_ID=" SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR,
+ "OFFENDING_USER=%s", k,
+ "CONFIG_FILE=%s", filename,
+ "CONFIG_LINE=%u", line);
+
return free_and_replace(*user, k);
}
if (isempty(rvalue)) {
/* Empty assignment resets the list */
c->syscall_filter = hashmap_free(c->syscall_filter);
- c->syscall_whitelist = false;
+ c->syscall_allow_list = false;
return 0;
}
if (invert)
/* Allow everything but the ones listed */
- c->syscall_whitelist = false;
+ c->syscall_allow_list = false;
else {
/* Allow nothing but the ones listed */
- c->syscall_whitelist = true;
+ c->syscall_allow_list = true;
- /* Accept default syscalls if we are on a whitelist */
+ /* Accept default syscalls if we are on a allow_list */
r = seccomp_parse_syscall_filter(
"@default", -1, c->syscall_filter,
- SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_WHITELIST,
+ SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_ALLOW_LIST,
unit,
NULL, 0);
if (r < 0)
name, num, c->syscall_filter,
SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
(invert ? SECCOMP_PARSE_INVERT : 0)|
- (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0),
+ (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
unit, filename, line);
if (r < 0)
return r;
return 0;
}
- r = set_ensure_allocated(archs, NULL);
- if (r < 0)
- return log_oom();
-
for (;;) {
_cleanup_free_ char *word = NULL;
uint32_t a;
continue;
}
- r = set_put(*archs, UINT32_TO_PTR(a + 1));
+ r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1));
if (r < 0)
return log_oom();
}
if (isempty(rvalue)) {
/* Empty assignment resets the list */
c->address_families = set_free(c->address_families);
- c->address_families_whitelist = false;
+ c->address_families_allow_list = false;
return 0;
}
if (!c->address_families)
return log_oom();
- c->address_families_whitelist = !invert;
+ c->address_families_allow_list = !invert;
}
for (p = rvalue;;) {
/* If we previously wanted to forbid an address family and now
* we want to allow it, then just remove it from the list.
*/
- if (!invert == c->address_families_whitelist) {
+ if (!invert == c->address_families_allow_list) {
r = set_put(c->address_families, INT_TO_PTR(af));
if (r < 0)
return log_oom();
uint64_t bytes = CGROUP_LIMIT_MAX;
int r;
- if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
+ if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
+ "DefaultMemoryMin",
+ "MemoryLow",
+ "MemoryMin"))
+ bytes = CGROUP_LIMIT_MIN;
+ else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
r = parse_permille(rvalue);
if (r < 0) {
}
if (streq(lvalue, "DefaultMemoryLow")) {
+ c->default_memory_low = bytes;
c->default_memory_low_set = true;
- if (isempty(rvalue))
- c->default_memory_low = CGROUP_LIMIT_MIN;
- else
- c->default_memory_low = bytes;
} else if (streq(lvalue, "DefaultMemoryMin")) {
+ c->default_memory_min = bytes;
c->default_memory_min_set = true;
- if (isempty(rvalue))
- c->default_memory_min = CGROUP_LIMIT_MIN;
- else
- c->default_memory_min = bytes;
} else if (streq(lvalue, "MemoryMin")) {
c->memory_min = bytes;
c->memory_min_set = true;
r = unit_full_printf(u, word, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
+ log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to resolve unit specifiers in \"%s\", ignoring: %m", word);
continue;
}
continue;
if (path_startswith(k, "private")) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
"%s= path can't be 'private', ignoring assignment: %s", lvalue, word);
continue;
}
r = unit_full_printf(u, source, &sresolved);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to resolved unit specifiers in \"%s\", ignoring: %m", source);
+ "Failed to resolve unit specifiers in \"%s\", ignoring: %m", source);
continue;
}
r = unit_full_printf(u, destination, &dresolved);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to resolved specifiers in \"%s\", ignoring: %m", destination);
+ "Failed to resolve specifiers in \"%s\", ignoring: %m", destination);
continue;
}
return 0;
}
+int config_parse_mount_images(
+ 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_strv_free_ char **l = NULL;
+ ExecContext *c = data;
+ const Unit *u = userdata;
+ char **source = NULL, **destination = NULL;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ c->mount_images = mount_image_free_many(c->mount_images, &c->n_mount_images);
+ return 0;
+ }
+
+ r = strv_split_colon_pairs(&l, rvalue);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ STRV_FOREACH_PAIR(source, destination, l) {
+ _cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
+ char *s = NULL;
+ bool permissive = false;
+
+ r = unit_full_printf(u, *source, &sresolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to resolve unit specifiers in \"%s\", ignoring: %m", *source);
+ continue;
+ }
+
+ s = sresolved;
+ if (s[0] == '-') {
+ permissive = true;
+ s++;
+ }
+
+ r = path_simplify_and_warn(s, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ continue;
+
+ if (isempty(*destination)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Missing destination in %s, ignoring: %s", lvalue, rvalue);
+ continue;
+ }
+
+ r = unit_full_printf(u, *destination, &dresolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to resolve specifiers in \"%s\", ignoring: %m", *destination);
+ continue;
+ }
+
+ r = path_simplify_and_warn(dresolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ continue;
+
+ r = mount_image_add(&c->mount_images, &c->n_mount_images,
+ &(MountImage) {
+ .source = s,
+ .destination = dresolved,
+ .ignore_enoent = permissive,
+ });
+ if (r < 0)
+ return log_oom();
+ }
+
+ return 0;
+}
+
int config_parse_job_timeout_sec(
const char* unit,
const char *filename,
int unit_load_fragment(Unit *u) {
const char *fragment;
_cleanup_set_free_free_ Set *names = NULL;
- struct stat st;
int r;
assert(u);
if (fragment) {
/* Open the file, check if this is a mask, otherwise read. */
_cleanup_fclose_ FILE *f = NULL;
+ struct stat st;
/* Try to open the file name. A symlink is OK, for example for linked files or masks. We
* expect that all symlinks within the lookup paths have been already resolved, but we don't
r = config_parse(u->id, fragment, f,
UNIT_VTABLE(u)->sections,
config_item_perf_lookup, load_fragment_gperf_lookup,
- CONFIG_PARSE_ALLOW_INCLUDE, u);
+ 0,
+ u,
+ NULL);
if (r == -ENOEXEC)
log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
if (r < 0)
}
}
- if (u->source_path) {
- if (stat(u->source_path, &st) >= 0)
- u->source_mtime = timespec_load(&st.st_mtim);
- else
- u->source_mtime = 0;
- }
-
/* We do the merge dance here because for some unit types, the unit might have aliases which are not
* declared in the file system. In particular, this is true (and frequent) for device and swap units.
*/
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
{ config_parse_service_type, "SERVICETYPE" },
{ config_parse_service_restart, "SERVICERESTART" },
+ { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
{ config_parse_kill_mode, "KILLMODE" },
{ config_parse_signal, "SIGNAL" },
{ config_parse_socket_listen, "SOCKET [...]" },