#include <seccomp.h>
#endif
#include <sched.h>
-#include <string.h>
#include <sys/resource.h>
-#include <sys/stat.h>
#include "af-list.h"
#include "alloc-util.h"
#include "bus-util.h"
#include "cap-list.h"
#include "capability-util.h"
-#include "cgroup.h"
+#include "cgroup-setup.h"
#include "conf-parser.h"
#include "cpu-set-util.h"
#include "env-util.h"
#include "limits-util.h"
#include "load-fragment.h"
#include "log.h"
-#include "missing.h"
#include "mountpoint-util.h"
#include "nulstr-util.h"
#include "parse-util.h"
#endif
#include "securebits-util.h"
#include "signal-util.h"
+#include "socket-netlink.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
if (r == 0) {
*console = NULL;
return 0;
- }
-
- if (r > 0) /* on with default tty */
+ } else if (r > 0) /* on with default tty */
s = strdup("/dev/console");
else if (is_path(value)) /* on with fully qualified path */
s = strdup(value);
else /* on with only a tty file name, not a fully qualified path */
- s = strjoin("/dev/", value);
+ s = path_join("/dev/", value);
if (!s)
return -ENOMEM;
DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_status_unit_format, status_unit_format, StatusUnitFormat, "Failed to parse status unit format");
int config_parse_unit_deps(
const char *unit,
void *userdata) {
_cleanup_free_ char *k = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(filename);
void *data,
void *userdata) {
- Unit *u = userdata;
+ const Unit *u = userdata;
_cleanup_free_ char *k = NULL;
int r;
void *userdata) {
_cleanup_free_ char *k = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
bool fatal = ltype;
void *userdata) {
char ***x = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
const char *p;
void *userdata) {
ExecCommand **e = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
const char *p;
bool semicolon;
int r;
assert(e);
e += ltype;
- rvalue += strspn(rvalue, WHITESPACE);
if (isempty(rvalue)) {
/* An empty assignment resets the list */
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
const char *n;
ExecInput ei;
int r;
_cleanup_free_ char *unescaped = NULL, *resolved = NULL;
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
size_t sz;
void *p;
int r;
_cleanup_free_ char *resolved = NULL;
const char *n;
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
ExecOutput eo;
int r;
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
bool ignore;
char *k;
int r;
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
bool ignore;
char *k;
int r;
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
bool ignore;
char *k;
int r;
_cleanup_(calendar_spec_freep) CalendarSpec *c = NULL;
_cleanup_free_ char *k = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
Timer *t = data;
usec_t usec = 0;
TimerValue *v;
void *userdata) {
_cleanup_free_ char *k = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(filename);
return 0;
}
-int config_parse_service_timeout_abort(
+int config_parse_timeout_abort(
const char *unit,
const char *filename,
unsigned line,
void *data,
void *userdata) {
- Service *s = userdata;
+ usec_t *ret = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(s);
+ assert(ret);
+
+ /* Note: apart from setting the arg, this returns an extra bit of information in the return value. */
- rvalue += strspn(rvalue, WHITESPACE);
if (isempty(rvalue)) {
- s->timeout_abort_set = false;
- return 0;
+ *ret = 0;
+ return 0; /* "not set" */
}
- r = parse_sec(rvalue, &s->timeout_abort_usec);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse TimeoutAbortSec= setting, ignoring: %s", rvalue);
- return 0;
- }
+ r = parse_sec(rvalue, ret);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= setting, ignoring: %s", lvalue, rvalue);
+
+ return 1; /* "set" */
+}
+
+int config_parse_service_timeout_abort(
+ 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) {
+
+ Service *s = userdata;
+ int r;
+
+ assert(s);
- s->timeout_abort_set = true;
+ r = config_parse_timeout_abort(unit, filename, line, section, section_line, lvalue, ltype, rvalue,
+ &s->timeout_abort_usec, s);
+ if (r >= 0)
+ s->timeout_abort_set = r;
return 0;
}
return 0;
}
-int config_parse_user_group(
+int config_parse_user_group_compat(
const char *unit,
const char *filename,
unsigned line,
_cleanup_free_ char *k = NULL;
char **user = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(filename);
return -ENOEXEC;
}
- if (!valid_user_group_name_or_id(k)) {
+ if (!valid_user_group_name_or_id_compat(k)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
return -ENOEXEC;
}
return free_and_replace(*user, k);
}
-int config_parse_user_group_strv(
+int config_parse_user_group_strv_compat(
const char *unit,
const char *filename,
unsigned line,
void *userdata) {
char ***users = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
const char *p = rvalue;
int r;
return -ENOEXEC;
}
- if (!valid_user_group_name_or_id(k)) {
+ if (!valid_user_group_name_or_id_compat(k)) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k);
return -ENOEXEC;
}
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
bool missing_ok;
int r;
void *userdata) {
char ***env = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
_cleanup_free_ char *n = NULL;
int r;
void *data,
void *userdata) {
- Unit *u = userdata;
+ const Unit *u = userdata;
char ***env = data;
const char *p;
int r;
size_t nlen = 0, nbufsize = 0;
char*** passenv = data;
const char *p = rvalue;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(filename);
size_t nlen = 0, nbufsize = 0;
char*** unsetenv = data;
const char *p = rvalue;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(filename);
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
const char *p = rvalue;
int r;
Condition **list = data, *c;
ConditionType t = ltype;
bool trigger, negate;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(filename);
Condition **list = data, *c;
ConditionType t = ltype;
bool trigger, negate;
- Unit *u = userdata;
+ const Unit *u = userdata;
int r;
assert(filename);
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
bool invert = false;
const char *p;
int r;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_free_ char *k = NULL;
- Unit *u = userdata, *slice = NULL;
+ Unit *u = userdata, *slice;
int r;
assert(filename);
return 0;
}
+int config_parse_allowed_cpus(
+ 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) {
+
+ CGroupContext *c = data;
+
+ (void) parse_cpu_set_extend(rvalue, &c->cpuset_cpus, true, unit, filename, line, lvalue);
+
+ return 0;
+}
+
+int config_parse_allowed_mems(
+ 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) {
+
+ CGroupContext *c = data;
+
+ (void) parse_cpu_set_extend(rvalue, &c->cpuset_mems, true, unit, filename, line, lvalue);
+
+ return 0;
+}
+
int config_parse_memory_limit(
const char *unit,
const char *filename,
void *data,
void *userdata) {
- uint64_t *tasks_max = data, v;
- Unit *u = userdata;
+ const Unit *u = userdata;
+ TasksMax *tasks_max = data;
+ uint64_t v;
int r;
if (isempty(rvalue)) {
- *tasks_max = u ? u->manager->default_tasks_max : UINT64_MAX;
+ *tasks_max = u ? u->manager->default_tasks_max : TASKS_MAX_UNSET;
return 0;
}
if (streq(rvalue, "infinity")) {
- *tasks_max = CGROUP_LIMIT_MAX;
+ *tasks_max = TASKS_MAX_UNSET;
return 0;
}
r = parse_permille(rvalue);
- if (r < 0) {
+ if (r >= 0)
+ *tasks_max = (TasksMax) { r, 1000U }; /* r‰ */
+ else {
r = safe_atou64(rvalue, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Invalid maximum tasks value '%s', ignoring: %m", rvalue);
return 0;
}
- } else
- v = system_tasks_max_scale(r, 1000U);
- if (v <= 0 || v >= UINT64_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
- return 0;
+ if (v <= 0 || v >= UINT64_MAX) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range, ignoring.", rvalue);
+ return 0;
+ }
+
+ *tasks_max = (TasksMax) { v };
}
- *tasks_max = v;
return 0;
}
void *userdata) {
char***rt = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
const char *p;
int r;
FOREACH_WORD(word, l, rvalue, state) {
_cleanup_free_ char *temp;
- int val;
- Set **set;
+ Bitmap *bitmap;
temp = strndup(word, l);
if (!temp)
return log_oom();
- r = safe_atoi(temp, &val);
- if (r < 0) {
- val = signal_from_string(temp);
+ /* We need to call exit_status_from_string() first, because we want
+ * to parse numbers as exit statuses, not signals. */
- if (val <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
- continue;
- }
- set = &status_set->signal;
+ r = exit_status_from_string(temp);
+ if (r >= 0) {
+ assert(r >= 0 && r < 256);
+ bitmap = &status_set->status;
} else {
- if (val < 0 || val > 255) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
+ r = signal_from_string(temp);
+
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Failed to parse value, ignoring: %s", word);
continue;
}
- set = &status_set->status;
+ bitmap = &status_set->signal;
}
- r = set_ensure_allocated(set, NULL);
- if (r < 0)
- return log_oom();
-
- r = set_put(*set, INT_TO_PTR(val));
+ r = bitmap_set(bitmap, r);
if (r < 0)
- return log_oom();
+ return log_error_errno(r, "Failed to set signal or status %s: %m", word);
}
if (!isempty(state))
log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
void *data,
void *userdata) {
- Unit *u = userdata;
+ const Unit *u = userdata;
char*** sv = data;
const char *p = rvalue;
int r;
void *data,
void *userdata) {
- Unit *u = userdata;
+ const Unit *u = userdata;
ExecContext *c = data;
const char *p = rvalue;
int r;
void *userdata) {
ExecContext *c = data;
- Unit *u = userdata;
+ const Unit *u = userdata;
const char *p;
int r;
void *userdata) {
_cleanup_free_ char *k = NULL, *n = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
char **s = data;
int r;
void *userdata) {
_cleanup_free_ char *resolved = NULL;
- Unit *u = userdata;
+ const Unit *u = userdata;
char ***paths = data;
int r;
return 0;
}
-#define FOLLOW_MAX 8
-
-static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
- char *id = NULL;
- unsigned c = 0;
- int fd, r;
- FILE *f;
-
- assert(filename);
- assert(*filename);
- assert(_f);
- assert(names);
-
- /* This will update the filename pointer if the loaded file is
- * reached by a symlink. The old string will be freed. */
-
- for (;;) {
- char *target, *name;
-
- if (c++ >= FOLLOW_MAX)
- return -ELOOP;
-
- path_simplify(*filename, false);
-
- /* Add the file name we are currently looking at to
- * the names of this unit, but only if it is a valid
- * unit name. */
- name = basename(*filename);
- if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
-
- id = set_get(names, name);
- if (!id) {
- id = strdup(name);
- if (!id)
- return -ENOMEM;
-
- r = set_consume(names, id);
- if (r < 0)
- return r;
- }
- }
-
- /* Try to open the file name, but don't if its a symlink */
- fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd >= 0)
- break;
-
- if (errno != ELOOP)
- return -errno;
-
- /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
- r = readlink_and_make_absolute(*filename, &target);
- if (r < 0)
- return r;
-
- free_and_replace(*filename, target);
- }
-
- f = fdopen(fd, "r");
- if (!f) {
- safe_close(fd);
- return -errno;
- }
-
- *_f = f;
- *_final = id;
-
- return 0;
-}
-
static int merge_by_names(Unit **u, Set *names, const char *id) {
char *k;
int r;
assert(u);
assert(*u);
- assert(names);
- /* Let's try to add in all symlink names we found */
+ /* Let's try to add in all names that are aliases of this unit */
while ((k = set_steal_first(names))) {
+ _cleanup_free_ _unused_ char *free_k = k;
- /* First try to merge in the other name into our
- * unit */
+ /* First try to merge in the other name into our unit */
r = unit_merge_by_name(*u, k);
if (r < 0) {
Unit *other;
- /* Hmm, we couldn't merge the other unit into
- * ours? Then let's try it the other way
- * round */
-
- /* If the symlink name we are looking at is unit template, then
- we must search for instance of this template */
- if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
- _cleanup_free_ char *instance = NULL;
-
- r = unit_name_replace_instance(k, (*u)->instance, &instance);
- if (r < 0)
- return r;
+ /* Hmm, we couldn't merge the other unit into ours? Then let's try it the other way
+ * round. */
- other = manager_get_unit((*u)->manager, instance);
- } else
- other = manager_get_unit((*u)->manager, k);
-
- free(k);
+ other = manager_get_unit((*u)->manager, k);
+ if (!other)
+ return r; /* return previous failure */
- if (other) {
- r = unit_merge(other, *u);
- if (r >= 0) {
- *u = other;
- return merge_by_names(u, names, NULL);
- }
- }
+ r = unit_merge(other, *u);
+ if (r < 0)
+ return r;
- return r;
+ *u = other;
+ return merge_by_names(u, names, NULL);
}
- if (id == k)
+ if (streq_ptr(id, k))
unit_choose_id(*u, id);
-
- free(k);
}
return 0;
}
-static int load_from_path(Unit *u, const char *path) {
- _cleanup_set_free_free_ Set *symlink_names = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *filename = NULL;
- char *id = NULL;
- Unit *merged;
+int unit_load_fragment(Unit *u) {
+ const char *fragment;
+ _cleanup_set_free_free_ Set *names = NULL;
struct stat st;
int r;
assert(u);
- assert(path);
-
- symlink_names = set_new(&string_hash_ops);
- if (!symlink_names)
- return -ENOMEM;
-
- if (path_is_absolute(path)) {
-
- filename = strdup(path);
- if (!filename)
- return -ENOMEM;
-
- r = open_follow(&filename, &f, symlink_names, &id);
- if (r < 0) {
- filename = mfree(filename);
- if (r != -ENOENT)
- return r;
- }
-
- } else {
- char **p;
-
- STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
-
- /* Instead of opening the path right away, we manually
- * follow all symlinks and add their name to our unit
- * name set while doing so */
- filename = path_make_absolute(path, *p);
- if (!filename)
- return -ENOMEM;
-
- if (u->manager->unit_path_cache &&
- !set_get(u->manager->unit_path_cache, filename))
- r = -ENOENT;
- else
- r = open_follow(&filename, &f, symlink_names, &id);
- if (r >= 0)
- break;
-
- /* ENOENT means that the file is missing or is a dangling symlink.
- * ENOTDIR means that one of paths we expect to be is a directory
- * is not a directory, we should just ignore that.
- * EACCES means that the directory or file permissions are wrong.
- */
- if (r == -EACCES)
- log_debug_errno(r, "Cannot access \"%s\": %m", filename);
- else if (!IN_SET(r, -ENOENT, -ENOTDIR))
- return r;
-
- filename = mfree(filename);
- /* Empty the symlink names for the next run */
- set_clear_free(symlink_names);
- }
- }
+ assert(u->load_state == UNIT_STUB);
+ assert(u->id);
- if (!filename)
- /* Hmm, no suitable file found? */
+ if (u->transient) {
+ u->load_state = UNIT_LOADED;
return 0;
-
- if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
- log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
- return -ELOOP;
}
- merged = u;
- r = merge_by_names(&merged, symlink_names, id);
+ /* Possibly rebuild the fragment map to catch new units */
+ r = unit_file_build_name_map(&u->manager->lookup_paths,
+ &u->manager->unit_cache_mtime,
+ &u->manager->unit_id_map,
+ &u->manager->unit_name_map,
+ &u->manager->unit_path_cache);
if (r < 0)
+ log_error_errno(r, "Failed to rebuild name map: %m");
+
+ r = unit_file_find_fragment(u->manager->unit_id_map,
+ u->manager->unit_name_map,
+ u->id,
+ &fragment,
+ &names);
+ if (r < 0 && r != -ENOENT)
return r;
- if (merged != u) {
- u->load_state = UNIT_MERGED;
- return 0;
- }
+ if (fragment) {
+ /* Open the file, check if this is a mask, otherwise read. */
+ _cleanup_fclose_ FILE *f = NULL;
- if (fstat(fileno(f), &st) < 0)
- return -errno;
+ /* 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
+ * verify this here. */
+ f = fopen(fragment, "re");
+ if (!f)
+ return log_unit_notice_errno(u, errno, "Failed to open %s: %m", fragment);
- if (null_or_empty(&st)) {
- u->load_state = UNIT_MASKED;
- u->fragment_mtime = 0;
- } else {
- u->load_state = UNIT_LOADED;
- u->fragment_mtime = timespec_load(&st.st_mtim);
+ if (fstat(fileno(f), &st) < 0)
+ return -errno;
- /* Now, parse the file contents */
- r = config_parse(u->id, filename, f,
- UNIT_VTABLE(u)->sections,
- config_item_perf_lookup, load_fragment_gperf_lookup,
- CONFIG_PARSE_ALLOW_INCLUDE, u);
+ r = free_and_strdup(&u->fragment_path, fragment);
if (r < 0)
return r;
- }
- free_and_replace(u->fragment_path, filename);
+ if (null_or_empty(&st)) {
+ /* Unit file is masked */
+
+ u->load_state = u->perpetual ? UNIT_LOADED : UNIT_MASKED; /* don't allow perpetual units to ever be masked */
+ u->fragment_mtime = 0;
+ } else {
+ u->load_state = UNIT_LOADED;
+ u->fragment_mtime = timespec_load(&st.st_mtim);
+
+ /* Now, parse the file contents */
+ r = config_parse(u->id, fragment, f,
+ UNIT_VTABLE(u)->sections,
+ config_item_perf_lookup, load_fragment_gperf_lookup,
+ CONFIG_PARSE_ALLOW_INCLUDE, u);
+ if (r == -ENOEXEC)
+ log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
+ if (r < 0)
+ return r;
+ }
+ }
if (u->source_path) {
if (stat(u->source_path, &st) >= 0)
u->source_mtime = 0;
}
- return 0;
-}
-
-int unit_load_fragment(Unit *u) {
- int r;
- Iterator i;
- const char *t;
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
- assert(u->id);
-
- if (u->transient) {
- u->load_state = UNIT_LOADED;
- return 0;
- }
-
- /* First, try to find the unit under its id. We always look
- * for unit files in the default directories, to make it easy
- * to override things by placing things in /etc/systemd/system */
- r = load_from_path(u, u->id);
- if (r < 0)
- return r;
-
- /* Try to find an alias we can load this with */
- if (u->load_state == UNIT_STUB) {
- SET_FOREACH(t, u->names, i) {
+ /* 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.
+ */
+ Unit *merged;
+ const char *id = u->id;
+ _cleanup_free_ char *free_id = NULL;
- if (t == u->id)
- continue;
+ if (fragment) {
+ id = basename(fragment);
+ if (unit_name_is_valid(id, UNIT_NAME_TEMPLATE)) {
+ assert(u->instance); /* If we're not trying to use a template for non-instanced unit,
+ * this must be set. */
- r = load_from_path(u, t);
+ r = unit_name_replace_instance(id, u->instance, &free_id);
if (r < 0)
- return r;
-
- if (u->load_state != UNIT_STUB)
- break;
+ return log_debug_errno(r, "Failed to build id (%s + %s): %m", id, u->instance);
+ id = free_id;
}
}
- /* And now, try looking for it under the suggested (originally linked) path */
- if (u->load_state == UNIT_STUB && u->fragment_path) {
-
- r = load_from_path(u, u->fragment_path);
- if (r < 0)
- return r;
-
- if (u->load_state == UNIT_STUB)
- /* Hmm, this didn't work? Then let's get rid
- * of the fragment path stored for us, so that
- * we don't point to an invalid location. */
- u->fragment_path = mfree(u->fragment_path);
- }
-
- /* Look for a template */
- if (u->load_state == UNIT_STUB && u->instance) {
- _cleanup_free_ char *k = NULL;
-
- r = unit_name_template(u->id, &k);
- if (r < 0)
- return r;
-
- r = load_from_path(u, k);
- if (r < 0) {
- if (r == -ENOEXEC)
- log_unit_notice(u, "Unit configuration has fatal error, unit will not be started.");
- return r;
- }
-
- if (u->load_state == UNIT_STUB) {
- SET_FOREACH(t, u->names, i) {
- _cleanup_free_ char *z = NULL;
-
- if (t == u->id)
- continue;
-
- r = unit_name_template(t, &z);
- if (r < 0)
- return r;
-
- r = load_from_path(u, z);
- if (r < 0)
- return r;
+ merged = u;
+ r = merge_by_names(&merged, names, id);
+ if (r < 0)
+ return r;
- if (u->load_state != UNIT_STUB)
- break;
- }
- }
- }
+ if (merged != u)
+ u->load_state = UNIT_MERGED;
return 0;
}
return 0;
}
-int config_parse_timeout_abort(
- const char* unit,
+int config_parse_swap_priority(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
void *data,
void *userdata) {
- usec_t *timeout_usec = data;
- int r;
+ Swap *s = userdata;
+ int r, priority;
+ assert(s);
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(timeout_usec);
+ assert(data);
- rvalue += strspn(rvalue, WHITESPACE);
if (isempty(rvalue)) {
- *timeout_usec = false;
+ s->parameters_fragment.priority = -1;
+ s->parameters_fragment.priority_set = false;
return 0;
}
- r = parse_sec(rvalue, timeout_usec);
+ r = safe_atoi(rvalue, &priority);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DefaultTimeoutAbortSec= setting, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid swap pririty '%s', ignoring.", rvalue);
+ return 0;
+ }
+
+ if (priority < -1) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Sorry, swap priorities smaller than -1 may only be assigned by the kernel itself, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (priority > 32767) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Swap priority out of range, ignoring: %s", rvalue);
return 0;
}
- *timeout_usec = true;
+ s->parameters_fragment.priority = priority;
+ s->parameters_fragment.priority_set = true;
return 0;
}