load-fragment.c revision ac97e2c559f5d386a332aba4a24bf9930cdb1c51
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright 2012 Holger Hans Peter Freyther
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/resource.h>
#include "unit.h"
#include "strv.h"
#include "conf-parser.h"
#include "load-fragment.h"
#include "log.h"
#include "ioprio.h"
#include "securebits.h"
#include "missing.h"
#include "unit-name.h"
#include "unit-printf.h"
#include "bus-errors.h"
#include "utf8.h"
#include "path-util.h"
#include "syscall-list.h"
#ifndef HAVE_SYSV_COMPAT
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
log_debug("[%s:%u] Support for option %s= has been disabled at compile time and is ignored", filename, line, lvalue);
return 0;
}
#endif
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
UnitDependency d = ltype;
char *w;
size_t l;
char *state;
int r;
t = strndup(w, l);
if (!t)
return -ENOMEM;
k = unit_name_printf(u, t);
if (!k)
return -ENOMEM;
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0)
log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s",
}
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *k;
int r;
assert(u);
k = unit_full_printf(u, rvalue);
if (!k)
return -ENOMEM;
free (k);
return r;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *k;
int r;
assert(u);
k = unit_full_printf(u, rvalue);
if (!k)
return -ENOMEM;
free(k);
return r;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *k;
int r;
assert(u);
k = unit_full_printf(u, rvalue);
if (!k)
return log_oom();
free(k);
return r;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
SocketPort *p, *tail;
Socket *s;
if (!p)
return -ENOMEM;
p->type = SOCKET_FIFO;
free(p);
return -ENOMEM;
}
path_kill_slashes(p->path);
p->type = SOCKET_SPECIAL;
free(p);
return -ENOMEM;
}
path_kill_slashes(p->path);
p->type = SOCKET_MQUEUE;
free(p);
return -ENOMEM;
}
path_kill_slashes(p->path);
char *k;
int r;
p->type = SOCKET_SOCKET;
r = socket_address_parse_netlink(&p->address, k);
free(k);
if (r < 0) {
free(p);
return 0;
}
} else {
char *k;
int r;
p->type = SOCKET_SOCKET;
r = socket_address_parse(&p->address, k);
free(k);
if (r < 0) {
free(p);
return 0;
}
else {
}
free(p);
return 0;
}
}
p->fd = -1;
if (s->ports) {
} else
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Socket *s;
if (b < 0) {
int r;
r = parse_boolean(rvalue);
if (r < 0) {
return 0;
}
} else
s->bind_ipv6_only = b;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
int priority;
return 0;
}
return 0;
}
c->nice_set = true;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
int oa;
log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
c->oom_score_adjust = oa;
c->oom_score_adjust_set = true;
return 0;
}
int config_parse_exec(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *path, **n;
unsigned k;
int r;
assert(e);
/* We accept an absolute path as first argument, or
* alternatively an absolute prefixed with @ to allow
* overriding of argv[0]. */
e += ltype;
for (;;) {
int i;
char *w;
size_t l;
char *state;
bool honour_argv0 = false, ignore = false;
n = NULL;
if (rvalue[0] == 0)
break;
for (i = 0; i < 2; i++) {
ignore = true;
rvalue ++;
}
honour_argv0 = true;
rvalue ++;
}
}
if (*rvalue != '/') {
log_error("[%s:%u] Executable path is not absolute, ignoring: %s",
return 0;
}
k = 0;
break;
k++;
}
n = new(char*, k + !honour_argv0);
if (!n)
return -ENOMEM;
k = 0;
break;
w ++;
if (honour_argv0 && w == rvalue) {
if (!path) {
r = -ENOMEM;
goto fail;
}
if (!utf8_is_valid(path)) {
r = 0;
goto fail;
}
} else {
char *c;
c = n[k++] = cunescape_length(w, l);
if (!c) {
r = -ENOMEM;
goto fail;
}
if (!utf8_is_valid(c)) {
r = 0;
goto fail;
}
}
}
n[k] = NULL;
if (!n[0]) {
r = 0;
goto fail;
}
if (!path) {
if (!path) {
r = -ENOMEM;
goto fail;
}
}
if (!nce) {
r = -ENOMEM;
goto fail;
}
}
return 0;
fail:
n[k] = NULL;
strv_free(n);
return r;
}
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");
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *n;
return -ENOMEM;
} else
n = NULL;
free(s->bind_to_device);
s->bind_to_device = n;
return 0;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
int x;
if (x < 0) {
return 0;
}
c->ioprio_set = true;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
int i;
return 0;
}
c->ioprio_set = true;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
int x;
if (x < 0) {
return 0;
}
c->cpu_sched_policy = x;
/* Moving to or from real-time policy? We need to adjust the priority */
c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
c->cpu_sched_set = true;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
return 0;
}
return 0;
}
c->cpu_sched_priority = i;
c->cpu_sched_set = true;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
char *w;
size_t l;
char *state;
char _cleanup_free_ *t = NULL;
int r;
unsigned cpu;
t = strndup(w, l);
if (!t)
return -ENOMEM;
if (!c->cpuset) {
if (!c->cpuset)
return -ENOMEM;
}
if (r < 0 || cpu >= c->cpuset_ncpus) {
log_error("[%s:%u] Failed to parse CPU affinity %s, ignoring: %s",
return 0;
}
}
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
return -ENOMEM;
return 0;
}
if (c->capabilities)
cap_free(c->capabilities);
c->capabilities = cap;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
char *w;
size_t l;
char *state;
if (first_word(w, "keep-caps"))
c->secure_bits |= SECURE_KEEP_CAPS;
else if (first_word(w, "keep-caps-locked"))
else if (first_word(w, "no-setuid-fixup"))
else if (first_word(w, "no-setuid-fixup-locked"))
else if (first_word(w, "noroot"))
c->secure_bits |= SECURE_NOROOT;
else if (first_word(w, "noroot-locked"))
c->secure_bits |= SECURE_NOROOT_LOCKED;
else {
log_error("[%s:%u] Failed to parse secure bits, ignoring: %s",
return 0;
}
}
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *w;
size_t l;
char *state;
bool invert = false;
if (rvalue[0] == '~') {
invert = true;
rvalue++;
}
/* Note that we store this inverted internally, since the
* kernel wants it like this. But we actually expose it
* non-inverted everywhere to have a fully normalized
* interface. */
char _cleanup_free_ *t = NULL;
int r;
t = strndup(w, l);
if (!t)
return -ENOMEM;
r = cap_from_name(t, &cap);
if (r < 0) {
log_error("[%s:%u] Failed to parse capability in bounding set, ignoring: %s",
continue;
}
}
if (invert)
else
return 0;
}
int config_parse_limit(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
unsigned long long u;
u = (unsigned long long) RLIM_INFINITY;
else if (safe_atollu(rvalue, &u) < 0) {
return 0;
}
if (!*rl)
return -ENOMEM;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *w;
size_t l;
char *state;
int r;
t = strndup(w, l);
if (!t)
return -ENOMEM;
k = unit_full_printf(u, t);
if (!k)
return -ENOMEM;
if (!ku)
return -ENOMEM;
r = unit_add_cgroup_from_text(u, ku);
if (r < 0) {
log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s",
return 0;
}
}
return 0;
}
#ifdef HAVE_SYSV_COMPAT
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int i;
return 0;
}
*priority = (int) i;
return 0;
}
#endif
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int i;
return 0;
}
*passno = (int) i;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
return 0;
}
*sig = r;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
char *w;
size_t l;
char *state;
unsigned long flags = 0;
char _cleanup_free_ *t;
t = strndup(w, l);
if (!t)
return -ENOMEM;
if (streq(t, "shared"))
else if (streq(t, "slave"))
else if (streq(w, "private"))
flags |= MS_PRIVATE;
else {
log_error("[%s:%u] Failed to parse mount flag %s, ignoring: %s",
return 0;
}
}
c->mount_flags = flags;
return 0;
}
int config_parse_timer(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
usec_t u;
TimerValue *v;
TimerBase b;
if ((b = timer_base_from_string(lvalue)) < 0) {
return 0;
}
if (parse_usec(rvalue, &u) < 0) {
return 0;
}
return -ENOMEM;
v->base = b;
v->value = u;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
Unit *u;
return 0;
}
if (r < 0) {
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
return 0;
}
unit_ref_set(&t->unit, u);
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
PathSpec *s;
PathType b;
char *k;
b = path_type_from_string(lvalue);
if (b < 0) {
return 0;
}
if (!k)
return log_oom();
if (!path_is_absolute(k)) {
free(k);
return 0;
}
if (!s) {
free(k);
return log_oom();
}
s->path = path_kill_slashes(k);
s->type = b;
s->inotify_fd = -1;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
Unit *u;
return 0;
}
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
return 0;
}
unit_ref_set(&t->unit, u);
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
Unit *x;
return 0;
}
if (r < 0) {
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
return 0;
}
unit_ref_set(&s->service, x);
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
char *state, *w;
size_t l;
t = strndup(w, l);
if (!t)
return -ENOMEM;
k = unit_name_printf(UNIT(s), t);
if (!k)
return -ENOMEM;
if (!endswith(k, ".socket")) {
log_error("[%s:%u] Unit must be of type socket, ignoring: %s",
continue;
}
if (r < 0)
log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s",
if (r < 0)
return r;
}
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
assert(s);
if (r)
return r;
s->start_timeout_defined = true;
s->timeout_stop_usec = s->timeout_start_usec;
s->start_timeout_defined = true;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char *s;
s = unit_full_printf(u, rvalue);
if (!s)
return -ENOMEM;
free(s);
return 0;
}
k = strv_append(*env, s);
free(s);
if (!k)
return -ENOMEM;
*env = k;
return 0;
}
int config_parse_ip_tos(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
x = ip_tos_from_string(rvalue);
if (x < 0) {
return 0;
}
*ip_tos = x;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Condition *c;
_cleanup_free_ char *p = NULL;
if (trigger)
rvalue++;
if (negate)
rvalue++;
p = unit_full_printf(u, rvalue);
if (!p)
return -ENOMEM;
if (!path_is_absolute(p)) {
return 0;
}
if (!c)
return -ENOMEM;
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Condition *c;
_cleanup_free_ char *s = NULL;
if (trigger)
rvalue++;
if (negate)
rvalue++;
s = unit_full_printf(u, rvalue);
if (!s)
return -ENOMEM;
if (!c)
return log_oom();
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Condition *c;
int b;
rvalue++;
rvalue++;
if ((b = parse_boolean(rvalue)) < 0) {
log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
return 0;
}
if (!b)
return -ENOMEM;
return 0;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char **l;
int r;
l = strv_split_quoted(rvalue);
if (!l)
return -ENOMEM;
if (strv_length(l) != 2) {
strv_free(l);
return 0;
}
strv_free(l);
if (r < 0) {
return 0;
}
return 0;
}
int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
int r;
unsigned long ul;
char *t;
return 0;
}
return -ENOMEM;
free(t);
if (r < 0) {
return 0;
}
return 0;
}
int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
int r;
char *t;
return 0;
}
return -ENOMEM;
r = unit_add_cgroup_attribute(u,
"memory",
t, NULL);
free(t);
if (r < 0) {
return 0;
}
return 0;
}
char **l;
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
if (streq(l[0], "*")) {
strv_free(l);
return -ENOMEM;
}
} else {
log_warning("Couldn't stat device %s", l[0]);
strv_free(l);
return -errno;
}
log_warning("%s is not a device.", l[0]);
strv_free(l);
return -ENODEV;
}
strv_free(l);
return -ENOMEM;
}
}
strv_free(l);
return 0;
}
int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
char **l;
int r;
unsigned k;
l = strv_split_quoted(rvalue);
if (!l)
return -ENOMEM;
k = strv_length(l);
if (k < 1 || k > 2) {
strv_free(l);
return 0;
}
strv_free(l);
return 0;
}
strv_free(l);
return 0;
}
strv_free(l);
r = unit_add_cgroup_attribute(u, "devices",
rvalue, device_map);
if (r < 0) {
return 0;
}
return 0;
}
char **l;
dev_t d;
l = strv_split_quoted(value);
if (!l)
return -ENOMEM;
log_warning("Couldn't stat device %s", l[0]);
strv_free(l);
return -errno;
}
/* If this is not a device node then find the block
* device this file is stored on */
/* If this is a partition, try to get the originating
* block device */
block_get_whole_disk(d, &d);
} else {
log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
strv_free(l);
return -ENODEV;
}
strv_free(l);
return -ENOMEM;
}
strv_free(l);
return 0;
}
int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
int r;
unsigned long ul;
unsigned k;
char *t, **l;
l = strv_split_quoted(rvalue);
if (!l)
return -ENOMEM;
k = strv_length(l);
if (k < 1 || k > 2) {
strv_free(l);
return 0;
}
if (k == 1)
weight = l[0];
else {
device = l[0];
weight = l[1];
}
strv_free(l);
return 0;
}
strv_free(l);
return 0;
}
if (device)
else
strv_free(l);
if (r < 0)
return -ENOMEM;
if (device)
else
free(t);
if (r < 0) {
return 0;
}
return 0;
}
int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
int r;
unsigned k;
char *t, **l;
l = strv_split_quoted(rvalue);
if (!l)
return -ENOMEM;
k = strv_length(l);
if (k != 2) {
strv_free(l);
return 0;
}
if (!path_is_absolute(l[0])) {
strv_free(l);
return 0;
}
log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue);
strv_free(l);
return 0;
}
strv_free(l);
if (r < 0)
return -ENOMEM;
r = unit_add_cgroup_attribute(u, "blkio",
t, blkio_map);
free(t);
if (r < 0) {
return 0;
}
return 0;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
bool empty_before;
empty_before = !u->requires_mounts_for;
/* Make it easy to find units with requires_mounts set */
if (empty_before && u->requires_mounts_for)
return r;
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
char **a, **b;
assert(u);
if (r < 0)
return r;
for (a = b = u->documentation; a && *a; a++) {
if (is_valid_documentation_url(*a))
*(b++) = *a;
else {
free(*a);
}
}
*b = NULL;
return r;
}
}
}
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
bool invert = false;
char *w;
size_t l;
char *state;
assert(u);
if (rvalue[0] == '~') {
invert = true;
rvalue++;
}
if (!c->syscall_filter) {
size_t n;
if (!c->syscall_filter)
return -ENOMEM;
/* Add these by default */
#ifdef __NR_sigreturn
#endif
}
int id;
char _cleanup_free_ *t = NULL;
t = strndup(w, l);
if (!t)
return -ENOMEM;
id = syscall_from_name(t);
if (id < 0) {
log_error("[%s:%u] Failed to parse syscall, ignoring: %s",
continue;
}
if (invert)
else
}
c->no_new_privileges = true;
return 0;
}
#define FOLLOW_MAX 8
unsigned c = 0;
int fd, r;
FILE *f;
/* This will update the filename pointer if the loaded file is
* reached by a symlink. The old string will be freed. */
for (;;) {
if (c++ >= FOLLOW_MAX)
return -ELOOP;
/* Add the file name we are currently looking at to
* the names of this unit, but only if it is a valid
* unit name. */
if (unit_name_is_valid(name, true)) {
if (!id) {
if (!id)
return -ENOMEM;
if (r < 0) {
return r;
}
}
}
/* Try to open the file name, but don't if its a symlink */
if (fd >= 0)
break;
return -errno;
/* Hmm, so this is a symlink. Let's read the name, and follow it manually */
if (r < 0)
return r;
}
if (!f) {
r = -errno;
return r;
}
*_f = f;
return 0;
}
char *k;
int r;
assert(u);
assert(*u);
/* Let's try to add in all symlink names we found */
while ((k = set_steal_first(names))) {
/* First try to merge in the other name into our
* unit */
r = unit_merge_by_name(*u, k);
if (r < 0) {
/* Hmm, we couldn't merge the other unit into
* ours? Then let's try it the other way
* round */
free(k);
if (other) {
r = unit_merge(other, *u);
if (r >= 0) {
*u = other;
}
}
return r;
}
if (id == k)
unit_choose_id(*u, id);
free(k);
}
return 0;
}
int r;
assert(u);
if (!symlink_names)
return -ENOMEM;
if (path_is_absolute(path)) {
if (!filename) {
r = -ENOMEM;
goto finish;
}
if (r < 0) {
if (r != -ENOENT)
goto finish;
}
} else {
char **p;
/* Instead of opening the path right away, we manually
* follow all symlinks and add their name to our unit
* name set while doing so */
if (!filename) {
r = -ENOMEM;
goto finish;
}
if (u->manager->unit_path_cache &&
r = -ENOENT;
else
if (r < 0) {
if (r != -ENOENT)
goto finish;
/* Empty the symlink names for the next run */
continue;
}
break;
}
}
if (!filename) {
/* Hmm, no suitable file found? */
r = 0;
goto finish;
}
merged = u;
if (r < 0)
goto finish;
if (merged != u) {
u->load_state = UNIT_MERGED;
r = 0;
goto finish;
}
r = -errno;
goto finish;
}
if (null_or_empty(&st))
u->load_state = UNIT_MASKED;
else {
/* Now, parse the file contents */
r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u);
if (r < 0)
goto finish;
u->load_state = UNIT_LOADED;
}
free(u->fragment_path);
u->fragment_path = filename;
if (u->source_path) {
else
u->source_mtime = 0;
}
r = 0;
if (f)
fclose(f);
return r;
}
int unit_load_fragment(Unit *u) {
int r;
Iterator i;
const char *t;
assert(u);
/* First, try to find the unit under its id. We always look
* for unit files in the default directories, to make it easy
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) {
if (t == u->id)
continue;
r = load_from_path(u, t);
if (r < 0)
return r;
if (u->load_state != UNIT_STUB)
break;
}
/* And now, try looking for it under the suggested (originally linked) 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. */
free(u->fragment_path);
u->fragment_path = NULL;
}
}
/* Look for a template */
char *k;
k = unit_name_template(u->id);
if (!k)
return -ENOMEM;
r = load_from_path(u, k);
free(k);
if (r < 0)
return r;
if (u->load_state == UNIT_STUB)
SET_FOREACH(t, u->names, i) {
if (t == u->id)
continue;
k = unit_name_template(t);
if (!k)
return -ENOMEM;
r = load_from_path(u, k);
free(k);
if (r < 0)
return r;
if (u->load_state != UNIT_STUB)
break;
}
}
return 0;
}
void unit_dump_config_items(FILE *f) {
static const struct {
const ConfigParserCallback callback;
const char *rvalue;
} table[] = {
{ config_parse_int, "INTEGER" },
{ config_parse_unsigned, "UNSIGNED" },
{ config_parse_bytes_size, "SIZE" },
{ config_parse_bool, "BOOLEAN" },
{ config_parse_string, "STRING" },
{ config_parse_path, "PATH" },
{ config_parse_unit_path_printf, "PATH" },
{ config_parse_strv, "STRING [...]" },
{ config_parse_exec_nice, "NICE" },
{ config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
{ config_parse_exec_io_class, "IOCLASS" },
{ config_parse_exec_io_priority, "IOPRIORITY" },
{ config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
{ config_parse_exec_cpu_sched_prio, "CPUSCHEDPRIO" },
{ config_parse_exec_cpu_affinity, "CPUAFFINITY" },
{ config_parse_mode, "MODE" },
{ config_parse_unit_env_file, "FILE" },
{ config_parse_output, "OUTPUT" },
{ config_parse_input, "INPUT" },
{ config_parse_facility, "FACILITY" },
{ config_parse_level, "LEVEL" },
{ config_parse_exec_capabilities, "CAPABILITIES" },
{ config_parse_exec_secure_bits, "SECUREBITS" },
{ config_parse_bounding_set, "BOUNDINGSET" },
{ config_parse_limit, "LIMIT" },
{ config_parse_unit_cgroup, "CGROUP [...]" },
{ config_parse_unit_deps, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
{ config_parse_service_type, "SERVICETYPE" },
{ config_parse_service_restart, "SERVICERESTART" },
#ifdef HAVE_SYSV_COMPAT
{ config_parse_sysv_priority, "SYSVPRIORITY" },
#else
{ config_parse_warn_compat, "NOTSUPPORTED" },
#endif
{ config_parse_kill_mode, "KILLMODE" },
{ config_parse_kill_signal, "SIGNAL" },
{ config_parse_socket_listen, "SOCKET [...]" },
{ config_parse_socket_bind, "SOCKETBIND" },
{ config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
{ config_parse_usec, "SECONDS" },
{ config_parse_nsec, "NANOSECONDS" },
{ config_parse_path_strv, "PATH [...]" },
{ config_parse_unit_requires_mounts_for, "PATH [...]" },
{ config_parse_exec_mount_flags, "MOUNTFLAG [...]" },
{ config_parse_unit_string_printf, "STRING" },
{ config_parse_timer, "TIMER" },
{ config_parse_timer_unit, "NAME" },
{ config_parse_path_spec, "PATH" },
{ config_parse_path_unit, "UNIT" },
{ config_parse_notify_access, "ACCESS" },
{ config_parse_ip_tos, "TOS" },
{ config_parse_unit_condition_path, "CONDITION" },
{ config_parse_unit_condition_string, "CONDITION" },
{ config_parse_unit_condition_null, "CONDITION" },
};
const char *i;
assert(f);
unsigned j;
const char *dot;
const ConfigPerfItem *p;
prefix_len = dot-i;
if (dot)
if (prev)
fputc('\n', f);
}
for (j = 0; j < ELEMENTSOF(table); j++)
break;
}
prev = i;
}
}