summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/automount.c42
-rw-r--r--src/core/automount.h10
-rw-r--r--src/core/bus-errors.h13
-rw-r--r--src/core/cgroup-attr.c62
-rw-r--r--src/core/cgroup-attr.h12
-rw-r--r--src/core/cgroup-semantics.c333
-rw-r--r--src/core/cgroup-semantics.h43
-rw-r--r--src/core/cgroup.c112
-rw-r--r--src/core/cgroup.h5
-rw-r--r--src/core/condition.c5
-rw-r--r--src/core/condition.h4
-rw-r--r--src/core/dbus-execute.c10
-rw-r--r--src/core/dbus-execute.h5
-rw-r--r--src/core/dbus-job.c25
-rw-r--r--src/core/dbus-manager.c306
-rw-r--r--src/core/dbus-mount.c2
-rw-r--r--src/core/dbus-path.c6
-rw-r--r--src/core/dbus-service.c2
-rw-r--r--src/core/dbus-snapshot.c20
-rw-r--r--src/core/dbus-socket.c64
-rw-r--r--src/core/dbus-swap.c2
-rw-r--r--src/core/dbus-timer.c79
-rw-r--r--src/core/dbus-unit.c467
-rw-r--r--src/core/dbus-unit.h42
-rw-r--r--src/core/dbus.c38
-rw-r--r--src/core/device.c24
-rw-r--r--src/core/device.h4
-rw-r--r--src/core/execute.c370
-rw-r--r--src/core/execute.h20
-rw-r--r--src/core/hostname-setup.c3
-rw-r--r--src/core/ima-setup.c5
-rw-r--r--src/core/job.c119
-rw-r--r--src/core/job.h30
-rw-r--r--src/core/kill.h10
-rw-r--r--src/core/killall.c99
-rw-r--r--src/core/kmod-setup.c72
-rw-r--r--src/core/load-dropin.c129
-rw-r--r--src/core/load-dropin.h1
-rw-r--r--src/core/load-fragment-gperf.gperf.m454
-rw-r--r--src/core/load-fragment.c1772
-rw-r--r--src/core/load-fragment.h102
-rw-r--r--src/core/locale-setup.c5
-rw-r--r--src/core/loopback-setup.c93
-rw-r--r--src/core/machine-id-setup.c119
-rw-r--r--src/core/macros.systemd.in4
-rw-r--r--src/core/main.c395
-rw-r--r--src/core/manager.c422
-rw-r--r--src/core/manager.h18
-rw-r--r--src/core/mount-setup.c114
-rw-r--r--src/core/mount.c226
-rw-r--r--src/core/mount.h12
-rw-r--r--src/core/namespace.c237
-rw-r--r--src/core/namespace.h14
-rw-r--r--src/core/path.c258
-rw-r--r--src/core/path.h18
-rw-r--r--src/core/selinux-access.c28
-rw-r--r--src/core/selinux-setup.c5
-rw-r--r--src/core/service.c519
-rw-r--r--src/core/service.h28
-rw-r--r--src/core/shutdown.c5
-rw-r--r--src/core/smack-setup.c151
-rw-r--r--src/core/smack-setup.h26
-rw-r--r--src/core/snapshot.c5
-rw-r--r--src/core/snapshot.h4
-rw-r--r--src/core/socket.c199
-rw-r--r--src/core/socket.h23
-rw-r--r--src/core/special.h10
-rw-r--r--src/core/swap.c149
-rw-r--r--src/core/swap.h12
-rw-r--r--src/core/sync.c65
-rw-r--r--src/core/sync.h24
-rw-r--r--src/core/syscall-list.c4
-rw-r--r--src/core/syscall-list.h14
-rw-r--r--src/core/system.conf2
-rw-r--r--src/core/target.c4
-rw-r--r--src/core/target.h4
-rw-r--r--src/core/timer.c162
-rw-r--r--src/core/timer.h21
-rw-r--r--src/core/transaction.c30
-rw-r--r--src/core/transaction.h3
-rw-r--r--src/core/umount.c35
-rw-r--r--src/core/unit-printf.c82
-rw-r--r--src/core/unit.c714
-rw-r--r--src/core/unit.h64
-rw-r--r--src/core/user.conf2
85 files changed, 5493 insertions, 3359 deletions
diff --git a/src/core/automount.c b/src/core/automount.c
index 4a98540d82..a20d5340f2 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -109,7 +109,6 @@ static void automount_done(Unit *u) {
assert(a);
unmount_autofs(a);
- unit_ref_unset(&a->mount);
free(a->where);
a->where = NULL;
@@ -200,8 +199,8 @@ static int automount_verify(Automount *a) {
}
static int automount_load(Unit *u) {
- int r;
Automount *a = AUTOMOUNT(u);
+ int r;
assert(u);
assert(u->load_state == UNIT_STUB);
@@ -222,17 +221,15 @@ static int automount_load(Unit *u) {
path_kill_slashes(a->where);
- r = automount_add_mount_links(a);
+ r = unit_load_related_unit(u, ".mount", &x);
if (r < 0)
return r;
- r = unit_load_related_unit(u, ".mount", &x);
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
if (r < 0)
return r;
- unit_ref_set(&a->mount, x);
-
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(a->mount), true);
+ r = automount_add_mount_links(a);
if (r < 0)
return r;
@@ -586,18 +583,17 @@ fail:
}
static void automount_enter_runnning(Automount *a) {
- int r;
- struct stat st;
_cleanup_dbus_error_free_ DBusError error;
+ struct stat st;
+ int r;
assert(a);
- assert(UNIT_DEREF(a->mount));
dbus_error_init(&error);
/* We don't take mount requests anymore if we are supposed to
* shut down anyway */
- if (unit_pending_inactive(UNIT(a))) {
+ if (unit_stop_pending(UNIT(a))) {
log_debug_unit(UNIT(a)->id,
"Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
automount_send_ready(a, -EHOSTDOWN);
@@ -616,11 +612,15 @@ static void automount_enter_runnning(Automount *a) {
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
log_info_unit(UNIT(a)->id,
"%s's automount point already active?", UNIT(a)->id);
- else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) {
- log_warning_unit(UNIT(a)->id,
- "%s failed to queue mount startup job: %s",
- UNIT(a)->id, bus_error(&error, r));
- goto fail;
+ else {
+ r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)),
+ JOB_REPLACE, true, &error, NULL);
+ if (r < 0) {
+ log_warning_unit(UNIT(a)->id,
+ "%s failed to queue mount startup job: %s",
+ UNIT(a)->id, bus_error(&error, r));
+ goto fail;
+ }
}
automount_set_state(a, AUTOMOUNT_RUNNING);
@@ -643,7 +643,7 @@ static int automount_start(Unit *u) {
return -EEXIST;
}
- if (UNIT_DEREF(a->mount)->load_state != UNIT_LOADED)
+ if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
return -ENOENT;
a->result = AUTOMOUNT_SUCCESS;
@@ -765,14 +765,12 @@ static const char *automount_sub_state_to_string(Unit *u) {
}
static bool automount_check_gc(Unit *u) {
- Automount *a = AUTOMOUNT(u);
-
- assert(a);
+ assert(u);
- if (!UNIT_DEREF(a->mount))
+ if (!UNIT_TRIGGER(u))
return false;
- return UNIT_VTABLE(UNIT_DEREF(a->mount))->check_gc(UNIT_DEREF(a->mount));
+ return UNIT_VTABLE(UNIT_TRIGGER(u))->check_gc(UNIT_TRIGGER(u));
}
static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
diff --git a/src/core/automount.h b/src/core/automount.h
index 3d5736d1cb..0c6b8a72e9 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -48,8 +48,6 @@ struct Automount {
char *where;
- UnitRef mount;
-
int pipe_fd;
mode_t directory_mode;
Watch pipe_watch;
@@ -66,8 +64,8 @@ int automount_send_ready(Automount *a, int status);
int automount_add_one_mount_link(Automount *a, Mount *m);
-const char* automount_state_to_string(AutomountState i);
-AutomountState automount_state_from_string(const char *s);
+const char* automount_state_to_string(AutomountState i) _const_;
+AutomountState automount_state_from_string(const char *s) _pure_;
-const char* automount_result_to_string(AutomountResult i);
-AutomountResult automount_result_from_string(const char *s);
+const char* automount_result_to_string(AutomountResult i) _const_;
+AutomountResult automount_result_from_string(const char *s) _pure_;
diff --git a/src/core/bus-errors.h b/src/core/bus-errors.h
index 04c1b2849d..7a4084ea15 100644
--- a/src/core/bus-errors.h
+++ b/src/core/bus-errors.h
@@ -21,9 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <string.h>
-#include <dbus/dbus.h>
-
#define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit"
#define BUS_ERROR_NO_SUCH_JOB "org.freedesktop.systemd1.NoSuchJob"
#define BUS_ERROR_NOT_SUBSCRIBED "org.freedesktop.systemd1.NotSubscribed"
@@ -43,13 +40,3 @@
#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess"
-
-static inline const char *bus_error(const DBusError *e, int r) {
- if (e && e->message)
- return e->message;
-
- if (r >= 0)
- return strerror(r);
-
- return strerror(-r);
-}
diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c
index 71af09cf87..7e3e08eabb 100644
--- a/src/core/cgroup-attr.c
+++ b/src/core/cgroup-attr.c
@@ -22,11 +22,11 @@
#include "cgroup-attr.h"
#include "cgroup-util.h"
#include "list.h"
+#include "fileio.h"
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
+ _cleanup_free_ char *path = NULL, *v = NULL;
int r;
- char *path = NULL;
- char *v = NULL;
assert(a);
@@ -34,25 +34,20 @@ int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
if (!b)
return 0;
- if (a->map_callback) {
- r = a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write) {
+ r = a->semantics->map_write(a->semantics, a->value, &v);
if (r < 0)
return r;
}
r = cg_get_path(a->controller, b->path, a->name, &path);
- if (r < 0) {
- free(v);
+ if (r < 0)
return r;
- }
- r = write_one_line_file(path, v ? v : a->value);
+ r = write_string_file(path, v ? v : a->value);
if (r < 0)
log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
- free(path);
- free(v);
-
return r;
}
@@ -71,23 +66,50 @@ int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
return r;
}
-CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) {
+bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) {
+ assert(a);
+
+ if (controller) {
+ if (streq(a->controller, controller) && (!name || streq(a->name, name)))
+ return true;
+
+ } else if (!name)
+ return true;
+ else if (streq(a->name, name)) {
+ size_t x, y;
+ x = strlen(a->controller);
+ y = strlen(name);
+
+ if (y > x &&
+ memcmp(a->controller, name, x) == 0 &&
+ name[x] == '.')
+ return true;
+ }
+
+ return false;
+}
+
+CGroupAttribute *cgroup_attribute_find_list(
+ CGroupAttribute *first,
+ const char *controller,
+ const char *name) {
CGroupAttribute *a;
- assert(controller);
assert(name);
LIST_FOREACH(by_unit, a, first)
- if (streq(a->controller, controller) &&
- streq(a->name, name))
+ if (cgroup_attribute_matches(a, controller, name))
return a;
return NULL;
}
-static void cgroup_attribute_free(CGroupAttribute *a) {
+void cgroup_attribute_free(CGroupAttribute *a) {
assert(a);
+ if (a->unit)
+ LIST_REMOVE(CGroupAttribute, by_unit, a->unit->cgroup_attributes, a);
+
free(a->controller);
free(a->name);
free(a->value);
@@ -100,3 +122,11 @@ void cgroup_attribute_free_list(CGroupAttribute *first) {
LIST_FOREACH_SAFE(by_unit, a, n, first)
cgroup_attribute_free(a);
}
+
+void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name) {
+ CGroupAttribute *a, *n;
+
+ LIST_FOREACH_SAFE(by_unit, a, n, first)
+ if (cgroup_attribute_matches(a, controller, name))
+ cgroup_attribute_free(a);
+}
diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h
index 2b754eac40..3a13b7c92d 100644
--- a/src/core/cgroup-attr.h
+++ b/src/core/cgroup-attr.h
@@ -23,17 +23,18 @@
typedef struct CGroupAttribute CGroupAttribute;
-typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
-
#include "unit.h"
#include "cgroup.h"
+#include "cgroup-semantics.h"
struct CGroupAttribute {
char *controller;
char *name;
char *value;
- CGroupAttributeMapCallback map_callback;
+ Unit *unit;
+
+ const CGroupSemantics *semantics;
LIST_FIELDS(CGroupAttribute, by_unit);
};
@@ -41,6 +42,9 @@ struct CGroupAttribute {
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
-CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
+bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) _pure_;
+CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) _pure_;
+void cgroup_attribute_free(CGroupAttribute *a);
void cgroup_attribute_free_list(CGroupAttribute *first);
+void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name);
diff --git a/src/core/cgroup-semantics.c b/src/core/cgroup-semantics.c
new file mode 100644
index 0000000000..82b02bbd78
--- /dev/null
+++ b/src/core/cgroup-semantics.c
@@ -0,0 +1,333 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ 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 "util.h"
+#include "strv.h"
+#include "path-util.h"
+#include "cgroup-util.h"
+
+#include "cgroup-semantics.h"
+
+static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (safe_atolu(value, &ul) < 0 || ul < 1)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
+ off_t sz;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (parse_bytes(value, &sz) < 0 || sz <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ char *x;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
+ return -EINVAL;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ return 1;
+}
+
+static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 1)
+ return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
+
+ if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %lu", l[0], ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ off_t bytes;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev")) {
+ return -EINVAL;
+ }
+
+ if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (streq(l[0], "*")) {
+
+ if (asprintf(ret, "a *:*%s%s",
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
+ return -ENOMEM;
+ } else {
+ struct stat st;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", l[0]);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%c %u:%u%s%s",
+ S_ISCHR(st.st_mode) ? 'c' : 'b',
+ major(st.st_rdev), minor(st.st_rdev),
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ struct stat st;
+ dev_t d;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return log_oom();
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (S_ISBLK(st.st_mode))
+ d = st.st_rdev;
+ else if (major(st.st_dev) != 0) {
+ /* If this is not a device node then find the block
+ * device this file is stored on */
+ d = st.st_dev;
+
+ /* 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]);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const CGroupSemantics semantics[] = {
+ { "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL },
+ { "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL },
+ { "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL },
+ { "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL },
+ { "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL },
+ { "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL },
+ { "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL },
+ { "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL },
+ { "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }
+};
+
+int cgroup_semantics_find(
+ const char *controller,
+ const char *name,
+ const char *value,
+ char **ret,
+ const CGroupSemantics **_s) {
+
+ _cleanup_free_ char *c = NULL;
+ unsigned i;
+ int r;
+
+ assert(name);
+ assert(_s);
+ assert(!value == !ret);
+
+ if (!controller) {
+ r = cg_controller_from_attr(name, &c);
+ if (r < 0)
+ return r;
+
+ controller = c;
+ }
+
+ for (i = 0; i < ELEMENTSOF(semantics); i++) {
+ const CGroupSemantics *s = semantics + i;
+ bool matches_name, matches_pretty;
+
+ if (controller && s->controller && !streq(s->controller, controller))
+ continue;
+
+ matches_name = s->name && streq(s->name, name);
+ matches_pretty = s->pretty && streq(s->pretty, name);
+
+ if (!matches_name && !matches_pretty)
+ continue;
+
+ if (value) {
+ if (matches_pretty && s->map_pretty) {
+
+ r = s->map_pretty(s, value, ret);
+ if (r < 0)
+ return r;
+
+ if (r == 0)
+ continue;
+
+ } else {
+ char *x;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ }
+ }
+
+ *_s = s;
+ return 1;
+ }
+
+ *ret = NULL;
+ *_s = NULL;
+ return 0;
+}
diff --git a/src/core/cgroup-semantics.h b/src/core/cgroup-semantics.h
new file mode 100644
index 0000000000..4f848f4bb7
--- /dev/null
+++ b/src/core/cgroup-semantics.h
@@ -0,0 +1,43 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ 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/>.
+***/
+
+typedef struct CGroupSemantics CGroupSemantics;
+
+struct CGroupSemantics {
+ const char *controller;
+ const char *name;
+ const char *pretty;
+
+ bool multiple;
+
+ /* This call is used for parsing the pretty value to the actual attribute value */
+ int (*map_pretty)(const CGroupSemantics *semantics, const char *value, char **ret);
+
+ /* Right before writing this attribute the attribute value is converted to a low-level value */
+ int (*map_write)(const CGroupSemantics *semantics, const char *value, char **ret);
+
+ /* If this attribute takes a list, this call can be used to reset the list to empty */
+ int (*reset)(const CGroupSemantics *semantics, const char *group);
+};
+
+int cgroup_semantics_find(const char *controller, const char *name, const char *value, char **ret, const CGroupSemantics **semantics);
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 8fc1731485..83df0f3c9a 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -40,7 +40,7 @@ int cgroup_bonding_realize(CGroupBonding *b) {
assert(b->path);
assert(b->controller);
- r = cg_create(b->controller, b->path);
+ r = cg_create(b->controller, b->path, NULL);
if (r < 0) {
log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
return r;
@@ -110,9 +110,8 @@ void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
cgroup_bonding_trim(b, delete_root);
}
-
int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
const char *path;
int r;
@@ -129,8 +128,6 @@ int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffi
path = b->path;
r = cg_create_and_attach(b->controller, path, pid);
- free(p);
-
if (r < 0)
return r;
@@ -151,6 +148,34 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgr
return 0;
}
+int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) {
+ CGroupBonding *q;
+ int ret = 0;
+
+ LIST_FOREACH(by_unit, q, list) {
+ int r;
+
+ if (q == b)
+ continue;
+
+ if (!q->ours)
+ continue;
+
+ r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false);
+ if (r < 0 && ret == 0)
+ ret = r;
+ }
+
+ return ret;
+}
+
+int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) {
+ assert(b);
+ assert(target);
+
+ return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem);
+}
+
int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
assert(b);
@@ -294,9 +319,10 @@ int cgroup_bonding_is_empty_list(CGroupBonding *first) {
}
int manager_setup_cgroup(Manager *m) {
- char *current = NULL, *path = NULL;
+ _cleanup_free_ char *current = NULL, *path = NULL;
+ char suffix_buffer[sizeof("/systemd-") + DECIMAL_STR_MAX(pid_t)];
+ const char *suffix;
int r;
- char suffix[32];
assert(m);
@@ -307,17 +333,17 @@ int manager_setup_cgroup(Manager *m) {
}
/* 1. Determine hierarchy */
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
if (r < 0) {
log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
- goto finish;
+ return r;
}
if (m->running_as == SYSTEMD_SYSTEM)
- strcpy(suffix, "/system");
+ suffix = "/system";
else {
- snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
- char_array_0(suffix);
+ sprintf(suffix_buffer, "/systemd-%lu", (unsigned long) getpid());
+ suffix = suffix_buffer;
}
free(m->cgroup_hierarchy);
@@ -325,39 +351,42 @@ int manager_setup_cgroup(Manager *m) {
/* We probably got reexecuted and can continue to use our root cgroup */
m->cgroup_hierarchy = current;
current = NULL;
-
} else {
/* We need a new root cgroup */
- m->cgroup_hierarchy = NULL;
- if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0) {
- r = log_oom();
- goto finish;
- }
+ if (streq(current, "/"))
+ m->cgroup_hierarchy = strdup(suffix);
+ else
+ m->cgroup_hierarchy = strappend(current, suffix);
+
+ if (!m->cgroup_hierarchy)
+ return log_oom();
}
/* 2. Show data */
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
if (r < 0) {
log_error("Cannot find cgroup mount point: %s", strerror(-r));
- goto finish;
+ return r;
}
log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
/* 3. Install agent */
- r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
- if (r < 0)
- log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
- else if (r > 0)
- log_debug("Installed release agent.");
- else
- log_debug("Release agent already installed.");
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
+ if (r < 0)
+ log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
+ else if (r > 0)
+ log_debug("Installed release agent.");
+ else
+ log_debug("Release agent already installed.");
+ }
/* 4. Realize the group */
r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0);
if (r < 0) {
log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
- goto finish;
+ return r;
}
/* 5. And pin it, so that it cannot be unmounted */
@@ -367,19 +396,21 @@ int manager_setup_cgroup(Manager *m) {
m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
if (r < 0) {
log_error("Failed to open pin file: %m");
- r = -errno;
- goto finish;
+ return -errno;
}
- log_debug("Created root group.");
-
+ /* 6. Remove non-existing controllers from the default controllers list */
cg_shorten_controllers(m->default_controllers);
-finish:
- free(current);
- free(path);
+ /* 7. Let's create the user and machine hierarchies
+ * right-away, so that people can inotify on them, if they
+ * wish, without this being racy. */
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../user");
+ cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../machine");
+ }
- return r;
+ return 0;
}
void manager_shutdown_cgroup(Manager *m, bool delete) {
@@ -411,7 +442,7 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding)
return 1;
}
- p = strdup(cgroup);
+ p = strdupa(cgroup);
if (!p)
return -ENOMEM;
@@ -419,8 +450,7 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding)
char *e;
e = strrchr(p, '/');
- if (!e || e == p) {
- free(p);
+ if (e == p || !e) {
*bonding = NULL;
return 0;
}
@@ -429,7 +459,6 @@ int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding)
b = hashmap_get(m->cgroup_bondings, p);
if (b) {
- free(p);
*bonding = b;
return 1;
}
@@ -484,7 +513,7 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
if (pid <= 1)
return NULL;
- if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
+ if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
return NULL;
l = hashmap_get(m->cgroup_bondings, group);
@@ -520,7 +549,8 @@ Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
CGroupBonding *b;
- assert(controller);
+ if (!controller)
+ controller = SYSTEMD_CGROUP_CONTROLLER;
LIST_FOREACH(by_unit, b, first)
if (streq(b->controller, controller))
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 229da52ba4..6555d89e37 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -58,6 +58,9 @@ void cgroup_bonding_free_list(CGroupBonding *first, bool trim);
int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *suffix);
int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *suffix);
+int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list);
+int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem);
+
int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
@@ -73,7 +76,7 @@ void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root);
int cgroup_bonding_is_empty(CGroupBonding *b);
int cgroup_bonding_is_empty_list(CGroupBonding *first);
-CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller);
+CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) _pure_;
char *cgroup_bonding_to_string(CGroupBonding *b);
diff --git a/src/core/condition.c b/src/core/condition.c
index b3184922b8..16cae6d23b 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -36,6 +36,7 @@
#include "condition.h"
#include "virt.h"
#include "path-util.h"
+#include "fileio.h"
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
Condition *c;
@@ -161,6 +162,10 @@ static bool test_security(const char *parameter) {
if (streq(parameter, "selinux"))
return is_selinux_enabled() > 0;
#endif
+ if (streq(parameter, "apparmor"))
+ return access("/sys/kernel/security/apparmor/", F_OK) == 0;
+ if (streq(parameter, "smack"))
+ return access("/sys/fs/smackfs", F_OK) == 0;
return false;
}
diff --git a/src/core/condition.h b/src/core/condition.h
index 1797385d26..50ed955af9 100644
--- a/src/core/condition.h
+++ b/src/core/condition.h
@@ -66,5 +66,5 @@ bool condition_test_list(Condition *c);
void condition_dump(Condition *c, FILE *f, const char *prefix);
void condition_dump_list(Condition *c, FILE *f, const char *prefix);
-const char* condition_type_to_string(ConditionType t);
-int condition_type_from_string(const char *s);
+const char* condition_type_to_string(ConditionType t) _const_;
+int condition_type_from_string(const char *s) _pure_;
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index e815cb58e4..2a8a0e1ac5 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -29,6 +29,7 @@
#include "strv.h"
#include "dbus-common.h"
#include "syscall-list.h"
+#include "fileio.h"
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput);
DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput);
@@ -159,12 +160,12 @@ int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *proper
if (c->cpu_sched_set)
n = c->cpu_sched_priority;
else {
- struct sched_param p;
- n = 0;
+ struct sched_param p = {};
- zero(p);
if (sched_getparam(0, &p) >= 0)
n = p.sched_priority;
+ else
+ n = 0;
}
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_INT32, &n))
@@ -278,9 +279,8 @@ int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *d
if (c->rlimit[r])
u = (uint64_t) c->rlimit[r]->rlim_max;
else {
- struct rlimit rl;
+ struct rlimit rl = {};
- zero(rl);
getrlimit(r, &rl);
u = (uint64_t) rl.rlim_max;
diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h
index eaa1b73e69..91d70e535f 100644
--- a/src/core/dbus-execute.h
+++ b/src/core/dbus-execute.h
@@ -37,6 +37,7 @@
#define BUS_EXEC_CONTEXT_INTERFACE \
" <property name=\"Environment\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"EnvironmentFiles\" type=\"a(sb)\" access=\"read\"/>\n" \
" <property name=\"UMask\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"LimitCPU\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"LimitFSIZE\" type=\"t\" access=\"read\"/>\n" \
@@ -88,13 +89,11 @@
" <property name=\"InaccessibleDirectories\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"MountFlags\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"PrivateTmp\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \
" <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"NoNewPrivileges\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SystemCallFilter\" type=\"au\" access=\"read\"/>\n"
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index fdc1dce177..98ccfa62ec 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -101,12 +101,11 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
+ job_finish_and_invalidate(j, JOB_CANCELED, true);
reply = dbus_message_new_method_return(message);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- job_finish_and_invalidate(j, JOB_CANCELED, true);
} else {
const BusBoundProperties bps[] = {
{ "org.freedesktop.systemd1.Job", bus_job_properties, j },
@@ -114,11 +113,10 @@ static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connec
};
SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
-
return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
}
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -187,7 +185,7 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
free(introspection);
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -220,7 +218,7 @@ const DBusObjectPathVTable bus_job_vtable = {
};
static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
- DBusMessage *m = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
int r;
assert(j);
@@ -229,9 +227,9 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
if (bus_has_subscriber(j->manager) || j->forgot_bus_clients) {
m = new_message(j);
if (!m)
- goto oom;
+ return -ENOMEM;
+
r = bus_broadcast(j->manager, m);
- dbus_message_unref(m);
if (r < 0)
return r;
@@ -240,18 +238,19 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
* to the client(s) which created the job */
JobBusClient *cl;
assert(j->bus_client_list);
+
LIST_FOREACH(client, cl, j->bus_client_list) {
assert(cl->bus);
m = new_message(j);
if (!m)
- goto oom;
+ return -ENOMEM;
if (!dbus_message_set_destination(m, cl->name))
- goto oom;
+ return -ENOMEM;
if (!dbus_connection_send(cl->bus, m, NULL))
- goto oom;
+ return -ENOMEM;
dbus_message_unref(m);
m = NULL;
@@ -259,10 +258,6 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
}
return 0;
-oom:
- if (m)
- dbus_message_unref(m);
- return -ENOMEM;
}
static DBusMessage* new_change_signal_message(Job *j) {
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 859fa1a906..56b02a1cf5 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -36,6 +36,7 @@
#include "path-util.h"
#include "dbus-unit.h"
#include "virt.h"
+#include "env-util.h"
#define BUS_MANAGER_INTERFACE_BEGIN \
" <interface name=\"org.freedesktop.systemd1.Manager\">\n"
@@ -102,10 +103,39 @@
" <method name=\"ResetFailedUnit\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
+ " <method name=\"SetUnitControlGroup\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetUnitControlGroup\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
+ " </method>\n" \
+ " <method name=\"GetUnitControlGroupAttribute\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetUnitControlGroupAttribute\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
+ " </method>\n" \
+ " <method name=\"UnsetUnitControlGroupAttributes\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"GetJob\">\n" \
" <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"CancelJob\">\n" \
+ " <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"ClearJobs\"/>\n" \
" <method name=\"ResetFailed\"/>\n" \
" <method name=\"ListUnits\">\n" \
@@ -124,6 +154,9 @@
" <arg name=\"cleanup\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"unit\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"RemoveSnapshot\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"Reload\"/>\n" \
" <method name=\"Reexecute\"/>\n" \
" <method name=\"Exit\"/>\n" \
@@ -257,8 +290,8 @@
" <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"RuntimeWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
- " <property name=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
+ " <property name=\"RuntimeWatchdogUSec\" type=\"t\" access=\"readwrite\"/>\n" \
+ " <property name=\"ShutdownWatchdogUSec\" type=\"t\" access=\"readwrite\"/>\n" \
" <property name=\"Virtualization\" type=\"s\" access=\"read\"/>\n"
#define BUS_MANAGER_INTERFACE_END \
@@ -774,10 +807,33 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "CancelJob")) {
+ uint32_t id;
+ Job *j;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_UINT32, &id,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ j = manager_get_job(m, id);
+ if (!j) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "Job %u does not exist.", (unsigned) id);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
+ job_finish_and_invalidate(j, JOB_CANCELED, true);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
SELINUX_ACCESS_CHECK(connection, message, "reboot");
-
manager_clear_jobs(m);
reply = dbus_message_new_method_return(message);
@@ -819,6 +875,151 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroup")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ r = bus_unit_cgroup_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroup")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ r = bus_unit_cgroup_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttribute")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ r = bus_unit_cgroup_attribute_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttribute")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ r = bus_unit_cgroup_attribute_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttribute")) {
+ const char *name;
+ Unit *u;
+ DBusMessageIter iter;
+ _cleanup_strv_free_ char **list = NULL;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
+ r = bus_unit_cgroup_attribute_get(u, &iter, &list);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (bus_append_strv_iter(&iter, list) < 0)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
DBusMessageIter iter, sub;
Iterator i;
@@ -985,11 +1186,9 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!client)
goto oom;
- r = set_put(s, client);
- if (r < 0) {
- free(client);
+ r = set_consume(s, client);
+ if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
- }
reply = dbus_message_new_method_return(message);
if (!reply)
@@ -1080,6 +1279,35 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "RemoveSnapshot")) {
+ const char *name;
+ Unit *u;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ if (u->type != UNIT_SNAPSHOT) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+ snapshot_remove(SNAPSHOT(u));
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
char *introspection = NULL;
FILE *f;
@@ -1251,7 +1479,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SwitchRoot")) {
const char *switch_root, *switch_root_init;
char *u, *v;
- int k;
+ bool good;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1275,20 +1503,24 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
/* Safety check */
- if (isempty(switch_root_init))
- k = access(switch_root, F_OK);
+ if (isempty(switch_root_init)) {
+ good = path_is_os_tree(switch_root);
+ if (!good)
+ log_error("Not switching root: %s does not seem to be an OS tree. /etc/os-release is missing.", switch_root);
+ }
else {
- char *p;
+ _cleanup_free_ char *p = NULL;
p = strjoin(switch_root, "/", switch_root_init, NULL);
if (!p)
goto oom;
- k = access(p, X_OK);
- free(p);
+ good = access(p, X_OK) >= 0;
+ if (!good)
+ log_error("Not switching root: cannot execute new init %s", p);
}
- if (k < 0)
- return bus_send_error_reply(connection, message, NULL, -errno);
+ if (!good)
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
u = strdup(switch_root);
if (!u)
@@ -1315,7 +1547,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
m->exit_code = MANAGER_SWITCH_ROOT;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
- char **l = NULL, **e = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ char **e = NULL;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1324,9 +1557,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
+ if (!strv_env_is_valid(l))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
e = strv_env_merge(2, m->environment, l);
- strv_free(l);
if (!e)
goto oom;
@@ -1340,7 +1574,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
m->environment = e;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
- char **l = NULL, **e = NULL;
+ _cleanup_strv_free_ char **l = NULL;
+ char **e = NULL;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1349,10 +1584,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
+ if (!strv_env_name_or_assignment_is_valid(l))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
e = strv_env_delete(m->environment, 1, l);
- strv_free(l);
-
if (!e)
goto oom;
@@ -1366,7 +1601,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
m->environment = e;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
- char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
+ _cleanup_strv_free_ char **l_set = NULL, **l_unset = NULL, **e = NULL;
+ char **f = NULL;
DBusMessageIter iter;
SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1379,33 +1615,25 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
+ if (!strv_env_name_or_assignment_is_valid(l_unset))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
- if (!dbus_message_iter_next(&iter)) {
- strv_free(l_unset);
+ if (!dbus_message_iter_next(&iter))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
- }
r = bus_parse_strv_iter(&iter, &l_set);
- if (r < 0) {
- strv_free(l_unset);
- if (r == -ENOMEM)
- goto oom;
-
+ if (r == -ENOMEM)
+ goto oom;
+ if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
- }
+ if (!strv_env_is_valid(l_set))
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
e = strv_env_delete(m->environment, 1, l_unset);
- strv_free(l_unset);
-
- if (!e) {
- strv_free(l_set);
+ if (!e)
goto oom;
- }
f = strv_env_merge(2, e, l_set);
- strv_free(l_set);
- strv_free(e);
-
if (!f)
goto oom;
@@ -1668,7 +1896,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
if (reply)
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index d81edeb807..0fcceb500d 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -40,6 +40,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
@@ -159,6 +160,7 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess
{ "org.freedesktop.systemd1.Mount", bus_mount_properties, m },
{ "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context },
{ "org.freedesktop.systemd1.Mount", bus_kill_context_properties, &m->kill_context },
+ { "org.freedesktop.systemd1.Mount", bus_unit_cgroup_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index f7fed1754d..1e62083d9b 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -84,15 +84,15 @@ static int bus_path_append_paths(DBusMessageIter *i, const char *property, void
}
static int bus_path_append_unit(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- Path *p = PATH(u);
+ Unit *u = data, *trigger;
const char *t;
assert(i);
assert(property);
assert(u);
- t = UNIT_DEREF(p->unit) ? UNIT_DEREF(p->unit)->id : "";
+ trigger = UNIT_TRIGGER(u);
+ t = trigger ? trigger->id : "";
return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
}
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index d99058dd46..e06a5dce97 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -50,6 +50,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \
@@ -152,6 +153,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio
{ "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context },
{ "org.freedesktop.systemd1.Service", bus_kill_context_properties, &s->kill_context },
{ "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status },
+ { "org.freedesktop.systemd1.Service", bus_unit_cgroup_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c
index 435c6df39c..2ae8574f59 100644
--- a/src/core/dbus-snapshot.c
+++ b/src/core/dbus-snapshot.c
@@ -54,19 +54,16 @@ static const BusProperty bus_snapshot_properties[] = {
DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
Snapshot *s = SNAPSHOT(u);
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
- DBusError error;
-
- dbus_error_init(&error);
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Snapshot", "Remove")) {
SELINUX_UNIT_ACCESS_CHECK(u, c, message, "stop");
- snapshot_remove(SNAPSHOT(u));
-
reply = dbus_message_new_method_return(message);
if (!reply)
- goto oom;
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ snapshot_remove(SNAPSHOT(u));
} else {
const BusBoundProperties bps[] = {
@@ -80,15 +77,8 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusM
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
- if (reply) {
- if (!dbus_connection_send(c, reply, NULL))
- goto oom;
- }
+ if (!bus_maybe_send_reply(c, message, reply))
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
return DBUS_HANDLER_RESULT_HANDLED;
-
-oom:
- dbus_error_free(&error);
-
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 095a031612..77d98ea0fd 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -39,6 +39,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
@@ -62,6 +63,7 @@
" <property name=\"NConnections\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \
" <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \
+ " <property name=\"Listen\" type=\"a(ss)\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SmackLabel\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SmackLabelIPIn\" type=\"s\" access=\"read\"/>\n" \
@@ -97,6 +99,66 @@ const char bus_socket_invalidating_properties[] =
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_socket_append_socket_result, socket_result, SocketResult);
+static int bus_socket_append_listen(DBusMessageIter *i, const char *property, void *data) {
+
+ Socket *s = SOCKET(data);
+ SocketPort *p;
+ DBusMessageIter array, stru;
+
+ assert(data);
+ assert(property);
+ assert(s);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &array))
+ return log_oom();
+
+ LIST_FOREACH(port, p, s->ports) {
+ const char *type = socket_port_type_to_string(p);
+ _cleanup_free_ char *address = NULL;
+ const char *a;
+
+ if (!dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, NULL, &stru))
+ return log_oom();
+
+ if (!dbus_message_iter_append_basic(&stru, DBUS_TYPE_STRING, &type))
+ return log_oom();
+
+ switch (p->type) {
+ case SOCKET_SOCKET: {
+ int r;
+
+ r = socket_address_print(&p->address, &address);
+ if (r) {
+ log_error("socket_address_print failed: %s", strerror(-r));
+ return r;
+ }
+ a = address;
+ break;
+ }
+
+ case SOCKET_SPECIAL:
+ case SOCKET_MQUEUE:
+ case SOCKET_FIFO:
+ a = p->path;
+ break;
+
+ default:
+ a = type;
+ }
+
+ if (!dbus_message_iter_append_basic(&stru, DBUS_TYPE_STRING, &a))
+ return -ENOMEM;
+
+ if (!dbus_message_iter_close_container(&array, &stru))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &array))
+ return -ENOMEM;
+
+ return 0;
+}
+
static const BusProperty bus_socket_properties[] = {
{ "BindIPv6Only", bus_socket_append_bind_ipv6_only, "s", offsetof(Socket, bind_ipv6_only) },
{ "Backlog", bus_property_append_unsigned, "u", offsetof(Socket, backlog) },
@@ -122,6 +184,7 @@ static const BusProperty bus_socket_properties[] = {
{ "Broadcast", bus_property_append_bool, "b", offsetof(Socket, broadcast) },
{ "PassCredentials",bus_property_append_bool, "b", offsetof(Socket, pass_cred) },
{ "PassSecurity", bus_property_append_bool, "b", offsetof(Socket, pass_sec) },
+ { "Listen", bus_socket_append_listen, "a(ss)", 0, },
{ "Mark", bus_property_append_int, "i", offsetof(Socket, mark) },
{ "MaxConnections", bus_property_append_unsigned, "u", offsetof(Socket, max_connections) },
{ "NConnections", bus_property_append_unsigned, "u", offsetof(Socket, n_connections) },
@@ -142,6 +205,7 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMes
{ "org.freedesktop.systemd1.Socket", bus_socket_properties, s },
{ "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context },
{ "org.freedesktop.systemd1.Socket", bus_kill_context_properties, &s->kill_context },
+ { "org.freedesktop.systemd1.Socket", bus_unit_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index 67ea0f24fe..2e99fba7db 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -38,6 +38,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
+ BUS_UNIT_CGROUP_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
@@ -106,6 +107,7 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessa
{ "org.freedesktop.systemd1.Swap", bus_swap_properties, s },
{ "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context },
{ "org.freedesktop.systemd1.Swap", bus_kill_context_properties, &s->kill_context },
+ { "org.freedesktop.systemd1.Swap", bus_unit_cgroup_properties, u },
{ NULL, }
};
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index 11d18cbd83..4082f7f9b9 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -30,8 +30,10 @@
#define BUS_TIMER_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Timer\">\n" \
" <property name=\"Unit\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Timers\" type=\"a(stt)\" access=\"read\"/>\n" \
- " <property name=\"NextElapseUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"TimersMonotonic\" type=\"a(stt)\" access=\"read\"/>\n" \
+ " <property name=\"TimersCalendar\" type=\"a(sst)\" access=\"read\"/>\n" \
+ " <property name=\"NextElapseUSecRealtime\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"NextElapseUSecMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
@@ -52,11 +54,13 @@
const char bus_timer_interface[] _introspect_("Timer") = BUS_TIMER_INTERFACE;
const char bus_timer_invalidating_properties[] =
- "Timers\0"
- "NextElapseUSec\0"
+ "TimersMonotonic\0"
+ "TimersRealtime\0"
+ "NextElapseUSecRealtime\0"
+ "NextElapseUSecMonotonic\0"
"Result\0";
-static int bus_timer_append_timers(DBusMessageIter *i, const char *property, void *data) {
+static int bus_timer_append_monotonic_timers(DBusMessageIter *i, const char *property, void *data) {
Timer *p = data;
DBusMessageIter sub, sub2;
TimerValue *k;
@@ -69,11 +73,14 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi
return -ENOMEM;
LIST_FOREACH(value, k, p->values) {
- char *buf;
+ _cleanup_free_ char *buf = NULL;
const char *t;
size_t l;
bool b;
+ if (k->base == TIMER_CALENDAR)
+ continue;
+
t = timer_base_to_string(k->base);
assert(endswith(t, "Sec"));
@@ -92,7 +99,48 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi
dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) &&
dbus_message_iter_close_container(&sub, &sub2);
- free(buf);
+ if (!b)
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_timer_append_calendar_timers(DBusMessageIter *i, const char *property, void *data) {
+ Timer *p = data;
+ DBusMessageIter sub, sub2;
+ TimerValue *k;
+
+ assert(i);
+ assert(property);
+ assert(p);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sst)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(value, k, p->values) {
+ _cleanup_free_ char *buf = NULL;
+ const char *t;
+ bool b;
+ int j;
+
+ if (k->base != TIMER_CALENDAR)
+ continue;
+
+ t = timer_base_to_string(k->base);
+ j = calendar_spec_to_string(k->calendar_spec, &buf);
+ if (j < 0)
+ return j;
+
+ b = dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &t) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &buf) &&
+ dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &k->next_elapse) &&
+ dbus_message_iter_close_container(&sub, &sub2);
+
if (!b)
return -ENOMEM;
}
@@ -104,15 +152,15 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi
}
static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- Timer *timer = TIMER(u);
+ Unit *u = data, *trigger;
const char *t;
assert(i);
assert(property);
assert(u);
- t = UNIT_DEREF(timer->unit) ? UNIT_DEREF(timer->unit)->id : "";
+ trigger = UNIT_TRIGGER(u);
+ t = trigger ? trigger->id : "";
return dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t) ? 0 : -ENOMEM;
}
@@ -120,11 +168,12 @@ static int bus_timer_append_unit(DBusMessageIter *i, const char *property, void
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_result, TimerResult);
static const BusProperty bus_timer_properties[] = {
- { "Unit", bus_timer_append_unit, "s", 0 },
- { "Timers", bus_timer_append_timers, "a(stt)", 0 },
- { "NextElapseUSec", bus_property_append_usec, "t", offsetof(Timer, next_elapse_monotonic) },
- { "NextElapseUSecRealtime", bus_property_append_usec, "t", offsetof(Timer, next_elapse_realtime) },
- { "Result", bus_timer_append_timer_result,"s", offsetof(Timer, result) },
+ { "Unit", bus_timer_append_unit, "s", 0 },
+ { "TimersMonotonic", bus_timer_append_monotonic_timers, "a(stt)", 0 },
+ { "TimersCalendar", bus_timer_append_calendar_timers, "a(sst)", 0 },
+ { "NextElapseUSecMonotonic", bus_property_append_usec, "t", offsetof(Timer, next_elapse_monotonic) },
+ { "NextElapseUSecRealtime", bus_property_append_usec, "t", offsetof(Timer, next_elapse_realtime) },
+ { "Result", bus_timer_append_timer_result, "s", offsetof(Timer, result) },
{ NULL, }
};
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 8433a720b2..575f8eb36a 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -27,6 +27,10 @@
#include "bus-errors.h"
#include "dbus-common.h"
#include "selinux-access.h"
+#include "cgroup-util.h"
+#include "strv.h"
+#include "path-util.h"
+#include "fileio.h"
const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
@@ -310,7 +314,7 @@ static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, voi
return -ENOMEM;
LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
bool success;
t = cgroup_bonding_to_string(cgb);
@@ -337,11 +341,11 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property
return -ENOMEM;
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char _cleanup_free_ *v = NULL;
+ _cleanup_free_ char *v = NULL;
bool success;
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write)
+ a->semantics->map_write(a->semantics, a->value, &v);
success =
dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
@@ -468,6 +472,90 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
+ } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+ } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
+ DBusMessageIter iter;
+ _cleanup_strv_free_ char **list = NULL;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_attribute_get(u, &iter, &list);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (bus_append_strv_iter(&iter, list) < 0)
+ goto oom;
+
+ } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_attribute_set(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
+ DBusMessageIter iter;
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ r = bus_unit_cgroup_attribute_unset(u, &iter);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
} else if (UNIT_VTABLE(u)->bus_message_handler)
return UNIT_VTABLE(u)->bus_message_handler(u, connection, message);
else
@@ -494,7 +582,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
}
if (reply)
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -585,7 +673,7 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
free(introspection);
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -798,7 +886,7 @@ DBusHandlerResult bus_unit_queue_job(
DBUS_TYPE_INVALID))
goto oom;
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
return DBUS_HANDLER_RESULT_HANDLED;
@@ -809,6 +897,360 @@ oom:
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
+static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
+ const char *mode;
+ int r;
+
+ assert(iter);
+ assert(runtime);
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
+ if (r < 0)
+ return r;
+
+ if (streq(mode, "runtime"))
+ *runtime = true;
+ else if (streq(mode, "persistent"))
+ *runtime = false;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
+ _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
+ const char *name;
+ CGroupBonding *b;
+ bool runtime;
+ int r;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ r = cg_split_spec(name, &controller, &new_path);
+ if (r < 0)
+ return r;
+
+ if (!new_path) {
+ new_path = unit_default_cgroup_path(u);
+ if (!new_path)
+ return -ENOMEM;
+ }
+
+ if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return -EINVAL;
+
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ if (streq(b->path, new_path))
+ return 0;
+
+ if (b->essential)
+ return -EINVAL;
+
+ old_path = strdup(b->path);
+ if (!old_path)
+ return -ENOMEM;
+ }
+
+ r = unit_add_cgroup_from_text(u, name, true, &b);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ CGroupAttribute *a;
+
+ /* Try to move things to the new place, and clean up the old place */
+ cgroup_bonding_realize(b);
+ cgroup_bonding_migrate(b, u->cgroup_bondings);
+
+ if (old_path)
+ cg_trim(controller, old_path, true);
+
+ /* Apply the attributes to the new group */
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes)
+ if (streq(a->controller, controller))
+ cgroup_attribute_apply(a, b);
+ }
+
+ contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
+ "ControlGroup=", name, "\n", NULL);
+ if (!contents)
+ return -ENOMEM;
+
+ return unit_write_drop_in(u, runtime, controller, contents);
+}
+
+int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
+ _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
+ const char *name;
+ CGroupAttribute *a, *n;
+ CGroupBonding *b;
+ bool runtime;
+ int r;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ r = cg_split_spec(name, &controller, &path);
+ if (r < 0)
+ return r;
+
+ if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return -EINVAL;
+
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (!b)
+ return -ENOENT;
+
+ if (path && !path_equal(path, b->path))
+ return -ENOENT;
+
+ if (b->essential)
+ return -EINVAL;
+
+ unit_remove_drop_in(u, runtime, controller);
+
+ /* Try to migrate the old group away */
+ if (cg_pid_get_path(controller, 0, &target) >= 0)
+ cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
+
+ cgroup_bonding_free(b, true);
+
+ /* Drop all attributes of this controller */
+ LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
+ if (!streq(a->controller, controller))
+ continue;
+
+ unit_remove_drop_in(u, runtime, a->name);
+ cgroup_attribute_free(a);
+ }
+
+ return 0;
+}
+
+int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
+ _cleanup_free_ char *controller = NULL;
+ CGroupAttribute *a;
+ CGroupBonding *b;
+ const char *name;
+ char **l = NULL;
+ int r;
+
+ assert(u);
+ assert(iter);
+ assert(_result);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
+ if (r < 0)
+ return r;
+
+ r = cg_controller_from_attr(name, &controller);
+ if (r < 0)
+ return r;
+
+ /* First attempt, read the value from the kernel */
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ _cleanup_free_ char *p = NULL, *v = NULL;
+
+ r = cg_get_path(b->controller, b->path, name, &p);
+ if (r < 0)
+ return r;
+
+ r = read_full_file(p, &v, NULL);
+ if (r >= 0) {
+ /* Split on new lines */
+ l = strv_split_newlines(v);
+ if (!l)
+ return -ENOMEM;
+
+ *_result = l;
+ return 0;
+
+ }
+ }
+
+ /* If that didn't work, read our cached value */
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+
+ if (!cgroup_attribute_matches(a, controller, name))
+ continue;
+
+ r = strv_extend(&l, a->value);
+ if (r < 0) {
+ strv_free(l);
+ return r;
+ }
+ }
+
+ if (!l)
+ return -ENOENT;
+
+ *_result = l;
+ return 0;
+}
+
+static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
+ _cleanup_free_ char *buf = NULL;
+ CGroupAttribute *a;
+
+ assert(u);
+ assert(name);
+
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+ if (!cgroup_attribute_matches(a, NULL, name))
+ continue;
+
+ if (!buf) {
+ buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
+ "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
+
+ if (!buf)
+ return -ENOMEM;
+ } else {
+ char *b;
+
+ b = strjoin(buf,
+ "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
+
+ if (!b)
+ return -ENOMEM;
+
+ free(buf);
+ buf = b;
+ }
+ }
+
+ if (buf)
+ return unit_write_drop_in(u, runtime, name, buf);
+ else
+ return unit_remove_drop_in(u, runtime, name);
+}
+
+int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
+ _cleanup_strv_free_ char **l = NULL;
+ int r;
+ bool runtime = false;
+ char **value;
+ const char *name;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = bus_parse_strv_iter(iter, &l);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_next(iter))
+ return -EINVAL;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(value, l) {
+ _cleanup_free_ char *v = NULL;
+ CGroupAttribute *a;
+ const CGroupSemantics *s;
+
+ r = cgroup_semantics_find(NULL, name, *value, &v, &s);
+ if (r < 0)
+ return r;
+
+ if (s && !s->multiple && l[1])
+ return -EINVAL;
+
+ r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
+ if (r < 0)
+ return r;
+
+ if (r > 0) {
+ CGroupBonding *b;
+
+ b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
+ if (!b) {
+ /* Doesn't exist yet? Then let's add it */
+ r = unit_add_cgroup_from_text(u, a->controller, false, &b);
+ if (r < 0)
+ return r;
+
+ if (r > 0) {
+ cgroup_bonding_realize(b);
+ cgroup_bonding_migrate(b, u->cgroup_bondings);
+ }
+ }
+
+ /* Make it count */
+ cgroup_attribute_apply(a, u->cgroup_bondings);
+ }
+
+ }
+
+ r = update_attribute_drop_in(u, runtime, name);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
+ const char *name;
+ bool runtime;
+ int r;
+
+ assert(u);
+ assert(iter);
+
+ if (!unit_get_exec_context(u))
+ return -EINVAL;
+
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = parse_mode(iter, &runtime, false);
+ if (r < 0)
+ return r;
+
+ cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
+ update_attribute_drop_in(u, runtime, name);
+
+ return 0;
+}
+
const BusProperty bus_unit_properties[] = {
{ "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
{ "Names", bus_unit_append_names, "as", 0 },
@@ -842,6 +1284,7 @@ const BusProperty bus_unit_properties[] = {
{ "SubState", bus_unit_append_sub_state, "s", 0 },
{ "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
{ "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
+ { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
{ "UnitFileState", bus_unit_append_file_state, "s", 0 },
{ "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
{ "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
@@ -864,9 +1307,6 @@ const BusProperty bus_unit_properties[] = {
{ "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
{ "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
{ "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
- { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
- { "ControlGroup", bus_unit_append_cgroups, "as", 0 },
- { "ControlGroupAttributes", bus_unit_append_cgroup_attrs,"a(sss)", 0 },
{ "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
{ "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
{ "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
@@ -875,3 +1315,10 @@ const BusProperty bus_unit_properties[] = {
{ "LoadError", bus_unit_append_load_error, "(ss)", 0 },
{ NULL, }
};
+
+const BusProperty bus_unit_cgroup_properties[] = {
+ { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
+ { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
+ { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },
+ { NULL, }
+};
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index ac6785a949..acd1ddbe78 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -69,11 +69,13 @@
" <property name=\"Requisite\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Wants\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"BindsTo\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"BindsTo\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"PartOf\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"WantedBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"BoundBy\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ConsistsOf\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Conflicts\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ConflictedBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \
@@ -86,6 +88,7 @@
" <property name=\"RequiresMountsFor\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SourcePath\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DropInPaths\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Documentation\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \
@@ -113,9 +116,6 @@
" <property name=\"OnFailureIsolate\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IgnoreOnIsolate\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IgnoreOnSnapshot\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ControlGroup\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
" <property name=\"NeedDaemonReload\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"JobTimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \
@@ -124,16 +124,42 @@
" <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
" </interface>\n"
+#define BUS_UNIT_CGROUP_INTERFACE \
+ " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
+ " <method name=\"SetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"UnsetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n"
+
#define BUS_UNIT_INTERFACES_LIST \
BUS_GENERIC_INTERFACES_LIST \
"org.freedesktop.systemd1.Unit\0"
extern const BusProperty bus_unit_properties[];
+extern const BusProperty bus_unit_cgroup_properties[];
void bus_unit_send_change_signal(Unit *u);
void bus_unit_send_removed_signal(Unit *u);
-
DBusHandlerResult bus_unit_queue_job(
DBusConnection *connection,
DBusMessage *message,
@@ -142,6 +168,12 @@ DBusHandlerResult bus_unit_queue_job(
JobMode mode,
bool reload_if_possible);
+int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter);
+int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter);
+int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result);
+int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter);
+int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter);
+
extern const DBusObjectPathVTable bus_unit_vtable;
extern const char bus_unit_interface[];
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 2a1c66054a..1272c938cf 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -48,7 +48,7 @@
#include "special.h"
#include "dbus-common.h"
-#define CONNECTIONS_MAX 52
+#define CONNECTIONS_MAX 512
/* Well-known address (http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-types) */
#define DBUS_SYSTEM_BUS_DEFAULT_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
@@ -203,13 +203,11 @@ static void bus_toggle_watch(DBusWatch *bus_watch, void *data) {
}
static int bus_timeout_arm(Manager *m, Watch *w) {
- struct itimerspec its;
+ struct itimerspec its = {};
assert(m);
assert(w);
- zero(its);
-
if (dbus_timeout_get_enabled(w->data.bus_timeout)) {
timespec_store(&its.it_value, dbus_timeout_get_interval(w->data.bus_timeout) * USEC_PER_MSEC);
its.it_interval = its.it_value;
@@ -365,8 +363,8 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus
log_debug("Got D-Bus activation request for %s", name);
- if (manager_unit_pending_inactive(m, SPECIAL_DBUS_SERVICE) ||
- manager_unit_pending_inactive(m, SPECIAL_DBUS_SOCKET)) {
+ if (manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SERVICE) ||
+ manager_unit_inactive_or_pending(m, SPECIAL_DBUS_SOCKET)) {
r = -EADDRNOTAVAIL;
dbus_set_error(&error, BUS_ERROR_SHUTTING_DOWN, "Refusing activation, D-Bus is shutting down.");
} else {
@@ -406,7 +404,7 @@ static DBusHandlerResult api_bus_message_filter(DBusConnection *connection, DBus
dbus_error_free(&error);
if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
+ if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
dbus_message_unref(reply);
@@ -978,9 +976,8 @@ static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type)
}
return connection;
+
fail:
- if (connection)
- dbus_connection_close(connection);
dbus_error_free(&error);
return NULL;
}
@@ -1054,7 +1051,7 @@ fail:
static int bus_init_private(Manager *m) {
DBusError error;
int r;
- const char *const external_only[] = {
+ static const char *const external_only[] = {
"EXTERNAL",
NULL
};
@@ -1077,18 +1074,33 @@ static int bus_init_private(Manager *m) {
} else {
const char *e;
char *p;
+ char *escaped;
e = secure_getenv("XDG_RUNTIME_DIR");
if (!e)
return 0;
- if (asprintf(&p, "unix:path=%s/systemd/private", e) < 0) {
+ if (asprintf(&p, "%s/systemd/private", e) < 0) {
+ r = log_oom();
+ goto fail;
+ }
+
+ mkdir_parents_label(p, 0755);
+ unlink(p);
+ free(p);
+
+ escaped = dbus_address_escape_value(e);
+ if (!escaped) {
+ r = log_oom();
+ goto fail;
+ }
+ if (asprintf(&p, "unix:path=%s/systemd/private", escaped) < 0) {
+ dbus_free(escaped);
r = log_oom();
goto fail;
}
+ dbus_free(escaped);
- mkdir_parents_label(p+10, 0755);
- unlink(p+10);
m->private_bus = dbus_server_listen(p, &error);
free(p);
}
diff --git a/src/core/device.c b/src/core/device.c
index 0b01718ad4..9fca82ab16 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -124,13 +124,13 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) {
prefix, strna(d->sysfs));
}
-static UnitActiveState device_active_state(Unit *u) {
+_pure_ static UnitActiveState device_active_state(Unit *u) {
assert(u);
return state_translation_table[DEVICE(u)->state];
}
-static const char *device_sub_state_to_string(Unit *u) {
+_pure_ static const char *device_sub_state_to_string(Unit *u) {
assert(u);
return device_state_to_string(DEVICE(u)->state);
@@ -281,16 +281,22 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
size_t l;
FOREACH_WORD_QUOTED(w, l, wants, state) {
- char *e;
+ char *e, *n;
e = strndup(w, l);
if (!e) {
r = -ENOMEM;
goto fail;
}
-
- r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
+ n = unit_name_mangle(e);
+ if (!n) {
+ r = -ENOMEM;
+ goto fail;
+ }
free(e);
+
+ r = unit_add_dependency_by_name(u, UNIT_WANTS, n, NULL, true);
+ free(n);
if (r < 0)
goto fail;
}
@@ -312,6 +318,7 @@ fail:
static int device_process_new_device(Manager *m, struct udev_device *dev, bool update_state) {
const char *sysfs, *dn;
struct udev_list_entry *item = NULL, *first = NULL;
+ int r;
assert(m);
@@ -319,7 +326,9 @@ static int device_process_new_device(Manager *m, struct udev_device *dev, bool u
return -ENOMEM;
/* Add the main unit named after the sysfs path */
- device_update_unit(m, dev, sysfs, true);
+ r = device_update_unit(m, dev, sysfs, true);
+ if (r < 0)
+ return r;
/* Add an additional unit for the device node */
if ((dn = udev_device_get_devnode(dev)))
@@ -477,7 +486,6 @@ static void device_shutdown(Manager *m) {
}
static int device_enumerate(Manager *m) {
- struct epoll_event ev;
int r;
struct udev_enumerate *e = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
@@ -485,6 +493,8 @@ static int device_enumerate(Manager *m) {
assert(m);
if (!m->udev) {
+ struct epoll_event ev;
+
if (!(m->udev = udev_new()))
return -ENOMEM;
diff --git a/src/core/device.h b/src/core/device.h
index 3c4604f60e..41e8de9703 100644
--- a/src/core/device.h
+++ b/src/core/device.h
@@ -52,5 +52,5 @@ extern const UnitVTable device_vtable;
void device_fd_event(Manager *m, int events);
-const char* device_state_to_string(DeviceState i);
-DeviceState device_state_from_string(const char *s);
+const char* device_state_to_string(DeviceState i) _const_;
+DeviceState device_state_from_string(const char *s) _pure_;
diff --git a/src/core/execute.c b/src/core/execute.c
index 7dc15044b4..3959ef9623 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -40,6 +40,7 @@
#include <sys/poll.h>
#include <linux/seccomp-bpf.h>
#include <glob.h>
+#include <libgen.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
@@ -64,6 +65,8 @@
#include "loopback-setup.h"
#include "path-util.h"
#include "syscall-list.h"
+#include "env-util.h"
+#include "fileio.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
@@ -141,7 +144,7 @@ static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
return 0;
}
-static const char *tty_path(const ExecContext *context) {
+_pure_ static const char *tty_path(const ExecContext *context) {
assert(context);
if (context->tty_path)
@@ -163,6 +166,26 @@ void exec_context_tty_reset(const ExecContext *context) {
vt_disallocate(context->tty_path);
}
+static bool is_terminal_output(ExecOutput o) {
+ return
+ o == EXEC_OUTPUT_TTY ||
+ o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
+ o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
+ o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+}
+
+void exec_context_serialize(const ExecContext *context, Unit *u, FILE *f) {
+ assert(context);
+ assert(u);
+ assert(f);
+
+ if (context->tmp_dir)
+ unit_serialize_item(u, f, "tmp-dir", context->tmp_dir);
+
+ if (context->var_tmp_dir)
+ unit_serialize_item(u, f, "var-tmp-dir", context->var_tmp_dir);
+}
+
static int open_null_as(int flags, int nfd) {
int fd, r;
@@ -182,7 +205,10 @@ static int open_null_as(int flags, int nfd) {
static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) {
int fd, r;
- union sockaddr_union sa;
+ union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/journal/stdout",
+ };
assert(context);
assert(output < _EXEC_OUTPUT_MAX);
@@ -193,10 +219,6 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
if (fd < 0)
return -errno;
- zero(sa);
- sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
-
r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
if (r < 0) {
close_nointr_nofail(fd);
@@ -222,7 +244,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
!!context->syslog_level_prefix,
output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
- output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
+ is_terminal_output(output));
if (fd != nfd) {
r = dup2(fd, nfd) < 0 ? -errno : nfd;
@@ -318,9 +340,10 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
}
}
-static int setup_output(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
+static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
ExecOutput o;
ExecInput i;
+ int r;
assert(context);
assert(ident);
@@ -328,91 +351,55 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i
i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
o = fixup_output(context->std_output, socket_fd);
- /* This expects the input is already set up */
+ if (fileno == STDERR_FILENO) {
+ ExecOutput e;
+ e = fixup_output(context->std_error, socket_fd);
- switch (o) {
+ /* This expects the input and output are already set up */
+
+ /* Don't change the stderr file descriptor if we inherit all
+ * the way and are not on a tty */
+ if (e == EXEC_OUTPUT_INHERIT &&
+ o == EXEC_OUTPUT_INHERIT &&
+ i == EXEC_INPUT_NULL &&
+ !is_terminal_input(context->std_input) &&
+ getppid () != 1)
+ return fileno;
- case EXEC_OUTPUT_INHERIT:
+ /* Duplicate from stdout if possible */
+ if (e == o || e == EXEC_OUTPUT_INHERIT)
+ return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
+ o = e;
+
+ } else if (o == EXEC_OUTPUT_INHERIT) {
/* If input got downgraded, inherit the original value */
if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
- return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
+ return open_terminal_as(tty_path(context), O_WRONLY, fileno);
/* If the input is connected to anything that's not a /dev/null, inherit that... */
if (i != EXEC_INPUT_NULL)
- return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+ return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
/* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
if (getppid() != 1)
- return STDOUT_FILENO;
-
- /* We need to open /dev/null here anew, to get the
- * right access mode. So we fall through */
-
- case EXEC_OUTPUT_NULL:
- return open_null_as(O_WRONLY, STDOUT_FILENO);
-
- case EXEC_OUTPUT_TTY:
- if (is_terminal_input(i))
- return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+ return fileno;
- /* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
-
- case EXEC_OUTPUT_SYSLOG:
- case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
- case EXEC_OUTPUT_KMSG:
- case EXEC_OUTPUT_KMSG_AND_CONSOLE:
- case EXEC_OUTPUT_JOURNAL:
- case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- return connect_logger_as(context, o, ident, unit_id, STDOUT_FILENO);
-
- case EXEC_OUTPUT_SOCKET:
- assert(socket_fd >= 0);
- return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
-
- default:
- assert_not_reached("Unknown output type");
+ /* We need to open /dev/null here anew, to get the right access mode. */
+ return open_null_as(O_WRONLY, fileno);
}
-}
-
-static int setup_error(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
- ExecOutput o, e;
- ExecInput i;
-
- assert(context);
- assert(ident);
-
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
- o = fixup_output(context->std_output, socket_fd);
- e = fixup_output(context->std_error, socket_fd);
-
- /* This expects the input and output are already set up */
- /* Don't change the stderr file descriptor if we inherit all
- * the way and are not on a tty */
- if (e == EXEC_OUTPUT_INHERIT &&
- o == EXEC_OUTPUT_INHERIT &&
- i == EXEC_INPUT_NULL &&
- !is_terminal_input(context->std_input) &&
- getppid () != 1)
- return STDERR_FILENO;
-
- /* Duplicate from stdout if possible */
- if (e == o || e == EXEC_OUTPUT_INHERIT)
- return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
-
- switch (e) {
+ switch (o) {
case EXEC_OUTPUT_NULL:
- return open_null_as(O_WRONLY, STDERR_FILENO);
+ return open_null_as(O_WRONLY, fileno);
case EXEC_OUTPUT_TTY:
if (is_terminal_input(i))
- return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+ return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
/* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
+ return open_terminal_as(tty_path(context), O_WRONLY, fileno);
case EXEC_OUTPUT_SYSLOG:
case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
@@ -420,11 +407,21 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id
case EXEC_OUTPUT_KMSG_AND_CONSOLE:
case EXEC_OUTPUT_JOURNAL:
case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- return connect_logger_as(context, e, ident, unit_id, STDERR_FILENO);
+ r = connect_logger_as(context, o, ident, unit_id, fileno);
+ if (r < 0) {
+ log_struct_unit(LOG_CRIT, unit_id,
+ "MESSAGE=Failed to connect std%s of %s to the journal socket: %s",
+ fileno == STDOUT_FILENO ? "out" : "err",
+ unit_id, strerror(-r),
+ "ERRNO=%d", -r,
+ NULL);
+ r = open_null_as(O_WRONLY, fileno);
+ }
+ return r;
case EXEC_OUTPUT_SOCKET:
assert(socket_fd >= 0);
- return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+ return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
default:
assert_not_reached("Unknown error type");
@@ -512,7 +509,7 @@ fail:
return r;
}
-static int write_confirm_message(const char *format, ...) {
+_printf_attr_(1, 2) static int write_confirm_message(const char *format, ...) {
int fd;
va_list ap;
@@ -673,9 +670,9 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
/* First step: If we need to keep capabilities but
* drop privileges we need to make sure we keep our
- * caps, whiel we drop privileges. */
+ * caps, while we drop privileges. */
if (uid != 0) {
- int sb = context->secure_bits|SECURE_KEEP_CAPS;
+ int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
if (prctl(PR_GET_SECUREBITS) != sb)
if (prctl(PR_SET_SECUREBITS, sb) < 0)
@@ -940,7 +937,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
int i;
unsigned n;
struct sock_filter *f;
- struct sock_fprog prog;
+ struct sock_fprog prog = {};
assert(syscall_filter);
@@ -957,7 +954,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
for (i = 0, n = 0; i < syscall_max(); i++)
if (syscall_filter[i >> 4] & (1 << (i & 31))) {
struct sock_filter item[] = {
- BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, i, 0, 1),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, INDEX_TO_SYSCALL(i), 0, 1),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
};
@@ -972,7 +969,6 @@ static int apply_seccomp(uint32_t *syscall_filter) {
memcpy(f + (ELEMENTSOF(header) + 2*n), footer, sizeof(footer));
/* Third: install the filter */
- zero(prog);
prog.len = ELEMENTSOF(header) + ELEMENTSOF(footer) + 2*n;
prog.filter = f;
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0)
@@ -983,7 +979,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
int exec_spawn(ExecCommand *command,
char **argv,
- const ExecContext *context,
+ ExecContext *context,
int fds[], unsigned n_fds,
char **environment,
bool apply_permissions,
@@ -1001,7 +997,7 @@ int exec_spawn(ExecCommand *command,
int r;
char *line;
int socket_fd;
- char _cleanup_strv_free_ **files_env = NULL;
+ _cleanup_strv_free_ char **files_env = NULL;
assert(command);
assert(context);
@@ -1024,8 +1020,8 @@ int exec_spawn(ExecCommand *command,
r = exec_context_load_environment(context, &files_env);
if (r < 0) {
- log_struct(LOG_ERR,
- "UNIT=%s", unit_id,
+ log_struct_unit(LOG_ERR,
+ unit_id,
"MESSAGE=Failed to load environment files: %s", strerror(-r),
"ERRNO=%d", -r,
NULL);
@@ -1039,18 +1035,30 @@ int exec_spawn(ExecCommand *command,
if (!line)
return log_oom();
- log_struct(LOG_DEBUG,
- "UNIT=%s", unit_id,
- "MESSAGE=About to execute %s", line,
- NULL);
+ log_struct_unit(LOG_DEBUG,
+ unit_id,
+ "EXECUTABLE=%s", command->path,
+ "MESSAGE=About to execute: %s", line,
+ NULL);
free(line);
r = cgroup_bonding_realize_list(cgroup_bondings);
if (r < 0)
return r;
+ /* We must initialize the attributes in the parent, before we
+ fork, because we really need them initialized before making
+ the process a member of the group (which we do in both the
+ child and the parent), and we cannot really apply them twice
+ (due to 'append' style attributes) */
cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
+ if (context->private_tmp && !context->tmp_dir && !context->var_tmp_dir) {
+ r = setup_tmpdirs(&context->tmp_dir, &context->var_tmp_dir);
+ if (r < 0)
+ return r;
+ }
+
pid = fork();
if (pid < 0)
return -errno;
@@ -1061,7 +1069,7 @@ int exec_spawn(ExecCommand *command,
const char *username = NULL, *home = NULL;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
- char _cleanup_strv_free_ **our_env = NULL, **pam_env = NULL,
+ _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL,
**final_env = NULL, **final_argv = NULL;
unsigned n_env = 0;
bool set_access = false;
@@ -1165,13 +1173,13 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
- err = setup_output(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+ err = setup_output(context, STDOUT_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
if (err < 0) {
r = EXIT_STDOUT;
goto fail_child;
}
- err = setup_error(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+ err = setup_output(context, STDERR_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
if (err < 0) {
r = EXIT_STDERR;
goto fail_child;
@@ -1191,7 +1199,7 @@ int exec_spawn(ExecCommand *command,
snprintf(t, sizeof(t), "%i", context->oom_score_adjust);
char_array_0(t);
- if (write_one_line_file("/proc/self/oom_score_adj", t) < 0) {
+ if (write_string_file("/proc/self/oom_score_adj", t) < 0) {
err = -errno;
r = EXIT_OOM_ADJUST;
goto fail_child;
@@ -1206,13 +1214,16 @@ int exec_spawn(ExecCommand *command,
}
if (context->cpu_sched_set) {
- struct sched_param param;
-
- zero(param);
- param.sched_priority = context->cpu_sched_priority;
+ struct sched_param param = {
+ .sched_priority = context->cpu_sched_priority,
+ };
- if (sched_setscheduler(0, context->cpu_sched_policy |
- (context->cpu_sched_reset_on_fork ? SCHED_RESET_ON_FORK : 0), &param) < 0) {
+ r = sched_setscheduler(0,
+ context->cpu_sched_policy |
+ (context->cpu_sched_reset_on_fork ?
+ SCHED_RESET_ON_FORK : 0),
+ &param);
+ if (r < 0) {
err = -errno;
r = EXIT_SETSCHEDULER;
goto fail_child;
@@ -1262,7 +1273,12 @@ int exec_spawn(ExecCommand *command,
if (cgroup_bondings && context->control_group_modify) {
err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid);
if (err >= 0)
- err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent);
+ err = cgroup_bonding_set_task_access_list(
+ cgroup_bondings,
+ 0644,
+ uid,
+ gid,
+ context->control_group_persistent);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
@@ -1273,7 +1289,12 @@ int exec_spawn(ExecCommand *command,
}
if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) {
- err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent);
+ err = cgroup_bonding_set_task_access_list(
+ cgroup_bondings,
+ (mode_t) -1,
+ (uid_t) -1,
+ (uid_t) -1,
+ context->control_group_persistent);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
@@ -1317,6 +1338,8 @@ int exec_spawn(ExecCommand *command,
err = setup_namespace(context->read_write_dirs,
context->read_only_dirs,
context->inaccessible_dirs,
+ context->tmp_dir,
+ context->var_tmp_dir,
context->private_tmp,
context->mount_flags);
if (err < 0) {
@@ -1339,7 +1362,7 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
} else {
- char _cleanup_free_ *d = NULL;
+ _cleanup_free_ char *d = NULL;
if (asprintf(&d, "%s/%s",
context->root_directory ? context->root_directory : "",
@@ -1431,7 +1454,8 @@ int exec_spawn(ExecCommand *command,
}
}
- if (!(our_env = new0(char*, 7))) {
+ our_env = new0(char*, 7);
+ if (!our_env) {
err = -ENOMEM;
r = EXIT_MEMORY;
goto fail_child;
@@ -1471,20 +1495,21 @@ int exec_spawn(ExecCommand *command,
assert(n_env <= 7);
- if (!(final_env = strv_env_merge(
- 5,
- environment,
- our_env,
- context->environment,
- files_env,
- pam_env,
- NULL))) {
+ final_env = strv_env_merge(5,
+ environment,
+ our_env,
+ context->environment,
+ files_env,
+ pam_env,
+ NULL);
+ if (!final_env) {
err = -ENOMEM;
r = EXIT_MEMORY;
goto fail_child;
}
- if (!(final_argv = replace_env_argv(argv, final_env))) {
+ final_argv = replace_env_argv(argv, final_env);
+ if (!final_argv) {
err = -ENOMEM;
r = EXIT_MEMORY;
goto fail_child;
@@ -1492,6 +1517,20 @@ int exec_spawn(ExecCommand *command,
final_env = strv_env_clean(final_env);
+ if (_unlikely_(log_get_max_level() >= LOG_PRI(LOG_DEBUG))) {
+ line = exec_command_line(final_argv);
+ if (line) {
+ log_open();
+ log_struct_unit(LOG_DEBUG,
+ unit_id,
+ "EXECUTABLE=%s", command->path,
+ "MESSAGE=Executing: %s", line,
+ NULL);
+ log_close();
+ free(line);
+ line = NULL;
+ }
+ }
execve(command->path, final_argv, final_env);
err = -errno;
r = EXIT_EXEC;
@@ -1512,19 +1551,18 @@ int exec_spawn(ExecCommand *command,
_exit(r);
}
- log_struct(LOG_DEBUG,
- "UNIT=%s", unit_id,
- "MESSAGE=Forked %s as %lu",
- command->path, (unsigned long) pid,
- NULL);
+ log_struct_unit(LOG_DEBUG,
+ unit_id,
+ "MESSAGE=Forked %s as %lu",
+ command->path, (unsigned long) pid,
+ NULL);
/* We add the new process to the cgroup both in the child (so
* that we can be sure that no user code is ever executed
* outside of the cgroup) and in the parent (so that we can be
* sure that when we kill the cgroup the process will be
* killed too). */
- if (cgroup_bondings)
- cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix);
+ cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix);
exec_status_start(&command->exec_status, pid);
@@ -1545,7 +1583,35 @@ void exec_context_init(ExecContext *c) {
c->timer_slack_nsec = (nsec_t) -1;
}
-void exec_context_done(ExecContext *c) {
+void exec_context_tmp_dirs_done(ExecContext *c) {
+ char* dirs[] = {c->tmp_dir ? c->tmp_dir : c->var_tmp_dir,
+ c->tmp_dir ? c->var_tmp_dir : NULL,
+ NULL};
+ char **dirp;
+
+ for(dirp = dirs; *dirp; dirp++) {
+ char *dir;
+ int r;
+
+ r = rm_rf_dangerous(*dirp, false, true, false);
+ dir = dirname(*dirp);
+ if (r < 0)
+ log_warning("Failed to remove content of temporary directory %s: %s",
+ dir, strerror(-r));
+ else {
+ r = rmdir(dir);
+ if (r < 0)
+ log_warning("Failed to remove temporary directory %s: %s",
+ dir, strerror(-r));
+ }
+
+ free(*dirp);
+ }
+
+ c->tmp_dir = c->var_tmp_dir = NULL;
+}
+
+void exec_context_done(ExecContext *c, bool reloading_or_reexecuting) {
unsigned l;
assert(c);
@@ -1609,6 +1675,9 @@ void exec_context_done(ExecContext *c) {
free(c->syscall_filter);
c->syscall_filter = NULL;
+
+ if (!reloading_or_reexecuting)
+ exec_context_tmp_dirs_done(c);
}
void exec_command_done(ExecCommand *c) {
@@ -1658,7 +1727,7 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
int k;
bool ignore = false;
char **p;
- glob_t pglob;
+ _cleanup_globfree_ glob_t pglob = {};
int count, n;
fn = *i;
@@ -1669,7 +1738,6 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
}
if (!path_is_absolute(fn)) {
-
if (ignore)
continue;
@@ -1678,10 +1746,8 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
}
/* Filename supports globbing, take all matching files */
- zero(pglob);
errno = 0;
if (glob(fn, 0, NULL, &pglob) != 0) {
- globfree(&pglob);
if (ignore)
continue;
@@ -1690,7 +1756,6 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
}
count = pglob.gl_pathc;
if (count == 0) {
- globfree(&pglob);
if (ignore)
continue;
@@ -1698,15 +1763,17 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
return -EINVAL;
}
for (n = 0; n < count; n++) {
- k = load_env_file(pglob.gl_pathv[n], &p);
+ k = load_env_file(pglob.gl_pathv[n], NULL, &p);
if (k < 0) {
if (ignore)
continue;
strv_free(r);
- globfree(&pglob);
return k;
}
+ /* Log invalid environment variables with filename */
+ if (p)
+ p = strv_env_clean_log(p, pglob.gl_pathv[n]);
if (r == NULL)
r = p;
@@ -1716,16 +1783,12 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
m = strv_env_merge(2, r, p);
strv_free(r);
strv_free(p);
-
- if (!m) {
- globfree(&pglob);
+ if (!m)
return -ENOMEM;
- }
r = m;
}
}
- globfree(&pglob);
}
*l = r;
@@ -1733,6 +1796,37 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
return 0;
}
+static bool tty_may_match_dev_console(const char *tty) {
+ char *active = NULL, *console;
+ bool b;
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ /* trivial identity? */
+ if (streq(tty, "console"))
+ return true;
+
+ console = resolve_dev_console(&active);
+ /* if we could not resolve, assume it may */
+ if (!console)
+ return true;
+
+ /* "tty0" means the active VC, so it may be the same sometimes */
+ b = streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty));
+ free(active);
+
+ return b;
+}
+
+bool exec_context_may_touch_console(ExecContext *ec) {
+ return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate ||
+ is_terminal_input(ec->std_input) ||
+ is_terminal_output(ec->std_output) ||
+ is_terminal_output(ec->std_error)) &&
+ tty_may_match_dev_console(tty_path(ec));
+}
+
static void strv_fprintf(FILE *f, char **l) {
char **g;
@@ -1827,7 +1921,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, c->cpu_sched_priority,
prefix, yes_no(c->cpu_sched_reset_on_fork));
free(policy_str);
- }
+ }
if (c->cpuset) {
fprintf(f, "%sCPUAffinity:", prefix);
@@ -1895,12 +1989,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->secure_bits)
fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
prefix,
- (c->secure_bits & SECURE_KEEP_CAPS) ? " keep-caps" : "",
- (c->secure_bits & SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
- (c->secure_bits & SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
- (c->secure_bits & SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
- (c->secure_bits & SECURE_NOROOT) ? " noroot" : "",
- (c->secure_bits & SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
+ (c->secure_bits & 1<<SECURE_KEEP_CAPS) ? " keep-caps" : "",
+ (c->secure_bits & 1<<SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
+ (c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
+ (c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
+ (c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
+ (c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
if (c->capability_bounding_set_drop) {
unsigned long l;
diff --git a/src/core/execute.h b/src/core/execute.h
index 2bcd2e1e6c..15574dc97e 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -36,6 +36,8 @@ typedef struct ExecContext ExecContext;
struct CGroupBonding;
struct CGroupAttribute;
+typedef struct Unit Unit;
+
#include "list.h"
#include "util.h"
@@ -141,6 +143,8 @@ struct ExecContext {
bool non_blocking;
bool private_tmp;
bool private_network;
+ char *tmp_dir;
+ char *var_tmp_dir;
bool no_new_privileges;
@@ -164,7 +168,7 @@ struct ExecContext {
int exec_spawn(ExecCommand *command,
char **argv,
- const ExecContext *context,
+ ExecContext *context,
int fds[], unsigned n_fds,
char **environment,
bool apply_permissions,
@@ -192,18 +196,22 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e);
int exec_command_set(ExecCommand *c, const char *path, ...);
void exec_context_init(ExecContext *c);
-void exec_context_done(ExecContext *c);
+void exec_context_done(ExecContext *c, bool reloading_or_reexecuting);
+void exec_context_tmp_dirs_done(ExecContext *c);
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
void exec_context_tty_reset(const ExecContext *context);
int exec_context_load_environment(const ExecContext *c, char ***l);
+bool exec_context_may_touch_console(ExecContext *c);
+void exec_context_serialize(const ExecContext *c, Unit *u, FILE *f);
+
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
-const char* exec_output_to_string(ExecOutput i);
-ExecOutput exec_output_from_string(const char *s);
+const char* exec_output_to_string(ExecOutput i) _const_;
+ExecOutput exec_output_from_string(const char *s) _pure_;
-const char* exec_input_to_string(ExecInput i);
-ExecInput exec_input_from_string(const char *s);
+const char* exec_input_to_string(ExecInput i) _const_;
+ExecInput exec_input_from_string(const char *s) _pure_;
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 7894f8a5f2..8aa1cff1d3 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -29,6 +29,7 @@
#include "macro.h"
#include "util.h"
#include "log.h"
+#include "fileio.h"
static int read_and_strip_hostname(const char *path, char **hn) {
char *s;
@@ -41,7 +42,7 @@ static int read_and_strip_hostname(const char *path, char **hn) {
if (r < 0)
return r;
- hostname_cleanup(s);
+ hostname_cleanup(s, false);
if (isempty(s)) {
free(s);
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index e8cc1ba8b6..7f8ec23d58 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -50,11 +50,6 @@ int ima_setup(void) {
int policyfd = -1, imafd = -1;
int result = 0;
-#ifndef HAVE_SELINUX
- /* Mount the securityfs filesystem */
- mount_setup_early();
-#endif
-
if (stat(IMA_POLICY_PATH, &st) < 0)
return 0;
diff --git a/src/core/job.c b/src/core/job.c
index 5ff95f5c90..d304a16d06 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -34,6 +34,9 @@
#include "load-dropin.h"
#include "log.h"
#include "dbus-job.h"
+#include "special.h"
+#include "sync.h"
+#include "virt.h"
JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
JobBusClient *cl;
@@ -166,6 +169,8 @@ static void job_merge_into_installed(Job *j, Job *other) {
assert(other->type == JOB_NOP);
j->override = j->override || other->override;
+ j->irreversible = j->irreversible || other->irreversible;
+ j->ignore_order = j->ignore_order || other->ignore_order;
}
Job* job_install(Job *j) {
@@ -201,6 +206,7 @@ Job* job_install(Job *j) {
"Merged into running job, re-running: %s/%s as %u",
uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
uj->state = JOB_WAITING;
+ uj->manager->n_running_jobs--;
return uj;
}
}
@@ -290,11 +296,13 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
"%s-> Job %u:\n"
"%s\tAction: %s -> %s\n"
"%s\tState: %s\n"
- "%s\tForced: %s\n",
+ "%s\tForced: %s\n"
+ "%s\tIrreversible: %s\n",
prefix, j->id,
prefix, j->unit->id, job_type_to_string(j->type),
prefix, job_state_to_string(j->state),
- prefix, yes_no(j->override));
+ prefix, yes_no(j->override),
+ prefix, yes_no(j->irreversible));
}
/*
@@ -476,7 +484,7 @@ static void job_change_type(Job *j, JobType newtype) {
int job_run_and_invalidate(Job *j) {
int r;
uint32_t id;
- Manager *m;
+ Manager *m = j->manager;
assert(j);
assert(j->installed);
@@ -493,6 +501,7 @@ int job_run_and_invalidate(Job *j) {
return -EAGAIN;
j->state = JOB_RUNNING;
+ m->n_running_jobs++;
job_add_to_dbus_queue(j);
/* While we execute this operation the job might go away (for
@@ -501,7 +510,6 @@ int job_run_and_invalidate(Job *j) {
* store the id here, so that we can verify the job is still
* valid. */
id = j->id;
- m = j->manager;
switch (j->type) {
@@ -551,16 +559,17 @@ int job_run_and_invalidate(Job *j) {
r = job_finish_and_invalidate(j, JOB_DONE, true);
else if (r == -ENOEXEC)
r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
- else if (r == -EAGAIN)
+ else if (r == -EAGAIN) {
j->state = JOB_WAITING;
- else if (r < 0)
+ m->n_running_jobs--;
+ } else if (r < 0)
r = job_finish_and_invalidate(j, JOB_FAILED, true);
}
return r;
}
-static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
+_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
const UnitStatusMessageFormats *format_table;
assert(u);
@@ -579,7 +588,7 @@ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult r
return NULL;
}
-static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
+_pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
const char *format;
assert(u);
@@ -635,20 +644,20 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
case JOB_DONE:
if (u->condition_result)
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
- unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
+ manager_status_printf(u->manager, false, NULL, "See 'systemctl status %s' for details.", u->id);
break;
case JOB_DEPENDENCY:
- unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
break;
default:
@@ -664,12 +673,12 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
switch (result) {
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_DONE:
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
default:
@@ -682,7 +691,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
* Most likely a DEPEND warning from a requisiting unit will
* occur next and it's nice to see what was requisited. */
if (result == JOB_SKIPPED)
- unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.");
}
}
@@ -713,25 +722,25 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
sd_id128_t mid;
mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(mid),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
} else if (t == JOB_STOP)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
else if (t == JOB_RELOAD)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR,
+ u->id,
MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
- "UNIT=%s", u->id,
"RESULT=%s", job_result_to_string(result),
"MESSAGE=%s", buf,
NULL);
@@ -753,6 +762,9 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
j->result = result;
+ if (j->state == JOB_RUNNING)
+ j->manager->n_running_jobs--;
+
log_debug_unit(u->id, "Job %s/%s finished, result=%s",
u->id, job_type_to_string(t), job_result_to_string(result));
@@ -818,19 +830,21 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
* this context. And JOB_FAILURE is already handled by the
* unit itself. */
if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
- log_struct(LOG_NOTICE,
- "UNIT=%s", u->id,
+ log_struct_unit(LOG_NOTICE,
+ u->id,
"JOB_TYPE=%s", job_type_to_string(t),
- "JOB_RESULT=%s", job_result_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(result),
"Job %s/%s failed with result '%s'.",
u->id,
job_type_to_string(t),
job_result_to_string(result),
NULL);
- unit_trigger_on_failure(u);
+ unit_start_on_failure(u);
}
+ unit_trigger_notify(u);
+
finish:
/* Try to start the next jobs that can be started */
SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
@@ -846,10 +860,12 @@ finish:
}
int job_start_timer(Job *j) {
- struct itimerspec its;
- struct epoll_event ev;
+ struct itimerspec its = {};
+ struct epoll_event ev = {
+ .data.ptr = &j->timer_watch,
+ .events = EPOLLIN,
+ };
int fd, r;
- assert(j);
if (j->unit->job_timeout <= 0 ||
j->timer_watch.type == WATCH_JOB_TIMER)
@@ -862,7 +878,6 @@ int job_start_timer(Job *j) {
goto fail;
}
- zero(its);
timespec_store(&its.it_value, j->unit->job_timeout);
if (timerfd_settime(fd, 0, &its, NULL) < 0) {
@@ -870,10 +885,6 @@ int job_start_timer(Job *j) {
goto fail;
}
- zero(ev);
- ev.data.ptr = &j->timer_watch;
- ev.events = EPOLLIN;
-
if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
r = -errno;
goto fail;
@@ -943,6 +954,7 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
fprintf(f, "job-override=%s\n", yes_no(j->override));
+ fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
/* Cannot save bus clients. Just note the fact that we're losing
@@ -1010,6 +1022,12 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
log_debug("Failed to parse job override flag %s", v);
else
j->override = j->override || b;
+ } else if (streq(l, "job-irreversible")) {
+ int b = parse_boolean(v);
+ if (b < 0)
+ log_debug("Failed to parse job irreversible flag %s", v);
+ else
+ j->irreversible = j->irreversible || b;
} else if (streq(l, "job-sent-dbus-new-signal")) {
int b = parse_boolean(v);
if (b < 0)
@@ -1045,21 +1063,43 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
}
int job_coldplug(Job *j) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .data.ptr = &j->timer_watch,
+ .events = EPOLLIN,
+ };
if (j->timer_watch.type != WATCH_JOB_TIMER)
return 0;
- zero(ev);
- ev.data.ptr = &j->timer_watch;
- ev.events = EPOLLIN;
-
if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, j->timer_watch.fd, &ev) < 0)
return -errno;
return 0;
}
+void job_shutdown_magic(Job *j) {
+ assert(j);
+
+ /* The shutdown target gets some special treatment here: we
+ * tell the kernel to begin with flushing its disk caches, to
+ * optimize shutdown time a bit. Ideally we wouldn't hardcode
+ * this magic into PID 1. However all other processes aren't
+ * options either since they'd exit much sooner than PID 1 and
+ * asynchronous sync() would cause their exit to be
+ * delayed. */
+
+ if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
+ return;
+
+ if (j->type != JOB_START)
+ return;
+
+ if (detect_container(NULL) > 0)
+ return;
+
+ asynchronous_sync();
+}
+
static const char* const job_state_table[_JOB_STATE_MAX] = {
[JOB_WAITING] = "waiting",
[JOB_RUNNING] = "running"
@@ -1083,6 +1123,7 @@ DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
+ [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
[JOB_ISOLATE] = "isolate",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"
diff --git a/src/core/job.h b/src/core/job.h
index 3aa49d4b46..d90bc96b76 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -83,6 +83,7 @@ enum JobState {
enum JobMode {
JOB_FAIL, /* Fail if a conflicting job is already queued */
JOB_REPLACE, /* Replace an existing conflicting job */
+ JOB_REPLACE_IRREVERSIBLY, /* Like JOB_REPLACE + produce irreversible jobs */
JOB_ISOLATE, /* Start a unit, and stop all others */
JOB_IGNORE_DEPENDENCIES, /* Ignore both requirement and ordering dependencies */
JOB_IGNORE_REQUIREMENTS, /* Ignore requirement dependencies */
@@ -161,6 +162,7 @@ struct Job {
bool sent_dbus_new_signal:1;
bool ignore_order:1;
bool forgot_bus_clients:1;
+ bool irreversible:1;
};
JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name);
@@ -181,22 +183,22 @@ void job_dependency_free(JobDependency *l);
int job_merge(Job *j, Job *other);
-JobType job_type_lookup_merge(JobType a, JobType b);
+JobType job_type_lookup_merge(JobType a, JobType b) _pure_;
-static inline bool job_type_is_mergeable(JobType a, JobType b) {
+_pure_ static inline bool job_type_is_mergeable(JobType a, JobType b) {
return job_type_lookup_merge(a, b) >= 0;
}
-static inline bool job_type_is_conflicting(JobType a, JobType b) {
+_pure_ static inline bool job_type_is_conflicting(JobType a, JobType b) {
return !job_type_is_mergeable(a, b);
}
-static inline bool job_type_is_superset(JobType a, JobType b) {
+_pure_ static inline bool job_type_is_superset(JobType a, JobType b) {
/* Checks whether operation a is a "superset" of b in its actions */
return a == job_type_lookup_merge(a, b);
}
-bool job_type_is_redundant(JobType a, UnitActiveState b);
+bool job_type_is_redundant(JobType a, UnitActiveState b) _pure_;
/* Collapses a state-dependent job type into a simpler type by observing
* the state of the unit which it is going to be applied to. */
@@ -217,14 +219,16 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive);
char *job_dbus_path(Job *j);
-const char* job_type_to_string(JobType t);
-JobType job_type_from_string(const char *s);
+void job_shutdown_magic(Job *j);
-const char* job_state_to_string(JobState t);
-JobState job_state_from_string(const char *s);
+const char* job_type_to_string(JobType t) _const_;
+JobType job_type_from_string(const char *s) _pure_;
-const char* job_mode_to_string(JobMode t);
-JobMode job_mode_from_string(const char *s);
+const char* job_state_to_string(JobState t) _const_;
+JobState job_state_from_string(const char *s) _pure_;
-const char* job_result_to_string(JobResult t);
-JobResult job_result_from_string(const char *s);
+const char* job_mode_to_string(JobMode t) _const_;
+JobMode job_mode_from_string(const char *s) _pure_;
+
+const char* job_result_to_string(JobResult t) _const_;
+JobResult job_result_from_string(const char *s) _pure_;
diff --git a/src/core/kill.h b/src/core/kill.h
index 3c9b0ab8db..71a0513e84 100644
--- a/src/core/kill.h
+++ b/src/core/kill.h
@@ -26,6 +26,8 @@ typedef struct KillContext KillContext;
#include <stdbool.h>
#include <stdio.h>
+#include "macro.h"
+
typedef enum KillMode {
/* The kill mode is a property of a unit. */
KILL_CONTROL_GROUP = 0,
@@ -53,8 +55,8 @@ typedef enum KillWho {
void kill_context_init(KillContext *c);
void kill_context_dump(KillContext *c, FILE *f, const char *prefix);
-const char *kill_mode_to_string(KillMode k);
-KillMode kill_mode_from_string(const char *s);
+const char *kill_mode_to_string(KillMode k) _const_;
+KillMode kill_mode_from_string(const char *s) _pure_;
-const char *kill_who_to_string(KillWho k);
-KillWho kill_who_from_string(const char *s);
+const char *kill_who_to_string(KillWho k) _const_;
+KillWho kill_who_from_string(const char *s) _pure_;
diff --git a/src/core/killall.c b/src/core/killall.c
index 55200ffa48..a0f57455fb 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -22,16 +22,17 @@
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
+#include <unistd.h>
#include "util.h"
#include "def.h"
#include "killall.h"
+#include "set.h"
-#define TIMEOUT_USEC (5 * USEC_PER_SEC)
+#define TIMEOUT_USEC (10 * USEC_PER_SEC)
static bool ignore_proc(pid_t pid) {
- char buf[PATH_MAX];
- FILE *f;
+ _cleanup_fclose_ FILE *f = NULL;
char c;
size_t count;
uid_t uid;
@@ -49,15 +50,11 @@ static bool ignore_proc(pid_t pid) {
if (uid != 0)
return false;
- snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid);
- char_array_0(buf);
-
- f = fopen(buf, "re");
+ f = fopen(procfs_file_alloca(pid, "cmdline"), "re");
if (!f)
return true; /* not really, but has the desired effect */
count = fread(&c, 1, 1, f);
- fclose(f);
/* Kernel threads have an empty cmdline */
if (count <= 0)
@@ -73,38 +70,68 @@ static bool ignore_proc(pid_t pid) {
return false;
}
-static void wait_for_children(int n_processes, sigset_t *mask) {
+static void wait_for_children(Set *pids, sigset_t *mask) {
usec_t until;
assert(mask);
+ if (set_isempty(pids))
+ return;
+
until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
for (;;) {
struct timespec ts;
int k;
usec_t n;
+ void *p;
+ Iterator i;
+ /* First, let the kernel inform us about killed
+ * children. Most processes will probably be our
+ * children, but some are not (might be our
+ * grandchildren instead...). */
for (;;) {
- pid_t pid = waitpid(-1, NULL, WNOHANG);
+ pid_t pid;
+ pid = waitpid(-1, NULL, WNOHANG);
if (pid == 0)
break;
+ if (pid < 0) {
+ if (errno == ECHILD)
+ break;
- if (pid < 0 && errno == ECHILD)
+ log_error("waitpid() failed: %m");
return;
+ }
+
+ set_remove(pids, ULONG_TO_PTR(pid));
+ }
- if (n_processes > 0)
- if (--n_processes == 0)
- return;
+ /* Now explicitly check who might be remaining, who
+ * might not be our child. */
+ SET_FOREACH(p, pids, i) {
+
+ /* We misuse getpgid as a check whether a
+ * process still exists. */
+ if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0)
+ continue;
+
+ if (errno != ESRCH)
+ continue;
+
+ set_remove(pids, p);
}
+ if (set_isempty(pids))
+ return;
+
n = now(CLOCK_MONOTONIC);
if (n >= until)
return;
timespec_store(&ts, until - n);
-
- if ((k = sigtimedwait(mask, NULL, &ts)) != SIGCHLD) {
+ k = sigtimedwait(mask, NULL, &ts);
+ if (k != SIGCHLD) {
if (k < 0 && errno != EAGAIN) {
log_error("sigtimedwait() failed: %m");
@@ -117,10 +144,9 @@ static void wait_for_children(int n_processes, sigset_t *mask) {
}
}
-static int killall(int sig) {
- DIR *dir;
+static int killall(int sig, Set *pids) {
+ _cleanup_closedir_ DIR *dir = NULL;
struct dirent *d;
- unsigned int n_processes = 0;
dir = opendir("/proc");
if (!dir)
@@ -139,20 +165,29 @@ static int killall(int sig) {
if (ignore_proc(pid))
continue;
- if (kill(pid, sig) >= 0)
- n_processes++;
- else if (errno != ENOENT)
+ if (sig == SIGKILL) {
+ _cleanup_free_ char *s;
+
+ get_process_comm(pid, &s);
+ log_notice("Sending SIGKILL to PID %lu (%s).", (unsigned long) pid, strna(s));
+ }
+
+ if (kill(pid, sig) >= 0) {
+ if (pids)
+ set_put(pids, ULONG_TO_PTR((unsigned long) pid));
+ } else if (errno != ENOENT)
log_warning("Could not kill %d: %m", pid);
}
- closedir(dir);
-
- return n_processes;
+ return set_size(pids);
}
void broadcast_signal(int sig, bool wait_for_exit) {
sigset_t mask, oldmask;
- int n_processes;
+ Set *pids = NULL;
+
+ if (wait_for_exit)
+ pids = set_new(trivial_hash_func, trivial_compare_func);
assert_se(sigemptyset(&mask) == 0);
assert_se(sigaddset(&mask, SIGCHLD) == 0);
@@ -161,17 +196,15 @@ void broadcast_signal(int sig, bool wait_for_exit) {
if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
log_warning("kill(-1, SIGSTOP) failed: %m");
- n_processes = killall(sig);
+ killall(sig, pids);
if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
log_warning("kill(-1, SIGCONT) failed: %m");
- if (n_processes <= 0)
- goto finish;
-
if (wait_for_exit)
- wait_for_children(n_processes, &mask);
+ wait_for_children(pids, &mask);
+
+ assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
-finish:
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ set_free(pids);
}
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index 20ab232374..e4885fb212 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -30,66 +30,72 @@
#include "kmod-setup.h"
-typedef struct Kmodule {
- const char *name;
- const char *directory;
- bool (*condition_fn)(void);
-} KModule;
-
-static const KModule kmod_table[] = {
- { "autofs4", "/sys/class/misc/autofs", NULL } ,
- { "ipv6", "/sys/module/ipv6", NULL },
- { "unix", "/proc/net/unix", NULL } ,
-};
-
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-static void systemd_kmod_log(void *data, int priority, const char *file, int line,
- const char *fn, const char *format, va_list args)
-{
+
+static void systemd_kmod_log(
+ void *data,
+ int priority,
+ const char *file, int line,
+ const char *fn,
+ const char *format,
+ va_list args) {
+
/* library logging is enabled at debug only */
log_metav(LOG_DEBUG, file, line, fn, format, args);
}
+
#pragma GCC diagnostic pop
int kmod_setup(void) {
- unsigned i;
+
+ static const char kmod_table[] =
+ /* This one we need to load explicitly, since
+ * auto-loading on use doesn't work before udev
+ * created the ghost device nodes, and we need it
+ * earlier than that. */
+ "autofs4\0" "/sys/class/misc/autofs\0"
+
+ /* This one we need to load explicitly, since
+ * auto-loading of IPv6 is not done when we try to
+ * configure ::1 on the loopback device. */
+ "ipv6\0" "/sys/module/ipv6\0"
+
+ "unix\0" "/proc/net/unix\0";
+
struct kmod_ctx *ctx = NULL;
- struct kmod_module *mod;
- int err;
+ const char *name, *path;
+ int r;
- for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) {
- if (kmod_table[i].condition_fn && !kmod_table[i].condition_fn())
- continue;
+ NULSTR_FOREACH_PAIR(name, path, kmod_table) {
+ struct kmod_module *mod;
- if (access(kmod_table[i].directory, F_OK) >= 0)
+ if (access(path, F_OK) >= 0)
continue;
log_debug("Your kernel apparently lacks built-in %s support. Might be a good idea to compile it in. "
"We'll now try to work around this by loading the module...",
- kmod_table[i].name);
+ name);
if (!ctx) {
ctx = kmod_new(NULL, NULL);
- if (!ctx) {
- log_error("Failed to allocate memory for kmod");
- return -ENOMEM;
- }
+ if (!ctx)
+ return log_oom();
kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
kmod_load_resources(ctx);
}
- err = kmod_module_new_from_name(ctx, kmod_table[i].name, &mod);
- if (err < 0) {
- log_error("Failed to lookup module '%s'", kmod_table[i].name);
+ r = kmod_module_new_from_name(ctx, name, &mod);
+ if (r < 0) {
+ log_error("Failed to lookup module '%s'", name);
continue;
}
- err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
- if (err == 0)
+ r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
+ if (r == 0)
log_info("Inserted module '%s'", kmod_module_get_name(mod));
- else if (err == KMOD_PROBE_APPLY_BLACKLIST)
+ else if (r == KMOD_PROBE_APPLY_BLACKLIST)
log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
else
log_error("Failed to insert module '%s'", kmod_module_get_name(mod));
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 86f81c7484..a877e66098 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -27,51 +27,78 @@
#include "log.h"
#include "strv.h"
#include "unit-name.h"
+#include "conf-parser.h"
+#include "load-fragment.h"
+#include "conf-files.h"
-static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) {
- DIR *d;
- struct dirent *de;
+static int iterate_dir(
+ Unit *u,
+ const char *path,
+ UnitDependency dependency,
+ char ***strv) {
+
+ _cleanup_closedir_ DIR *d = NULL;
int r;
assert(u);
assert(path);
+ /* The config directories are special, since the order of the
+ * drop-ins matters */
+ if (dependency < 0) {
+ r = strv_extend(strv, path);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+ }
+
d = opendir(path);
if (!d) {
-
if (errno == ENOENT)
return 0;
return -errno;
}
- while ((de = readdir(d))) {
- char *f;
+ for (;;) {
+ struct dirent *de;
+ union dirent_storage buf;
+ _cleanup_free_ char *f = NULL;
+ int k;
+
+ k = readdir_r(d, &buf.de, &de);
+ if (k != 0) {
+ log_error("Failed to read directory %s: %s", path, strerror(k));
+ return -k;
+ }
+
+ if (!de)
+ break;
if (ignore_file(de->d_name))
continue;
f = strjoin(path, "/", de->d_name, NULL);
- if (!f) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!f)
+ return log_oom();
r = unit_add_dependency_by_name(u, dependency, de->d_name, f, true);
- free(f);
-
if (r < 0)
log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r));
}
- r = 0;
-
-finish:
- closedir(d);
- return r;
+ return 0;
}
-static int process_dir(Unit *u, const char *unit_path, const char *name, const char *suffix, UnitDependency dependency) {
+static int process_dir(
+ Unit *u,
+ const char *unit_path,
+ const char *name,
+ const char *suffix,
+ UnitDependency dependency,
+ char ***strv) {
+
int r;
char *path;
@@ -82,13 +109,13 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
path = strjoin(unit_path, "/", name, suffix, NULL);
if (!path)
- return -ENOMEM;
+ return log_oom();
if (u->manager->unit_path_cache &&
!set_get(u->manager->unit_path_cache, path))
r = 0;
else
- r = iterate_dir(u, path, dependency);
+ r = iterate_dir(u, path, dependency, strv);
free(path);
if (r < 0)
@@ -100,19 +127,19 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
template = unit_name_template(name);
if (!template)
- return -ENOMEM;
+ return log_oom();
path = strjoin(unit_path, "/", template, suffix, NULL);
free(template);
if (!path)
- return -ENOMEM;
+ return log_oom();
if (u->manager->unit_path_cache &&
!set_get(u->manager->unit_path_cache, path))
r = 0;
else
- r = iterate_dir(u, path, dependency);
+ r = iterate_dir(u, path, dependency, strv);
free(path);
if (r < 0)
@@ -122,9 +149,43 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
return 0;
}
-int unit_load_dropin(Unit *u) {
+char **unit_find_dropin_paths(Unit *u) {
+ _cleanup_strv_free_ char **strv = NULL;
+ char **configs = NULL;
Iterator i;
char *t;
+ int r;
+
+ assert(u);
+
+ SET_FOREACH(t, u->names, i) {
+ char **p;
+
+ STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+ /* This loads the drop-in config snippets */
+ r = process_dir(u, *p, t, ".d", _UNIT_DEPENDENCY_INVALID, &strv);
+ if (r < 0)
+ return NULL;
+ }
+ }
+
+ if (strv_isempty(strv))
+ return NULL;
+
+ r = conf_files_list_strv(&configs, ".conf", NULL, (const char**) strv);
+ if (r < 0) {
+ log_error("Failed to get list of configuration files: %s", strerror(-r));
+ strv_free(configs);
+ return NULL;
+ }
+
+ return configs;
+}
+
+int unit_load_dropin(Unit *u) {
+ Iterator i;
+ char *t, **f;
+ int r;
assert(u);
@@ -134,17 +195,29 @@ int unit_load_dropin(Unit *u) {
char **p;
STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
- int r;
-
- r = process_dir(u, *p, t, ".wants", UNIT_WANTS);
+ r = process_dir(u, *p, t, ".wants", UNIT_WANTS, NULL);
if (r < 0)
return r;
- r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES);
+ r = process_dir(u, *p, t, ".requires", UNIT_REQUIRES, NULL);
if (r < 0)
return r;
}
}
+ u->dropin_paths = unit_find_dropin_paths(u);
+ if (! u->dropin_paths)
+ return 0;
+
+ STRV_FOREACH(f, u->dropin_paths) {
+ r = config_parse(u->id, *f, NULL,
+ UNIT_VTABLE(u)->sections, config_item_perf_lookup,
+ (void*) load_fragment_gperf_lookup, false, false, u);
+ if (r < 0)
+ return r;
+ }
+
+ u->dropin_mtime = now(CLOCK_REALTIME);
+
return 0;
}
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
index 1d2fafeee6..fd551179e2 100644
--- a/src/core/load-dropin.h
+++ b/src/core/load-dropin.h
@@ -25,4 +25,5 @@
/* Read service data supplementary drop-in directories */
+char **unit_find_dropin_paths(Unit *u);
int unit_load_dropin(Unit *u);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 7fba0cfb77..4e1454ee6c 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -31,7 +31,7 @@ $1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0,
$1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork)
$1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context)
$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask)
-$1.Environment, config_parse_unit_strv_printf, 0, offsetof($1, exec_context.environment)
+$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input)
$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output)
@@ -48,7 +48,7 @@ $1.Capabilities, config_parse_exec_capabilities, 0,
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop)
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
-$1.NoNewPrivileges config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges)
+$1.NoNewPrivileges, config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges)
$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context)
$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)
@@ -68,14 +68,14 @@ $1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPR
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
$1.ControlGroup, config_parse_unit_cgroup, 0, 0
$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0
-$1.CPUShares, config_parse_unit_cpu_shares, 0, 0
-$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0
-$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0
-$1.DeviceAllow, config_parse_unit_device_allow, 0, 0
-$1.DeviceDeny, config_parse_unit_device_allow, 0, 0
-$1.BlockIOWeight, config_parse_unit_blkio_weight, 0, 0
-$1.BlockIOReadBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
-$1.BlockIOWriteBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
+$1.CPUShares, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.MemoryLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.MemorySoftLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.DeviceAllow, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.DeviceDeny, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOWeight, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOReadBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOWriteBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
@@ -90,7 +90,7 @@ $1.ControlGroupModify, config_parse_bool, 0,
$1.ControlGroupPersistent, config_parse_tristate, 0, offsetof($1, exec_context.control_group_persistent)'
)m4_dnl
m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
-`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill)
+`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill)
$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode)
$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, kill_context.kill_signal)'
)m4_dnl
@@ -122,7 +122,7 @@ Unit.DefaultDependencies, config_parse_bool, 0,
Unit.OnFailureIsolate, config_parse_bool, 0, offsetof(Unit, on_failure_isolate)
Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot)
-Unit.JobTimeoutSec, config_parse_usec, 0, offsetof(Unit, job_timeout)
+Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout)
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, 0
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, 0
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, 0
@@ -147,12 +147,12 @@ Service.ExecStartPost, config_parse_exec, SERVICE_EXE
Service.ExecReload, config_parse_exec, SERVICE_EXEC_RELOAD, offsetof(Service, exec_command)
Service.ExecStop, config_parse_exec, SERVICE_EXEC_STOP, offsetof(Service, exec_command)
Service.ExecStopPost, config_parse_exec, SERVICE_EXEC_STOP_POST, offsetof(Service, exec_command)
-Service.RestartSec, config_parse_usec, 0, offsetof(Service, restart_usec)
+Service.RestartSec, config_parse_sec, 0, offsetof(Service, restart_usec)
Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec)
-Service.WatchdogSec, config_parse_usec, 0, offsetof(Service, watchdog_usec)
-Service.StartLimitInterval, config_parse_usec, 0, offsetof(Service, start_limit.interval)
+Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
+Service.StartLimitInterval, config_parse_sec, 0, offsetof(Service, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst)
Service.StartLimitAction, config_parse_start_limit_action, 0, offsetof(Service, start_limit_action)
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
@@ -174,13 +174,13 @@ Service.FsckPassNo, config_parse_fsck_passno, 0,
EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
m4_dnl
-Socket.ListenStream, config_parse_socket_listen, 0, 0
-Socket.ListenDatagram, config_parse_socket_listen, 0, 0
-Socket.ListenSequentialPacket, config_parse_socket_listen, 0, 0
-Socket.ListenFIFO, config_parse_socket_listen, 0, 0
-Socket.ListenNetlink, config_parse_socket_listen, 0, 0
-Socket.ListenSpecial, config_parse_socket_listen, 0, 0
-Socket.ListenMessageQueue, config_parse_socket_listen, 0, 0
+Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0
+Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
+Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
Socket.BindIPv6Only, config_parse_socket_bind, 0, 0,
Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
@@ -188,7 +188,7 @@ Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC
Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command)
Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command)
Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command)
-Socket.TimeoutSec, config_parse_usec, 0, offsetof(Socket, timeout_usec)
+Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
@@ -221,7 +221,7 @@ Mount.Where, config_parse_path, 0,
Mount.Options, config_parse_string, 0, offsetof(Mount, parameters_fragment.options)
Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype)
Mount.FsckPassNo, config_parse_fsck_passno, 0, offsetof(Mount, parameters_fragment.passno)
-Mount.TimeoutSec, config_parse_usec, 0, offsetof(Mount, timeout_usec)
+Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec)
Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
@@ -231,7 +231,7 @@ Automount.DirectoryMode, config_parse_mode, 0,
m4_dnl
Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what)
Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority)
-Swap.TimeoutSec, config_parse_usec, 0, offsetof(Swap, timeout_usec)
+Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec)
EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
m4_dnl
@@ -241,14 +241,14 @@ Timer.OnBootSec, config_parse_timer, 0,
Timer.OnStartupSec, config_parse_timer, 0, 0
Timer.OnUnitActiveSec, config_parse_timer, 0, 0
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
-Timer.Unit, config_parse_timer_unit, 0, 0
+Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0
Path.PathExistsGlob, config_parse_path_spec, 0, 0
Path.PathChanged, config_parse_path_spec, 0, 0
Path.PathModified, config_parse_path_spec, 0, 0
Path.DirectoryNotEmpty, config_parse_path_spec, 0, 0
-Path.Unit, config_parse_path_unit, 0, 0
+Path.Unit, config_parse_trigger_unit, 0, 0
Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory)
Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode)
m4_dnl The [Install] section is ignored here.
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e35fdbc5ec..e2015ed58f 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -34,6 +34,8 @@
#include <sys/time.h>
#include <sys/resource.h>
+#include <systemd/sd-messages.h>
+
#include "unit.h"
#include "strv.h"
#include "conf-parser.h"
@@ -44,36 +46,39 @@
#include "missing.h"
#include "unit-name.h"
#include "unit-printf.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "utf8.h"
#include "path-util.h"
#include "syscall-list.h"
+#include "env-util.h"
#ifndef HAVE_SYSV_COMPAT
-int config_parse_warn_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);
+int config_parse_warn_compat(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
+ "Support for option %s= has been disabled at compile time and is ignored",
+ lvalue);
return 0;
}
#endif
-int config_parse_unit_deps(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_deps(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
UnitDependency d = ltype;
Unit *u = userdata;
@@ -86,39 +91,38 @@ int config_parse_unit_deps(
assert(rvalue);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL, *k = NULL;
+ _cleanup_free_ char *t = NULL, *k = NULL;
int r;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_name_printf(u, t);
if (!k)
- return -ENOMEM;
+ return log_oom();
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",
- filename, line, k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
}
return 0;
}
-int config_parse_unit_string_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_string_printf(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
@@ -127,27 +131,25 @@ int config_parse_unit_string_printf(
k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata);
- free (k);
-
- return r;
+ return config_parse_string(unit, filename, line, section, lvalue, ltype,
+ k ? k : rvalue, data, userdata);
}
-int config_parse_unit_strv_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_strv_printf(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
@@ -156,27 +158,25 @@ int config_parse_unit_strv_printf(
k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
-
- return r;
+ return config_parse_strv(unit, filename, line, section, lvalue, ltype,
+ k ? k : rvalue, data, userdata);
}
-int config_parse_unit_path_printf(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_path_printf(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
@@ -185,23 +185,22 @@ int config_parse_unit_path_printf(
k = unit_full_printf(u, rvalue);
if (!k)
- return log_oom();
-
- r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- return r;
+ return config_parse_path(unit, filename, line, section, lvalue, ltype,
+ k ? k : rvalue, data, userdata);
}
-int config_parse_socket_listen(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_listen(const char *unit,
+ 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;
@@ -213,67 +212,64 @@ int config_parse_socket_listen(
s = SOCKET(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment removes all ports */
+ socket_free_ports(s);
+ return 0;
+ }
+
p = new0(SocketPort, 1);
if (!p)
- return -ENOMEM;
-
- if (streq(lvalue, "ListenFIFO")) {
- p->type = SOCKET_FIFO;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenSpecial")) {
- p->type = SOCKET_SPECIAL;
+ return log_oom();
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenMessageQueue")) {
-
- p->type = SOCKET_MQUEUE;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
+ if (ltype != SOCKET_SOCKET) {
+
+ p->type = ltype;
+ p->path = unit_full_printf(UNIT(s), rvalue);
+ if (!p->path) {
+ p->path = strdup(rvalue);
+ if (!p->path) {
+ free(p);
+ return log_oom();
+ } else
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
}
path_kill_slashes(p->path);
} else if (streq(lvalue, "ListenNetlink")) {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
p->type = SOCKET_SOCKET;
k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse_netlink(&p->address, k);
- free(k);
+ if (!k)
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
if (r < 0) {
- log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse address value, ignoring: %s", rvalue);
free(p);
return 0;
}
} else {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
p->type = SOCKET_SOCKET;
k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse(&p->address, k);
- free(k);
+ if (!k)
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ r = socket_address_parse(&p->address, k ? k : rvalue);
if (r < 0) {
- log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse address value, ignoring: %s", rvalue);
free(p);
return 0;
}
@@ -288,7 +284,8 @@ int config_parse_socket_listen(
}
if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
- log_error("[%s:%u] Address family not supported, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
+ "Address family not supported, ignoring: %s", rvalue);
free(p);
return 0;
}
@@ -305,15 +302,15 @@ int config_parse_socket_listen(
return 0;
}
-int config_parse_socket_bind(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_bind(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s;
SocketAddressBindIPv6Only b;
@@ -331,7 +328,8 @@ int config_parse_socket_bind(
r = parse_boolean(rvalue);
if (r < 0) {
- log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
return 0;
}
@@ -342,31 +340,34 @@ int config_parse_socket_bind(
return 0;
}
-int config_parse_exec_nice(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_nice(const char *unit,
+ 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;
+ int priority, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &priority) < 0) {
- log_error("[%s:%u] Failed to parse nice priority, ignoring: %s. ", filename, line, rvalue);
+ r = safe_atoi(rvalue, &priority);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse nice priority, ignoring: %s. ", rvalue);
return 0;
}
if (priority < PRIO_MIN || priority >= PRIO_MAX) {
- log_error("[%s:%u] Nice priority out of range, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "Nice priority out of range, ignoring: %s", rvalue);
return 0;
}
@@ -376,31 +377,34 @@ int config_parse_exec_nice(
return 0;
}
-int config_parse_exec_oom_score_adjust(
- const char *filename,
- unsigned line,
- const char *section,
- 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,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
- int oa;
+ int oa, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &oa) < 0) {
- log_error("[%s:%u] Failed to parse the OOM score adjust value, ignoring: %s", filename, line, rvalue);
+ 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);
return 0;
}
if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
- log_error("[%s:%u] OOM score adjust value out of range, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "OOM score adjust value out of range, ignoring: %s", rvalue);
return 0;
}
@@ -410,15 +414,15 @@ int config_parse_exec_oom_score_adjust(
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) {
+int config_parse_exec(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecCommand **e = data, *nce;
char *path, **n;
@@ -430,12 +434,18 @@ int config_parse_exec(
assert(rvalue);
assert(e);
+ e += ltype;
+
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the list */
+ exec_command_free_list(*e);
+ *e = NULL;
+ return 0;
+ }
+
/* 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;
@@ -465,14 +475,14 @@ int config_parse_exec(
}
if (*rvalue != '/') {
- log_error("[%s:%u] Executable path is not absolute, ignoring: %s",
- filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Executable path is not absolute, ignoring: %s", rvalue);
return 0;
}
k = 0;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", MAX(l, 1U)) == 0)
+ if (strneq(w, ";", MAX(l, 1U)))
break;
k++;
@@ -480,13 +490,13 @@ int config_parse_exec(
n = new(char*, k + !honour_argv0);
if (!n)
- return -ENOMEM;
+ return log_oom();
k = 0;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, ";", MAX(l, 1U)) == 0)
+ if (strneq(w, ";", MAX(l, 1U)))
break;
- else if (strncmp(w, "\\;", MAX(l, 1U)) == 0)
+ else if (strneq(w, "\\;", MAX(l, 1U)))
w ++;
if (honour_argv0 && w == rvalue) {
@@ -494,12 +504,14 @@ int config_parse_exec(
path = strndup(w, l);
if (!path) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
if (!utf8_is_valid(path)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not UTF-8 clean, ignoring assignment: %s",
+ rvalue);
r = 0;
goto fail;
}
@@ -509,12 +521,14 @@ int config_parse_exec(
c = n[k++] = cunescape_length(w, l);
if (!c) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
if (!utf8_is_valid(c)) {
- log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not UTF-8 clean, ignoring assignment: %s",
+ rvalue);
r = 0;
goto fail;
}
@@ -524,7 +538,8 @@ int config_parse_exec(
n[k] = NULL;
if (!n[0]) {
- log_error("[%s:%u] Invalid command line, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid command line, ignoring: %s", rvalue);
r = 0;
goto fail;
}
@@ -532,7 +547,7 @@ int config_parse_exec(
if (!path) {
path = strdup(n[0]);
if (!path) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
}
@@ -541,7 +556,7 @@ int config_parse_exec(
nce = new0(ExecCommand, 1);
if (!nce) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
@@ -570,15 +585,15 @@ fail:
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");
-int config_parse_socket_bindtodevice(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_bindtodevice(const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s = data;
char *n;
@@ -589,8 +604,9 @@ int config_parse_socket_bindtodevice(
assert(data);
if (rvalue[0] && !streq(rvalue, "*")) {
- if (!(n = strdup(rvalue)))
- return -ENOMEM;
+ n = strdup(rvalue);
+ if (!n)
+ return log_oom();
} else
n = NULL;
@@ -603,15 +619,15 @@ int config_parse_socket_bindtodevice(
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");
-int config_parse_exec_io_class(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_io_class(const char *unit,
+ 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;
@@ -623,7 +639,8 @@ int config_parse_exec_io_class(
x = ioprio_class_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse IO scheduling class, ignoring: %s", rvalue);
return 0;
}
@@ -633,26 +650,28 @@ int config_parse_exec_io_class(
return 0;
}
-int config_parse_exec_io_priority(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_io_priority(const char *unit,
+ 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;
+ int i, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) < 0 || i < 0 || i >= IOPRIO_BE_NR) {
- log_error("[%s:%u] Failed to parse io priority, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse IO priority, ignoring: %s", rvalue);
return 0;
}
@@ -662,15 +681,15 @@ int config_parse_exec_io_priority(
return 0;
}
-int config_parse_exec_cpu_sched_policy(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_cpu_sched_policy(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
@@ -683,7 +702,8 @@ int config_parse_exec_cpu_sched_policy(
x = sched_policy_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -x,
+ "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
@@ -695,36 +715,38 @@ int config_parse_exec_cpu_sched_policy(
return 0;
}
-int config_parse_exec_cpu_sched_prio(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_cpu_sched_prio(const char *unit,
+ 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, min, max;
+ int i, min, max, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) < 0) {
- log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
-
/* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
min = sched_get_priority_min(c->cpu_sched_policy);
max = sched_get_priority_max(c->cpu_sched_policy);
if (i < min || i > max) {
- log_error("[%s:%u] CPU scheduling priority is out of range, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "CPU scheduling priority is out of range, ignoring: %s", rvalue);
return 0;
}
@@ -734,15 +756,15 @@ int config_parse_exec_cpu_sched_prio(
return 0;
}
-int config_parse_exec_cpu_affinity(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_cpu_affinity(const char *unit,
+ 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;
@@ -754,26 +776,34 @@ int config_parse_exec_cpu_affinity(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the CPU list */
+ if (c->cpuset)
+ CPU_FREE(c->cpuset);
+ c->cpuset = NULL;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
int r;
unsigned cpu;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
r = safe_atou(t, &cpu);
if (!c->cpuset) {
c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
if (!c->cpuset)
- return -ENOMEM;
+ return log_oom();
}
if (r < 0 || cpu >= c->cpuset_ncpus) {
- log_error("[%s:%u] Failed to parse CPU affinity %s, ignoring: %s",
- filename, line, t, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, ERANGE,
+ "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
return 0;
}
@@ -783,15 +813,15 @@ int config_parse_exec_cpu_affinity(
return 0;
}
-int config_parse_exec_capabilities(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_capabilities(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
cap_t cap;
@@ -801,11 +831,10 @@ int config_parse_exec_capabilities(
assert(rvalue);
assert(data);
- if (!(cap = cap_from_text(rvalue))) {
- if (errno == ENOMEM)
- return -ENOMEM;
-
- log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue);
+ cap = cap_from_text(rvalue);
+ if (!cap) {
+ log_syntax(unit, LOG_ERR, filename, line, errno,
+ "Failed to parse capabilities, ignoring: %s", rvalue);
return 0;
}
@@ -816,15 +845,15 @@ int config_parse_exec_capabilities(
return 0;
}
-int config_parse_exec_secure_bits(
- const char *filename,
- unsigned line,
- const char *section,
- 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,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
char *w;
@@ -836,22 +865,28 @@ int config_parse_exec_secure_bits(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the field */
+ c->secure_bits = 0;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
if (first_word(w, "keep-caps"))
- c->secure_bits |= SECURE_KEEP_CAPS;
+ c->secure_bits |= 1<<SECURE_KEEP_CAPS;
else if (first_word(w, "keep-caps-locked"))
- c->secure_bits |= SECURE_KEEP_CAPS_LOCKED;
+ c->secure_bits |= 1<<SECURE_KEEP_CAPS_LOCKED;
else if (first_word(w, "no-setuid-fixup"))
- c->secure_bits |= SECURE_NO_SETUID_FIXUP;
+ c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP;
else if (first_word(w, "no-setuid-fixup-locked"))
- c->secure_bits |= SECURE_NO_SETUID_FIXUP_LOCKED;
+ c->secure_bits |= 1<<SECURE_NO_SETUID_FIXUP_LOCKED;
else if (first_word(w, "noroot"))
- c->secure_bits |= SECURE_NOROOT;
+ c->secure_bits |= 1<<SECURE_NOROOT;
else if (first_word(w, "noroot-locked"))
- c->secure_bits |= SECURE_NOROOT_LOCKED;
+ c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
else {
- log_error("[%s:%u] Failed to parse secure bits, ignoring: %s",
- filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse secure bits, ignoring: %s", rvalue);
return 0;
}
}
@@ -859,15 +894,15 @@ int config_parse_exec_secure_bits(
return 0;
}
-int config_parse_bounding_set(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_bounding_set(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
uint64_t *capability_bounding_set_drop = data;
char *w;
@@ -892,18 +927,18 @@ int config_parse_bounding_set(
* interface. */
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
int r;
cap_value_t cap;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
r = cap_from_name(t, &cap);
if (r < 0) {
- log_error("[%s:%u] Failed to parse capability in bounding set, ignoring: %s",
- filename, line, t);
+ log_syntax(unit, LOG_ERR, filename, line, errno,
+ "Failed to parse capability in bounding set, ignoring: %s", t);
continue;
}
@@ -918,15 +953,15 @@ int config_parse_bounding_set(
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) {
+int config_parse_limit(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
struct rlimit **rl = data;
unsigned long long u;
@@ -940,54 +975,72 @@ int config_parse_limit(
if (streq(rvalue, "infinity"))
u = (unsigned long long) RLIM_INFINITY;
- else if (safe_atollu(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse resource value, ignoring: %s", filename, line, rvalue);
- return 0;
+ else {
+ int r;
+
+ r = safe_atollu(rvalue, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse resource value, ignoring: %s", rvalue);
+ return 0;
+ }
}
- if (!*rl)
- if (!(*rl = new(struct rlimit, 1)))
- return -ENOMEM;
+ if (!*rl) {
+ *rl = new(struct rlimit, 1);
+ if (!*rl)
+ return log_oom();
+ }
(*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
return 0;
}
-int config_parse_unit_cgroup(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_cgroup(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
char *w;
size_t l;
char *state;
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the list */
+ cgroup_bonding_free_list(u->cgroup_bondings, false);
+ u->cgroup_bondings = NULL;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL;
+ _cleanup_free_ char *t = NULL, *k = NULL, *ku = NULL;
int r;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_full_printf(u, t);
if (!k)
- return -ENOMEM;
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.",
+ t);
- ku = cunescape(k);
+ ku = cunescape(k ? k : t);
if (!ku)
- return -ENOMEM;
+ return log_oom();
- r = unit_add_cgroup_from_text(u, ku);
+ r = unit_add_cgroup_from_text(u, ku, true, NULL);
if (r < 0) {
- log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s",
- filename, line, k, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse cgroup value %s, ignoring: %s",
+ k, rvalue);
return 0;
}
}
@@ -996,26 +1049,28 @@ int config_parse_unit_cgroup(
}
#ifdef HAVE_SYSV_COMPAT
-int config_parse_sysv_priority(
- const char *filename,
- unsigned line,
- const char *section,
- 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,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *priority = data;
- int i;
+ int i, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) < 0 || i < 0) {
- log_error("[%s:%u] Failed to parse SysV start priority, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r < 0 || i < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse SysV start priority, ignoring: %s", rvalue);
return 0;
}
@@ -1024,26 +1079,28 @@ int config_parse_sysv_priority(
}
#endif
-int config_parse_fsck_passno(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_fsck_passno(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *passno = data;
- int i;
+ int i, r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atoi(rvalue, &i) || i < 0) {
- log_error("[%s:%u] Failed to parse fsck pass number, ignoring: %s", filename, line, rvalue);
+ r = safe_atoi(rvalue, &i);
+ if (r || i < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse fsck pass number, ignoring: %s", rvalue);
return 0;
}
@@ -1053,15 +1110,15 @@ int config_parse_fsck_passno(
DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
-int config_parse_kill_signal(
- const char *filename,
- unsigned line,
- const char *section,
- 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,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *sig = data;
int r;
@@ -1071,8 +1128,10 @@ int config_parse_kill_signal(
assert(rvalue);
assert(sig);
- if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
- log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
+ r = signal_from_string_try_harder(rvalue);
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse kill signal, ignoring: %s", rvalue);
return 0;
}
@@ -1080,15 +1139,15 @@ int config_parse_kill_signal(
return 0;
}
-int config_parse_exec_mount_flags(
- const char *filename,
- unsigned line,
- const char *section,
- 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,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
char *w;
@@ -1102,11 +1161,11 @@ int config_parse_exec_mount_flags(
assert(data);
FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
- char _cleanup_free_ *t;
+ _cleanup_free_ char *t;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
if (streq(t, "shared"))
flags |= MS_SHARED;
@@ -1115,8 +1174,9 @@ int config_parse_exec_mount_flags(
else if (streq(w, "private"))
flags |= MS_PRIVATE;
else {
- log_error("[%s:%u] Failed to parse mount flag %s, ignoring: %s",
- filename, line, t, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse mount flag %s, ignoring: %s",
+ t, rvalue);
return 0;
}
}
@@ -1125,15 +1185,15 @@ int config_parse_exec_mount_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) {
+int config_parse_timer(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Timer *t = data;
usec_t u = 0;
@@ -1147,22 +1207,33 @@ int config_parse_timer(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets list */
+ timer_free_values(t);
+ return 0;
+ }
+
b = timer_base_from_string(lvalue);
if (b < 0) {
- log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -b,
+ "Failed to parse timer base, ignoring: %s", lvalue);
return 0;
}
if (b == TIMER_CALENDAR) {
if (calendar_spec_from_string(rvalue, &c) < 0) {
- log_error("[%s:%u] Failed to parse calendar specification, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse calendar specification, ignoring: %s",
+ rvalue);
return 0;
}
id = CLOCK_REALTIME;
} else {
- if (parse_usec(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
+ if (parse_sec(rvalue, &u) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse timer value, ignoring: %s",
+ rvalue);
return 0;
}
@@ -1171,7 +1242,7 @@ int config_parse_timer(
v = new0(TimerValue, 1);
if (!v)
- return -ENOMEM;
+ return log_oom();
v->base = b;
v->clock_id = id;
@@ -1183,7 +1254,8 @@ int config_parse_timer(
return 0;
}
-int config_parse_timer_unit(
+int config_parse_trigger_unit(
+ const char *unit,
const char *filename,
unsigned line,
const char *section,
@@ -1193,78 +1265,105 @@ int config_parse_timer_unit(
void *data,
void *userdata) {
- Timer *t = data;
+ _cleanup_free_ char *p = NULL;
+ Unit *u = data;
+ UnitType type;
int r;
- DBusError error;
- Unit *u;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- dbus_error_init(&error);
+ if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Multiple units to trigger specified, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ p = unit_name_printf(u, rvalue);
+ if (!p)
+ return log_oom();
- if (endswith(rvalue, ".timer")) {
- log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue);
+ type = unit_name_to_type(p);
+ if (type < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Unit type not valid, ignoring: %s", rvalue);
return 0;
}
- r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u);
- if (r < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
- dbus_error_free(&error);
+ if (type == u->type) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Trigger cannot be of same type, ignoring: %s", rvalue);
return 0;
}
- unit_ref_set(&t->unit, u);
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add trigger on %s, ignoring: %s", p, strerror(-r));
+ return 0;
+ }
return 0;
}
-int config_parse_path_spec(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_path_spec(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Path *p = data;
PathSpec *s;
PathType b;
- char *k;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment clears list */
+ path_free_specs(p);
+ return 0;
+ }
+
b = path_type_from_string(lvalue);
if (b < 0) {
- log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse path type, ignoring: %s", lvalue);
return 0;
}
k = unit_full_printf(UNIT(p), rvalue);
- if (!k)
- return log_oom();
+ if (!k) {
+ k = strdup(rvalue);
+ if (!k)
+ return log_oom();
+ else
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.",
+ rvalue);
+ }
if (!path_is_absolute(k)) {
- log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, k);
- free(k);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not absolute, ignoring: %s", k);
return 0;
}
s = new0(PathSpec, 1);
- if (!s) {
- free(k);
+ if (!s)
return log_oom();
- }
s->path = path_kill_slashes(k);
+ k = NULL;
s->type = b;
s->inotify_fd = -1;
@@ -1273,58 +1372,21 @@ int config_parse_path_spec(
return 0;
}
-int config_parse_path_unit(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Path *t = data;
- int r;
- DBusError error;
- Unit *u;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- dbus_error_init(&error);
-
- if (endswith(rvalue, ".path")) {
- log_error("[%s:%u] Unit cannot be of type path, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if ((r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, &error, &u)) < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
- dbus_error_free(&error);
- return 0;
- }
-
- unit_ref_set(&t->unit, u);
-
- return 0;
-}
-
-int config_parse_socket_service(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_service(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s = data;
int r;
DBusError error;
Unit *x;
+ _cleanup_free_ char *p = NULL;
assert(filename);
assert(lvalue);
@@ -1333,14 +1395,21 @@ int config_parse_socket_service(
dbus_error_init(&error);
- if (!endswith(rvalue, ".service")) {
- log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
+ p = unit_name_printf(UNIT(s), rvalue);
+ if (!p)
+ return log_oom();
+
+ if (!endswith(p, ".service")) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
- r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x);
+ r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
if (r < 0) {
- log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to load unit %s, ignoring: %s",
+ rvalue, bus_error(&error, r));
dbus_error_free(&error);
return 0;
}
@@ -1350,15 +1419,15 @@ int config_parse_socket_service(
return 0;
}
-int config_parse_service_sockets(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_service_sockets(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Service *s = data;
int r;
@@ -1371,26 +1440,27 @@ int config_parse_service_sockets(
assert(data);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char _cleanup_free_ *t = NULL, *k = NULL;
+ _cleanup_free_ char *t = NULL, *k = NULL;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_name_printf(UNIT(s), t);
if (!k)
- return -ENOMEM;
+ return log_oom();
if (!endswith(k, ".socket")) {
- log_error("[%s:%u] Unit must be of type socket, ignoring: %s",
- filename, line, k);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Unit must be of type socket, ignoring: %s", k);
continue;
}
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s",
- filename, line, k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add dependency on %s, ignoring: %s",
+ k, strerror(-r));
r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
if (r < 0)
@@ -1400,15 +1470,15 @@ int config_parse_service_sockets(
return 0;
}
-int config_parse_service_timeout(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_service_timeout(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Service *s = userdata;
int r;
@@ -1418,9 +1488,9 @@ int config_parse_service_timeout(
assert(rvalue);
assert(s);
- r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata);
-
- if (r)
+ r = config_parse_sec(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
+ if (r < 0)
return r;
if (streq(lvalue, "TimeoutSec")) {
@@ -1432,55 +1502,115 @@ int config_parse_service_timeout(
return 0;
}
-int config_parse_unit_env_file(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char ***env = data, **k;
+int config_parse_unit_env_file(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char ***env = data;
Unit *u = userdata;
- char *s;
+ _cleanup_free_ char *s = NULL;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment frees the list */
+ strv_free(*env);
+ *env = NULL;
+ return 0;
+ }
+
s = unit_full_printf(u, rvalue);
if (!s)
- return -ENOMEM;
+ return log_oom();
if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
- log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
- free(s);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path '%s' is not absolute, ignoring.", s);
+ return 0;
+ }
+
+ r = strv_extend(env, s);
+ if (r < 0)
+ return log_oom();
+
+ return 0;
+}
+
+int config_parse_environ(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Unit *u = userdata;
+ char*** env = data, *w, *state;
+ size_t l;
+ _cleanup_free_ char *k = NULL;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(*env);
+ *env = NULL;
return 0;
}
- k = strv_append(*env, s);
- free(s);
+ k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
+ return log_oom();
- strv_free(*env);
- *env = k;
+ FOREACH_WORD_QUOTED(w, l, k, state) {
+ _cleanup_free_ char *n;
+ char **x;
+
+ n = cunescape_length(w, l);
+ if (!n)
+ return log_oom();
+
+ if (!env_assignment_is_valid(n)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid environment assignment, ignoring: %s", rvalue);
+ continue;
+ }
+
+ x = strv_env_set(*env, n);
+ if (!x)
+ return log_oom();
+
+ strv_free(*env);
+ *env = x;
+ }
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) {
+int config_parse_ip_tos(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
int *ip_tos = data, x;
@@ -1491,7 +1621,8 @@ int config_parse_ip_tos(
x = ip_tos_from_string(rvalue);
if (x < 0) {
- log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse IP TOS value, ignoring: %s", rvalue);
return 0;
}
@@ -1499,15 +1630,15 @@ int config_parse_ip_tos(
return 0;
}
-int config_parse_unit_condition_path(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_condition_path(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ConditionType cond = ltype;
Unit *u = data;
@@ -1520,6 +1651,13 @@ int config_parse_unit_condition_path(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
trigger = rvalue[0] == '|';
if (trigger)
rvalue++;
@@ -1530,30 +1668,31 @@ int config_parse_unit_condition_path(
p = unit_full_printf(u, rvalue);
if (!p)
- return -ENOMEM;
+ return log_oom();
if (!path_is_absolute(p)) {
- log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, p);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path in condition not absolute, ignoring: %s", p);
return 0;
}
c = condition_new(cond, p, trigger, negate);
if (!c)
- return -ENOMEM;
+ return log_oom();
LIST_PREPEND(Condition, conditions, u->conditions, c);
return 0;
}
-int config_parse_unit_condition_string(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_condition_string(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ConditionType cond = ltype;
Unit *u = data;
@@ -1566,6 +1705,13 @@ int config_parse_unit_condition_string(
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
trigger = rvalue[0] == '|';
if (trigger)
rvalue++;
@@ -1576,7 +1722,7 @@ int config_parse_unit_condition_string(
s = unit_full_printf(u, rvalue);
if (!s)
- return -ENOMEM;
+ return log_oom();
c = condition_new(cond, s, trigger, negate);
if (!c)
@@ -1586,15 +1732,15 @@ int config_parse_unit_condition_string(
return 0;
}
-int config_parse_unit_condition_null(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_condition_null(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = data;
Condition *c;
@@ -1606,22 +1752,35 @@ int config_parse_unit_condition_null(
assert(rvalue);
assert(data);
- if ((trigger = rvalue[0] == '|'))
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
+ trigger = rvalue[0] == '|';
+ if (trigger)
rvalue++;
- if ((negate = rvalue[0] == '!'))
+ negate = rvalue[0] == '!';
+ if (negate)
rvalue++;
- if ((b = parse_boolean(rvalue)) < 0) {
- log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
+ b = parse_boolean(rvalue);
+ if (b < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -b,
+ "Failed to parse boolean value in condition, ignoring: %s",
+ rvalue);
return 0;
}
if (!b)
negate = !negate;
- if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
- return -ENOMEM;
+ c = condition_new(CONDITION_NULL, NULL, trigger, negate);
+ if (!c)
+ return log_oom();
LIST_PREPEND(Condition, conditions, u->conditions, c);
return 0;
@@ -1630,383 +1789,117 @@ int config_parse_unit_condition_null(
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");
-int config_parse_unit_cgroup_attr(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = data;
- char **l;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- if (strv_length(l) != 2) {
- log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
- strv_free(l);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
+int config_parse_unit_cgroup_attr(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
-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) {
Unit *u = data;
+ size_t a, b;
+ _cleanup_free_ char *n = NULL, *v = NULL;
+ const CGroupSemantics *s;
int r;
- unsigned long ul;
- char *t;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
- log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (asprintf(&t, "%lu", ul) < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
- free(t);
-
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ if (isempty(rvalue)) {
+ /* Empty assignment clears the list */
+ cgroup_attribute_free_list(u->cgroup_attributes);
+ u->cgroup_attributes = NULL;
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) {
- Unit *u = data;
- int r;
- off_t sz;
- char *t;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
- log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
+ a = strcspn(rvalue, WHITESPACE);
+ b = strspn(rvalue + a, WHITESPACE);
+ if (a <= 0 || b <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse cgroup attribute value, ignoring: %s",
+ rvalue);
return 0;
}
- if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u,
- "memory",
- streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
- t, NULL);
- free(t);
+ n = strndup(rvalue, a);
+ if (!n)
+ return log_oom();
+ r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-static int device_map(const char *controller, const char *name, const char *value, char **ret) {
- char **l;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- assert(strv_length(l) >= 1);
-
- if (streq(l[0], "*")) {
-
- if (asprintf(ret, "a *:*%s%s",
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
- strv_free(l);
- return -ENOMEM;
- }
-
- } else {
- struct stat st;
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- strv_free(l);
- return -errno;
- }
-
- if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_warning("%s is not a device.", l[0]);
- strv_free(l);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%c %u:%u%s%s",
- S_ISCHR(st.st_mode) ? 'c' : 'b',
- major(st.st_rdev), minor(st.st_rdev),
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
-
- 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) {
- Unit *u = data;
- char **l;
- int r;
- unsigned k;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
- log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
- strv_free(l);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse cgroup attribute value, ignoring: %s",
+ rvalue);
return 0;
}
- if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
- log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
- strv_free(l);
-
- r = unit_add_cgroup_attribute(u, "devices",
- streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
- rvalue, device_map);
-
+ r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add cgroup attribute value, ignoring: %s", rvalue);
return 0;
}
return 0;
}
-static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
- struct stat st;
- char **l;
- dev_t d;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- assert(strv_length(l) == 2);
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- strv_free(l);
- return -errno;
- }
-
- if (S_ISBLK(st.st_mode))
- d = st.st_rdev;
- else if (major(st.st_dev) != 0) {
- /* If this is not a device node then find the block
- * device this file is stored on */
- d = st.st_dev;
-
- /* 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;
- }
-
- if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) {
- strv_free(l);
- return -ENOMEM;
- }
+int config_parse_unit_cgroup_attr_pretty(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- 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) {
Unit *u = data;
+ _cleanup_free_ char *v = NULL;
+ const CGroupSemantics *s;
int r;
- unsigned long ul;
- const char *device = NULL, *weight;
- unsigned k;
- char *t, **l;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (k == 1)
- weight = l[0];
- else {
- device = l[0];
- weight = l[1];
- }
-
- if (device && !path_is_absolute(device)) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
- log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- if (device)
- r = asprintf(&t, "%s %lu", device, ul);
- else
- r = asprintf(&t, "%lu", ul);
- strv_free(l);
-
- if (r < 0)
- return -ENOMEM;
-
- if (device)
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map);
- else
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL);
- free(t);
-
+ r = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- 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) {
- Unit *u = data;
- int r;
- off_t bytes;
- unsigned k;
- char *t, **l;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k != 2) {
- log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse cgroup attribute value, ignoring: %s",
+ rvalue);
return 0;
- }
-
- if (!path_is_absolute(l[0])) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
+ } else if (r == 0) {
+ log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
+ "Unknown or unsupported cgroup attribute %s, ignoring: %s",
+ lvalue, rvalue);
return 0;
}
- if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
- log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
- return 0;
- }
-
- r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
- strv_free(l);
-
- if (r < 0)
- return -ENOMEM;
-
- r = unit_add_cgroup_attribute(u, "blkio",
- streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
- t, blkio_map);
- free(t);
-
+ r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to add cgroup attribute value, ignoring: %s", rvalue);
return 0;
}
return 0;
}
-int config_parse_unit_requires_mounts_for(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_requires_mounts_for(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
int r;
@@ -2019,7 +1912,8 @@ int config_parse_unit_requires_mounts_for(
empty_before = !u->requires_mounts_for;
- r = config_parse_path_strv(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+ r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
/* Make it easy to find units with requires_mounts set */
if (empty_before && u->requires_mounts_for)
@@ -2028,15 +1922,15 @@ int config_parse_unit_requires_mounts_for(
return r;
}
-int config_parse_documentation(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_documentation(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
int r;
@@ -2047,7 +1941,15 @@ int config_parse_documentation(
assert(rvalue);
assert(u);
- r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(u->documentation);
+ u->documentation = NULL;
+ return 0;
+ }
+
+ r = config_parse_unit_strv_printf(unit, filename, line, section, lvalue, ltype,
+ rvalue, data, userdata);
if (r < 0)
return r;
@@ -2056,7 +1958,8 @@ int config_parse_documentation(
if (is_valid_documentation_url(*a))
*(b++) = *a;
else {
- log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid URL, ignoring: %s", *a);
free(*a);
}
}
@@ -2066,22 +1969,24 @@ int config_parse_documentation(
}
static void syscall_set(uint32_t *p, int nr) {
+ nr = SYSCALL_TO_INDEX(nr);
p[nr >> 4] |= 1 << (nr & 31);
}
static void syscall_unset(uint32_t *p, int nr) {
+ nr = SYSCALL_TO_INDEX(nr);
p[nr >> 4] &= ~(1 << (nr & 31));
}
-int config_parse_syscall_filter(
- const char *filename,
- unsigned line,
- const char *section,
- 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,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
Unit *u = userdata;
@@ -2095,6 +2000,13 @@ int config_parse_syscall_filter(
assert(rvalue);
assert(u);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ free(c->syscall_filter);
+ c->syscall_filter = NULL;
+ return 0;
+ }
+
if (rvalue[0] == '~') {
invert = true;
rvalue++;
@@ -2106,7 +2018,7 @@ int config_parse_syscall_filter(
n = (syscall_max() + 31) >> 4;
c->syscall_filter = new(uint32_t, n);
if (!c->syscall_filter)
- return -ENOMEM;
+ return log_oom();
memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
@@ -2122,17 +2034,16 @@ int config_parse_syscall_filter(
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
int id;
- char _cleanup_free_ *t = NULL;
+ _cleanup_free_ char *t = NULL;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
id = syscall_from_name(t);
-
if (id < 0) {
- log_error("[%s:%u] Failed to parse syscall, ignoring: %s",
- filename, line, t);
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to parse syscall, ignoring: %s", t);
continue;
}
@@ -2184,11 +2095,9 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
if (!id)
return -ENOMEM;
- r = set_put(names, id);
- if (r < 0) {
- free(id);
+ r = set_consume(names, id);
+ if (r < 0)
return r;
- }
}
}
@@ -2359,7 +2268,9 @@ static int load_from_path(Unit *u, const char *path) {
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);
+ r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
+ config_item_perf_lookup,
+ (void*) load_fragment_gperf_lookup, false, true, u);
if (r < 0)
goto finish;
@@ -2521,16 +2432,15 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_socket_listen, "SOCKET [...]" },
{ config_parse_socket_bind, "SOCKETBIND" },
{ config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
- { config_parse_usec, "SECONDS" },
+ { config_parse_sec, "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_trigger_unit, "UNIT" },
{ 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" },
@@ -2557,7 +2467,7 @@ void unit_dump_config_items(FILE *f) {
prefix_len = dot-i;
if (dot)
- if (!prev || strncmp(prev, i, prefix_len+1) != 0) {
+ if (!prev || !strneq(prev, i, prefix_len+1)) {
if (prev)
fputc('\n', f);
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 24f738464c..ff7f22a6f0 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -29,59 +29,55 @@ int unit_load_fragment(Unit *u);
void unit_dump_config_items(FILE *f);
-int config_parse_warn_compat(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_deps(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_documentation(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_oom_score_adjust(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_timeout(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_restart(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_bindtodevice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_input(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_io_class(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_io_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_sched_policy(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_sched_prio(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_cpu_affinity(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_capabilities(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_secure_bits(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_sysv_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_timer(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_timer_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_spec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_socket_service(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_service_sockets(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-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);
-int config_parse_unit_condition_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-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 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 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);
-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 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 config_parse_unit_requires_mounts_for(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_syscall_filter(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_warn_compat(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_string_printf(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_path_printf(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_documentation(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_nice(const char *unit, const char *filename, unsigned line, const char *section, 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, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_timeout(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_type(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_restart(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bindtodevice(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_output(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_input(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_class(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_priority(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_policy(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_capabilities(const char *unit, const char *filename, unsigned line, const char *section, 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, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, 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, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup(const char *unit, const char *filename, unsigned line, const char *section, 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, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_fsck_passno(const char *unit, const char *filename, unsigned line, const char *section, 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, 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, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_timer(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_trigger_unit(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_spec(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_service(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_sockets(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_env_file(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ip_tos(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_path(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_string(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_null(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_kill_mode(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_notify_access(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_start_limit_action(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup_attr(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup_attr_pretty(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_requires_mounts_for(const char *unit, const char *filename, unsigned line, const char *section, 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, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index 48b59bf448..d7113b9795 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -27,6 +27,7 @@
#include "util.h"
#include "macro.h"
#include "virt.h"
+#include "fileio.h"
enum {
/* We don't list LC_ALL here on purpose. People should be
@@ -67,11 +68,9 @@ static const char * const variable_names[_VARIABLE_MAX] = {
};
int locale_setup(void) {
- char *variables[_VARIABLE_MAX];
+ char *variables[_VARIABLE_MAX] = {};
int r = 0, i;
- zero(variables);
-
if (detect_container(NULL) <= 0) {
r = parse_env_file("/proc/cmdline", WHITESPACE,
"locale.LANG", &variables[VARIABLE_LANG],
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 065b75a6e3..aff24fa642 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -88,25 +88,26 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
- } sa;
+ } sa = {
+ .nl.nl_family = AF_NETLINK,
+ };
+
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
RTA_LENGTH(sizeof(struct in6_addr))];
- } request;
+ } request = {
+ .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+ .header.nlmsg_type = RTM_NEWADDR,
+ .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK,
+ .header.nlmsg_seq = *requests + 1,
+ };
struct ifaddrmsg *ifaddrmsg;
uint32_t ipv4_address = htonl(INADDR_LOOPBACK);
int r;
- zero(request);
-
- request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- request.header.nlmsg_type = RTM_NEWADDR;
- request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_ACK;
- request.header.nlmsg_seq = *requests + 1;
-
ifaddrmsg = NLMSG_DATA(&request.header);
ifaddrmsg->ifa_family = AF_INET;
ifaddrmsg->ifa_prefixlen = 8;
@@ -114,13 +115,11 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) {
ifaddrmsg->ifa_scope = RT_SCOPE_HOST;
ifaddrmsg->ifa_index = if_loopback;
- r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &ipv4_address, sizeof(ipv4_address));
+ r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
+ &ipv4_address, sizeof(ipv4_address));
if (r < 0)
return r;
- zero(sa);
- sa.nl.nl_family = AF_NETLINK;
-
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
(*requests)++;
@@ -134,7 +133,8 @@ static int add_adresses(int fd, int if_loopback, unsigned *requests) {
ifaddrmsg->ifa_family = AF_INET6;
ifaddrmsg->ifa_prefixlen = 128;
- r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL, &in6addr_loopback, sizeof(in6addr_loopback));
+ r = add_rtattr(&request.header, sizeof(request), IFA_LOCAL,
+ &in6addr_loopback, sizeof(in6addr_loopback));
if (r < 0)
return r;
@@ -149,31 +149,29 @@ static int start_interface(int fd, int if_loopback, unsigned *requests) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
- } sa;
+ } sa = {
+ .nl.nl_family = AF_NETLINK,
+ };
+
union {
struct nlmsghdr header;
uint8_t buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
NLMSG_ALIGN(sizeof(struct ifinfomsg))];
- } request;
+ } request = {
+ .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .header.nlmsg_type = RTM_NEWLINK,
+ .header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK,
+ .header.nlmsg_seq = *requests + 1,
+ };
struct ifinfomsg *ifinfomsg;
- zero(request);
-
- request.header.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- request.header.nlmsg_type = RTM_NEWLINK;
- request.header.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- request.header.nlmsg_seq = *requests + 1;
-
ifinfomsg = NLMSG_DATA(&request.header);
ifinfomsg->ifi_family = AF_UNSPEC;
ifinfomsg->ifi_index = if_loopback;
ifinfomsg->ifi_flags = IFF_UP;
ifinfomsg->ifi_change = IFF_UP;
- zero(sa);
- sa.nl.nl_family = AF_NETLINK;
-
if (sendto_loop(fd, &request, request.header.nlmsg_len, 0, &sa.sa, sizeof(sa)) < 0)
return -errno;
@@ -229,11 +227,15 @@ static int read_response(int fd, unsigned requests_max) {
}
static int check_loopback(void) {
- int r, fd;
+ int r;
+ _cleanup_close_ int fd;
union {
struct sockaddr sa;
struct sockaddr_in in;
- } sa;
+ } sa = {
+ .in.sin_family = AF_INET,
+ .in.sin_addr.s_addr = INADDR_LOOPBACK,
+ };
/* If we failed to set up the loop back device, check whether
* it might already be set up */
@@ -242,17 +244,11 @@ static int check_loopback(void) {
if (fd < 0)
return -errno;
- zero(sa);
- sa.in.sin_family = AF_INET;
- sa.in.sin_addr.s_addr = INADDR_LOOPBACK;
-
if (bind(fd, &sa.sa, sizeof(sa.in)) >= 0)
r = 1;
else
r = errno == EADDRNOTAVAIL ? 0 : -errno;
- close_nointr_nofail(fd);
-
return r;
}
@@ -261,9 +257,11 @@ int loopback_setup(void) {
union {
struct sockaddr sa;
struct sockaddr_nl nl;
- } sa;
+ } sa = {
+ .nl.nl_family = AF_NETLINK,
+ };
unsigned requests = 0, i;
- int fd;
+ _cleanup_close_ int fd = -1;
bool eperm = false;
errno = 0;
@@ -275,20 +273,18 @@ int loopback_setup(void) {
if (fd < 0)
return -errno;
- zero(sa);
- sa.nl.nl_family = AF_NETLINK;
if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
r = -errno;
- goto finish;
+ goto error;
}
r = add_adresses(fd, if_loopback, &requests);
if (r < 0)
- goto finish;
+ goto error;
r = start_interface(fd, if_loopback, &requests);
if (r < 0)
- goto finish;
+ goto error;
for (i = 0; i < requests; i++) {
r = read_response(fd, requests);
@@ -296,22 +292,17 @@ int loopback_setup(void) {
if (r == -EPERM)
eperm = true;
else if (r < 0)
- goto finish;
+ goto error;
}
if (eperm && check_loopback() < 0) {
r = -EPERM;
- goto finish;
+ goto error;
}
- r = 0;
-
-finish:
- if (r < 0)
- log_warning("Failed to configure loopback device: %s", strerror(-r));
-
- if (fd >= 0)
- close_nointr_nofail(fd);
+ return 0;
+error:
+ log_warning("Failed to configure loopback device: %s", strerror(-r));
return r;
}
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 7f4c23b130..18e015fe7f 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -35,6 +35,7 @@
#include "mkdir.h"
#include "log.h"
#include "virt.h"
+#include "fileio.h"
static int shorten_uuid(char destination[36], const char *source) {
unsigned i, j;
@@ -71,16 +72,19 @@ static int generate(char id[34]) {
/* First, try reading the D-Bus machine id, unless it is a symlink */
fd = open("/var/lib/dbus/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
-
- k = loop_read(fd, id, 32, false);
+ k = loop_read(fd, id, 33, false);
close_nointr_nofail(fd);
- if (k >= 32) {
- id[32] = '\n';
- id[33] = 0;
+ if (k == 33 && id[32] == '\n') {
- log_info("Initializing machine ID from D-Bus machine ID.");
- return 0;
+ id[32] = 0;
+ if (id128_is_valid(id)) {
+ id[32] = '\n';
+ id[33] = 0;
+
+ log_info("Initializing machine ID from D-Bus machine ID.");
+ return 0;
+ }
}
}
@@ -112,7 +116,7 @@ static int generate(char id[34]) {
* $container_uuid the way libvirt/LXC does it */
r = detect_container(NULL);
if (r > 0) {
- char *e;
+ _cleanup_free_ char *e = NULL;
r = getenv_for_pid(1, "container_uuid", &e);
if (r > 0) {
@@ -120,12 +124,9 @@ static int generate(char id[34]) {
r = shorten_uuid(id, e);
if (r >= 0) {
log_info("Initializing machine ID from container UUID.");
- free(e);
return 0;
}
}
-
- free(e);
}
}
@@ -150,62 +151,57 @@ static int generate(char id[34]) {
}
int machine_id_setup(void) {
- int fd, r;
+ _cleanup_close_ int fd = -1;
+ int r;
bool writable;
struct stat st;
char id[34]; /* 32 + \n + \0 */
- mode_t m;
-
- m = umask(0000);
-
- /* We create this 0444, to indicate that this isn't really
- * something you should ever modify. Of course, since the file
- * will be owned by root it doesn't matter much, but maybe
- * people look. */
-
- fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
- if (fd >= 0)
- writable = true;
- else {
- fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0) {
- umask(m);
- log_error("Cannot open /etc/machine-id: %m");
- return -errno;
- }
- writable = false;
- }
+ RUN_WITH_UMASK(0000) {
+ /* We create this 0444, to indicate that this isn't really
+ * something you should ever modify. Of course, since the file
+ * will be owned by root it doesn't matter much, but maybe
+ * people look. */
+
+ fd = open("/etc/machine-id", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
+ if (fd >= 0)
+ writable = true;
+ else {
+ fd = open("/etc/machine-id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0) {
+ log_error("Cannot open /etc/machine-id: %m");
+ return -errno;
+ }
- umask(m);
+ writable = false;
+ }
+ }
if (fstat(fd, &st) < 0) {
log_error("fstat() failed: %m");
- r = -errno;
- goto finish;
+ return -errno;
}
- if (S_ISREG(st.st_mode)) {
- if (loop_read(fd, id, 32, false) >= 32) {
- r = 0;
- goto finish;
+ if (S_ISREG(st.st_mode))
+ if (loop_read(fd, id, 33, false) == 33 && id[32] == '\n') {
+ id[32] = 0;
+
+ if (id128_is_valid(id))
+ return 0;
}
- }
/* Hmm, so, the id currently stored is not useful, then let's
* generate one */
r = generate(id);
if (r < 0)
- goto finish;
+ return r;
if (S_ISREG(st.st_mode) && writable) {
lseek(fd, 0, SEEK_SET);
- if (loop_write(fd, id, 33, false) == 33) {
- r = 0;
- goto finish;
- }
+ if (loop_write(fd, id, 33, false) == 33)
+ return 0;
}
close_nointr_nofail(fd);
@@ -214,33 +210,28 @@ int machine_id_setup(void) {
/* Hmm, we couldn't write it? So let's write it to
* /run/machine-id as a replacement */
- m = umask(0022);
- r = write_one_line_file("/run/machine-id", id);
- umask(m);
-
+ RUN_WITH_UMASK(0022) {
+ r = write_string_file("/run/machine-id", id);
+ }
if (r < 0) {
log_error("Cannot write /run/machine-id: %s", strerror(-r));
-
unlink("/run/machine-id");
- goto finish;
+ return r;
}
/* And now, let's mount it over */
- r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL) < 0 ? -errno : 0;
+ r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL);
if (r < 0) {
- unlink("/run/machine-id");
- log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
- } else {
- log_info("Installed transient /etc/machine-id file.");
-
- /* Mark the mount read-only */
- mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL);
+ log_error("Failed to mount /etc/machine-id: %m");
+ unlink_noerrno("/run/machine-id");
+ return -errno;
}
-finish:
+ log_info("Installed transient /etc/machine-id file.");
- if (fd >= 0)
- close_nointr_nofail(fd);
+ /* Mark the mount read-only */
+ if (mount(NULL, "/etc/machine-id", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
+ log_warning("Failed to make transient /etc/machine-id read-only: %m");
- return r;
+ return 0;
}
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index 647cce6913..f77082c2db 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -61,11 +61,11 @@ fi \
%{nil}
%udev_hwdb_update() \
-@bindir@/udevadm hwdb --update >/dev/null 2>&1 || : \
+@rootbindir@/udevadm hwdb --update >/dev/null 2>&1 || : \
%{nil}
%udev_rules_update() \
-@bindir@/udevadm control --reload >/dev/null 2>&1 || : \
+@rootbindir@/udevadm control --reload >/dev/null 2>&1 || : \
%{nil}
%journal_catalog_update() \
diff --git a/src/core/main.c b/src/core/main.c
index 1ee3c9c0e8..7fc06bea05 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -40,7 +40,7 @@
#include "fdset.h"
#include "special.h"
#include "conf-parser.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "missing.h"
#include "label.h"
#include "build.h"
@@ -52,6 +52,10 @@
#include "switch-root.h"
#include "capability.h"
#include "killall.h"
+#include "env-util.h"
+#include "hwclock.h"
+#include "sd-daemon.h"
+#include "sd-messages.h"
#include "mount-setup.h"
#include "loopback-setup.h"
@@ -61,10 +65,11 @@
#include "hostname-setup.h"
#include "machine-id-setup.h"
#include "locale-setup.h"
-#include "hwclock.h"
#include "selinux-setup.h"
#include "ima-setup.h"
-#include "sd-daemon.h"
+#include "fileio.h"
+#include "smack-setup.h"
+#include "efivars.h"
static enum {
ACTION_RUN,
@@ -104,20 +109,21 @@ _noreturn_ static void crash(int sig) {
if (!arg_dump_core)
log_error("Caught <%s>, not dumping core.", signal_to_string(sig));
else {
- struct sigaction sa;
+ struct sigaction sa = {
+ .sa_handler = nop_handler,
+ .sa_flags = SA_NOCLDSTOP|SA_RESTART,
+ };
pid_t pid;
/* We want to wait for the core process, hence let's enable SIGCHLD */
- zero(sa);
- sa.sa_handler = nop_handler;
- sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
- if ((pid = fork()) < 0)
+ pid = fork();
+ if (pid < 0)
log_error("Caught <%s>, cannot fork for core dump: %s", signal_to_string(sig), strerror(errno));
else if (pid == 0) {
- struct rlimit rl;
+ struct rlimit rl = {};
/* Enable default signal handler for core dump */
zero(sa);
@@ -125,7 +131,6 @@ _noreturn_ static void crash(int sig) {
assert_se(sigaction(sig, &sa, NULL) == 0);
/* Don't limit the core dump size */
- zero(rl);
rl.rlim_cur = RLIM_INFINITY;
rl.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &rl);
@@ -144,7 +149,8 @@ _noreturn_ static void crash(int sig) {
int r;
/* Order things nicely. */
- if ((r = wait_for_terminate(pid, &status)) < 0)
+ r = wait_for_terminate(pid, &status);
+ if (r < 0)
log_error("Caught <%s>, waitpid() failed: %s", signal_to_string(sig), strerror(-r));
else if (status.si_code != CLD_DUMPED)
log_error("Caught <%s>, core dump failed.", signal_to_string(sig));
@@ -157,16 +163,16 @@ _noreturn_ static void crash(int sig) {
chvt(arg_crash_chvt);
if (arg_crash_shell) {
- struct sigaction sa;
+ struct sigaction sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
+ };
pid_t pid;
log_info("Executing crash shell in 10s...");
sleep(10);
/* Let the kernel reap children for us */
- zero(sa);
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
pid = fork();
@@ -188,12 +194,10 @@ _noreturn_ static void crash(int sig) {
}
static void install_crash_handler(void) {
- struct sigaction sa;
-
- zero(sa);
-
- sa.sa_handler = crash;
- sa.sa_flags = SA_NODEFER;
+ struct sigaction sa = {
+ .sa_handler = crash,
+ .sa_flags = SA_NODEFER,
+ };
sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1);
}
@@ -342,7 +346,8 @@ static int parse_proc_cmdline_word(const char *word) {
else
arg_default_std_error = r;
} else if (startswith(word, "systemd.setenv=")) {
- char *cenv, *eq;
+ _cleanup_free_ char *cenv = NULL;
+ char *eq;
int r;
cenv = strdup(word + 15);
@@ -351,40 +356,58 @@ static int parse_proc_cmdline_word(const char *word) {
eq = strchr(cenv, '=');
if (!eq) {
- r = unsetenv(cenv);
- if (r < 0)
- log_warning("unsetenv failed %m. Ignoring.");
+ if (!env_name_is_valid(cenv))
+ log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv);
+ else {
+ r = unsetenv(cenv);
+ if (r < 0)
+ log_warning("Unsetting environment variable '%s' failed, ignoring: %m", cenv);
+ }
} else {
- *eq = 0;
- r = setenv(cenv, eq + 1, 1);
- if (r < 0)
- log_warning("setenv failed %m. Ignoring.");
+ if (!env_assignment_is_valid(cenv))
+ log_warning("Environment variable assignment '%s' is not valid. Ignoring.", cenv);
+ else {
+ *eq = 0;
+ r = setenv(cenv, eq + 1, 1);
+ if (r < 0)
+ log_warning("Setting environment variable '%s=%s' failed, ignoring: %m", cenv, eq + 1);
+ }
}
- free(cenv);
} else if (startswith(word, "systemd.") ||
(in_initrd() && startswith(word, "rd.systemd."))) {
- log_warning("Unknown kernel switch %s. Ignoring.", word);
-
- log_info("Supported kernel switches:\n"
- "systemd.unit=UNIT Default unit to start\n"
- "rd.systemd.unit=UNIT Default unit to start when run in initrd\n"
- "systemd.dump_core=0|1 Dump core on crash\n"
- "systemd.crash_shell=0|1 Run shell on crash\n"
- "systemd.crash_chvt=N Change to VT #N on crash\n"
- "systemd.confirm_spawn=0|1 Confirm every process spawn\n"
- "systemd.show_status=0|1 Show status updates on the console during bootup\n"
- "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n"
- " Log target\n"
- "systemd.log_level=LEVEL Log level\n"
- "systemd.log_color=0|1 Highlight important log messages\n"
- "systemd.log_location=0|1 Include code location in log messages\n"
- "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
- " Set default log output for services\n"
- "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
- " Set default log error output for services\n"
- "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n");
+ const char *c;
+
+ /* Ignore systemd.journald.xyz and friends */
+ c = word;
+ if (startswith(c, "rd."))
+ c += 3;
+ if (startswith(c, "systemd."))
+ c += 8;
+ if (c[strcspn(c, ".=")] != '.') {
+
+ log_warning("Unknown kernel switch %s. Ignoring.", word);
+
+ log_info("Supported kernel switches:\n"
+ "systemd.unit=UNIT Default unit to start\n"
+ "rd.systemd.unit=UNIT Default unit to start when run in initrd\n"
+ "systemd.dump_core=0|1 Dump core on crash\n"
+ "systemd.crash_shell=0|1 Run shell on crash\n"
+ "systemd.crash_chvt=N Change to VT #N on crash\n"
+ "systemd.confirm_spawn=0|1 Confirm every process spawn\n"
+ "systemd.show_status=0|1 Show status updates on the console during bootup\n"
+ "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n"
+ " Log target\n"
+ "systemd.log_level=LEVEL Log level\n"
+ "systemd.log_color=0|1 Highlight important log messages\n"
+ "systemd.log_location=0|1 Include code location in log messages\n"
+ "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
+ " Set default log output for services\n"
+ "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
+ " Set default log error output for services\n"
+ "systemd.setenv=ASSIGNMENT Set an environment variable for all spawned processes\n");
+ }
} else if (streq(word, "quiet"))
arg_show_status = false;
@@ -400,87 +423,47 @@ static int parse_proc_cmdline_word(const char *word) {
return 0;
}
-static int config_parse_level2(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- log_set_max_level_from_string(rvalue);
- return 0;
-}
-
-static int config_parse_target(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- log_set_target_from_string(rvalue);
- return 0;
-}
-
-static int config_parse_color(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+#define DEFINE_SETTER(name, func, descr) \
+ static int name(const char *unit, \
+ const char *filename, \
+ unsigned line, \
+ const char *section, \
+ const char *lvalue, \
+ int ltype, \
+ const char *rvalue, \
+ void *data, \
+ void *userdata) { \
+ \
+ int r; \
+ \
+ assert(filename); \
+ assert(lvalue); \
+ assert(rvalue); \
+ \
+ r = func(rvalue); \
+ if (r < 0) \
+ log_syntax(unit, LOG_ERR, filename, line, -r, \
+ "Invalid " descr "'%s': %s", \
+ rvalue, strerror(-r)); \
+ \
+ return 0; \
+ }
- assert(filename);
- assert(lvalue);
- assert(rvalue);
+DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level")
+DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target")
+DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" )
+DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location")
- log_show_color_from_string(rvalue);
- return 0;
-}
-static int config_parse_location(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- log_show_location_from_string(rvalue);
- return 0;
-}
-
-static int config_parse_cpu_affinity2(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+static int config_parse_cpu_affinity2(const char *unit,
+ 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;
@@ -508,7 +491,8 @@ static int config_parse_cpu_affinity2(
return log_oom();
if (r < 0 || cpu >= ncpus) {
- log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to parse CPU affinity '%s'", rvalue);
CPU_FREE(c);
return -EBADMSG;
}
@@ -518,7 +502,7 @@ static int config_parse_cpu_affinity2(
if (c) {
if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
- log_warning("Failed to set CPU affinity: %m");
+ log_warning_unit(unit, "Failed to set CPU affinity: %m");
CPU_FREE(c);
}
@@ -539,22 +523,19 @@ static void strv_free_free(char ***l) {
}
static void free_join_controllers(void) {
- if (!arg_join_controllers)
- return;
-
strv_free_free(arg_join_controllers);
arg_join_controllers = NULL;
}
-static int config_parse_join_controllers(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+static int config_parse_join_controllers(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
unsigned n = 0;
char *state, *w;
@@ -660,8 +641,8 @@ static int parse_config_file(void) {
{ "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output },
{ "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error },
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
- { "Manager", "RuntimeWatchdogSec", config_parse_usec, 0, &arg_runtime_watchdog },
- { "Manager", "ShutdownWatchdogSec", config_parse_usec, 0, &arg_shutdown_watchdog },
+ { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
+ { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
{ "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
{ "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
{ "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU]},
@@ -683,11 +664,11 @@ static int parse_config_file(void) {
{ NULL, NULL, NULL, 0, NULL }
};
- FILE *f;
+ _cleanup_fclose_ FILE *f;
const char *fn;
int r;
- fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
+ fn = arg_running_as == SYSTEMD_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf";
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
@@ -697,17 +678,16 @@ static int parse_config_file(void) {
return 0;
}
- r = config_parse(fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, NULL);
+ r = config_parse(NULL, fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, false, NULL);
if (r < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
- fclose(f);
-
return 0;
}
static int parse_proc_cmdline(void) {
- char *line, *w, *state;
+ _cleanup_free_ char *line = NULL;
+ char *w, *state;
int r;
size_t l;
@@ -716,34 +696,27 @@ static int parse_proc_cmdline(void) {
if (detect_container(NULL) > 0)
return 0;
- if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+ r = read_one_line_file("/proc/cmdline", &line);
+ if (r < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
FOREACH_WORD_QUOTED(w, l, line, state) {
- char *word;
+ _cleanup_free_ char *word;
- if (!(word = strndup(w, l))) {
- r = -ENOMEM;
- goto finish;
- }
+ word = strndup(w, l);
+ if (!word)
+ return log_oom();
r = parse_proc_cmdline_word(word);
if (r < 0) {
log_error("Failed on cmdline argument %s: %s", word, strerror(-r));
- free(word);
- goto finish;
+ return r;
}
-
- free(word);
}
- r = 0;
-
-finish:
- free(line);
- return r;
+ return 0;
}
static int parse_argv(int argc, char *argv[]) {
@@ -1069,7 +1042,7 @@ static int version(void) {
return 0;
}
-static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize_jobs) {
+static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) {
FILE *f = NULL;
FDSet *fds = NULL;
int r;
@@ -1094,7 +1067,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool serialize
goto fail;
}
- r = manager_serialize(m, f, fds, serialize_jobs);
+ r = manager_serialize(m, f, fds, switching_root);
if (r < 0) {
log_error("Failed to serialize state: %s", strerror(-r));
goto fail;
@@ -1244,14 +1217,14 @@ static int initialize_join_controllers(void) {
return -ENOMEM;
arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
- if (!arg_join_controllers[0])
- return -ENOMEM;
-
arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
- if (!arg_join_controllers[1])
+ arg_join_controllers[2] = NULL;
+
+ if (!arg_join_controllers[0] || !arg_join_controllers[1]) {
+ free_join_controllers();
return -ENOMEM;
+ }
- arg_join_controllers[2] = NULL;
return 0;
}
@@ -1264,6 +1237,10 @@ int main(int argc, char *argv[]) {
bool reexecute = false;
const char *shutdown_verb = NULL;
dual_timestamp initrd_timestamp = { 0ULL, 0ULL };
+ dual_timestamp userspace_timestamp = { 0ULL, 0ULL };
+ dual_timestamp kernel_timestamp = { 0ULL, 0ULL };
+ dual_timestamp firmware_timestamp = { 0ULL, 0ULL };
+ dual_timestamp loader_timestamp = { 0ULL, 0ULL };
static char systemd[] = "systemd";
bool skip_setup = false;
int j;
@@ -1285,22 +1262,19 @@ int main(int argc, char *argv[]) {
}
#endif
+ dual_timestamp_from_monotonic(&kernel_timestamp, 0);
+ dual_timestamp_get(&userspace_timestamp);
+
/* Determine if this is a reexecution or normal bootup. We do
* the full command line parsing much later, so let's just
* have a quick peek here. */
- for (j = 1; j < argc; j++)
- if (streq(argv[j], "--deserialize")) {
- skip_setup = true;
- break;
- }
+ if (strv_find(argv+1, "--deserialize"))
+ skip_setup = true;
/* If we have switched root, do all the special setup
* things */
- for (j = 1; j < argc; j++)
- if (streq(argv[j], "--switched-root")) {
- skip_setup = false;
- break;
- }
+ if (strv_find(argv+1, "--switched-root"))
+ skip_setup = false;
/* If we get started via the /sbin/init symlink then we are
called 'init'. After a subsequent reexecution we are then
@@ -1315,7 +1289,9 @@ int main(int argc, char *argv[]) {
log_show_color(isatty(STDERR_FILENO) > 0);
if (getpid() == 1 && detect_container(NULL) <= 0) {
-
+#ifdef ENABLE_EFI
+ efi_get_boot_timestamps(&userspace_timestamp, &firmware_timestamp, &loader_timestamp);
+#endif
/* Running outside of a container as PID 1 */
arg_running_as = SYSTEMD_SYSTEM;
make_null_stdio();
@@ -1325,7 +1301,7 @@ int main(int argc, char *argv[]) {
if (in_initrd()) {
char *rd_timestamp = NULL;
- dual_timestamp_get(&initrd_timestamp);
+ initrd_timestamp = userspace_timestamp;
asprintf(&rd_timestamp, "%llu %llu",
(unsigned long long) initrd_timestamp.realtime,
(unsigned long long) initrd_timestamp.monotonic);
@@ -1336,10 +1312,13 @@ int main(int argc, char *argv[]) {
}
if (!skip_setup) {
+ mount_setup_early();
if (selinux_setup(&loaded_policy) < 0)
goto finish;
if (ima_setup() < 0)
goto finish;
+ if (smack_setup() < 0)
+ goto finish;
}
if (label_init(NULL) < 0)
@@ -1382,7 +1361,6 @@ int main(int argc, char *argv[]) {
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
} else if (getpid() == 1) {
-
/* Running inside a container, as PID 1 */
arg_running_as = SYSTEMD_SYSTEM;
log_set_target(LOG_TARGET_CONSOLE);
@@ -1391,12 +1369,21 @@ int main(int argc, char *argv[]) {
/* For the later on, see above... */
log_set_target(LOG_TARGET_JOURNAL);
- } else {
+ /* clear the kernel timestamp,
+ * because we are in a container */
+ kernel_timestamp.monotonic = 0ULL;
+ kernel_timestamp.realtime = 0ULL;
+ } else {
/* Running as user instance */
arg_running_as = SYSTEMD_USER;
log_set_target(LOG_TARGET_AUTO);
log_open();
+
+ /* clear the kernel timestamp,
+ * because we are not PID 1 */
+ kernel_timestamp.monotonic = 0ULL;
+ kernel_timestamp.realtime = 0ULL;
}
/* Initialize default unit */
@@ -1412,7 +1399,7 @@ int main(int argc, char *argv[]) {
/* Mount /proc, /sys and friends, so that /proc/cmdline and
* /proc/$PID/fd is available. */
- if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) {
+ if (getpid() == 1) {
r = mount_setup(loaded_policy);
if (r < 0)
goto finish;
@@ -1554,10 +1541,9 @@ int main(int argc, char *argv[]) {
/* Make sure we leave a core dump without panicing the
* kernel. */
- if (getpid() == 1)
+ if (getpid() == 1) {
install_crash_handler();
- if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) {
r = mount_cgroup_controllers(arg_join_controllers);
if (r < 0)
goto finish;
@@ -1639,12 +1625,14 @@ int main(int argc, char *argv[]) {
m->default_std_error = arg_default_std_error;
m->runtime_watchdog = arg_runtime_watchdog;
m->shutdown_watchdog = arg_shutdown_watchdog;
+ m->userspace_timestamp = userspace_timestamp;
+ m->kernel_timestamp = kernel_timestamp;
+ m->firmware_timestamp = firmware_timestamp;
+ m->loader_timestamp = loader_timestamp;
+ m->initrd_timestamp = initrd_timestamp;
manager_set_default_rlimits(m, arg_default_rlimit);
- if (dual_timestamp_is_set(&initrd_timestamp))
- m->initrd_timestamp = initrd_timestamp;
-
if (arg_default_controllers)
manager_set_default_controllers(m, arg_default_controllers);
@@ -1710,18 +1698,29 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
}
- r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job);
- if (r < 0) {
- log_error("Failed to start default target: %s", bus_error(&error, r));
+ r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, false, &error, &default_unit_job);
+ if (r == -EPERM) {
+ log_debug("Default target could not be isolated, starting instead: %s", bus_error(&error, r));
+ dbus_error_free(&error);
+
+ r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job);
+ if (r < 0) {
+ log_error("Failed to start default target: %s", bus_error(&error, r));
+ dbus_error_free(&error);
+ goto finish;
+ }
+ } else if (r < 0) {
+ log_error("Failed to isolate default target: %s", bus_error(&error, r));
dbus_error_free(&error);
goto finish;
}
+
m->default_unit_job_id = default_unit_job->id;
after_startup = now(CLOCK_MONOTONIC);
log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG,
"Loaded units and determined initial transaction in %s.",
- format_timespan(timespan, sizeof(timespan), after_startup - before_startup));
+ format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 0));
if (arg_action == ACTION_TEST) {
printf("-> By jobs:\n");
@@ -1754,7 +1753,7 @@ int main(int argc, char *argv[]) {
case MANAGER_REEXECUTE:
- if (prepare_reexecute(m, &serialization, &fds, true) < 0)
+ if (prepare_reexecute(m, &serialization, &fds, false) < 0)
goto finish;
reexecute = true;
@@ -1768,7 +1767,7 @@ int main(int argc, char *argv[]) {
m->switch_root = m->switch_root_init = NULL;
if (!switch_root_init)
- if (prepare_reexecute(m, &serialization, &fds, false) < 0)
+ if (prepare_reexecute(m, &serialization, &fds, true) < 0)
goto finish;
reexecute = true;
diff --git a/src/core/manager.c b/src/core/manager.c
index df0fd63e87..c7f8f20a70 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -70,6 +70,7 @@
#include "cgroup-util.h"
#include "path-util.h"
#include "audit-fd.h"
+#include "env-util.h"
/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_ENTRIES_MAX 16
@@ -77,18 +78,28 @@
/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
+/* Initial delay and the interval for printing status messages about running jobs */
+#define JOBS_IN_PROGRESS_WAIT_SEC 5
+#define JOBS_IN_PROGRESS_PERIOD_SEC 1
+#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
+
/* Where clients shall send notification messages to */
#define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify"
+#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
+
static int manager_setup_notify(Manager *m) {
union {
struct sockaddr sa;
struct sockaddr_un un;
- } sa;
- struct epoll_event ev;
- int one = 1;
-
- assert(m);
+ } sa = {
+ .sa.sa_family = AF_UNIX,
+ };
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->notify_watch,
+ };
+ int one = 1, r;
m->notify_watch.type = WATCH_NOTIFY;
m->notify_watch.fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
@@ -97,9 +108,6 @@ static int manager_setup_notify(Manager *m) {
return -errno;
}
- zero(sa);
- sa.sa.sa_family = AF_UNIX;
-
if (getpid() != 1 || detect_container(NULL) > 0)
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET "/%llu", random_ull());
else
@@ -107,22 +115,22 @@ static int manager_setup_notify(Manager *m) {
sa.un.sun_path[0] = 0;
- if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
+ r = bind(m->notify_watch.fd, &sa.sa,
+ offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1));
+ if (r < 0) {
log_error("bind() failed: %m");
return -errno;
}
- if (setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) {
+ r = setsockopt(m->notify_watch.fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ if (r < 0) {
log_error("SO_PASSCRED failed: %m");
return -errno;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->notify_watch;
-
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0) {
- log_error("Failed to add timer change fd to epoll: %m");
+ r = epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev);
+ if (r < 0) {
+ log_error("Failed to add notification socket fd to epoll: %m");
return -errno;
}
@@ -136,11 +144,151 @@ static int manager_setup_notify(Manager *m) {
return 0;
}
+static int manager_jobs_in_progress_mod_timer(Manager *m) {
+ struct itimerspec its = {
+ .it_value.tv_sec = JOBS_IN_PROGRESS_WAIT_SEC,
+ .it_interval.tv_sec = JOBS_IN_PROGRESS_PERIOD_SEC,
+ };
+
+ if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS)
+ return 0;
+
+ if (timerfd_settime(m->jobs_in_progress_watch.fd, 0, &its, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int manager_watch_jobs_in_progress(Manager *m) {
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->jobs_in_progress_watch,
+ };
+ int r;
+
+ if (m->jobs_in_progress_watch.type != WATCH_INVALID)
+ return 0;
+
+ m->jobs_in_progress_watch.type = WATCH_JOBS_IN_PROGRESS;
+ m->jobs_in_progress_watch.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (m->jobs_in_progress_watch.fd < 0) {
+ log_error("Failed to create timerfd: %m");
+ r = -errno;
+ goto err;
+ }
+
+ r = manager_jobs_in_progress_mod_timer(m);
+ if (r < 0) {
+ log_error("Failed to set up timer for jobs progress watch: %s", strerror(-r));
+ goto err;
+ }
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->jobs_in_progress_watch.fd, &ev) < 0) {
+ log_error("Failed to add jobs progress timer fd to epoll: %m");
+ r = -errno;
+ goto err;
+ }
+
+ log_debug("Set up jobs progress timerfd.");
+
+ return 0;
+
+err:
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ return r;
+}
+
+static void manager_unwatch_jobs_in_progress(Manager *m) {
+ if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS)
+ return;
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->jobs_in_progress_watch.fd, NULL) >= 0);
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ m->jobs_in_progress_iteration = 0;
+
+ log_debug("Closed jobs progress timerfd.");
+}
+
+#define CYLON_BUFFER_EXTRA (2*strlen(ANSI_RED_ON) + strlen(ANSI_HIGHLIGHT_RED_ON) + 2*strlen(ANSI_HIGHLIGHT_OFF))
+static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) {
+ char *p = buffer;
+
+ assert(buflen >= CYLON_BUFFER_EXTRA + width + 1);
+ assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */
+
+ if (pos > 1) {
+ if (pos > 2)
+ p = mempset(p, ' ', pos-2);
+ p = stpcpy(p, ANSI_RED_ON);
+ *p++ = '*';
+ }
+
+ if (pos > 0 && pos <= width) {
+ p = stpcpy(p, ANSI_HIGHLIGHT_RED_ON);
+ *p++ = '*';
+ }
+
+ p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+
+ if (pos < width) {
+ p = stpcpy(p, ANSI_RED_ON);
+ *p++ = '*';
+ if (pos < width-1)
+ p = mempset(p, ' ', width-1-pos);
+ p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+ }
+}
+
+static void manager_print_jobs_in_progress(Manager *m) {
+ Iterator i;
+ Job *j;
+ char *job_of_n = NULL;
+ unsigned counter = 0, print_nr;
+ char cylon[6 + CYLON_BUFFER_EXTRA + 1];
+ unsigned cylon_pos;
+
+ print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ if (j->state == JOB_RUNNING && counter++ == print_nr)
+ break;
+
+ /* m->n_running_jobs must be consistent with the contents of m->jobs,
+ * so the above loop must have succeeded in finding j. */
+ assert(counter == print_nr + 1);
+
+ cylon_pos = m->jobs_in_progress_iteration % 14;
+ if (cylon_pos >= 8)
+ cylon_pos = 14 - cylon_pos;
+ draw_cylon(cylon, sizeof(cylon), 6, cylon_pos);
+
+ if (m->n_running_jobs > 1)
+ if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0)
+ job_of_n = NULL;
+
+ manager_status_printf(m, true, cylon, "%sA %s job is running for %s",
+ strempty(job_of_n), job_type_to_string(j->type), unit_description(j->unit));
+ free(job_of_n);
+
+ m->jobs_in_progress_iteration++;
+}
+
static int manager_setup_time_change(Manager *m) {
- struct epoll_event ev;
- struct itimerspec its;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->time_change_watch,
+ };
+
+ /* We only care for the cancellation event, hence we set the
+ * timeout to the latest possible value. */
+ struct itimerspec its = {
+ .it_value.tv_sec = TIME_T_MAX,
+ };
+ assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
- assert(m);
assert(m->time_change_watch.type == WATCH_INVALID);
/* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
@@ -153,13 +301,6 @@ static int manager_setup_time_change(Manager *m) {
return -errno;
}
- zero(its);
-
- /* We only care for the cancellation event, hence we set the
- * timeout to the latest possible value. */
- assert_cc(sizeof(time_t) == sizeof(long));
- its.it_value.tv_sec = LONG_MAX;
-
if (timerfd_settime(m->time_change_watch.fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) {
log_debug("Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m");
close_nointr_nofail(m->time_change_watch.fd);
@@ -167,10 +308,6 @@ static int manager_setup_time_change(Manager *m) {
return 0;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->time_change_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->time_change_watch.fd, &ev) < 0) {
log_error("Failed to add timer change fd to epoll: %m");
return -errno;
@@ -210,15 +347,18 @@ static int enable_special_signals(Manager *m) {
static int manager_setup_signals(Manager *m) {
sigset_t mask;
- struct epoll_event ev;
- struct sigaction sa;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->signal_watch,
+ };
+ struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_NOCLDSTOP|SA_RESTART,
+ };
assert(m);
/* We are not interested in SIGSTOP and friends. */
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
assert_se(sigemptyset(&mask) == 0);
@@ -260,10 +400,6 @@ static int manager_setup_signals(Manager *m) {
if (m->signal_watch.fd < 0)
return -errno;
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.ptr = &m->signal_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
return -errno;
@@ -286,6 +422,9 @@ static void manager_strip_environment(Manager *m) {
* the initrd interface:
* http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
strv_remove_prefix(m->environment, "RD_");
+
+ /* Drop invalid entries */
+ strv_env_clean(m->environment);
}
int manager_new(SystemdRunningAs running_as, Manager **_m) {
@@ -300,9 +439,6 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
if (!m)
return -ENOMEM;
- dual_timestamp_get(&m->userspace_timestamp);
- dual_timestamp_from_monotonic(&m->kernel_timestamp, 0);
-
m->running_as = running_as;
m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
@@ -314,6 +450,7 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
watch_init(&m->swap_watch);
watch_init(&m->udev_watch);
watch_init(&m->time_change_watch);
+ watch_init(&m->jobs_in_progress_watch);
m->epoll_fd = m->dev_autofs_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
@@ -519,6 +656,9 @@ static void manager_clear_jobs_and_units(Manager *m) {
assert(hashmap_isempty(m->jobs));
assert(hashmap_isempty(m->units));
+
+ m->n_on_console = 0;
+ m->n_running_jobs = 0;
}
void manager_free(Manager *m) {
@@ -554,6 +694,8 @@ void manager_free(Manager *m) {
close_nointr_nofail(m->notify_watch.fd);
if (m->time_change_watch.fd >= 0)
close_nointr_nofail(m->time_change_watch.fd);
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
free(m->notify_socket);
@@ -617,14 +759,15 @@ int manager_coldplug(Manager *m) {
static void manager_build_unit_path_cache(Manager *m) {
char **i;
- DIR *d = NULL;
+ _cleanup_free_ DIR *d = NULL;
int r;
assert(m);
set_free_free(m->unit_path_cache);
- if (!(m->unit_path_cache = set_new(string_hash_func, string_compare_func))) {
+ m->unit_path_cache = set_new(string_hash_func, string_compare_func);
+ if (!m->unit_path_cache) {
log_error("Failed to allocate unit path cache.");
return;
}
@@ -637,7 +780,8 @@ static void manager_build_unit_path_cache(Manager *m) {
d = opendir(*i);
if (!d) {
- log_error("Failed to open directory: %m");
+ if (errno != ENOENT)
+ log_error("Failed to open directory %s: %m", *i);
continue;
}
@@ -653,10 +797,9 @@ static void manager_build_unit_path_cache(Manager *m) {
goto fail;
}
- if ((r = set_put(m->unit_path_cache, p)) < 0) {
- free(p);
+ r = set_consume(m->unit_path_cache, p);
+ if (r < 0)
goto fail;
- }
}
closedir(d);
@@ -670,9 +813,6 @@ fail:
set_free_free(m->unit_path_cache);
m->unit_path_cache = NULL;
-
- if (d)
- closedir(d);
}
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
@@ -756,7 +896,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
job_type_collapse(&type, unit);
- tr = transaction_new();
+ tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY);
if (!tr)
return -ENOMEM;
@@ -978,6 +1118,10 @@ unsigned manager_dispatch_run_queue(Manager *m) {
}
m->dispatching_run_queue = false;
+
+ if (m->n_running_jobs > 0)
+ manager_watch_jobs_in_progress(m);
+
return n;
}
@@ -1018,30 +1162,29 @@ static int manager_process_notify_fd(Manager *m) {
for (;;) {
char buf[4096];
- struct msghdr msghdr;
- struct iovec iovec;
- struct ucred *ucred;
+ struct iovec iovec = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)-1,
+ };
+
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
- } control;
+ } control = {};
+
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+ struct ucred *ucred;
Unit *u;
- char **tags;
-
- zero(iovec);
- iovec.iov_base = buf;
- iovec.iov_len = sizeof(buf)-1;
-
- zero(control);
- zero(msghdr);
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
- msghdr.msg_control = &control;
- msghdr.msg_controllen = sizeof(control);
+ _cleanup_strv_free_ char **tags = NULL;
n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT);
if (n <= 0) {
- if (n >= 0)
+ if (n == 0)
return -EIO;
if (errno == EAGAIN || errno == EINTR)
@@ -1079,8 +1222,6 @@ static int manager_process_notify_fd(Manager *m) {
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
-
- strv_free(tags);
}
return 0;
@@ -1090,12 +1231,10 @@ static int manager_dispatch_sigchld(Manager *m) {
assert(m);
for (;;) {
- siginfo_t si;
+ siginfo_t si = {};
Unit *u;
int r;
- zero(si);
-
/* First we call waitd() for a PID and do not reap the
* zombie. That way we can still access /proc/$PID for
* it while it is a zombie. */
@@ -1114,7 +1253,7 @@ static int manager_dispatch_sigchld(Manager *m) {
break;
if (si.si_code == CLD_EXITED || si.si_code == CLD_KILLED || si.si_code == CLD_DUMPED) {
- char _cleanup_free_ *name = NULL;
+ _cleanup_free_ char *name = NULL;
get_process_comm(si.si_pid, &name);
log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name));
@@ -1505,6 +1644,8 @@ static int process_event(Manager *m, struct epoll_event *ev) {
NULL);
/* Restart the watch */
+ epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->time_change_watch.fd,
+ NULL);
close_nointr_nofail(m->time_change_watch.fd);
watch_init(&m->time_change_watch);
manager_setup_time_change(m);
@@ -1517,6 +1658,16 @@ static int process_event(Manager *m, struct epoll_event *ev) {
break;
}
+ case WATCH_JOBS_IN_PROGRESS: {
+ uint64_t v;
+
+ /* not interested in the data */
+ read(w->fd, &v, sizeof(v));
+
+ manager_print_jobs_in_progress(m);
+ break;
+ }
+
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
@@ -1724,7 +1875,8 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
/* We set SOCK_NONBLOCK here so that we rather drop the
* message then wait for plymouth */
- if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0) {
log_error("socket() failed: %m");
return;
}
@@ -1807,7 +1959,6 @@ void manager_dispatch_bus_query_pid_done(
int manager_open_serialization(Manager *m, FILE **_f) {
char *path = NULL;
- mode_t saved_umask;
int fd;
FILE *f;
@@ -1821,9 +1972,9 @@ int manager_open_serialization(Manager *m, FILE **_f) {
if (!path)
return -ENOMEM;
- saved_umask = umask(0077);
- fd = mkostemp(path, O_RDWR|O_CLOEXEC);
- umask(saved_umask);
+ RUN_WITH_UMASK(0077) {
+ fd = mkostemp(path, O_RDWR|O_CLOEXEC);
+ }
if (fd < 0) {
free(path);
@@ -1844,10 +1995,11 @@ int manager_open_serialization(Manager *m, FILE **_f) {
return 0;
}
-int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
+int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
Iterator i;
Unit *u;
const char *t;
+ char **e;
int r;
assert(m);
@@ -1871,6 +2023,16 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
}
+ if (!switching_root) {
+ STRV_FOREACH(e, m->environment) {
+ _cleanup_free_ char *ce;
+
+ ce = cescape(*e);
+ if (ce)
+ fprintf(f, "env=%s\n", *e);
+ }
+ }
+
fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
@@ -1884,7 +2046,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
fputs(u->id, f);
fputc('\n', f);
- if ((r = unit_serialize(u, f, fds, serialize_jobs)) < 0) {
+ if ((r = unit_serialize(u, f, fds, !switching_root)) < 0) {
m->n_reloading --;
return r;
}
@@ -1971,7 +2133,25 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
dual_timestamp_deserialize(l+20, &m->userspace_timestamp);
else if (startswith(l, "finish-timestamp="))
dual_timestamp_deserialize(l+17, &m->finish_timestamp);
- else
+ else if (startswith(l, "env=")) {
+ _cleanup_free_ char *uce = NULL;
+ char **e;
+
+ uce = cunescape(l+4);
+ if (!uce) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ e = strv_env_set(m->environment, uce);
+ if (!e) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ strv_free(m->environment);
+ m->environment = e;
+ } else
log_debug("Unknown serialization item '%s'", l);
}
@@ -2054,7 +2234,7 @@ int manager_reload(Manager *m) {
goto finish;
}
- r = manager_serialize(m, f, fds, true);
+ r = manager_serialize(m, f, fds, false);
if (r < 0) {
m->n_reloading --;
goto finish;
@@ -2115,7 +2295,7 @@ finish:
return r;
}
-bool manager_is_booting_or_shutting_down(Manager *m) {
+static bool manager_is_booting_or_shutting_down(Manager *m) {
Unit *u;
assert(m);
@@ -2132,6 +2312,12 @@ bool manager_is_booting_or_shutting_down(Manager *m) {
return false;
}
+bool manager_is_reloading_or_reexecuting(Manager *m) {
+ assert(m);
+
+ return m->n_reloading != 0;
+}
+
void manager_reset_failed(Manager *m) {
Unit *u;
Iterator i;
@@ -2142,7 +2328,7 @@ void manager_reset_failed(Manager *m) {
unit_reset_failed(u);
}
-bool manager_unit_pending_inactive(Manager *m, const char *name) {
+bool manager_unit_inactive_or_pending(Manager *m, const char *name) {
Unit *u;
assert(m);
@@ -2153,7 +2339,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) {
if (!u)
return true;
- return unit_pending_inactive(u);
+ return unit_inactive_or_pending(u);
}
void manager_check_finished(Manager *m) {
@@ -2162,8 +2348,13 @@ void manager_check_finished(Manager *m) {
assert(m);
- if (hashmap_size(m->jobs) > 0)
+ if (m->n_running_jobs == 0)
+ manager_unwatch_jobs_in_progress(m);
+
+ if (hashmap_size(m->jobs) > 0) {
+ manager_jobs_in_progress_mod_timer(m);
return;
+ }
/* Notify Type=idle units that we are done now */
close_pipe(m->idle_pipe);
@@ -2200,10 +2391,10 @@ void manager_check_finished(Manager *m) {
"INITRD_USEC=%llu", (unsigned long long) initrd_usec,
"USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
"MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
- format_timespan(kernel, sizeof(kernel), kernel_usec),
- format_timespan(initrd, sizeof(initrd), initrd_usec),
- format_timespan(userspace, sizeof(userspace), userspace_usec),
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+ format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC),
+ format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
} else {
kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic;
@@ -2215,9 +2406,9 @@ void manager_check_finished(Manager *m) {
"KERNEL_USEC=%llu", (unsigned long long) kernel_usec,
"USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
"MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.",
- format_timespan(kernel, sizeof(kernel), kernel_usec),
- format_timespan(userspace, sizeof(userspace), userspace_usec),
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC),
+ format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
}
} else {
@@ -2229,7 +2420,7 @@ void manager_check_finished(Manager *m) {
MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
"USERSPACE_USEC=%llu", (unsigned long long) userspace_usec,
"MESSAGE=Startup finished in %s.",
- format_timespan(sum, sizeof(sum), total_usec),
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC),
NULL);
}
@@ -2237,7 +2428,7 @@ void manager_check_finished(Manager *m) {
sd_notifyf(false,
"READY=1\nSTATUS=Startup finished in %s.",
- format_timespan(sum, sizeof(sum), total_usec));
+ format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC));
}
static int create_generator_dir(Manager *m, char **generator, const char *name) {
@@ -2259,7 +2450,8 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
r = mkdir_p_label(p, 0755);
if (r < 0) {
- log_error("Failed to create generator directory: %s", strerror(-r));
+ log_error("Failed to create generator directory %s: %s",
+ p, strerror(-r));
free(p);
return r;
}
@@ -2269,8 +2461,9 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
return log_oom();
if (!mkdtemp(p)) {
+ log_error("Failed to create generator directory %s: %m",
+ p);
free(p);
- log_error("Failed to create generator directory: %m");
return -errno;
}
}
@@ -2298,7 +2491,6 @@ void manager_run_generators(Manager *m) {
DIR *d = NULL;
const char *generator_path;
const char *argv[5];
- mode_t u;
int r;
assert(m);
@@ -2309,7 +2501,8 @@ void manager_run_generators(Manager *m) {
if (errno == ENOENT)
return;
- log_error("Failed to enumerate generator directory: %m");
+ log_error("Failed to enumerate generator directory %s: %m",
+ generator_path);
return;
}
@@ -2331,9 +2524,9 @@ void manager_run_generators(Manager *m) {
argv[3] = m->generator_unit_path_late;
argv[4] = NULL;
- u = umask(0022);
- execute_directory(generator_path, d, (char**) argv);
- umask(u);
+ RUN_WITH_UMASK(0022) {
+ execute_directory(generator_path, d, (char**) argv);
+ }
trim_generator_dir(m, &m->generator_unit_path);
trim_generator_dir(m, &m->generator_unit_path_early);
@@ -2439,7 +2632,7 @@ void manager_set_show_status(Manager *m, bool b) {
unlink("/run/systemd/show-status");
}
-bool manager_get_show_status(Manager *m) {
+static bool manager_get_show_status(Manager *m) {
assert(m);
if (m->running_as != SYSTEMD_SYSTEM)
@@ -2454,6 +2647,25 @@ bool manager_get_show_status(Manager *m) {
return plymouth_running();
}
+void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) {
+ va_list ap;
+
+ if (!manager_get_show_status(m))
+ return;
+
+ /* XXX We should totally drop the check for ephemeral here
+ * and thus effectively make 'Type=idle' pointless. */
+ if (ephemeral && m->n_on_console > 0)
+ return;
+
+ if (!manager_is_booting_or_shutting_down(m))
+ return;
+
+ va_start(ap, format);
+ status_vprintf(status, true, ephemeral, format, ap);
+ va_end(ap);
+}
+
void watch_init(Watch *w) {
assert(w);
diff --git a/src/core/manager.h b/src/core/manager.h
index cc4edf8f1e..bf833540ae 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -61,7 +61,8 @@ enum WatchType {
WATCH_UDEV,
WATCH_DBUS_WATCH,
WATCH_DBUS_TIMEOUT,
- WATCH_TIME_CHANGE
+ WATCH_TIME_CHANGE,
+ WATCH_JOBS_IN_PROGRESS
};
struct Watch {
@@ -84,6 +85,7 @@ struct Watch {
#include "set.h"
#include "dbus.h"
#include "path-lookup.h"
+#include "execute.h"
struct Manager {
/* Note that the set of units we know of is allowed to be
@@ -127,6 +129,7 @@ struct Manager {
Watch notify_watch;
Watch signal_watch;
Watch time_change_watch;
+ Watch jobs_in_progress_watch;
int epoll_fd;
@@ -225,6 +228,11 @@ struct Manager {
unsigned n_installed_jobs;
unsigned n_failed_jobs;
+ /* Jobs in progress watching */
+ unsigned n_running_jobs;
+ unsigned n_on_console;
+ unsigned jobs_in_progress_iteration;
+
/* Type=idle pipes */
int idle_pipe[2];
@@ -270,20 +278,20 @@ void manager_dispatch_bus_query_pid_done(Manager *m, const char *name, pid_t pid
int manager_open_serialization(Manager *m, FILE **_f);
-int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs);
+int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root);
int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
int manager_distribute_fds(Manager *m, FDSet *fds);
int manager_reload(Manager *m);
-bool manager_is_booting_or_shutting_down(Manager *m);
+bool manager_is_reloading_or_reexecuting(Manager *m) _pure_;
void manager_reset_failed(Manager *m);
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
void manager_send_unit_plymouth(Manager *m, Unit *u);
-bool manager_unit_pending_inactive(Manager *m, const char *name);
+bool manager_unit_inactive_or_pending(Manager *m, const char *name);
void manager_check_finished(Manager *m);
@@ -293,6 +301,6 @@ void manager_undo_generators(Manager *m);
void manager_recheck_journal(Manager *m);
void manager_set_show_status(Manager *m, bool b);
-bool manager_get_show_status(Manager *m);
+void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) _printf_attr_(4,5);
void watch_init(Watch *w);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 98614d0c3e..4629808a7a 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -41,6 +41,7 @@
#include "path-util.h"
#include "missing.h"
#include "virt.h"
+#include "efivars.h"
#ifndef TTY_GID
#define TTY_GID 5
@@ -65,7 +66,7 @@ typedef struct MountPoint {
/* The first three entries we might need before SELinux is up. The
* fourth (securityfs) is needed by IMA to load a custom policy. The
* other ones we can delay until SELinux and IMA are loaded. */
-#define N_EARLY_MOUNT 4
+#define N_EARLY_MOUNT 5
static const MountPoint mount_table[] = {
{ "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
@@ -76,8 +77,8 @@ static const MountPoint mount_table[] = {
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
- { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- is_efiboot, MNT_NONE },
+ { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
+ NULL, MNT_NONE },
{ "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
@@ -86,8 +87,18 @@ static const MountPoint mount_table[] = {
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
NULL, MNT_IN_CONTAINER },
+#ifdef HAVE_XATTR
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_IN_CONTAINER },
+#endif
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_IN_CONTAINER },
+ { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_NONE },
+#ifdef ENABLE_EFI
+ { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ is_efi_boot, MNT_NONE },
+#endif
};
/* These are API file systems that might be mounted by other software,
@@ -200,9 +211,9 @@ int mount_setup_early(void) {
int mount_cgroup_controllers(char ***join_controllers) {
int r;
- FILE *f;
char buf[LINE_MAX];
- Set *controllers;
+ _cleanup_set_free_free_ Set *controllers = NULL;
+ _cleanup_fclose_ FILE *f;
/* Mount all available cgroup controllers that are built into the kernel. */
@@ -213,10 +224,8 @@ int mount_cgroup_controllers(char ***join_controllers) {
}
controllers = set_new(string_hash_func, string_compare_func);
- if (!controllers) {
- r = log_oom();
- goto finish;
- }
+ if (!controllers)
+ return log_oom();
/* Ignore the header line */
(void) fgets(buf, sizeof(buf), f);
@@ -231,8 +240,7 @@ int mount_cgroup_controllers(char ***join_controllers) {
break;
log_error("Failed to parse /proc/cgroups.");
- r = -EIO;
- goto finish;
+ return -EIO;
}
if (!enabled) {
@@ -240,18 +248,22 @@ int mount_cgroup_controllers(char ***join_controllers) {
continue;
}
- r = set_put(controllers, controller);
+ r = set_consume(controllers, controller);
if (r < 0) {
log_error("Failed to add controller to set.");
- free(controller);
- goto finish;
+ return r;
}
}
for (;;) {
- MountPoint p;
- char *controller, *where, *options;
+ MountPoint p = {
+ .what = "cgroup",
+ .type = "cgroup",
+ .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ .mode = MNT_IN_CONTAINER,
+ };
char ***k = NULL;
+ _cleanup_free_ char *options = NULL, *controller;
controller = set_steal_first(controllers);
if (!controller)
@@ -268,14 +280,13 @@ int mount_cgroup_controllers(char ***join_controllers) {
for (i = *k, j = *k; *i; i++) {
if (!streq(*i, controller)) {
- char *t;
+ char _cleanup_free_ *t;
t = set_remove(controllers, *i);
if (!t) {
free(*i);
continue;
}
- free(t);
}
*(j++) = *i;
@@ -284,76 +295,36 @@ int mount_cgroup_controllers(char ***join_controllers) {
*j = NULL;
options = strv_join(*k, ",");
- if (!options) {
- free(controller);
- r = log_oom();
- goto finish;
- }
-
+ if (!options)
+ return log_oom();
} else {
options = controller;
controller = NULL;
}
- where = strappend("/sys/fs/cgroup/", options);
- if (!where) {
- free(options);
- r = log_oom();
- goto finish;
- }
-
- zero(p);
- p.what = "cgroup";
- p.where = where;
- p.type = "cgroup";
+ p.where = strappenda("/sys/fs/cgroup/", options);
p.options = options;
- p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
r = mount_one(&p, true);
- free(controller);
- free(where);
-
- if (r < 0) {
- free(options);
- goto finish;
- }
+ if (r < 0)
+ return r;
if (r > 0 && k && *k) {
char **i;
for (i = *k; *i; i++) {
- char *t;
-
- t = strappend("/sys/fs/cgroup/", *i);
- if (!t) {
- r = log_oom();
- free(options);
- goto finish;
- }
+ char *t = strappenda("/sys/fs/cgroup/", *i);
r = symlink(options, t);
- free(t);
-
if (r < 0 && errno != EEXIST) {
- log_error("Failed to create symlink: %m");
- r = -errno;
- free(options);
- goto finish;
+ log_error("Failed to create symlink %s: %m", t);
+ return -errno;
}
}
}
-
- free(options);
}
- r = 0;
-
-finish:
- set_free_free(controllers);
-
- fclose(f);
-
- return r;
+ return 0;
}
static int nftw_cb(
@@ -415,7 +386,7 @@ int mount_setup(bool loaded_policy) {
after_relabel = now(CLOCK_MONOTONIC);
log_info("Relabelled /dev and /run in %s.",
- format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
+ format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
}
/* Create a few default symlinks, which are normally created
@@ -433,9 +404,14 @@ int mount_setup(bool loaded_policy) {
if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
log_warning("Failed to set up the root directory for shared mount propagation: %m");
- /* Create a few directories we always want around */
+ /* Create a few directories we always want around, Note that
+ * sd_booted() checks for /run/systemd/system, so this mkdir
+ * really needs to stay for good, otherwise software that
+ * copied sd-daemon.c into their sources will misdetect
+ * systemd. */
mkdir_label("/run/systemd", 0755);
mkdir_label("/run/systemd/system", 0755);
+ mkdir_label("/run/systemd/inaccessible", 0000);
return 0;
}
diff --git a/src/core/mount.c b/src/core/mount.c
index 5d2b010013..10073b50be 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -25,6 +25,7 @@
#include <sys/epoll.h>
#include <signal.h>
+#include "manager.h"
#include "unit.h"
#include "mount.h"
#include "load-fragment.h"
@@ -126,7 +127,7 @@ static void mount_done(Unit *u) {
mount_parameters_done(&m->parameters_proc_self_mountinfo);
mount_parameters_done(&m->parameters_fragment);
- exec_context_done(&m->exec_context);
+ exec_context_done(&m->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
@@ -135,7 +136,7 @@ static void mount_done(Unit *u) {
unit_unwatch_timer(u, &m->timer_watch);
}
-static MountParameters* get_mount_parameters_fragment(Mount *m) {
+_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
assert(m);
if (m->from_fragment)
@@ -144,7 +145,7 @@ static MountParameters* get_mount_parameters_fragment(Mount *m) {
return NULL;
}
-static MountParameters* get_mount_parameters(Mount *m) {
+_pure_ static MountParameters* get_mount_parameters(Mount *m) {
assert(m);
if (m->from_proc_self_mountinfo)
@@ -292,7 +293,7 @@ static int mount_add_requires_mounts_links(Mount *m) {
}
static char* mount_test_option(const char *haystack, const char *needle) {
- struct mntent me;
+ struct mntent me = { .mnt_opts = (char*) haystack };
assert(needle);
@@ -302,9 +303,6 @@ static char* mount_test_option(const char *haystack, const char *needle) {
if (!haystack)
return NULL;
- zero(me);
- me.mnt_opts = (char*) haystack;
-
return hasmntopt(&me, needle);
}
@@ -329,6 +327,12 @@ static bool mount_is_bind(MountParameters *p) {
if (p->fstype && streq(p->fstype, "bind"))
return true;
+ if (mount_test_option(p->options, "rbind"))
+ return true;
+
+ if (p->fstype && streq(p->fstype, "rbind"))
+ return true;
+
return false;
}
@@ -432,31 +436,49 @@ static int mount_add_quota_links(Mount *m) {
}
static int mount_add_default_dependencies(Mount *m) {
- int r;
+ const char *after, *after2, *online;
MountParameters *p;
- const char *after;
+ int r;
assert(m);
if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
return 0;
- p = get_mount_parameters_fragment(m);
+ p = get_mount_parameters(m);
+
if (!p)
return 0;
if (path_equal(m->where, "/"))
return 0;
- if (mount_is_network(p))
+ if (mount_is_network(p)) {
after = SPECIAL_REMOTE_FS_PRE_TARGET;
- else
+ after2 = SPECIAL_NETWORK_TARGET;
+ online = SPECIAL_NETWORK_ONLINE_TARGET;
+ } else {
after = SPECIAL_LOCAL_FS_PRE_TARGET;
+ after2 = NULL;
+ online = NULL;
+ }
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, after, NULL, true);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
if (r < 0)
return r;
+ if (after2) {
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after2, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
+ if (online) {
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, online, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -495,7 +517,7 @@ static int mount_fix_timeouts(Mount *m) {
if (!t)
return -ENOMEM;
- r = parse_usec(t, &u);
+ r = parse_sec(t, &u);
free(t);
if (r < 0) {
@@ -717,9 +739,9 @@ static void mount_set_state(Mount *m, MountState state) {
state == MOUNT_UNMOUNTING_SIGTERM ||
state == MOUNT_UNMOUNTING_SIGKILL ||
state == MOUNT_FAILED) {
- if (state != old_state)
- mount_notify_automount(m, -ENODEV);
- }
+ if (state != old_state)
+ mount_notify_automount(m, -ENODEV);
+ }
if (state != old_state)
log_debug_unit(UNIT(m)->id,
@@ -863,6 +885,7 @@ static void mount_enter_dead(Mount *m, MountResult f) {
if (f != MOUNT_SUCCESS)
m->result = f;
+ exec_context_tmp_dirs_done(&m->exec_context);
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
}
@@ -877,56 +900,23 @@ static void mount_enter_mounted(Mount *m, MountResult f) {
static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(m);
if (f != MOUNT_SUCCESS)
m->result = f;
- if (m->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == MOUNT_MOUNTING_SIGTERM ||
- state == MOUNT_UNMOUNTING_SIGTERM ||
- state == MOUNT_REMOUNTING_SIGTERM) ? m->kill_context.kill_signal : SIGKILL;
-
- if (m->control_pid > 0) {
- if (kill_and_sigcont(m->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning_unit(UNIT(m)->id,
- "Failed to kill control process %li: %m",
- (long) m->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (m->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (m->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0)
- goto fail;
-
- r = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(m)->id,
- "Failed to kill control group: %s",
- strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(m),
+ &m->kill_context,
+ state != MOUNT_MOUNTING_SIGTERM && state != MOUNT_UNMOUNTING_SIGTERM && state != MOUNT_REMOUNTING_SIGTERM,
+ -1,
+ m->control_pid,
+ false);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch);
if (r < 0)
goto fail;
@@ -947,19 +937,20 @@ fail:
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
else
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
}
void warn_if_dir_nonempty(const char *unit, const char* where) {
+ assert(unit);
+ assert(where);
+
if (dir_is_empty(where) > 0)
return;
- log_struct(LOG_NOTICE,
+
+ log_struct_unit(LOG_NOTICE,
+ unit,
"MESSAGE=%s: Directory %s to mount over is not empty, mounting anyway.",
unit, where,
"WHERE=%s", where,
- "_SYSTEMD_UNIT=%s", unit,
MESSAGE_ID(SD_MESSAGE_OVERMOUNTING),
NULL);
}
@@ -1188,6 +1179,8 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
if (m->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
+ exec_context_serialize(&m->exec_context, UNIT(m), f);
+
return 0;
}
@@ -1244,7 +1237,22 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
m->control_command_id = id;
m->control_command = m->exec_command + id;
}
+ } else if (streq(key, "tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ m->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ m->exec_context.var_tmp_dir = t;
} else
log_debug_unit(UNIT(m)->id,
"Unknown serialization key '%s'", key);
@@ -1252,19 +1260,19 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
return 0;
}
-static UnitActiveState mount_active_state(Unit *u) {
+_pure_ static UnitActiveState mount_active_state(Unit *u) {
assert(u);
return state_translation_table[MOUNT(u)->state];
}
-static const char *mount_sub_state_to_string(Unit *u) {
+_pure_ static const char *mount_sub_state_to_string(Unit *u) {
assert(u);
return mount_state_to_string(MOUNT(u)->state);
}
-static bool mount_check_gc(Unit *u) {
+_pure_ static bool mount_check_gc(Unit *u) {
Mount *m = MOUNT(u);
assert(m);
@@ -1520,6 +1528,20 @@ static int mount_add_one(
goto fail;
}
+ u->source_path = strdup("/proc/self/mountinfo");
+ if (!u->source_path) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ r = unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_LOCAL_FS_TARGET, NULL, true);
+ if (r < 0)
+ goto fail;
+
+ r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
+ if (r < 0)
+ goto fail;
+
unit_add_to_load_queue(u);
} else {
delete = false;
@@ -1681,25 +1703,27 @@ static void mount_shutdown(Manager *m) {
static int mount_enumerate(Manager *m) {
int r;
- struct epoll_event ev;
assert(m);
if (!m->proc_self_mountinfo) {
- if (!(m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+ struct epoll_event ev = {
+ .events = EPOLLPRI,
+ .data.ptr = &m->mount_watch,
+ };
+
+ m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+ if (!m->proc_self_mountinfo)
return -errno;
m->mount_watch.type = WATCH_MOUNT;
m->mount_watch.fd = fileno(m->proc_self_mountinfo);
- zero(ev);
- ev.events = EPOLLPRI;
- ev.data.ptr = &m->mount_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->mount_watch.fd, &ev) < 0)
return -errno;
}
- if ((r = mount_load_proc_self_mountinfo(m, false)) < 0)
+ r = mount_load_proc_self_mountinfo(m, false);
+ if (r < 0)
goto fail;
return 0;
@@ -1800,53 +1824,7 @@ static void mount_reset_failed(Unit *u) {
}
static int mount_kill(Unit *u, KillWho who, int signo, DBusError *error) {
- Mount *m = MOUNT(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(m);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Mount units have no main processes");
- return -ESRCH;
- }
-
- if (m->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (m->control_pid > 0)
- if (kill(m->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (m->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(m->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(m)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error);
}
static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
@@ -1888,13 +1866,15 @@ DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);
const UnitVTable mount_vtable = {
.object_size = sizeof(Mount),
- .exec_context_offset = offsetof(Mount, exec_context),
.sections =
"Unit\0"
"Mount\0"
"Install\0",
+ .exec_context_offset = offsetof(Mount, exec_context),
+ .exec_section = "Mount",
+
.no_alias = true,
.no_instances = true,
diff --git a/src/core/mount.h b/src/core/mount.h
index 30c6d9b249..bcc10ee0d4 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -111,13 +111,13 @@ extern const UnitVTable mount_vtable;
void mount_fd_event(Manager *m, int events);
-const char* mount_state_to_string(MountState i);
-MountState mount_state_from_string(const char *s);
+const char* mount_state_to_string(MountState i) _const_;
+MountState mount_state_from_string(const char *s) _pure_;
-const char* mount_exec_command_to_string(MountExecCommand i);
-MountExecCommand mount_exec_command_from_string(const char *s);
+const char* mount_exec_command_to_string(MountExecCommand i) _const_;
+MountExecCommand mount_exec_command_from_string(const char *s) _pure_;
-const char* mount_result_to_string(MountResult i);
-MountResult mount_result_from_string(const char *s);
+const char* mount_result_to_string(MountResult i) _const_;
+MountResult mount_result_from_string(const char *s) _pure_;
void warn_if_dir_nonempty(const char *unit, const char* where);
diff --git a/src/core/namespace.c b/src/core/namespace.c
index ba18ddc5b0..7e33d84156 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -36,23 +36,24 @@
#include "path-util.h"
#include "namespace.h"
#include "missing.h"
+#include "execute.h"
-typedef enum PathMode {
+typedef enum MountMode {
/* This is ordered by priority! */
INACCESSIBLE,
READONLY,
PRIVATE_TMP,
PRIVATE_VAR_TMP,
READWRITE
-} PathMode;
+} MountMode;
-typedef struct Path {
+typedef struct BindMount {
const char *path;
- PathMode mode;
+ MountMode mode;
bool done;
-} Path;
+} BindMount;
-static int append_paths(Path **p, char **strv, PathMode mode) {
+static int append_mounts(BindMount **p, char **strv, MountMode mode) {
char **i;
STRV_FOREACH(i, strv) {
@@ -68,8 +69,8 @@ static int append_paths(Path **p, char **strv, PathMode mode) {
return 0;
}
-static int path_compare(const void *a, const void *b) {
- const Path *p = a, *q = b;
+static int mount_path_compare(const void *a, const void *b) {
+ const BindMount *p = a, *q = b;
if (path_equal(p->path, q->path)) {
@@ -93,14 +94,13 @@ static int path_compare(const void *a, const void *b) {
return 0;
}
-static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) {
- Path *f, *t, *previous;
+static void drop_duplicates(BindMount *m, unsigned *n) {
+ BindMount *f, *t, *previous;
- assert(p);
+ assert(m);
assert(n);
- assert(need_inaccessible);
- for (f = p, t = p, previous = NULL; f < p+*n; f++) {
+ for (f = m, t = m, previous = NULL; f < m+*n; f++) {
/* The first one wins */
if (previous && path_equal(f->path, previous->path))
@@ -109,37 +109,33 @@ static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) {
t->path = f->path;
t->mode = f->mode;
- if (t->mode == INACCESSIBLE)
- *need_inaccessible = true;
-
previous = t;
t++;
}
- *n = t - p;
+ *n = t - m;
}
static int apply_mount(
- Path *p,
+ BindMount *m,
const char *tmp_dir,
- const char *var_tmp_dir,
- const char *inaccessible_dir) {
+ const char *var_tmp_dir) {
const char *what;
int r;
- assert(p);
+ assert(m);
- switch (p->mode) {
+ switch (m->mode) {
case INACCESSIBLE:
- what = inaccessible_dir;
+ what = "/run/systemd/inaccessible";
break;
case READONLY:
case READWRITE:
- what = p->path;
+ what = m->path;
break;
case PRIVATE_TMP:
@@ -156,155 +152,115 @@ static int apply_mount(
assert(what);
- r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL);
+ r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
if (r >= 0)
- log_debug("Successfully mounted %s to %s", what, p->path);
+ log_debug("Successfully mounted %s to %s", what, m->path);
return r;
}
-static int make_read_only(Path *p) {
+static int make_read_only(BindMount *m) {
int r;
- assert(p);
+ assert(m);
- if (p->mode != INACCESSIBLE && p->mode != READONLY)
+ if (m->mode != INACCESSIBLE && m->mode != READONLY)
return 0;
- r = mount(NULL, p->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
+ r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
if (r < 0)
return -errno;
return 0;
}
-int setup_namespace(
- char **writable,
- char **readable,
- char **inaccessible,
- bool private_tmp,
- unsigned long flags) {
-
- char
- tmp_dir[] = "/tmp/systemd-private-XXXXXX",
- var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX",
- inaccessible_dir[] = "/tmp/systemd-inaccessible-XXXXXX";
-
- Path *paths, *p;
- unsigned n;
- bool need_inaccessible = false;
- bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false;
- int r;
-
- if (!flags)
- flags = MS_SHARED;
-
- n =
- strv_length(writable) +
- strv_length(readable) +
- strv_length(inaccessible) +
- (private_tmp ? 2 : 0);
+int setup_tmpdirs(char **tmp_dir,
+ char **var_tmp_dir) {
+ int r = 0;
+ char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX",
+ var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX";
- p = paths = alloca(sizeof(Path) * n);
- if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
- (r = append_paths(&p, readable, READONLY)) < 0 ||
- (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
- goto fail;
-
- if (private_tmp) {
- p->path = "/tmp";
- p->mode = PRIVATE_TMP;
- p++;
-
- p->path = "/var/tmp";
- p->mode = PRIVATE_VAR_TMP;
- p++;
- }
+ assert(tmp_dir);
+ assert(var_tmp_dir);
- assert(paths + n == p);
+ r = create_tmp_dir(tmp_dir_template, tmp_dir);
+ if (r < 0)
+ return r;
- qsort(paths, n, sizeof(Path), path_compare);
- drop_duplicates(paths, &n, &need_inaccessible);
+ r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir);
+ if (r == 0)
+ return 0;
- if (need_inaccessible) {
- mode_t u;
- char *d;
+ /* failure */
+ rmdir(*tmp_dir);
+ rmdir(tmp_dir_template);
+ free(*tmp_dir);
+ *tmp_dir = NULL;
- u = umask(0777);
- d = mkdtemp(inaccessible_dir);
- umask(u);
+ return r;
+}
- if (!d) {
- r = -errno;
- goto fail;
- }
+int setup_namespace(char** read_write_dirs,
+ char** read_only_dirs,
+ char** inaccessible_dirs,
+ char* tmp_dir,
+ char* var_tmp_dir,
+ bool private_tmp,
+ unsigned mount_flags) {
+
+ unsigned n = strv_length(read_write_dirs) +
+ strv_length(read_only_dirs) +
+ strv_length(inaccessible_dirs) +
+ (private_tmp ? 2 : 0);
+ BindMount *m, *mounts;
+ int r = 0;
+
+ if (!mount_flags)
+ mount_flags = MS_SHARED;
+
+ if (unshare(CLONE_NEWNS) < 0)
+ return -errno;
- remove_inaccessible = true;
- }
+ m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
+ if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
+ (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
+ (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
+ return r;
if (private_tmp) {
- mode_t u;
- char *d;
-
- u = umask(0000);
- d = mkdtemp(tmp_dir);
- umask(u);
-
- if (!d) {
- r = -errno;
- goto fail;
- }
+ m->path = "/tmp";
+ m->mode = PRIVATE_TMP;
+ m++;
- remove_tmp = true;
-
- u = umask(0000);
- d = mkdtemp(var_tmp_dir);
- umask(u);
-
- if (!d) {
- r = -errno;
- goto fail;
- }
-
- remove_var_tmp = true;
-
- if (chmod(tmp_dir, 0777 + S_ISVTX) < 0) {
- r = -errno;
- goto fail;
- }
-
- if (chmod(var_tmp_dir, 0777 + S_ISVTX) < 0) {
- r = -errno;
- goto fail;
- }
+ m->path = "/var/tmp";
+ m->mode = PRIVATE_VAR_TMP;
+ m++;
}
- if (unshare(CLONE_NEWNS) < 0) {
- r = -errno;
- goto fail;
- }
+ assert(mounts + n == m);
+
+ qsort(mounts, n, sizeof(BindMount), mount_path_compare);
+ drop_duplicates(mounts, &n);
/* Remount / as SLAVE so that nothing now mounted in the namespace
shows up in the parent */
- if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
- r = -errno;
- goto fail;
- }
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+ return -errno;
- for (p = paths; p < paths + n; p++) {
- r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir);
+ for (m = mounts; m < mounts + n; ++m) {
+ r = apply_mount(m, tmp_dir, var_tmp_dir);
if (r < 0)
goto undo_mounts;
}
- for (p = paths; p < paths + n; p++) {
- r = make_read_only(p);
+ for (m = mounts; m < mounts + n; ++m) {
+ r = make_read_only(m);
if (r < 0)
goto undo_mounts;
}
/* Remount / as the desired mode */
- if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) {
+ if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
r = -errno;
goto undo_mounts;
}
@@ -312,19 +268,10 @@ int setup_namespace(
return 0;
undo_mounts:
- for (p = paths; p < paths + n; p++)
- if (p->done)
- umount2(p->path, MNT_DETACH);
-
-fail:
- if (remove_inaccessible)
- rmdir(inaccessible_dir);
-
- if (remove_tmp)
- rmdir(tmp_dir);
-
- if (remove_var_tmp)
- rmdir(var_tmp_dir);
+ for (m = mounts; m < mounts + n; ++m) {
+ if (m->done)
+ umount2(m->path, MNT_DETACH);
+ }
return r;
}
diff --git a/src/core/namespace.h b/src/core/namespace.h
index 5d72ed91fb..ddb579468d 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -23,9 +23,11 @@
#include <stdbool.h>
-int setup_namespace(
- char **writable,
- char **readable,
- char **inaccessible,
- bool private_tmp,
- unsigned long flags);
+int setup_tmpdirs(char **tmp_dir, char **var_tmp_dir);
+int setup_namespace(char **read_write_dirs,
+ char **read_only_dirs,
+ char **inaccessible_dirs,
+ char *tmp_dir,
+ char *var_tmp_dir,
+ bool private_tmp,
+ unsigned mount_flags);
diff --git a/src/core/path.c b/src/core/path.c
index 767620ba75..8a09deb4ff 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -31,8 +31,9 @@
#include "mkdir.h"
#include "dbus-path.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "path-util.h"
+#include "macro.h"
static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
[PATH_DEAD] = UNIT_INACTIVE,
@@ -52,7 +53,7 @@ int path_spec_watch(PathSpec *s, Unit *u) {
};
bool exists = false;
- char *k, *slash;
+ char *slash, *oldslash = NULL;
int r;
assert(u);
@@ -60,47 +61,85 @@ int path_spec_watch(PathSpec *s, Unit *u) {
path_spec_unwatch(s, u);
- if (!(k = strdup(s->path)))
- return -ENOMEM;
-
- if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
+ s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (s->inotify_fd < 0) {
r = -errno;
goto fail;
}
- if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) {
- r = -errno;
+ r = unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch);
+ if (r < 0)
goto fail;
- }
- s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type]);
- if (s->primary_wd >= 0)
- exists = true;
+ /* This assumes the path was passed through path_kill_slashes()! */
- do {
+ for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
+ char *cut = NULL;
int flags;
+ char tmp;
+
+ if (slash) {
+ cut = slash + (slash == s->path);
+ tmp = *cut;
+ *cut = '\0';
+
+ flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
+ } else
+ flags = flags_table[s->type];
+
+ r = inotify_add_watch(s->inotify_fd, s->path, flags);
+ if (r < 0) {
+ if (errno == EACCES || errno == ENOENT) {
+ if (cut)
+ *cut = tmp;
+ break;
+ }
+
+ log_warning("Failed to add watch on %s: %m", s->path);
+ r = -errno;
+ if (cut)
+ *cut = tmp;
+ goto fail;
+ } else {
+ exists = true;
- /* This assumes the path was passed through path_kill_slashes()! */
- slash = strrchr(k, '/');
- if (!slash)
- break;
+ /* Path exists, we don't need to watch parent
+ too closely. */
+ if (oldslash) {
+ char *cut2 = oldslash + (oldslash == s->path);
+ char tmp2 = *cut2;
+ *cut2 = '\0';
+
+ inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
+ /* Error is ignored, the worst can happen is
+ we get spurious events. */
- /* Trim the path at the last slash. Keep the slash if it's the root dir. */
- slash[slash == k] = 0;
+ *cut2 = tmp2;
+ }
+ }
- flags = IN_MOVE_SELF;
- if (!exists)
- flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
+ if (cut)
+ *cut = tmp;
- if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
- exists = true;
- } while (slash != k);
+ if (slash)
+ oldslash = slash;
+ else {
+ /* whole path has been iterated over */
+ s->primary_wd = r;
+ break;
+ }
+ }
+
+ if (!exists) {
+ log_error("Failed to add watch on any of the components of %s: %m",
+ s->path);
+ r = -errno; /* either EACCESS or ENOENT */
+ goto fail;
+ }
return 0;
fail:
- free(k);
-
path_spec_unwatch(s, u);
return r;
}
@@ -117,7 +156,7 @@ void path_spec_unwatch(PathSpec *s, Unit *u) {
}
int path_spec_fd_event(PathSpec *s, uint32_t events) {
- uint8_t *buf = NULL;
+ _cleanup_free_ uint8_t *buf = NULL;
struct inotify_event *e;
ssize_t k;
int l;
@@ -125,30 +164,24 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) {
if (events != EPOLLIN) {
log_error("Got invalid poll event on inotify.");
- r = -EINVAL;
- goto out;
+ return -EINVAL;
}
if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) {
log_error("FIONREAD failed: %m");
- r = -errno;
- goto out;
+ return -errno;
}
assert(l > 0);
buf = malloc(l);
- if (!buf) {
- log_error("Failed to allocate buffer: %m");
- r = -errno;
- goto out;
- }
+ if (!buf)
+ return log_oom();
k = read(s->inotify_fd, buf, l);
if (k < 0) {
log_error("Failed to read inotify event: %m");
- r = -errno;
- goto out;
+ return -errno;
}
e = (struct inotify_event*) buf;
@@ -166,8 +199,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) {
e = (struct inotify_event*) ((uint8_t*) e + step);
k -= step;
}
-out:
- free(buf);
+
return r;
}
@@ -248,22 +280,27 @@ static void path_init(Unit *u) {
p->directory_mode = 0755;
}
-static void path_done(Unit *u) {
- Path *p = PATH(u);
+void path_free_specs(Path *p) {
PathSpec *s;
assert(p);
- unit_ref_unset(&p->unit);
-
while ((s = p->specs)) {
- path_spec_unwatch(s, u);
+ path_spec_unwatch(s, UNIT(p));
LIST_REMOVE(PathSpec, spec, p->specs, s);
path_spec_done(s);
free(s);
}
}
+static void path_done(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(p);
+
+ path_free_specs(p);
+}
+
int path_add_one_mount_link(Path *p, Mount *m) {
PathSpec *s;
int r;
@@ -276,11 +313,12 @@ int path_add_one_mount_link(Path *p, Mount *m) {
return 0;
LIST_FOREACH(spec, s, p->specs) {
-
if (!path_spec_startswith(s, m->where))
continue;
- if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
+ UNIT(m), true);
+ if (r < 0)
return r;
}
@@ -293,9 +331,11 @@ static int path_add_mount_links(Path *p) {
assert(p);
- LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT])
- if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) {
+ r = path_add_one_mount_link(p, MOUNT(other));
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -320,15 +360,20 @@ static int path_add_default_dependencies(Path *p) {
assert(p);
- if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
- return r;
+ r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
+ SPECIAL_PATHS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
- if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
+ r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (r < 0)
return r;
}
- return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
static int path_load(Unit *u) {
@@ -338,31 +383,33 @@ static int path_load(Unit *u) {
assert(u);
assert(u->load_state == UNIT_STUB);
- if ((r = unit_load_fragment_and_dropin(u)) < 0)
+ r = unit_load_fragment_and_dropin(u);
+ if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
- if (!UNIT_DEREF(p->unit)) {
+ if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
Unit *x;
r = unit_load_related_unit(u, ".service", &x);
if (r < 0)
return r;
- unit_ref_set(&p->unit, x);
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
+ if (r < 0)
+ return r;
}
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true);
+ r = path_add_mount_links(p);
if (r < 0)
return r;
- if ((r = path_add_mount_links(p)) < 0)
- return r;
-
- if (UNIT(p)->default_dependencies)
- if ((r = path_add_default_dependencies(p)) < 0)
+ if (UNIT(p)->default_dependencies) {
+ r = path_add_default_dependencies(p);
+ if (r < 0)
return r;
+ }
}
return path_verify(p);
@@ -370,11 +417,14 @@ static int path_load(Unit *u) {
static void path_dump(Unit *u, FILE *f, const char *prefix) {
Path *p = PATH(u);
+ Unit *trigger;
PathSpec *s;
assert(p);
assert(f);
+ trigger = UNIT_TRIGGER(u);
+
fprintf(f,
"%sPath State: %s\n"
"%sResult: %s\n"
@@ -383,7 +433,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) {
"%sDirectoryMode: %04o\n",
prefix, path_state_to_string(p->state),
prefix, path_result_to_string(p->result),
- prefix, UNIT_DEREF(p->unit)->id,
+ prefix, trigger ? trigger->id : "n/a",
prefix, yes_no(p->make_directory),
prefix, p->directory_mode);
@@ -406,9 +456,11 @@ static int path_watch(Path *p) {
assert(p);
- LIST_FOREACH(spec, s, p->specs)
- if ((r = path_spec_watch(s, UNIT(p))) < 0)
+ LIST_FOREACH(spec, s, p->specs) {
+ r = path_spec_watch(s, UNIT(p));
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -463,32 +515,35 @@ static void path_enter_dead(Path *p, PathResult f) {
}
static void path_enter_running(Path *p) {
+ _cleanup_dbus_error_free_ DBusError error;
int r;
- DBusError error;
assert(p);
+
dbus_error_init(&error);
/* Don't start job if we are supposed to go down */
- if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP)
+ if (unit_stop_pending(UNIT(p)))
return;
- if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0)
+ r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
+ JOB_REPLACE, true, &error, NULL);
+ if (r < 0)
goto fail;
p->inotify_triggered = false;
- if ((r = path_watch(p)) < 0)
+ r = path_watch(p);
+ if (r < 0)
goto fail;
path_set_state(p, PATH_RUNNING);
return;
fail:
- log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r));
+ log_warning("%s failed to queue unit startup job: %s",
+ UNIT(p)->id, bus_error(&error, r));
path_enter_dead(p, PATH_FAILURE_RESOURCES);
-
- dbus_error_free(&error);
}
static bool path_check_good(Path *p, bool initial) {
@@ -517,7 +572,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
return;
}
- if ((r = path_watch(p)) < 0)
+ r = path_watch(p);
+ if (r < 0)
goto fail;
/* Hmm, so now we have created inotify watches, but the file
@@ -535,7 +591,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
return;
fail:
- log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r));
+ log_warning("%s failed to enter waiting state: %s",
+ UNIT(p)->id, strerror(-r));
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
@@ -557,7 +614,7 @@ static int path_start(Unit *u) {
assert(p);
assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
- if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED)
+ if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
return -ENOENT;
path_mkdir(p);
@@ -602,7 +659,8 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD
if (streq(key, "state")) {
PathState state;
- if ((state = path_state_from_string(value)) < 0)
+ state = path_state_from_string(value);
+ if (state < 0)
log_debug("Failed to parse state value %s", value);
else
p->deserialized_state = state;
@@ -622,13 +680,13 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD
return 0;
}
-static UnitActiveState path_active_state(Unit *u) {
+_pure_ static UnitActiveState path_active_state(Unit *u) {
assert(u);
return state_translation_table[PATH(u)->state];
}
-static const char *path_sub_state_to_string(Unit *u) {
+_pure_ static const char *path_sub_state_to_string(Unit *u) {
assert(u);
return path_state_to_string(PATH(u)->state);
@@ -677,32 +735,28 @@ fail:
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
-void path_unit_notify(Unit *u, UnitActiveState new_state) {
- Iterator i;
- Unit *k;
-
- if (u->type == UNIT_PATH)
- return;
-
- SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
- Path *p;
+static void path_trigger_notify(Unit *u, Unit *other) {
+ Path *p = PATH(u);
- if (k->type != UNIT_PATH)
- continue;
+ assert(u);
+ assert(other);
- if (k->load_state != UNIT_LOADED)
- continue;
+ /* Invoked whenever the unit we trigger changes state or gains
+ * or loses a job */
- p = PATH(k);
+ if (other->load_state != UNIT_LOADED)
+ return;
- if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
- log_debug("%s got notified about unit deactivation.", UNIT(p)->id);
+ if (p->state == PATH_RUNNING &&
+ UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
+ log_debug_unit(UNIT(p)->id,
+ "%s got notified about unit deactivation.",
+ UNIT(p)->id);
- /* Hmm, so inotify was triggered since the
- * last activation, so I guess we need to
- * recheck what is going on. */
- path_enter_waiting(p, false, p->inotify_triggered);
- }
+ /* Hmm, so inotify was triggered since the
+ * last activation, so I guess we need to
+ * recheck what is going on. */
+ path_enter_waiting(p, false, p->inotify_triggered);
}
}
@@ -769,6 +823,8 @@ const UnitVTable path_vtable = {
.fd_event = path_fd_event,
+ .trigger_notify = path_trigger_notify,
+
.reset_failed = path_reset_failed,
.bus_interface = "org.freedesktop.systemd1.Path",
diff --git a/src/core/path.h b/src/core/path.h
index 77926888ae..6adab5897d 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -80,8 +80,6 @@ struct Path {
LIST_HEAD(PathSpec, specs);
- UnitRef unit;
-
PathState state, deserialized_state;
bool inotify_triggered;
@@ -92,19 +90,19 @@ struct Path {
PathResult result;
};
-void path_unit_notify(Unit *u, UnitActiveState new_state);
-
/* Called from the mount code figure out if a mount is a dependency of
* any of the paths of this path object */
int path_add_one_mount_link(Path *p, Mount *m);
+void path_free_specs(Path *p);
+
extern const UnitVTable path_vtable;
-const char* path_state_to_string(PathState i);
-PathState path_state_from_string(const char *s);
+const char* path_state_to_string(PathState i) _const_;
+PathState path_state_from_string(const char *s) _pure_;
-const char* path_type_to_string(PathType i);
-PathType path_type_from_string(const char *s);
+const char* path_type_to_string(PathType i) _const_;
+PathType path_type_from_string(const char *s) _pure_;
-const char* path_result_to_string(PathResult i);
-PathResult path_result_from_string(const char *s);
+const char* path_result_to_string(PathResult i) _const_;
+PathResult path_result_from_string(const char *s) _pure_;
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index 6dfe8b45f3..426aed07d2 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -104,8 +104,6 @@ static int bus_get_selinux_security_context(
*scon = b;
- log_debug("GetConnectionSELinuxSecurityContext %s (pid %ld)", *scon, (long) bus_get_unix_process_id(connection, name, error));
-
return 0;
}
@@ -134,7 +132,7 @@ static int bus_get_audit_data(
if (r < 0)
return r;
- r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
+ r = get_process_cmdline(pid, 0, true, &audit->cmdline);
if (r < 0)
return r;
@@ -176,20 +174,25 @@ static int audit_callback(
user_avc's into the /var/log/audit/audit.log, otherwise they will be
sent to syslog.
*/
-static int log_callback(int type, const char *fmt, ...) {
+_printf_attr_(2, 3) static int log_callback(int type, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
#ifdef HAVE_AUDIT
if (get_audit_fd() >= 0) {
- char buf[LINE_MAX];
+ _cleanup_free_ char *buf = NULL;
+ int r;
- vsnprintf(buf, sizeof(buf), fmt, ap);
- audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
+ r = vasprintf(&buf, fmt, ap);
va_end(ap);
- return 0;
+ if (r >= 0) {
+ audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
+ return 0;
+ }
+
+ va_start(ap, fmt);
}
#endif
log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
@@ -258,7 +261,7 @@ static int get_audit_data(
const char *sender;
int r, fd;
struct ucred ucred;
- socklen_t len;
+ socklen_t len = sizeof(ucred);
sender = dbus_message_get_sender(message);
if (sender)
@@ -280,7 +283,7 @@ static int get_audit_data(
if (r < 0)
return r;
- r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
+ r = get_process_cmdline(ucred.pid, 0, true, &audit->cmdline);
if (r < 0)
return r;
@@ -308,8 +311,6 @@ static int get_calling_context(
*/
sender = dbus_message_get_sender(message);
if (sender) {
- log_error("SELinux Got Sender %s", sender);
-
r = bus_get_selinux_security_context(connection, sender, scon, error);
if (r >= 0)
return r;
@@ -318,7 +319,6 @@ static int get_calling_context(
return r;
}
- log_debug("SELinux No Sender");
if (!dbus_connection_get_unix_fd(connection, &fd)) {
log_error("bus_connection_get_unix_fd failed %m");
return -EINVAL;
@@ -363,8 +363,6 @@ int selinux_access_check(
if (r < 0)
return r;
- log_debug("SELinux access check for path=%s permission=%s", strna(path), permission);
-
audit.uid = audit.loginuid = (uid_t) -1;
audit.gid = (gid_t) -1;
audit.cmdline = NULL;
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
index e9c0de92f1..7a32ed59a0 100644
--- a/src/core/selinux-setup.c
+++ b/src/core/selinux-setup.c
@@ -58,9 +58,6 @@ int selinux_setup(bool *loaded_policy) {
cb.func_log = null_log;
selinux_set_callback(SELINUX_CB_LOG, cb);
- /* Make sure getcon() works, which needs /proc and /sys */
- mount_setup_early();
-
/* Already initialized by somebody else? */
r = getcon_raw(&con);
if (r == 0) {
@@ -104,7 +101,7 @@ int selinux_setup(bool *loaded_policy) {
after_load = now(CLOCK_MONOTONIC);
log_info("Successfully loaded SELinux policy in %s.",
- format_timespan(timespan, sizeof(timespan), after_load - before_load));
+ format_timespan(timespan, sizeof(timespan), after_load - before_load, 0));
*loaded_policy = true;
diff --git a/src/core/service.c b/src/core/service.c
index 2a4e691e78..3617c24711 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -36,12 +36,14 @@
#include "unit-printf.h"
#include "dbus-service.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "exit-status.h"
#include "def.h"
#include "path-util.h"
#include "util.h"
#include "utf8.h"
+#include "env-util.h"
+#include "fileio.h"
#ifdef HAVE_SYSV_COMPAT
@@ -281,7 +283,7 @@ static void service_done(Unit *u) {
free(s->status_text);
s->status_text = NULL;
- exec_context_done(&s->exec_context);
+ exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
s->main_command = NULL;
@@ -322,17 +324,15 @@ static void service_done(Unit *u) {
static char *sysv_translate_name(const char *name) {
char *r;
- if (!(r = new(char, strlen(name) + sizeof(".service"))))
+ r = new(char, strlen(name) + sizeof(".service"));
+ if (!r)
return NULL;
if (endswith(name, ".sh"))
- /* Drop Debian-style .sh suffix */
+ /* Drop .sh suffix */
strcpy(stpcpy(r, name) - 3, ".service");
- if (startswith(name, "rc."))
- /* Drop Frugalware-style rc. prefix */
- strcpy(stpcpy(r, name + 3), ".service");
else
- /* Normal init scripts */
+ /* Normal init script name */
strcpy(stpcpy(r, name), ".service");
return r;
@@ -348,24 +348,13 @@ static int sysv_translate_facility(const char *name, const char *filename, char
static const char * const table[] = {
/* LSB defined facilities */
- "local_fs", SPECIAL_LOCAL_FS_TARGET,
- /* Due to unfortunate name selection in Mandriva,
- * $network is provided by network-up which is ordered
- * after network which actually starts interfaces.
- * To break the loop, just ignore it */
+ "local_fs", NULL,
"network", SPECIAL_NETWORK_TARGET,
"named", SPECIAL_NSS_LOOKUP_TARGET,
"portmap", SPECIAL_RPCBIND_TARGET,
"remote_fs", SPECIAL_REMOTE_FS_TARGET,
- "syslog", SPECIAL_SYSLOG_TARGET,
+ "syslog", NULL,
"time", SPECIAL_TIME_SYNC_TARGET,
-
- /* common extensions */
- "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE,
- "null", NULL,
- "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
};
unsigned i;
@@ -385,8 +374,9 @@ static int sysv_translate_facility(const char *name, const char *filename, char
if (!table[i+1])
return 0;
- if (!(r = strdup(table[i+1])))
- return -ENOMEM;
+ r = strdup(table[i+1]);
+ if (!r)
+ return log_oom();
goto finish;
}
@@ -772,7 +762,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
continue;
if (unit_name_to_type(m) == UNIT_SERVICE)
- r = unit_add_name(u, m);
+ r = unit_merge_by_name(u, m);
else
/* NB: SysV targets
* which are provided
@@ -815,7 +805,6 @@ static int service_load_sysv_path(Service *s, const char *path) {
}
r = sysv_translate_facility(n, path_get_file_name(path), &m);
-
if (r < 0) {
log_error_unit(u->id,
"[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
@@ -992,10 +981,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
assert(s);
assert(name);
- /* For SysV services we strip the rc.* and *.sh
- * prefixes/suffixes. */
- if (startswith(name, "rc.") ||
- endswith(name, ".sh.service"))
+ /* For SysV services we strip the *.sh suffixes. */
+ if (endswith(name, ".sh.service"))
return -ENOENT;
STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) {
@@ -1012,25 +999,12 @@ static int service_load_sysv_name(Service *s, const char *name) {
r = service_load_sysv_path(s, path);
if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try Debian style *.sh source'able init scripts */
+ /* Try *.sh source'able init scripts */
strcat(path, ".sh");
r = service_load_sysv_path(s, path);
}
free(path);
- if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try Frugalware style rc.* init scripts */
-
- path = strjoin(*p, "/rc.", name, NULL);
- if (!path)
- return -ENOMEM;
-
- /* Drop .service suffix */
- path[strlen(path)-8] = 0;
- r = service_load_sysv_path(s, path);
- free(path);
- }
-
if (r < 0)
return r;
@@ -1108,7 +1082,8 @@ static int fsck_fix_order(Service *s) {
else
continue;
- if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
+ r = unit_add_dependency(UNIT(s), d, UNIT(t), true);
+ if (r < 0)
return r;
}
@@ -1163,18 +1138,32 @@ static int service_add_default_dependencies(Service *s) {
/* First, pull in base system */
if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
-
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_BASIC_TARGET, NULL, true);
+ if (r < 0)
return r;
} else if (UNIT(s)->manager->running_as == SYSTEMD_USER) {
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_SOCKETS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_TIMERS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_PATHS_TARGET, NULL, true);
+ if (r < 0)
return r;
}
/* Second, activate normal shutdown */
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ return r;
}
static void service_fix_output(Service *s) {
@@ -1232,18 +1221,22 @@ static int service_load(Unit *u) {
service_fix_output(s);
- if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
+ r = unit_add_exec_dependencies(u, &s->exec_context);
+ if (r < 0)
return r;
- if ((r = unit_add_default_cgroups(u)) < 0)
+ r = unit_add_default_cgroups(u);
+ if (r < 0)
return r;
#ifdef HAVE_SYSV_COMPAT
- if ((r = sysv_fix_order(s)) < 0)
+ r = sysv_fix_order(s);
+ if (r < 0)
return r;
#endif
- if ((r = fsck_fix_order(s)) < 0)
+ r = fsck_fix_order(s);
+ if (r < 0)
return r;
if (s->bus_name)
@@ -1256,13 +1249,18 @@ static int service_load(Unit *u) {
if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE)
s->notify_access = NOTIFY_MAIN;
- if (s->type == SERVICE_DBUS || s->bus_name)
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0)
+ if (s->type == SERVICE_DBUS || s->bus_name) {
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES,
+ SPECIAL_DBUS_SOCKET, NULL, true);
+ if (r < 0)
return r;
+ }
- if (UNIT(s)->default_dependencies)
- if ((r = service_add_default_dependencies(s)) < 0)
+ if (UNIT(s)->default_dependencies) {
+ r = service_add_default_dependencies(s);
+ if (r < 0)
return r;
+ }
r = unit_exec_context_defaults(u, &s->exec_context);
if (r < 0)
@@ -1277,7 +1275,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
ServiceExecCommand c;
Service *s = SERVICE(u);
const char *prefix2;
- char *p2;
+ _cleanup_free_ char *p2 = NULL;
assert(s);
@@ -1372,12 +1370,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
if (s->status_text)
fprintf(f, "%sStatus Text: %s\n",
prefix, s->status_text);
-
- free(p2);
}
static int service_load_pid_file(Service *s, bool may_warn) {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
pid_t pid;
@@ -1386,7 +1382,8 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (!s->pid_file)
return -ENOENT;
- if ((r = read_one_line_file(s->pid_file, &k)) < 0) {
+ r = read_one_line_file(s->pid_file, &k);
+ if (r < 0) {
if (may_warn)
log_info_unit(UNIT(s)->id,
"PID file %s not readable (yet?) after %s.",
@@ -1395,10 +1392,13 @@ static int service_load_pid_file(Service *s, bool may_warn) {
}
r = parse_pid(k, &pid);
- free(k);
-
- if (r < 0)
+ if (r < 0) {
+ if (may_warn)
+ log_info_unit(UNIT(s)->id,
+ "Failed to read PID from file %s: %s",
+ s->pid_file, strerror(-r));
return r;
+ }
if (kill(pid, 0) < 0 && errno != EPERM) {
if (may_warn)
@@ -1421,12 +1421,18 @@ static int service_load_pid_file(Service *s, bool may_warn) {
log_debug_unit(UNIT(s)->id,
"Main PID loaded: %lu", (unsigned long) pid);
- if ((r = service_set_main_pid(s, pid)) < 0)
+ r = service_set_main_pid(s, pid);
+ if (r < 0)
return r;
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ r = unit_watch_pid(UNIT(s), pid);
+ if (r < 0) {
/* FIXME: we need to do something here */
+ log_warning_unit(UNIT(s)->id,
+ "Failed to watch PID %lu from service %s",
+ (unsigned long) pid, UNIT(s)->id);
return r;
+ }
return 0;
}
@@ -1447,16 +1453,22 @@ static int service_search_main_pid(Service *s) {
assert(s->main_pid <= 0);
- if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0)
+ pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings);
+ if (pid <= 0)
return -ENOENT;
log_debug_unit(UNIT(s)->id,
"Main PID guessed: %lu", (unsigned long) pid);
- if ((r = service_set_main_pid(s, pid)) < 0)
+ r = service_set_main_pid(s, pid);
+ if (r < 0)
return r;
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ r = unit_watch_pid(UNIT(s), pid);
+ if (r < 0)
/* FIXME: we need to do something here */
+ log_warning_unit(UNIT(s)->id,
+ "Failed to watch PID %lu from service %s",
+ (unsigned long) pid, UNIT(s)->id);
return r;
return 0;
@@ -1531,6 +1543,9 @@ static void service_set_state(Service *s, ServiceState state) {
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
+ if (state == SERVICE_FAILED)
+ service_notify_sockets_dead(s, s->result == SERVICE_FAILURE_START_LIMIT);
+
if (state == SERVICE_DEAD ||
state == SERVICE_STOP ||
state == SERVICE_STOP_SIGTERM ||
@@ -1538,7 +1553,6 @@ static void service_set_state(Service *s, ServiceState state) {
state == SERVICE_STOP_POST ||
state == SERVICE_FINAL_SIGTERM ||
state == SERVICE_FINAL_SIGKILL ||
- state == SERVICE_FAILED ||
state == SERVICE_AUTO_RESTART)
service_notify_sockets_dead(s, false);
@@ -1558,7 +1572,7 @@ static void service_set_state(Service *s, ServiceState state) {
service_connection_unref(s);
}
- if (state == SERVICE_STOP)
+ if (state == SERVICE_STOP || state == SERVICE_STOP_SIGTERM)
service_stop_watchdog(s);
/* For the inactive states unit_notify() will trim the cgroup,
@@ -1618,9 +1632,11 @@ static int service_coldplug(Unit *u) {
s->deserialized_state == SERVICE_STOP ||
s->deserialized_state == SERVICE_STOP_SIGTERM ||
s->deserialized_state == SERVICE_STOP_SIGKILL)
- if (s->main_pid > 0)
- if ((r = unit_watch_pid(UNIT(s), s->main_pid)) < 0)
+ if (s->main_pid > 0) {
+ r = unit_watch_pid(UNIT(s), s->main_pid);
+ if (r < 0)
return r;
+ }
if (s->deserialized_state == SERVICE_START_PRE ||
s->deserialized_state == SERVICE_START ||
@@ -1632,9 +1648,11 @@ static int service_coldplug(Unit *u) {
s->deserialized_state == SERVICE_STOP_POST ||
s->deserialized_state == SERVICE_FINAL_SIGTERM ||
s->deserialized_state == SERVICE_FINAL_SIGKILL)
- if (s->control_pid > 0)
- if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ if (s->control_pid > 0) {
+ r = unit_watch_pid(UNIT(s), s->control_pid);
+ if (r < 0)
return r;
+ }
if (s->deserialized_state == SERVICE_START_POST ||
s->deserialized_state == SERVICE_RUNNING)
@@ -1669,7 +1687,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
sock = SOCKET(u);
- if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0)
+ r = socket_collect_fds(sock, &cfds, &cn_fds);
+ if (r < 0)
goto fail;
if (!cfds)
@@ -1681,7 +1700,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
} else {
int *t;
- if (!(t = new(int, rn_fds+cn_fds))) {
+ t = new(int, rn_fds+cn_fds);
+ if (!t) {
free(cfds);
r = -ENOMEM;
goto fail;
@@ -1722,9 +1742,11 @@ static int service_spawn(
pid_t pid;
int r;
- int *fds = NULL, *fdsbuf = NULL;
+ int *fds = NULL;
+ _cleanup_free_ int *fdsbuf = NULL;
unsigned n_fds = 0, n_env = 0;
- char **argv = NULL, **final_env = NULL, **our_env = NULL;
+ _cleanup_strv_free_ char
+ **argv = NULL, **final_env = NULL, **our_env = NULL;
assert(s);
assert(c);
@@ -1739,7 +1761,8 @@ static int service_spawn(
fds = &s->socket_fd;
n_fds = 1;
} else {
- if ((r = service_collect_fds(s, &fdsbuf, &n_fds)) < 0)
+ r = service_collect_fds(s, &fdsbuf, &n_fds);
+ if (r < 0)
goto fail;
fds = fdsbuf;
@@ -1747,13 +1770,15 @@ static int service_spawn(
}
if (timeout && s->timeout_start_usec) {
- r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_start_usec, &s->timer_watch);
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true,
+ s->timeout_start_usec, &s->timer_watch);
if (r < 0)
goto fail;
} else
unit_unwatch_timer(UNIT(s), &s->timer_watch);
- if (!(argv = unit_full_printf_strv(UNIT(s), c->argv))) {
+ argv = unit_full_printf_strv(UNIT(s), c->argv);
+ if (!argv) {
r = -ENOMEM;
goto fail;
}
@@ -1809,29 +1834,19 @@ static int service_spawn(
UNIT(s)->id,
s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
&pid);
-
if (r < 0)
goto fail;
- if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ r = unit_watch_pid(UNIT(s), pid);
+ if (r < 0)
/* FIXME: we need to do something here */
goto fail;
- free(fdsbuf);
- strv_free(argv);
- strv_free(our_env);
- strv_free(final_env);
-
*_pid = pid;
return 0;
fail:
- free(fdsbuf);
- strv_free(argv);
- strv_free(our_env);
- strv_free(final_env);
-
if (timeout)
unit_unwatch_timer(UNIT(s), &s->timer_watch);
@@ -1863,7 +1878,7 @@ static int main_pid_good(Service *s) {
return -EAGAIN;
}
-static int control_pid_good(Service *s) {
+_pure_ static int control_pid_good(Service *s) {
assert(s);
return s->control_pid > 0;
@@ -1874,7 +1889,8 @@ static int cgroup_good(Service *s) {
assert(s);
- if ((r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings)) < 0)
+ r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings);
+ if (r < 0)
return r;
return !r;
@@ -1911,6 +1927,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
s->forbid_restart = false;
+ /* we want fresh tmpdirs in case service is started again immediately */
+ exec_context_tmp_dirs_done(&s->exec_context);
+
return;
fail:
@@ -1931,7 +1950,8 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_STOP_POST];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_STOP_POST;
r = service_spawn(s,
@@ -1963,66 +1983,26 @@ fail:
static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(s);
if (f != SERVICE_SUCCESS)
s->result = f;
- if (s->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL;
-
- if (s->main_pid > 0) {
- if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill main process %li: %m", (long) s->main_pid);
- else
- wait_for_exit = !s->main_pid_alien;
- }
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control process %li: %m", (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the main/control pids from being killed via the cgroup */
- if (s->main_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0)
- goto fail;
-
- if (s->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
- goto fail;
-
- r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(s),
+ &s->kill_context,
+ state != SERVICE_STOP_SIGTERM && state != SERVICE_FINAL_SIGTERM,
+ s->main_pid,
+ s->control_pid,
+ s->main_pid_alien);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
if (s->timeout_stop_usec > 0) {
- r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch);
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true,
+ s->timeout_stop_usec, &s->timer_watch);
if (r < 0)
goto fail;
}
@@ -2043,9 +2023,6 @@ fail:
service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
else
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
-
- if (pid_set)
- set_free(pid_set);
}
static void service_enter_stop(Service *s, ServiceResult f) {
@@ -2058,7 +2035,8 @@ static void service_enter_stop(Service *s, ServiceResult f) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_STOP])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_STOP];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_STOP;
r = service_spawn(s,
@@ -2114,7 +2092,8 @@ static void service_enter_start_post(Service *s) {
if (s->watchdog_usec > 0)
service_reset_watchdog(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_START_POST])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_START_POST];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_START_POST;
r = service_spawn(s,
@@ -2176,7 +2155,8 @@ static void service_enter_start(Service *s) {
r = service_spawn(s,
c,
- s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT,
+ s->type == SERVICE_FORKING || s->type == SERVICE_DBUS ||
+ s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT,
true,
true,
true,
@@ -2233,11 +2213,13 @@ static void service_enter_start_pre(Service *s) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_START_PRE];
+ if (s->control_command) {
/* Before we start anything, let's clear up what might
* be left from previous runs. */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control");
+ cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
+ true,true, NULL, "control");
s->control_command_id = SERVICE_EXEC_START_PRE;
@@ -2317,7 +2299,8 @@ static void service_enter_reload(Service *s) {
service_unwatch_control_pid(s);
- if ((s->control_command = s->exec_command[SERVICE_EXEC_RELOAD])) {
+ s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
+ if (s->control_command) {
s->control_command_id = SERVICE_EXEC_RELOAD;
r = service_spawn(s,
@@ -2451,7 +2434,9 @@ static int service_start_limit_test(Service *s) {
log_warning_unit(UNIT(s)->id,
"%s start request repeated too quickly, rebooting.", UNIT(s)->id);
- r = manager_add_job_by_name(UNIT(s)->manager, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job_by_name(UNIT(s)->manager, JOB_START,
+ SPECIAL_REBOOT_TARGET, JOB_REPLACE,
+ true, &error, NULL);
if (r < 0) {
log_error_unit(UNIT(s)->id,
"Failed to reboot: %s.", bus_error(&error, r));
@@ -2585,7 +2570,7 @@ static int service_reload(Unit *u) {
return 0;
}
-static bool service_can_reload(Unit *u) {
+_pure_ static bool service_can_reload(Unit *u) {
Service *s = SERVICE(u);
assert(s);
@@ -2605,7 +2590,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result));
if (s->control_pid > 0)
- unit_serialize_item_format(u, f, "control-pid", "%lu", (unsigned long) s->control_pid);
+ unit_serialize_item_format(u, f, "control-pid", "%lu",
+ (unsigned long) s->control_pid);
if (s->main_pid_known && s->main_pid > 0)
unit_serialize_item_format(u, f, "main-pid", "%lu", (unsigned long) s->main_pid);
@@ -2619,7 +2605,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
* multiple commands attached here, we will start from the
* first one again */
if (s->control_command_id >= 0)
- unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
+ unit_serialize_item(u, f, "control-command",
+ service_exec_command_to_string(s->control_command_id));
if (s->socket_fd >= 0) {
int copy;
@@ -2631,17 +2618,29 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
}
if (s->main_exec_status.pid > 0) {
- unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", (unsigned long) s->main_exec_status.pid);
- dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
- dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
+ unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu",
+ (unsigned long) s->main_exec_status.pid);
+ dual_timestamp_serialize(f, "main-exec-status-start",
+ &s->main_exec_status.start_timestamp);
+ dual_timestamp_serialize(f, "main-exec-status-exit",
+ &s->main_exec_status.exit_timestamp);
if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
- unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code);
- unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status);
+ unit_serialize_item_format(u, f, "main-exec-status-code", "%i",
+ s->main_exec_status.code);
+ unit_serialize_item_format(u, f, "main-exec-status-status", "%i",
+ s->main_exec_status.status);
}
}
if (dual_timestamp_is_set(&s->watchdog_timestamp))
- dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
+ dual_timestamp_serialize(f, "watchdog-timestamp",
+ &s->watchdog_timestamp);
+
+ if (s->exec_context.tmp_dir)
+ unit_serialize_item(u, f, "tmp-dir", s->exec_context.tmp_dir);
+
+ if (s->exec_context.var_tmp_dir)
+ unit_serialize_item(u, f, "var-tmp-dir", s->exec_context.var_tmp_dir);
return 0;
}
@@ -2657,7 +2656,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
if (streq(key, "state")) {
ServiceState state;
- if ((state = service_state_from_string(value)) < 0)
+ state = service_state_from_string(value);
+ if (state < 0)
log_debug_unit(u->id, "Failed to parse state value %s", value);
else
s->deserialized_state = state;
@@ -2696,14 +2696,18 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
} else if (streq(key, "main-pid-known")) {
int b;
- if ((b = parse_boolean(value)) < 0)
+ b = parse_boolean(value);
+ if (b < 0)
log_debug_unit(u->id, "Failed to parse main-pid-known value %s", value);
else
s->main_pid_known = b;
} else if (streq(key, "status-text")) {
char *t;
- if ((t = strdup(value))) {
+ t = strdup(value);
+ if (!t)
+ log_oom();
+ else {
free(s->status_text);
s->status_text = t;
}
@@ -2711,7 +2715,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
} else if (streq(key, "control-command")) {
ServiceExecCommand id;
- if ((id = service_exec_command_from_string(value)) < 0)
+ id = service_exec_command_from_string(value);
+ if (id < 0)
log_debug_unit(u->id, "Failed to parse exec-command value %s", value);
else {
s->control_command_id = id;
@@ -2755,13 +2760,29 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
else if (streq(key, "watchdog-timestamp"))
dual_timestamp_deserialize(value, &s->watchdog_timestamp);
- else
+ else if (streq(key, "tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.var_tmp_dir = t;
+ } else
log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
-static UnitActiveState service_active_state(Unit *u) {
+_pure_ static UnitActiveState service_active_state(Unit *u) {
const UnitActiveState *table;
assert(u);
@@ -2797,7 +2818,7 @@ static bool service_check_gc(Unit *u) {
return false;
}
-static bool service_check_snapshot(Unit *u) {
+_pure_ static bool service_check_snapshot(Unit *u) {
Service *s = SERVICE(u);
assert(s);
@@ -2832,6 +2853,9 @@ static int service_watch_pid_file(Service *s) {
goto fail;
/* the pidfile might have appeared just before we set the watch */
+ log_debug_unit(UNIT(s)->id,
+ "Trying to read %s's PID file %s in case it changed",
+ UNIT(s)->id, s->pid_file_pathspec->path);
service_retry_pid_file(s);
return 0;
@@ -2926,24 +2950,34 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->main_pid = 0;
exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status);
- /* If this is not a forking service than the main
- * process got started and hence we copy the exit
- * status so that it is recorded both as main and as
- * control process exit status */
if (s->main_command) {
+ /* If this is not a forking service than the
+ * main process got started and hence we copy
+ * the exit status so that it is recorded both
+ * as main and as control process exit
+ * status */
+
s->main_command->exec_status = s->main_exec_status;
if (s->main_command->ignore)
f = SERVICE_SUCCESS;
+ } else if (s->exec_command[SERVICE_EXEC_START]) {
+
+ /* If this is a forked process, then we should
+ * ignore the return value if this was
+ * configured for the starter process */
+
+ if (s->exec_command[SERVICE_EXEC_START]->ignore)
+ f = SERVICE_SUCCESS;
}
- log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ log_struct_unit(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ u->id,
"MESSAGE=%s: main process exited, code=%s, status=%i/%s",
u->id, sigchld_code_to_string(code), status,
strna(code == CLD_EXITED
? exit_status_to_string(status, EXIT_STATUS_FULL)
: signal_to_string(status)),
- "UNIT=%s", u->id,
"EXIT_CODE=%s", sigchld_code_to_string(code),
"EXIT_STATUS=%i", status,
NULL);
@@ -3013,7 +3047,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status);
+ exec_status_exit(&s->control_command->exec_status,
+ &s->exec_context, pid, code, status);
if (s->control_command->ignore)
f = SERVICE_SUCCESS;
@@ -3029,7 +3064,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* Immediately get rid of the cgroup, so that the
* kernel doesn't delay the cgroup empty messages for
* the service cgroup any longer than necessary */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control");
+ cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
+ true, true, NULL, "control");
if (s->control_command &&
s->control_command->command_next &&
@@ -3398,7 +3434,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
if (strv_find(tags, "WATCHDOG=1")) {
log_debug_unit(u->id,
"%s: got WATCHDOG=1", u->id);
- service_reset_watchdog(s);
+ if (dual_timestamp_is_set(&s->watchdog_timestamp))
+ service_reset_watchdog(s);
}
/* Notify clients about changed status or main pid */
@@ -3410,9 +3447,10 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
static int service_enumerate(Manager *m) {
char **p;
unsigned i;
- DIR *d = NULL;
- char *path = NULL, *fpath = NULL, *name = NULL;
- Set *runlevel_services[ELEMENTSOF(rcnd_table)], *shutdown_services = NULL;
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *path = NULL, *fpath = NULL, *name = NULL;
+ Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
+ _cleanup_set_free_ Set *shutdown_services = NULL;
Unit *service;
Iterator j;
int r;
@@ -3422,8 +3460,6 @@ static int service_enumerate(Manager *m) {
if (m->running_as != SYSTEMD_SYSTEM)
return 0;
- zero(runlevel_services);
-
STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
struct dirent *de;
@@ -3438,9 +3474,10 @@ static int service_enumerate(Manager *m) {
if (d)
closedir(d);
- if (!(d = opendir(path))) {
+ d = opendir(path);
+ if (!d) {
if (errno != ENOENT)
- log_warning("opendir() failed on %s: %s", path, strerror(errno));
+ log_warning("opendir(%s) failed: %s", path, strerror(errno));
continue;
}
@@ -3479,8 +3516,9 @@ static int service_enumerate(Manager *m) {
}
free(name);
- if (!(name = sysv_translate_name(de->d_name + 3))) {
- r = -ENOMEM;
+ name = sysv_translate_name(de->d_name + 3);
+ if (!name) {
+ r = log_oom();
goto finish;
}
@@ -3499,19 +3537,25 @@ static int service_enumerate(Manager *m) {
SERVICE(service)->sysv_enabled = true;
}
- if ((r = set_ensure_allocated(&runlevel_services[i], trivial_hash_func, trivial_compare_func)) < 0)
+ r = set_ensure_allocated(&runlevel_services[i],
+ trivial_hash_func, trivial_compare_func);
+ if (r < 0)
goto finish;
- if ((r = set_put(runlevel_services[i], service)) < 0)
+ r = set_put(runlevel_services[i], service);
+ if (r < 0)
goto finish;
} else if (de->d_name[0] == 'K' &&
(rcnd_table[i].type == RUNLEVEL_DOWN)) {
- if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0)
+ r = set_ensure_allocated(&shutdown_services,
+ trivial_hash_func, trivial_compare_func);
+ if (r < 0)
goto finish;
- if ((r = set_put(shutdown_services, service)) < 0)
+ r = set_put(shutdown_services, service);
+ if (r < 0)
goto finish;
}
}
@@ -3532,7 +3576,10 @@ static int service_enumerate(Manager *m) {
if (service->fragment_path)
continue;
- if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, rcnd_table[i].target, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name_inverse(
+ service, UNIT_AFTER, UNIT_WANTS,
+ rcnd_table[i].target, NULL, true);
+ if (r < 0)
goto finish;
}
@@ -3547,23 +3594,19 @@ static int service_enumerate(Manager *m) {
if (service->fragment_path)
continue;
- if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(
+ service, UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ if (r < 0)
goto finish;
}
r = 0;
finish:
- free(path);
- free(fpath);
- free(name);
for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
set_free(runlevel_services[i]);
- set_free(shutdown_services);
-
- if (d)
- closedir(d);
return r;
}
@@ -3686,65 +3729,7 @@ static void service_reset_failed(Unit *u) {
static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) {
Service *s = SERVICE(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (s->main_pid <= 0 && who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_MAIN || who == KILL_ALL)
- if (s->main_pid > 0)
- if (kill(s->main_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control/main pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- if (s->main_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->main_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
}
static const char* const service_state_table[_SERVICE_STATE_MAX] = {
@@ -3830,13 +3815,15 @@ DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction);
const UnitVTable service_vtable = {
.object_size = sizeof(Service),
- .exec_context_offset = offsetof(Service, exec_context),
.sections =
"Unit\0"
"Service\0"
"Install\0",
+ .exec_context_offset = offsetof(Service, exec_context),
+ .exec_section = "Service",
+
.init = service_init,
.done = service_done,
.load = service_load,
diff --git a/src/core/service.h b/src/core/service.h
index d1e53bf727..703d3faa45 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -203,23 +203,23 @@ struct Socket;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket);
-const char* service_state_to_string(ServiceState i);
-ServiceState service_state_from_string(const char *s);
+const char* service_state_to_string(ServiceState i) _const_;
+ServiceState service_state_from_string(const char *s) _pure_;
-const char* service_restart_to_string(ServiceRestart i);
-ServiceRestart service_restart_from_string(const char *s);
+const char* service_restart_to_string(ServiceRestart i) _const_;
+ServiceRestart service_restart_from_string(const char *s) _pure_;
-const char* service_type_to_string(ServiceType i);
-ServiceType service_type_from_string(const char *s);
+const char* service_type_to_string(ServiceType i) _const_;
+ServiceType service_type_from_string(const char *s) _pure_;
-const char* service_exec_command_to_string(ServiceExecCommand i);
-ServiceExecCommand service_exec_command_from_string(const char *s);
+const char* service_exec_command_to_string(ServiceExecCommand i) _const_;
+ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
-const char* notify_access_to_string(NotifyAccess i);
-NotifyAccess notify_access_from_string(const char *s);
+const char* notify_access_to_string(NotifyAccess i) _const_;
+NotifyAccess notify_access_from_string(const char *s) _pure_;
-const char* service_result_to_string(ServiceResult i);
-ServiceResult service_result_from_string(const char *s);
+const char* service_result_to_string(ServiceResult i) _const_;
+ServiceResult service_result_from_string(const char *s) _pure_;
-const char* start_limit_action_to_string(StartLimitAction i);
-StartLimitAction start_limit_action_from_string(const char *s);
+const char* start_limit_action_to_string(StartLimitAction i) _const_;
+StartLimitAction start_limit_action_from_string(const char *s) _pure_;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 0b0e0c3d47..2db761de36 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -261,6 +261,8 @@ int main(int argc, char *argv[]) {
if (retries >= FINALIZE_ATTEMPTS)
log_error("Too many iterations, giving up.");
+ else
+ log_info("Storage is finalized.");
arguments[0] = NULL;
arguments[1] = argv[1];
@@ -272,6 +274,9 @@ int main(int argc, char *argv[]) {
if (prepare_new_root() >= 0 &&
pivot_to_new_root() >= 0) {
+
+ log_info("Returning to initrd...");
+
execv("/shutdown", argv);
log_error("Failed to execute shutdown binary: %m");
}
diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
new file mode 100644
index 0000000000..73eeb04190
--- /dev/null
+++ b/src/core/smack-setup.c
@@ -0,0 +1,151 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Intel Corporation
+ Authors:
+ Nathaniel Chen <nathaniel.chen@intel.com>
+
+ systemd is free software; you can redistribute it and/or modify it
+ 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 <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/vfs.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/mount.h>
+#include <stdint.h>
+
+#include "macro.h"
+#include "smack-setup.h"
+#include "util.h"
+#include "log.h"
+#include "label.h"
+
+#define SMACK_CONFIG "/etc/smack/accesses.d/"
+#define CIPSO_CONFIG "/etc/smack/cipso/"
+
+static int write_rules(const char* dstpath, const char* srcdir) {
+ _cleanup_fclose_ FILE *dst = NULL;
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *entry;
+ char buf[NAME_MAX];
+ int dfd = -1;
+ int r = 0;
+
+ dst = fopen(dstpath, "we");
+ if (!dst) {
+ if (errno != ENOENT)
+ log_warning("Failed to open %s: %m", dstpath);
+ return -errno; /* negative error */
+ }
+
+ /* write rules to dst from every file in the directory */
+ dir = opendir(srcdir);
+ if (!dir) {
+ if (errno != ENOENT)
+ log_warning("Failed to opendir %s: %m", srcdir);
+ return errno; /* positive on purpose */
+ }
+
+ dfd = dirfd(dir);
+ assert(dfd >= 0);
+
+ FOREACH_DIRENT(entry, dir, return 0) {
+ int fd;
+ _cleanup_fclose_ FILE *policy = NULL;
+
+ fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ if (r == 0)
+ r = -errno;
+ log_warning("Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ policy = fdopen(fd, "re");
+ if (!policy) {
+ if (r == 0)
+ r = -errno;
+ close_nointr_nofail(fd);
+ log_error("Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ /* load2 write rules in the kernel require a line buffered stream */
+ FOREACH_LINE(buf, policy,
+ log_error("Failed to read line from %s: %m",
+ entry->d_name)) {
+ if (!fputs(buf, dst)) {
+ if (r == 0)
+ r = -EINVAL;
+ log_error("Failed to write line to %s", dstpath);
+ break;
+ }
+ if (fflush(dst)) {
+ if (r == 0)
+ r = -errno;
+ log_error("Failed to flush writes to %s: %m", dstpath);
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+
+int smack_setup(void) {
+ int r;
+
+ r = write_rules("/sys/fs/smackfs/load2", SMACK_CONFIG);
+ switch(r) {
+ case -ENOENT:
+ log_debug("Smack is not enabled in the kernel.");
+ return 0;
+ case ENOENT:
+ log_debug("Smack access rules directory " SMACK_CONFIG " not found");
+ return 0;
+ case 0:
+ log_info("Successfully loaded Smack policies.");
+ break;
+ default:
+ log_warning("Failed to load Smack access rules: %s, ignoring.",
+ strerror(abs(r)));
+ return 0;
+ }
+
+ r = write_rules("/sys/fs/smackfs/cipso2", CIPSO_CONFIG);
+ switch(r) {
+ case -ENOENT:
+ log_debug("Smack/CIPSO is not enabled in the kernel.");
+ return 0;
+ case ENOENT:
+ log_debug("Smack/CIPSO access rules directory " CIPSO_CONFIG " not found");
+ return 0;
+ case 0:
+ log_info("Successfully loaded Smack/CIPSO policies.");
+ return 0;
+ default:
+ log_warning("Failed to load Smack/CIPSO access rules: %s, ignoring.",
+ strerror(abs(r)));
+ return 0;
+ }
+}
diff --git a/src/core/smack-setup.h b/src/core/smack-setup.h
new file mode 100644
index 0000000000..ffe91843c3
--- /dev/null
+++ b/src/core/smack-setup.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright (C) 2013 Intel Corporation
+ Authors:
+ Nathaniel Chen <nathaniel.chen@intel.com>
+
+ systemd is free software; you can redistribute it and/or modify it
+ 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/>.
+***/
+
+int smack_setup(void);
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
index 5c2a319cb6..a63eccd8de 100644
--- a/src/core/snapshot.c
+++ b/src/core/snapshot.c
@@ -173,13 +173,13 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value
return 0;
}
-static UnitActiveState snapshot_active_state(Unit *u) {
+_pure_ static UnitActiveState snapshot_active_state(Unit *u) {
assert(u);
return state_translation_table[SNAPSHOT(u)->state];
}
-static const char *snapshot_sub_state_to_string(Unit *u) {
+_pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
assert(u);
return snapshot_state_to_string(SNAPSHOT(u)->state);
@@ -256,6 +256,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Sn
}
SNAPSHOT(u)->cleanup = cleanup;
+ u->allow_isolate = true;
*_s = SNAPSHOT(u);
return 0;
diff --git a/src/core/snapshot.h b/src/core/snapshot.h
index 9662d93164..56f87cff4d 100644
--- a/src/core/snapshot.h
+++ b/src/core/snapshot.h
@@ -46,5 +46,5 @@ extern const UnitVTable snapshot_vtable;
int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **s);
void snapshot_remove(Snapshot *s);
-const char* snapshot_state_to_string(SnapshotState i);
-SnapshotState snapshot_state_from_string(const char *s);
+const char* snapshot_state_to_string(SnapshotState i) _const_;
+SnapshotState snapshot_state_from_string(const char *s) _pure_;
diff --git a/src/core/socket.c b/src/core/socket.c
index fcbcdbe192..1b08f0a5fd 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -28,7 +28,7 @@
#include <signal.h>
#include <arpa/inet.h>
#include <mqueue.h>
-#ifdef HAVE_ATTR_XATTR_H
+#ifdef HAVE_XATTR
#include <attr/xattr.h>
#endif
@@ -46,7 +46,7 @@
#include "dbus-socket.h"
#include "missing.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
#include "label.h"
#include "exit-status.h"
#include "def.h"
@@ -102,8 +102,7 @@ static void socket_unwatch_control_pid(Socket *s) {
s->control_pid = 0;
}
-static void socket_done(Unit *u) {
- Socket *s = SOCKET(u);
+void socket_free_ports(Socket *s) {
SocketPort *p;
assert(s);
@@ -119,8 +118,16 @@ static void socket_done(Unit *u) {
free(p->path);
free(p);
}
+}
+
+static void socket_done(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ socket_free_ports(s);
- exec_context_done(&s->exec_context);
+ exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -308,7 +315,7 @@ static int socket_add_device_link(Socket *s) {
assert(s);
- if (!s->bind_to_device)
+ if (!s->bind_to_device || streq(s->bind_to_device, "lo"))
return 0;
if (asprintf(&t, "/sys/subsystem/net/devices/%s", s->bind_to_device) < 0)
@@ -324,18 +331,20 @@ static int socket_add_default_dependencies(Socket *s) {
int r;
assert(s);
- if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
- return r;
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
- if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ if (UNIT(s)->manager->running_as == SYSTEMD_SYSTEM) {
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (r < 0)
return r;
}
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
-static bool socket_has_exec(Socket *s) {
+_pure_ static bool socket_has_exec(Socket *s) {
unsigned i;
assert(s);
@@ -401,7 +410,7 @@ static int socket_load(Unit *u) {
return socket_verify(s);
}
-static const char* listen_lookup(int family, int type) {
+_const_ static const char* listen_lookup(int family, int type) {
if (family == AF_NETLINK)
return "ListenNetlink";
@@ -779,7 +788,7 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
log_warning_unit(UNIT(s)->id, "TCP_CONGESTION failed: %m");
-#ifdef HAVE_ATTR_XATTR_H
+#ifdef HAVE_XATTR
if (s->smack_ip_in)
if (fsetxattr(fd, "security.SMACK64IPIN", s->smack_ip_in, strlen(s->smack_ip_in), 0) < 0)
log_error_unit(UNIT(s)->id,
@@ -801,7 +810,7 @@ static void socket_apply_fifo_options(Socket *s, int fd) {
log_warning_unit(UNIT(s)->id,
"F_SETPIPE_SZ: %m");
-#ifdef HAVE_ATTR_XATTR_H
+#ifdef HAVE_XATTR
if (s->smack)
if (fsetxattr(fd, "security.SMACK64", s->smack, strlen(s->smack), 0) < 0)
log_error_unit(UNIT(s)->id,
@@ -1246,6 +1255,7 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
if (f != SOCKET_SUCCESS)
s->result = f;
+ exec_context_tmp_dirs_done(&s->exec_context);
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
}
@@ -1281,54 +1291,23 @@ fail:
static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(s);
if (f != SOCKET_SUCCESS)
s->result = f;
- if (s->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL;
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control process %li: %m",
- (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
- r = -ENOMEM;
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
- goto fail;
-
- r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control group: %s",
- strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(s),
+ &s->kill_context,
+ state != SOCKET_STOP_PRE_SIGTERM && state != SOCKET_FINAL_SIGTERM,
+ -1,
+ s->control_pid,
+ false);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
if (r < 0)
goto fail;
@@ -1350,9 +1329,6 @@ fail:
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
else
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
}
static void socket_enter_stop_pre(Socket *s, SocketResult f) {
@@ -1471,7 +1447,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* We don't take connections anymore if we are supposed to
* shut down anyway */
- if (unit_pending_inactive(UNIT(s))) {
+ if (unit_stop_pending(UNIT(s))) {
log_debug_unit(UNIT(s)->id,
"Suppressing connection request on %s since unit stop is scheduled.",
UNIT(s)->id);
@@ -1502,7 +1478,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* If there's already a start pending don't bother to
* do anything */
SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
- if (unit_pending_active(u)) {
+ if (unit_active_or_pending(u)) {
pending = true;
break;
}
@@ -1658,7 +1634,7 @@ static int socket_start(Unit *u) {
service = SERVICE(UNIT_DEREF(s->service));
if (UNIT(service)->load_state != UNIT_LOADED) {
- log_error_unit(UNIT(service)->id,
+ log_error_unit(u->id,
"Socket service %s not loaded, refusing.",
UNIT(service)->id);
return -ENOENT;
@@ -1669,7 +1645,7 @@ static int socket_start(Unit *u) {
if (service->state != SERVICE_DEAD &&
service->state != SERVICE_FAILED &&
service->state != SERVICE_AUTO_RESTART) {
- log_error_unit(UNIT(service)->id,
+ log_error_unit(u->id,
"Socket service %s already active, refusing.",
UNIT(service)->id);
return -EBUSY;
@@ -1677,7 +1653,7 @@ static int socket_start(Unit *u) {
#ifdef HAVE_SYSV_COMPAT
if (service->is_sysv) {
- log_error_unit(UNIT(s)->id,
+ log_error_unit(u->id,
"Using SysV services for socket activation is not supported. Refusing.");
return -ENOENT;
}
@@ -1769,6 +1745,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
}
}
+ exec_context_serialize(&s->exec_context, UNIT(s), f);
+
return 0;
}
@@ -1928,7 +1906,22 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
p->fd = fdset_remove(fds, fd);
}
}
+ } else if (streq(key, "tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.var_tmp_dir = t;
} else
log_debug_unit(UNIT(s)->id,
"Unknown serialization key '%s'", key);
@@ -1964,19 +1957,41 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) {
return 0;
}
-static UnitActiveState socket_active_state(Unit *u) {
+_pure_ static UnitActiveState socket_active_state(Unit *u) {
assert(u);
return state_translation_table[SOCKET(u)->state];
}
-static const char *socket_sub_state_to_string(Unit *u) {
+_pure_ static const char *socket_sub_state_to_string(Unit *u) {
assert(u);
return socket_state_to_string(SOCKET(u)->state);
}
-static bool socket_check_gc(Unit *u) {
+const char* socket_port_type_to_string(SocketPort *p) {
+
+ assert(p);
+
+ switch (p->type) {
+ case SOCKET_SOCKET:
+ switch (p->address.type) {
+ case SOCK_STREAM: return "Stream";
+ case SOCK_DGRAM: return "Datagram";
+ case SOCK_SEQPACKET: return "SequentialPacket";
+ case SOCK_RAW:
+ if (socket_address_family(&p->address) == AF_NETLINK)
+ return "Netlink";
+ default: return "Invalid";
+ }
+ case SOCKET_SPECIAL: return "Special";
+ case SOCKET_MQUEUE: return "MessageQueue";
+ case SOCKET_FIFO: return "FIFO";
+ default: return NULL;
+ }
+}
+
+_pure_ static bool socket_check_gc(Unit *u) {
Socket *s = SOCKET(u);
assert(u);
@@ -2292,53 +2307,7 @@ static void socket_reset_failed(Unit *u) {
}
static int socket_kill(Unit *u, KillWho who, int signo, DBusError *error) {
- Socket *s = SOCKET(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Socket units have no main processes");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error);
}
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
@@ -2381,13 +2350,15 @@ DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
const UnitVTable socket_vtable = {
.object_size = sizeof(Socket),
- .exec_context_offset = offsetof(Socket, exec_context),
.sections =
"Unit\0"
"Socket\0"
"Install\0",
+ .exec_context_offset = offsetof(Socket, exec_context),
+ .exec_section = "Socket",
+
.init = socket_init,
.done = socket_done,
.load = socket_load,
diff --git a/src/core/socket.h b/src/core/socket.h
index f099520dce..9d48cde0a6 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -130,6 +130,10 @@ struct Socket {
bool broadcast;
bool pass_cred;
bool pass_sec;
+
+ /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
+ SocketAddressBindIPv6Only bind_ipv6_only;
+
int priority;
int mark;
size_t receive_buffer;
@@ -142,9 +146,6 @@ struct Socket {
long mq_maxmsg;
long mq_msgsize;
- /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
- SocketAddressBindIPv6Only bind_ipv6_only;
-
char *smack;
char *smack_ip_in;
char *smack_ip_out;
@@ -163,13 +164,17 @@ int socket_add_one_mount_link(Socket *s, Mount *m);
/* Called from the service code when a per-connection service ended */
void socket_connection_unref(Socket *s);
+void socket_free_ports(Socket *s);
+
extern const UnitVTable socket_vtable;
-const char* socket_state_to_string(SocketState i);
-SocketState socket_state_from_string(const char *s);
+const char* socket_state_to_string(SocketState i) _const_;
+SocketState socket_state_from_string(const char *s) _pure_;
+
+const char* socket_exec_command_to_string(SocketExecCommand i) _const_;
+SocketExecCommand socket_exec_command_from_string(const char *s) _pure_;
-const char* socket_exec_command_to_string(SocketExecCommand i);
-SocketExecCommand socket_exec_command_from_string(const char *s);
+const char* socket_result_to_string(SocketResult i) _const_;
+SocketResult socket_result_from_string(const char *s) _pure_;
-const char* socket_result_to_string(SocketResult i);
-SocketResult socket_result_from_string(const char *s);
+const char* socket_port_type_to_string(SocketPort *p) _pure_;
diff --git a/src/core/special.h b/src/core/special.h
index ef72260ecd..a9b50bce05 100644
--- a/src/core/special.h
+++ b/src/core/special.h
@@ -46,21 +46,23 @@
/* Early boot targets */
#define SPECIAL_SYSINIT_TARGET "sysinit.target"
#define SPECIAL_SOCKETS_TARGET "sockets.target"
-#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" /* LSB's $local_fs */
+#define SPECIAL_TIMERS_TARGET "timers.target"
+#define SPECIAL_PATHS_TARGET "paths.target"
+#define SPECIAL_LOCAL_FS_TARGET "local-fs.target"
#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target"
+#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target"
+#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target"
#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */
#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target"
#define SPECIAL_SWAP_TARGET "swap.target"
+#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target"
#define SPECIAL_BASIC_TARGET "basic.target"
/* LSB compatibility */
#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
-#define SPECIAL_SYSLOG_TARGET "syslog.target" /* LSB's $syslog */
#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
-#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Common extension of LSB */
-#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Common extension of LSB */
/*
* Rules regarding adding further high level targets like the above:
diff --git a/src/core/swap.c b/src/core/swap.c
index c8e25d0665..d503fe20df 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -125,7 +125,7 @@ static void swap_done(Unit *u) {
free(s->parameters_fragment.what);
s->parameters_fragment.what = NULL;
- exec_context_done(&s->exec_context);
+ exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -214,7 +214,7 @@ static int swap_add_default_dependencies(Swap *s) {
static int swap_verify(Swap *s) {
bool b;
- char _cleanup_free_ *e = NULL;
+ _cleanup_free_ char *e = NULL;
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
@@ -315,7 +315,7 @@ static int swap_add_one(
bool set_flags) {
Unit *u = NULL;
- char _cleanup_free_ *e = NULL;
+ _cleanup_free_ char *e = NULL;
char *wp = NULL;
bool delete = false;
int r;
@@ -632,6 +632,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
if (f != SWAP_SUCCESS)
s->result = f;
+ exec_context_tmp_dirs_done(&s->exec_context);
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
}
@@ -646,57 +647,23 @@ static void swap_enter_active(Swap *s, SwapResult f) {
static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
int r;
- Set *pid_set = NULL;
- bool wait_for_exit = false;
assert(s);
if (f != SWAP_SUCCESS)
s->result = f;
- if (s->kill_context.kill_mode != KILL_NONE) {
- int sig = (state == SWAP_ACTIVATING_SIGTERM ||
- state == SWAP_DEACTIVATING_SIGTERM) ? s->kill_context.kill_signal : SIGKILL;
-
- if (s->control_pid > 0) {
- if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
-
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control process %li: %m",
- (long) s->control_pid);
- else
- wait_for_exit = true;
- }
-
- if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) {
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set) {
- r = log_oom();
- goto fail;
- }
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (r < 0)
- goto fail;
- }
-
- r = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, sig, true, false, pid_set, NULL);
- if (r < 0) {
- if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_warning_unit(UNIT(s)->id,
- "Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
- wait_for_exit = true;
-
- set_free(pid_set);
- pid_set = NULL;
- }
- }
+ r = unit_kill_context(
+ UNIT(s),
+ &s->kill_context,
+ state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM,
+ -1,
+ s->control_pid,
+ false);
+ if (r < 0)
+ goto fail;
- if (wait_for_exit) {
+ if (r > 0) {
r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
if (r < 0)
goto fail;
@@ -712,9 +679,6 @@ fail:
"%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
-
- if (pid_set)
- set_free(pid_set);
}
static void swap_enter_activating(Swap *s) {
@@ -868,6 +832,8 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
if (s->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id));
+ exec_context_serialize(&s->exec_context, UNIT(s), f);
+
return 0;
}
@@ -911,26 +877,41 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
s->control_command_id = id;
s->control_command = s->exec_command + id;
}
+ } else if (streq(key, "tmp-dir")) {
+ char *t;
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.tmp_dir = t;
+ } else if (streq(key, "var-tmp-dir")) {
+ char *t;
+
+ t = strdup(value);
+ if (!t)
+ return log_oom();
+
+ s->exec_context.var_tmp_dir = t;
} else
log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
-static UnitActiveState swap_active_state(Unit *u) {
+_pure_ static UnitActiveState swap_active_state(Unit *u) {
assert(u);
return state_translation_table[SWAP(u)->state];
}
-static const char *swap_sub_state_to_string(Unit *u) {
+_pure_ static const char *swap_sub_state_to_string(Unit *u) {
assert(u);
return swap_state_to_string(SWAP(u)->state);
}
-static bool swap_check_gc(Unit *u) {
+_pure_ static bool swap_check_gc(Unit *u) {
Swap *s = SWAP(u);
assert(s);
@@ -1261,10 +1242,14 @@ static void swap_shutdown(Manager *m) {
static int swap_enumerate(Manager *m) {
int r;
- struct epoll_event ev;
assert(m);
if (!m->proc_swaps) {
+ struct epoll_event ev = {
+ .events = EPOLLPRI,
+ .data.ptr = &m->swap_watch,
+ };
+
m->proc_swaps = fopen("/proc/swaps", "re");
if (!m->proc_swaps)
return (errno == ENOENT) ? 0 : -errno;
@@ -1272,10 +1257,6 @@ static int swap_enumerate(Manager *m) {
m->swap_watch.type = WATCH_SWAP;
m->swap_watch.fd = fileno(m->proc_swaps);
- zero(ev);
- ev.events = EPOLLPRI;
- ev.data.ptr = &m->swap_watch;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->swap_watch.fd, &ev) < 0)
return -errno;
}
@@ -1299,53 +1280,7 @@ static void swap_reset_failed(Unit *u) {
}
static int swap_kill(Unit *u, KillWho who, int signo, DBusError *error) {
- Swap *s = SWAP(u);
- int r = 0;
- Set *pid_set = NULL;
-
- assert(s);
-
- if (who == KILL_MAIN) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Swap units have no main processes");
- return -ESRCH;
- }
-
- if (s->control_pid <= 0 && who == KILL_CONTROL) {
- dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
- return -ESRCH;
- }
-
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (s->control_pid > 0)
- if (kill(s->control_pid, signo) < 0)
- r = -errno;
-
- if (who == KILL_ALL) {
- int q;
-
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
- if (!pid_set)
- return -ENOMEM;
-
- /* Exclude the control pid from being killed via the cgroup */
- if (s->control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
- if (q < 0) {
- r = q;
- goto finish;
- }
- }
-
- q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
- }
-
-finish:
- if (pid_set)
- set_free(pid_set);
-
- return r;
+ return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error);
}
static const char* const swap_state_table[_SWAP_STATE_MAX] = {
@@ -1382,13 +1317,15 @@ DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult);
const UnitVTable swap_vtable = {
.object_size = sizeof(Swap),
- .exec_context_offset = offsetof(Swap, exec_context),
.sections =
"Unit\0"
"Swap\0"
"Install\0",
+ .exec_context_offset = offsetof(Swap, exec_context),
+ .exec_section = "Swap",
+
.no_alias = true,
.no_instances = true,
diff --git a/src/core/swap.h b/src/core/swap.h
index 35d47fd46f..121889d1d5 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -111,11 +111,11 @@ int swap_add_one_mount_link(Swap *s, Mount *m);
int swap_dispatch_reload(Manager *m);
int swap_fd_event(Manager *m, int events);
-const char* swap_state_to_string(SwapState i);
-SwapState swap_state_from_string(const char *s);
+const char* swap_state_to_string(SwapState i) _const_;
+SwapState swap_state_from_string(const char *s) _pure_;
-const char* swap_exec_command_to_string(SwapExecCommand i);
-SwapExecCommand swap_exec_command_from_string(const char *s);
+const char* swap_exec_command_to_string(SwapExecCommand i) _const_;
+SwapExecCommand swap_exec_command_from_string(const char *s) _pure_;
-const char* swap_result_to_string(SwapResult i);
-SwapResult swap_result_from_string(const char *s);
+const char* swap_result_to_string(SwapResult i) _const_;
+SwapResult swap_result_from_string(const char *s) _pure_;
diff --git a/src/core/sync.c b/src/core/sync.c
new file mode 100644
index 0000000000..7e74b63071
--- /dev/null
+++ b/src/core/sync.c
@@ -0,0 +1,65 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ 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 <pthread.h>
+#include <unistd.h>
+
+#include "sync.h"
+
+static void *sync_thread(void *p) {
+ sync();
+ return NULL;
+}
+
+int asynchronous_sync(void) {
+ pthread_attr_t a;
+ pthread_t t;
+ int r;
+
+ /* It kinda sucks that we have to resort to threads to
+ * implement an asynchronous sync(), but well, such is
+ * life.
+ *
+ * Note that issuing this command right before exiting a
+ * process will cause the process to wait for the sync() to
+ * complete. This function hence is nicely asynchronous really
+ * only in long running processes. */
+
+ r = pthread_attr_init(&a);
+ if (r != 0)
+ return -r;
+
+ r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
+ if (r != 0) {
+ r = -r;
+ goto finish;
+ }
+
+ r = pthread_create(&t, &a, sync_thread, NULL);
+ if (r != 0) {
+ r = -r;
+ goto finish;
+ }
+
+finish:
+ pthread_attr_destroy(&a);
+ return r;
+}
diff --git a/src/core/sync.h b/src/core/sync.h
new file mode 100644
index 0000000000..eb26c88deb
--- /dev/null
+++ b/src/core/sync.h
@@ -0,0 +1,24 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ 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/>.
+***/
+
+int asynchronous_sync(void);
diff --git a/src/core/syscall-list.c b/src/core/syscall-list.c
index 05fad3e158..35216b2a88 100644
--- a/src/core/syscall-list.c
+++ b/src/core/syscall-list.c
@@ -26,12 +26,14 @@
#include "syscall-list.h"
-const struct syscall_name *lookup_syscall(register const char *str, register unsigned int len);
+static const struct syscall_name* lookup_syscall(register const char *str,
+ register unsigned int len);
#include "syscall-to-name.h"
#include "syscall-from-name.h"
const char *syscall_to_name(int id) {
+ id = SYSCALL_TO_INDEX(id);
if (id < 0 || id >= (int) ELEMENTSOF(syscall_names))
return NULL;
diff --git a/src/core/syscall-list.h b/src/core/syscall-list.h
index 0fc6859605..6bb4d91f91 100644
--- a/src/core/syscall-list.h
+++ b/src/core/syscall-list.h
@@ -22,6 +22,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#if defined __x86_64__ && defined __ILP32__
+/* The x32 ABI defines all of its syscalls with bit 30 set, which causes
+ issues when attempting to use syscalls as simple indices into an array.
+ Instead, use the syscall id & ~SYSCALL_MASK as the index, and | the
+ internal id with the syscall mask as needed.
+*/
+#include <asm/unistd.h>
+#define SYSCALL_TO_INDEX(x) ((x) & ~__X32_SYSCALL_BIT)
+#define INDEX_TO_SYSCALL(x) ((x) | __X32_SYSCALL_BIT)
+#else
+#define SYSCALL_TO_INDEX(x) (x)
+#define INDEX_TO_SYSCALL(x) (x)
+#endif
+
const char *syscall_to_name(int id);
int syscall_from_name(const char *name);
diff --git a/src/core/system.conf b/src/core/system.conf
index 68076d9735..508e0f5fa4 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -5,7 +5,7 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# See systemd.conf(5) for details
+# See systemd-system.conf(5) for details
[Manager]
#LogLevel=info
diff --git a/src/core/target.c b/src/core/target.c
index 981424132b..3fffa0d2f5 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -184,13 +184,13 @@ static int target_deserialize_item(Unit *u, const char *key, const char *value,
return 0;
}
-static UnitActiveState target_active_state(Unit *u) {
+_pure_ static UnitActiveState target_active_state(Unit *u) {
assert(u);
return state_translation_table[TARGET(u)->state];
}
-static const char *target_sub_state_to_string(Unit *u) {
+_pure_ static const char *target_sub_state_to_string(Unit *u) {
assert(u);
return target_state_to_string(TARGET(u)->state);
diff --git a/src/core/target.h b/src/core/target.h
index 1676553add..a5398d9e98 100644
--- a/src/core/target.h
+++ b/src/core/target.h
@@ -40,5 +40,5 @@ struct Target {
extern const UnitVTable target_vtable;
-const char* target_state_to_string(TargetState i);
-TargetState target_state_from_string(const char *s);
+const char* target_state_to_string(TargetState i) _const_;
+TargetState target_state_from_string(const char *s) _pure_;
diff --git a/src/core/timer.c b/src/core/timer.c
index 31ef176e7e..9166c1e2fc 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -26,7 +26,7 @@
#include "timer.h"
#include "dbus-timer.h"
#include "special.h"
-#include "bus-errors.h"
+#include "dbus-common.h"
static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
[TIMER_DEAD] = UNIT_INACTIVE,
@@ -48,8 +48,7 @@ static void timer_init(Unit *u) {
watch_init(&t->realtime_watch);
}
-static void timer_done(Unit *u) {
- Timer *t = TIMER(u);
+void timer_free_values(Timer *t) {
TimerValue *v;
assert(t);
@@ -62,11 +61,17 @@ static void timer_done(Unit *u) {
free(v);
}
+}
+
+static void timer_done(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+
+ timer_free_values(t);
unit_unwatch_timer(u, &t->monotonic_watch);
unit_unwatch_timer(u, &t->realtime_watch);
-
- unit_ref_unset(&t->unit);
}
static int timer_verify(Timer *t) {
@@ -89,11 +94,11 @@ static int timer_add_default_dependencies(Timer *t) {
assert(t);
- if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
- r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true);
- if (r < 0)
- return r;
+ r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ if (UNIT(t)->manager->running_as == SYSTEMD_SYSTEM) {
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -115,20 +120,18 @@ static int timer_load(Unit *u) {
if (u->load_state == UNIT_LOADED) {
- if (!UNIT_DEREF(t->unit)) {
+ if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
Unit *x;
r = unit_load_related_unit(u, ".service", &x);
if (r < 0)
return r;
- unit_ref_set(&t->unit, x);
+ r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
+ if (r < 0)
+ return r;
}
- r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(t->unit), true);
- if (r < 0)
- return r;
-
if (UNIT(t)->default_dependencies) {
r = timer_add_default_dependencies(t);
if (r < 0)
@@ -141,15 +144,18 @@ static int timer_load(Unit *u) {
static void timer_dump(Unit *u, FILE *f, const char *prefix) {
Timer *t = TIMER(u);
+ Unit *trigger;
TimerValue *v;
+ trigger = UNIT_TRIGGER(u);
+
fprintf(f,
"%sTimer State: %s\n"
"%sResult: %s\n"
"%sUnit: %s\n",
prefix, timer_state_to_string(t->state),
prefix, timer_result_to_string(t->result),
- prefix, UNIT_DEREF(t->unit)->id);
+ prefix, trigger ? trigger->id : "n/a");
LIST_FOREACH(value, v, t->values) {
@@ -170,7 +176,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
"%s%s: %s\n",
prefix,
timer_base_to_string(v->base),
- strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
+ strna(format_timespan(timespan1, sizeof(timespan1), v->value, 0)));
}
}
}
@@ -245,11 +251,6 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (r < 0)
continue;
- if (!initial && v->next_elapse < ts.realtime) {
- v->disabled = true;
- continue;
- }
-
if (!found_realtime)
t->next_elapse_realtime = v->next_elapse;
else
@@ -278,18 +279,26 @@ static void timer_enter_waiting(Timer *t, bool initial) {
case TIMER_UNIT_ACTIVE:
- if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
+ base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
+
+ if (base <= 0)
+ base = t->last_trigger_monotonic;
+
+ if (base <= 0)
continue;
- base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
break;
case TIMER_UNIT_INACTIVE:
- if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
+ base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
+
+ if (base <= 0)
+ base = t->last_trigger_monotonic;
+
+ if (base <= 0)
continue;
- base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
break;
default:
@@ -298,7 +307,10 @@ static void timer_enter_waiting(Timer *t, bool initial) {
v->next_elapse = base + v->value;
- if (!initial && v->next_elapse < ts.monotonic) {
+ if (!initial &&
+ v->next_elapse < ts.monotonic &&
+ (v->base == TIMER_ACTIVE || v->base == TIMER_BOOT || v->base == TIMER_STARTUP)) {
+ /* This is a one time trigger, disable it now */
v->disabled = true;
continue;
}
@@ -321,9 +333,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_monotonic) {
char buf[FORMAT_TIMESPAN_MAX];
log_debug_unit(UNIT(t)->id,
- "%s: Monotonic timer elapses in %s the next time.",
+ "%s: Monotonic timer elapses in %s.",
UNIT(t)->id,
- format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic));
+ format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0, 0));
r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
if (r < 0)
@@ -334,7 +346,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
log_debug_unit(UNIT(t)->id,
- "%s: Realtime timer elapses at %s the next time.",
+ "%s: Realtime timer elapses at %s.",
UNIT(t)->id,
format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
@@ -362,13 +374,16 @@ static void timer_enter_running(Timer *t) {
dbus_error_init(&error);
/* Don't start job if we are supposed to go down */
- if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
+ if (unit_stop_pending(UNIT(t)))
return;
- r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
+ JOB_REPLACE, true, &error, NULL);
if (r < 0)
goto fail;
+ t->last_trigger_monotonic = now(CLOCK_MONOTONIC);
+
timer_set_state(t, TIMER_RUNNING);
return;
@@ -387,7 +402,7 @@ static int timer_start(Unit *u) {
assert(t);
assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
- if (UNIT_DEREF(t->unit)->load_state != UNIT_LOADED)
+ if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
return -ENOENT;
t->result = TIMER_SUCCESS;
@@ -449,13 +464,13 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
return 0;
}
-static UnitActiveState timer_active_state(Unit *u) {
+_pure_ static UnitActiveState timer_active_state(Unit *u) {
assert(u);
return state_translation_table[TIMER(u)->state];
}
-static const char *timer_sub_state_to_string(Unit *u) {
+_pure_ static const char *timer_sub_state_to_string(Unit *u) {
assert(u);
return timer_state_to_string(TIMER(u)->state);
@@ -474,58 +489,47 @@ static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
timer_enter_running(t);
}
-void timer_unit_notify(Unit *u, UnitActiveState new_state) {
- Iterator i;
- Unit *k;
-
- if (u->type == UNIT_TIMER)
- return;
+static void timer_trigger_notify(Unit *u, Unit *other) {
+ Timer *t = TIMER(u);
+ TimerValue *v;
- SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
- Timer *t;
- TimerValue *v;
+ assert(u);
+ assert(other);
- if (k->type != UNIT_TIMER)
- continue;
+ if (other->load_state != UNIT_LOADED)
+ return;
- if (k->load_state != UNIT_LOADED)
- continue;
+ /* Reenable all timers that depend on unit state */
+ LIST_FOREACH(value, v, t->values)
+ if (v->base == TIMER_UNIT_ACTIVE ||
+ v->base == TIMER_UNIT_INACTIVE)
+ v->disabled = false;
- t = TIMER(k);
+ switch (t->state) {
- /* Reenable all timers that depend on unit state */
- LIST_FOREACH(value, v, t->values)
- if (v->base == TIMER_UNIT_ACTIVE ||
- v->base == TIMER_UNIT_INACTIVE)
- v->disabled = false;
+ case TIMER_WAITING:
+ case TIMER_ELAPSED:
- switch (t->state) {
+ /* Recalculate sleep time */
+ timer_enter_waiting(t, false);
+ break;
- case TIMER_WAITING:
- case TIMER_ELAPSED:
+ case TIMER_RUNNING:
- /* Recalculate sleep time */
+ if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
+ log_debug_unit(UNIT(t)->id,
+ "%s got notified about unit deactivation.",
+ UNIT(t)->id);
timer_enter_waiting(t, false);
- break;
-
- case TIMER_RUNNING:
-
- if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
- log_debug_unit(UNIT(t)->id,
- "%s got notified about unit deactivation.",
- UNIT(t)->id);
- timer_enter_waiting(t, false);
- }
-
- break;
+ }
+ break;
- case TIMER_DEAD:
- case TIMER_FAILED:
- break;
+ case TIMER_DEAD:
+ case TIMER_FAILED:
+ break;
- default:
- assert_not_reached("Unknown timer state");
- }
+ default:
+ assert_not_reached("Unknown timer state");
}
}
@@ -548,8 +552,8 @@ static void timer_time_change(Unit *u) {
if (t->state != TIMER_WAITING)
return;
- log_info_unit(u->id,
- "%s: time change, recalculating next elapse.", u->id);
+ log_debug_unit(u->id,
+ "%s: time change, recalculating next elapse.", u->id);
timer_enter_waiting(t, false);
}
@@ -607,6 +611,8 @@ const UnitVTable timer_vtable = {
.timer_event = timer_timer_event,
+ .trigger_notify = timer_trigger_notify,
+
.reset_failed = timer_reset_failed,
.time_change = timer_time_change,
diff --git a/src/core/timer.h b/src/core/timer.h
index 57a514a68c..4168553e9d 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -52,8 +52,8 @@ typedef struct TimerValue {
bool disabled;
clockid_t clock_id;
- usec_t value;
- CalendarSpec *calendar_spec;
+ usec_t value; /* only for monotonic events */
+ CalendarSpec *calendar_spec; /* only for calendar events */
usec_t next_elapse;
LIST_FIELDS(struct TimerValue, value);
@@ -74,23 +74,24 @@ struct Timer {
usec_t next_elapse_realtime;
TimerState state, deserialized_state;
- UnitRef unit;
Watch monotonic_watch;
Watch realtime_watch;
TimerResult result;
+
+ usec_t last_trigger_monotonic;
};
-void timer_unit_notify(Unit *u, UnitActiveState new_state);
+void timer_free_values(Timer *t);
extern const UnitVTable timer_vtable;
-const char *timer_state_to_string(TimerState i);
-TimerState timer_state_from_string(const char *s);
+const char *timer_state_to_string(TimerState i) _const_;
+TimerState timer_state_from_string(const char *s) _pure_;
-const char *timer_base_to_string(TimerBase i);
-TimerBase timer_base_from_string(const char *s);
+const char *timer_base_to_string(TimerBase i) _const_;
+TimerBase timer_base_from_string(const char *s) _pure_;
-const char* timer_result_to_string(TimerResult i);
-TimerResult timer_result_from_string(const char *s);
+const char* timer_result_to_string(TimerResult i) _const_;
+TimerResult timer_result_from_string(const char *s) _pure_;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 1854047afd..fa97b69755 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -24,6 +24,7 @@
#include "transaction.h"
#include "bus-errors.h"
+#include "dbus-common.h"
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
@@ -97,6 +98,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other
j->type = t;
j->state = JOB_WAITING;
j->override = j->override || other->override;
+ j->irreversible = j->irreversible || other->irreversible;
j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
@@ -138,7 +140,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other
transaction_delete_job(tr, other, true);
}
-static bool job_is_conflicted_by(Job *j) {
+_pure_ static bool job_is_conflicted_by(Job *j) {
JobDependency *l;
assert(j);
@@ -318,7 +320,7 @@ rescan:
}
}
-static bool unit_matters_to_anchor(Unit *u, Job *j) {
+_pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
assert(u);
assert(!j->transaction_prev);
@@ -395,8 +397,8 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
"Job %s/%s deleted to break ordering cycle starting with %s/%s",
delete->unit->id, job_type_to_string(delete->type),
j->unit->id, job_type_to_string(j->type));
- status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true,
- "Ordering cycle found, skipping %s", unit_description(delete->unit));
+ unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF,
+ "Ordering cycle found, skipping %s");
transaction_delete_unit(tr, delete->unit);
return -EAGAIN;
}
@@ -488,7 +490,7 @@ rescan:
}
}
-static int transaction_is_destructive(Transaction *tr, DBusError *e) {
+static int transaction_is_destructive(Transaction *tr, JobMode mode, DBusError *e) {
Iterator i;
Job *j;
@@ -503,7 +505,7 @@ static int transaction_is_destructive(Transaction *tr, DBusError *e) {
assert(!j->transaction_prev);
assert(!j->transaction_next);
- if (j->unit->job &&
+ if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
!job_type_is_superset(j->type, j->unit->job->type)) {
dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
@@ -620,6 +622,7 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
job_add_to_run_queue(j);
job_add_to_dbus_queue(j);
job_start_timer(j);
+ job_shutdown_magic(j);
}
return 0;
@@ -708,12 +711,10 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e
transaction_drop_redundant(tr);
/* Ninth step: check whether we can actually apply this */
- if (mode == JOB_FAIL) {
- r = transaction_is_destructive(tr, e);
- if (r < 0) {
- log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
- return r;
- }
+ r = transaction_is_destructive(tr, mode, e);
+ if (r < 0) {
+ log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
+ return r;
}
/* Tenth step: apply changes */
@@ -769,6 +770,7 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b
j->marker = NULL;
j->matters_to_anchor = false;
j->override = override;
+ j->irreversible = tr->irreversible;
LIST_PREPEND(Job, transaction, f, j);
@@ -1105,7 +1107,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
return 0;
}
-Transaction *transaction_new(void) {
+Transaction *transaction_new(bool irreversible) {
Transaction *tr;
tr = new0(Transaction, 1);
@@ -1118,6 +1120,8 @@ Transaction *transaction_new(void) {
return NULL;
}
+ tr->irreversible = irreversible;
+
return tr;
}
diff --git a/src/core/transaction.h b/src/core/transaction.h
index 67ace4da0b..12f9194927 100644
--- a/src/core/transaction.h
+++ b/src/core/transaction.h
@@ -33,9 +33,10 @@ struct Transaction {
/* Jobs to be added */
Hashmap *jobs; /* Unit object => Job object list 1:1 */
Job *anchor_job; /* the job the user asked for */
+ bool irreversible;
};
-Transaction *transaction_new(void);
+Transaction *transaction_new(bool irreversible);
void transaction_free(Transaction *tr);
int transaction_add_job_and_dependencies(
diff --git a/src/core/umount.c b/src/core/umount.c
index 96232d38db..1e95ad70dd 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -219,7 +219,8 @@ static int loopback_list_get(MountPoint **head) {
}
if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
- udev_enumerate_add_match_sysname(e, "loop*") < 0) {
+ udev_enumerate_add_match_sysname(e, "loop*") < 0 ||
+ udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL) < 0) {
r = -EIO;
goto finish;
}
@@ -378,25 +379,23 @@ static int delete_loopback(const char *device) {
}
static int delete_dm(dev_t devnum) {
- int fd, r;
- struct dm_ioctl dm;
+ _cleanup_close_ int fd = -1;
+ int r;
+ struct dm_ioctl dm = {
+ .version = {DM_VERSION_MAJOR,
+ DM_VERSION_MINOR,
+ DM_VERSION_PATCHLEVEL},
+ .data_size = sizeof(dm),
+ .dev = devnum,
+ };
assert(major(devnum) != 0);
- if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
+ fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
+ if (fd < 0)
return -errno;
- zero(dm);
- dm.version[0] = DM_VERSION_MAJOR;
- dm.version[1] = DM_VERSION_MINOR;
- dm.version[2] = DM_VERSION_PATCHLEVEL;
-
- dm.data_size = sizeof(dm);
- dm.dev = devnum;
-
r = ioctl(fd, DM_DEV_REMOVE, &dm);
- close_nointr_nofail(fd);
-
return r >= 0 ? 0 : -errno;
}
@@ -419,7 +418,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
*
* Mount points can be stacked. If a mount
* point is stacked below / or /usr, we
- * cannnot umount or remount it directly,
+ * cannot umount or remount it directly,
* since there is no way to refer to the
* underlying mount. There's nothing we can do
* about it for the general case, but we can
@@ -442,9 +441,11 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
)
continue;
- /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ /* Trying to umount. We don't force here since we rely
+ * on busy NFS and FUSE file systems to return EBUSY
+ * until we closed everything on top of them. */
log_info("Unmounting %s.", m->path);
- if (umount2(m->path, MNT_FORCE) == 0) {
+ if (umount2(m->path, 0) == 0) {
if (changed)
*changed = true;
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index a58c96c238..85a05b872a 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -26,6 +26,7 @@
#include "strv.h"
#include "unit-name.h"
#include "unit-printf.h"
+#include "macro.h"
static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
Unit *u = userdata;
@@ -123,6 +124,7 @@ static char *specifier_user_name(char specifier, void *data, void *userdata) {
ExecContext *c;
int r;
const char *username;
+ _cleanup_free_ char *tmp = NULL;
uid_t uid;
char *printed = NULL;
@@ -130,12 +132,13 @@ static char *specifier_user_name(char specifier, void *data, void *userdata) {
c = unit_get_exec_context(u);
- /* get USER env from our own env if set */
- if (!c || !c->user)
- return getusername_malloc();
+ if (c && c->user)
+ username = c->user;
+ else
+ /* get USER env from env or our own uid */
+ username = tmp = getusername_malloc();
/* fish username from passwd */
- username = c->user;
r = get_user_creds(&username, &uid, NULL, NULL, NULL);
if (r < 0)
return NULL;
@@ -187,64 +190,37 @@ static char *specifier_user_shell(char specifier, void *data, void *userdata) {
ExecContext *c;
int r;
const char *username, *shell;
+ char *ret;
assert(u);
c = unit_get_exec_context(u);
- /* return HOME if set, otherwise from passwd */
- if (!c || !c->user) {
- char *sh;
-
- r = get_shell(&sh);
- if (r < 0)
- return strdup("/bin/sh");
-
- return sh;
- }
+ if (c && c->user)
+ username = c->user;
+ else
+ username = "root";
- username = c->user;
+ /* return /bin/sh for root, otherwise the value from passwd */
r = get_user_creds(&username, NULL, NULL, NULL, &shell);
- if (r < 0)
- return strdup("/bin/sh");
-
- return strdup(shell);
-}
-
-static char *specifier_machine_id(char specifier, void *data, void *userdata) {
- sd_id128_t id;
- char *buf;
- int r;
-
- r = sd_id128_get_machine(&id);
- if (r < 0)
- return NULL;
-
- buf = new(char, 33);
- if (!buf)
- return NULL;
-
- return sd_id128_to_string(id, buf);
-}
-
-static char *specifier_boot_id(char specifier, void *data, void *userdata) {
- sd_id128_t id;
- char *buf;
- int r;
-
- r = sd_id128_get_boot(&id);
- if (r < 0)
+ if (r < 0) {
+ log_warning_unit(u->id,
+ "Failed to determine shell: %s",
+ strerror(-r));
return NULL;
+ }
- buf = new(char, 33);
- if (!buf)
- return NULL;
+ if (!path_is_absolute(shell)) {
+ log_warning_unit(u->id,
+ "Shell %s is not absolute, ignoring.",
+ shell);
+ }
- return sd_id128_to_string(id, buf);
-}
+ ret = strdup(shell);
+ if (!ret)
+ log_oom();
-static char *specifier_host_name(char specifier, void *data, void *userdata) {
- return gethostname_malloc();
+ return ret;
}
char *unit_name_printf(Unit *u, const char* format) {
@@ -283,12 +259,13 @@ char *unit_full_printf(Unit *u, const char *format) {
* %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711")
* %R parent of root cgroup path (e.g. "/usr/lennart/shared")
* %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
+ * %U the UID of the configured user or running user
* %u the username of the configured user or running user
* %h the homedir of the configured user or running user
* %s the shell of the configured user or running user
* %m the machine ID of the running system
- * %b the boot ID of the running system
* %H the host name of the running system
+ * %b the boot ID of the running system
*/
const Specifier table[] = {
@@ -315,7 +292,6 @@ char *unit_full_printf(Unit *u, const char *format) {
{ 0, NULL, NULL }
};
- assert(u);
assert(format);
return specifier_printf(format, table, u);
diff --git a/src/core/unit.c b/src/core/unit.c
index 45453dce64..9b36b225fa 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -45,6 +45,10 @@
#include "cgroup-util.h"
#include "missing.h"
#include "cgroup-attr.h"
+#include "mkdir.h"
+#include "label.h"
+#include "fileio-label.h"
+#include "bus-errors.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
@@ -404,6 +408,7 @@ void unit_free(Unit *u) {
strv_free(u->documentation);
free(u->fragment_path);
free(u->source_path);
+ strv_free(u->dropin_paths);
free(u->instance);
set_free_free(u->names);
@@ -613,9 +618,11 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
- if (u->manager->running_as == SYSTEMD_SYSTEM)
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_REQUIRES, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true)) < 0)
+ if (u->manager->running_as == SYSTEMD_SYSTEM) {
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -690,8 +697,11 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->source_path)
fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
+ STRV_FOREACH(j, u->dropin_paths)
+ fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
+
if (u->job_timeout > 0)
- fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout));
+ fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
condition_dump_list(u->conditions, f, prefix);
@@ -744,15 +754,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, b->controller, b->path);
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char *v = NULL;
+ _cleanup_free_ char *v = NULL;
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write)
+ a->semantics->map_write(a->semantics, a->value, &v);
fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
prefix, a->controller, a->name, v ? v : a->value);
-
- free(v);
}
if (UNIT_VTABLE(u)->dump)
@@ -906,8 +914,8 @@ int unit_load(Unit *u) {
if (u->on_failure_isolate &&
set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
- log_error("More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.",
- u->id);
+ log_error_unit(u->id,
+ "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id);
r = -EINVAL;
goto fail;
@@ -926,7 +934,8 @@ fail:
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
- log_debug("Failed to load configuration for %s: %s", u->id, strerror(-r));
+ log_debug_unit(u->id, "Failed to load configuration for %s: %s",
+ u->id, strerror(-r));
return r;
}
@@ -940,7 +949,7 @@ bool unit_condition_test(Unit *u) {
return u->condition_result;
}
-static const char* unit_get_status_message_format(Unit *u, JobType t) {
+_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
const UnitStatusMessageFormats *format_table;
assert(u);
@@ -957,7 +966,7 @@ static const char* unit_get_status_message_format(Unit *u, JobType t) {
return format_table->starting_stopping[t == JOB_STOP];
}
-static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
+_pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
const char *format;
assert(u);
@@ -991,7 +1000,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
if (!format)
return;
- unit_status_printf(u, "", format, unit_description(u));
+ unit_status_printf(u, "", format);
}
#pragma GCC diagnostic push
@@ -1022,11 +1031,11 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING :
SD_MESSAGE_UNIT_RELOADING;
- log_struct(LOG_INFO,
- MESSAGE_ID(mid),
- "UNIT=%s", u->id,
- "MESSAGE=%s", buf,
- NULL);
+ log_struct_unit(LOG_INFO,
+ u->id,
+ MESSAGE_ID(mid),
+ "MESSAGE=%s", buf,
+ NULL);
}
#pragma GCC diagnostic pop
@@ -1059,13 +1068,14 @@ int unit_start(Unit *u) {
* but we don't want to recheck the condition in that case. */
if (state != UNIT_ACTIVATING &&
!unit_condition_test(u)) {
- log_debug("Starting of %s requested but condition failed. Ignoring.", u->id);
+ log_debug_unit(u->id, "Starting of %s requested but condition failed. Ignoring.", u->id);
return -EALREADY;
}
/* Forward to the main object, if we aren't it. */
if ((following = unit_following(u))) {
- log_debug("Redirecting start request from %s to %s.", u->id, following->id);
+ log_debug_unit(u->id, "Redirecting start request from %s to %s.",
+ u->id, following->id);
return unit_start(following);
}
@@ -1116,7 +1126,8 @@ int unit_stop(Unit *u) {
return -EALREADY;
if ((following = unit_following(u))) {
- log_debug("Redirecting stop request from %s to %s.", u->id, following->id);
+ log_debug_unit(u->id, "Redirecting stop request from %s to %s.",
+ u->id, following->id);
return unit_stop(following);
}
@@ -1156,7 +1167,8 @@ int unit_reload(Unit *u) {
return -ENOEXEC;
if ((following = unit_following(u))) {
- log_debug("Redirecting reload request from %s to %s.", u->id, following->id);
+ log_debug_unit(u->id, "Redirecting reload request from %s to %s.",
+ u->id, following->id);
return unit_reload(following);
}
@@ -1194,22 +1206,22 @@ static void unit_check_unneeded(Unit *u) {
return;
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (unit_pending_active(other))
+ if (unit_active_or_pending(other))
return;
- log_info("Service %s is not needed anymore. Stopping.", u->id);
+ log_info_unit(u->id, "Service %s is not needed anymore. Stopping.", u->id);
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
@@ -1237,11 +1249,6 @@ static void retroactively_start_dependencies(Unit *u) {
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
-
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
@@ -1297,7 +1304,7 @@ static void check_unneeded_dependencies(Unit *u) {
unit_check_unneeded(other);
}
-void unit_trigger_on_failure(Unit *u) {
+void unit_start_on_failure(Unit *u) {
Unit *other;
Iterator i;
@@ -1306,17 +1313,30 @@ void unit_trigger_on_failure(Unit *u) {
if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
return;
- log_info("Triggering OnFailure= dependencies of %s.", u->id);
+ log_info_unit(u->id, "Triggering OnFailure= dependencies of %s.", u->id);
SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
int r;
- if ((r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL)) < 0)
- log_error("Failed to enqueue OnFailure= job: %s", strerror(-r));
+ r = manager_add_job(u->manager, JOB_START, other, u->on_failure_isolate ? JOB_ISOLATE : JOB_REPLACE, true, NULL, NULL);
+ if (r < 0)
+ log_error_unit(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r));
}
}
+void unit_trigger_notify(Unit *u) {
+ Unit *other;
+ Iterator i;
+
+ assert(u);
+
+ SET_FOREACH(other, u->dependencies[UNIT_TRIGGERED_BY], i)
+ if (UNIT_VTABLE(other)->trigger_notify)
+ UNIT_VTABLE(other)->trigger_notify(other, u);
+}
+
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
+ Manager *m;
bool unexpected;
assert(u);
@@ -1329,7 +1349,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
* behavior here. For example: if a mount point is remounted
* this function will be called too! */
- if (u->manager->n_reloading <= 0) {
+ m = u->manager;
+
+ if (m->n_reloading <= 0) {
dual_timestamp ts;
dual_timestamp_get(&ts);
@@ -1343,14 +1365,21 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
u->active_enter_timestamp = ts;
else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns))
u->active_exit_timestamp = ts;
-
- timer_unit_notify(u, ns);
- path_unit_notify(u, ns);
}
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
cgroup_bonding_trim_list(u->cgroup_bondings, true);
+ if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
+ ExecContext *ec = unit_get_exec_context(u);
+ if (ec && exec_context_may_touch_console(ec)) {
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ m->n_on_console--;
+ else
+ m->n_on_console++;
+ }
+ }
+
if (u->job) {
unexpected = false;
@@ -1417,7 +1446,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
} else
unexpected = true;
- if (u->manager->n_reloading <= 0) {
+ if (m->n_reloading <= 0) {
/* If this state change happened without being
* requested by a job, then let's retroactively start
@@ -1438,11 +1467,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
check_unneeded_dependencies(u);
if (ns != os && ns == UNIT_FAILED) {
- log_struct(LOG_NOTICE,
- "MESSAGE=Unit %s entered failed state", u->id,
- "UNIT=%s", u->id,
- NULL);
- unit_trigger_on_failure(u);
+ log_notice_unit(u->id,
+ "Unit %s entered failed state.", u->id);
+ unit_start_on_failure(u);
}
}
@@ -1453,18 +1480,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
/* The bus just might have become available,
* hence try to connect to it, if we aren't
* yet connected. */
- bus_init(u->manager, true);
+ bus_init(m, true);
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- u->manager->n_reloading <= 0) {
+ m->n_reloading <= 0) {
/* Write audit record if we have just finished starting up */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
u->in_audit = true;
}
if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
- manager_send_unit_plymouth(u->manager, u);
+ manager_send_unit_plymouth(m, u);
} else {
@@ -1474,46 +1501,47 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (u->type == UNIT_SERVICE &&
UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os) &&
- u->manager->n_reloading <= 0) {
+ m->n_reloading <= 0) {
/* Hmm, if there was no start record written
* write it now, so that we always have a nice
* pair */
if (!u->in_audit) {
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
if (ns == UNIT_INACTIVE)
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true);
} else
/* Write audit record if we have just finished shutting down */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
u->in_audit = false;
}
}
- manager_recheck_journal(u->manager);
+ manager_recheck_journal(m);
+ unit_trigger_notify(u);
/* Maybe we finished startup and are now ready for being
* stopped because unneeded? */
- unit_check_unneeded(u);
+ if (u->manager->n_reloading <= 0)
+ unit_check_unneeded(u);
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
}
int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .data.ptr = w,
+ .events = events,
+ };
assert(u);
assert(fd >= 0);
assert(w);
assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->data.unit == u));
- zero(ev);
- ev.data.ptr = w;
- ev.events = events;
-
if (epoll_ctl(u->manager->epoll_fd,
w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD,
fd,
@@ -1561,7 +1589,7 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
}
int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Watch *w) {
- struct itimerspec its;
+ struct itimerspec its = {};
int flags, fd;
bool ours;
@@ -1586,8 +1614,6 @@ int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Wa
} else
assert_not_reached("Invalid watch type");
- zero(its);
-
if (usec <= 0) {
/* Set absolute time in the past, but not 0, since we
* don't want to disarm the timer */
@@ -1605,11 +1631,10 @@ int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Wa
goto fail;
if (w->type == WATCH_INVALID) {
- struct epoll_event ev;
-
- zero(ev);
- ev.data.ptr = w;
- ev.events = EPOLLIN;
+ struct epoll_event ev = {
+ .data.ptr = w,
+ .events = EPOLLIN,
+ };
if (epoll_ctl(u->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
goto fail;
@@ -1781,6 +1806,7 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
assert(u);
assert(name || path);
+ assert(p);
if (!name)
name = path_get_file_name(path);
@@ -1793,13 +1819,13 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
if (u->instance)
s = unit_name_replace_instance(name, u->instance);
else {
- char *i;
+ _cleanup_free_ char *i = NULL;
- if (!(i = unit_name_to_prefix(u->id)))
+ i = unit_name_to_prefix(u->id);
+ if (!i)
return NULL;
s = unit_name_replace_instance(name, i);
- free(i);
}
if (!s)
@@ -1812,22 +1838,20 @@ static const char *resolve_template(Unit *u, const char *name, const char*path,
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
- char *s;
+ _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
- if (!(name = resolve_template(u, name, path, &s)))
+ name = resolve_template(u, name, path, &s);
+ if (!name)
return -ENOMEM;
- if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
-
- r = unit_add_dependency(u, d, other, add_reference);
+ r = manager_load_unit(u->manager, name, path, NULL, &other);
+ if (r < 0)
+ return r;
-finish:
- free(s);
- return r;
+ return unit_add_dependency(u, d, other, add_reference);
}
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
@@ -1895,30 +1919,12 @@ finish:
}
int set_unit_path(const char *p) {
- char *cwd, *c;
- int r;
+ _cleanup_free_ char *c = NULL;
/* This is mostly for debug purposes */
-
- if (path_is_absolute(p)) {
- if (!(c = strdup(p)))
- return -ENOMEM;
- } else {
- if (!(cwd = get_current_dir_name()))
- return -errno;
-
- r = asprintf(&c, "%s/%s", cwd, p);
- free(cwd);
-
- if (r < 0)
- return -ENOMEM;
- }
-
- if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
- r = -errno;
- free(c);
- return r;
- }
+ c = path_make_absolute_cwd(p);
+ if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0)
+ return -errno;
return 0;
}
@@ -1932,7 +1938,7 @@ char *unit_dbus_path(Unit *u) {
return unit_dbus_path_from_name(u->id);
}
-int unit_add_cgroup(Unit *u, CGroupBonding *b) {
+static int unit_add_cgroup(Unit *u, CGroupBonding *b) {
int r;
assert(u);
@@ -1941,8 +1947,9 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
assert(b->path);
if (!b->controller) {
- if (!(b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER)))
- return -ENOMEM;
+ b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
+ if (!b->controller)
+ return log_oom();
b->ours = true;
}
@@ -1956,7 +1963,8 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
l = hashmap_get(u->manager->cgroup_bondings, b->path);
LIST_PREPEND(CGroupBonding, by_path, l, b);
- if ((r = hashmap_replace(u->manager->cgroup_bondings, b->path, l)) < 0) {
+ r = hashmap_replace(u->manager->cgroup_bondings, b->path, l);
+ if (r < 0) {
LIST_REMOVE(CGroupBonding, by_path, l, b);
return r;
}
@@ -1969,26 +1977,31 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
}
char *unit_default_cgroup_path(Unit *u) {
- char *p;
+ _cleanup_free_ char *escaped_instance = NULL;
assert(u);
+ escaped_instance = cg_escape(u->id);
+ if (!escaped_instance)
+ return NULL;
+
if (u->instance) {
- char *t;
+ _cleanup_free_ char *t = NULL, *escaped_template = NULL;
t = unit_name_template(u->id);
if (!t)
return NULL;
- p = strjoin(u->manager->cgroup_hierarchy, "/", t, "/", u->instance, NULL);
- free(t);
- } else
- p = strjoin(u->manager->cgroup_hierarchy, "/", u->id, NULL);
+ escaped_template = cg_escape(t);
+ if (!escaped_template)
+ return NULL;
- return p;
+ return strjoin(u->manager->cgroup_hierarchy, "/", escaped_template, "/", escaped_instance, NULL);
+ } else
+ return strjoin(u->manager->cgroup_hierarchy, "/", escaped_instance, NULL);
}
-int unit_add_cgroup_from_text(Unit *u, const char *name) {
+int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) {
char *controller = NULL, *path = NULL;
CGroupBonding *b = NULL;
bool ours = false;
@@ -1997,7 +2010,8 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) {
assert(u);
assert(name);
- if ((r = cg_split_spec(name, &controller, &path)) < 0)
+ r = cg_split_spec(name, &controller, &path);
+ if (r < 0)
return r;
if (!path) {
@@ -2006,23 +2020,59 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) {
}
if (!controller) {
- controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
+ controller = strdup("systemd");
ours = true;
}
if (!path || !controller) {
free(path);
free(controller);
+ return log_oom();
+ }
- return -ENOMEM;
+ if (streq(controller, "systemd")) {
+ /* Within the systemd unit hierarchy we do not allow changes. */
+ if (path_startswith(path, "/system")) {
+ log_warning_unit(u->id, "Manipulating the systemd:/system cgroup hierarchy is not permitted.");
+ free(path);
+ free(controller);
+ return -EPERM;
+ }
}
- if (cgroup_bonding_find_list(u->cgroup_bondings, controller)) {
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ if (streq(path, b->path)) {
+ free(path);
+ free(controller);
+
+ if (ret)
+ *ret = b;
+ return 0;
+ }
+
+ if (overwrite && !b->essential) {
+ free(controller);
+
+ free(b->path);
+ b->path = path;
+
+ b->ours = ours;
+ b->realized = false;
+
+ if (ret)
+ *ret = b;
+
+ return 1;
+ }
+
r = -EEXIST;
+ b = NULL;
goto fail;
}
- if (!(b = new0(CGroupBonding, 1))) {
+ b = new0(CGroupBonding, 1);
+ if (!b) {
r = -ENOMEM;
goto fail;
}
@@ -2032,10 +2082,14 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) {
b->ours = ours;
b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
- if ((r = unit_add_cgroup(u, b)) < 0)
+ r = unit_add_cgroup(u, b);
+ if (r < 0)
goto fail;
- return 0;
+ if (ret)
+ *ret = b;
+
+ return 1;
fail:
free(path);
@@ -2051,16 +2105,21 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
assert(u);
+ if (controller && !cg_controller_is_valid(controller, true))
+ return -EINVAL;
+
if (!controller)
controller = SYSTEMD_CGROUP_CONTROLLER;
if (cgroup_bonding_find_list(u->cgroup_bondings, controller))
return 0;
- if (!(b = new0(CGroupBonding, 1)))
+ b = new0(CGroupBonding, 1);
+ if (!b)
return -ENOMEM;
- if (!(b->controller = strdup(controller)))
+ b->controller = strdup(controller);
+ if (!b->controller)
goto fail;
b->path = unit_default_cgroup_path(u);
@@ -2070,10 +2129,11 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
b->ours = true;
b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
- if ((r = unit_add_cgroup(u, b)) < 0)
+ r = unit_add_cgroup(u, b);
+ if (r < 0)
goto fail;
- return 0;
+ return 1;
fail:
free(b->path);
@@ -2096,7 +2156,8 @@ int unit_add_default_cgroups(Unit *u) {
if (!u->manager->cgroup_hierarchy)
return 0;
- if ((r = unit_add_one_default_cgroup(u, NULL)) < 0)
+ r = unit_add_one_default_cgroup(u, NULL);
+ if (r < 0)
return r;
STRV_FOREACH(c, u->manager->default_controllers)
@@ -2111,42 +2172,90 @@ int unit_add_default_cgroups(Unit *u) {
CGroupBonding* unit_get_default_cgroup(Unit *u) {
assert(u);
- return cgroup_bonding_find_list(u->cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
+ return cgroup_bonding_find_list(u->cgroup_bondings, NULL);
}
-int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
- int r;
- char *c = NULL;
+int unit_add_cgroup_attribute(
+ Unit *u,
+ const CGroupSemantics *semantics,
+ const char *controller,
+ const char *name,
+ const char *value,
+ CGroupAttribute **ret) {
+
+ _cleanup_free_ char *c = NULL;
CGroupAttribute *a;
+ int r;
assert(u);
- assert(name);
assert(value);
- if (!controller) {
- const char *dot;
+ if (semantics) {
+ /* Semantics always take precedence */
+ if (semantics->name)
+ name = semantics->name;
- dot = strchr(name, '.');
- if (!dot)
- return -EINVAL;
+ if (semantics->controller)
+ controller = semantics->controller;
+ }
- c = strndup(name, dot - name);
- if (!c)
- return -ENOMEM;
+ if (!name)
+ return -EINVAL;
+
+ if (!controller) {
+ r = cg_controller_from_attr(name, &c);
+ if (r < 0)
+ return -EINVAL;
controller = c;
}
- if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
- r = -EINVAL;
- goto finish;
+ if (!controller ||
+ streq(controller, SYSTEMD_CGROUP_CONTROLLER) ||
+ streq(controller, "systemd"))
+ return -EINVAL;
+
+ if (!filename_is_safe(name))
+ return -EINVAL;
+
+ if (!cg_controller_is_valid(controller, false))
+ return -EINVAL;
+
+ /* Check if this attribute already exists. Note that we will
+ * explicitly check for the value here too, as there are
+ * attributes which accept multiple values. */
+ a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name);
+ if (a) {
+ if (streq(value, a->value)) {
+ /* Exactly the same value is always OK, let's ignore this */
+ if (ret)
+ *ret = a;
+
+ return 0;
+ }
+
+ if (semantics && !semantics->multiple) {
+ char *v;
+
+ /* If this is a single-item entry, we can
+ * simply patch the existing attribute */
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ free(a->value);
+ a->value = v;
+
+ if (ret)
+ *ret = a;
+ return 1;
+ }
}
a = new0(CGroupAttribute, 1);
- if (!a) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!a)
+ return -ENOMEM;
if (c) {
a->controller = c;
@@ -2162,58 +2271,54 @@ int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name,
free(a->name);
free(a->value);
free(a);
-
return -ENOMEM;
}
- a->map_callback = map_callback;
+ a->semantics = semantics;
+ a->unit = u;
LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
- r = 0;
+ if (ret)
+ *ret = a;
-finish:
- free(c);
- return r;
+ return 1;
}
int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
- char *t;
+ _cleanup_free_ char *t = NULL;
int r;
assert(u);
assert(type);
assert(_found);
- if (!(t = unit_name_change_suffix(u->id, type)))
+ t = unit_name_change_suffix(u->id, type);
+ if (!t)
return -ENOMEM;
assert(!unit_has_name(u, t));
r = manager_load_unit(u->manager, t, NULL, NULL, _found);
- free(t);
-
assert(r < 0 || *_found != u);
-
return r;
}
int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
+ _cleanup_free_ char *t = NULL;
Unit *found;
- char *t;
assert(u);
assert(type);
assert(_found);
- if (!(t = unit_name_change_suffix(u->id, type)))
+ t = unit_name_change_suffix(u->id, type);
+ if (!t)
return -ENOMEM;
assert(!unit_has_name(u, t));
found = manager_get_unit(u->manager, t);
- free(t);
-
if (!found)
return -ENOENT;
@@ -2371,6 +2476,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
job_free(j);
return r;
}
+
+ if (j->state == JOB_RUNNING)
+ u->manager->n_running_jobs++;
} else {
/* legacy */
JobType type = job_type_from_string(v);
@@ -2473,25 +2581,18 @@ int unit_coldplug(Unit *u) {
return 0;
}
-void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
- va_list ap;
-
- assert(u);
- assert(format);
-
- if (!manager_get_show_status(u->manager))
- return;
-
- if (!manager_is_booting_or_shutting_down(u->manager))
- return;
-
- va_start(ap, format);
- status_vprintf(status, true, format, ap);
- va_end(ap);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+ manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u));
}
+#pragma GCC diagnostic pop
bool unit_need_daemon_reload(Unit *u) {
+ _cleanup_strv_free_ char **t = NULL;
+ char **path;
struct stat st;
+ unsigned loaded_cnt, current_cnt;
assert(u);
@@ -2516,7 +2617,30 @@ bool unit_need_daemon_reload(Unit *u) {
return true;
}
- return false;
+ t = unit_find_dropin_paths(u);
+ loaded_cnt = strv_length(t);
+ current_cnt = strv_length(u->dropin_paths);
+
+ if (loaded_cnt == current_cnt) {
+ if (loaded_cnt == 0)
+ return false;
+
+ if (strv_overlap(u->dropin_paths, t)) {
+ STRV_FOREACH(path, u->dropin_paths) {
+ zero(st);
+ if (stat(*path, &st) < 0)
+ return true;
+
+ if (u->dropin_mtime > 0 &&
+ timespec_load(&st.st_mtim) > u->dropin_mtime)
+ return true;
+ }
+
+ return false;
+ } else
+ return true;
+ } else
+ return true;
}
void unit_reset_failed(Unit *u) {
@@ -2535,7 +2659,19 @@ Unit *unit_following(Unit *u) {
return NULL;
}
-bool unit_pending_inactive(Unit *u) {
+bool unit_stop_pending(Unit *u) {
+ assert(u);
+
+ /* This call does check the current state of the unit. It's
+ * hence useful to be called from state change calls of the
+ * unit itself, where the state isn't updated yet. This is
+ * different from unit_inactive_or_pending() which checks both
+ * the current state and for a queued job. */
+
+ return u->job && u->job->type == JOB_STOP;
+}
+
+bool unit_inactive_or_pending(Unit *u) {
assert(u);
/* Returns true if the unit is inactive or going down */
@@ -2543,13 +2679,13 @@ bool unit_pending_inactive(Unit *u) {
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)))
return true;
- if (u->job && u->job->type == JOB_STOP)
+ if (unit_stop_pending(u))
return true;
return false;
}
-bool unit_pending_active(Unit *u) {
+bool unit_active_or_pending(Unit *u) {
assert(u);
/* Returns true if the unit is active or going up */
@@ -2578,6 +2714,71 @@ int unit_kill(Unit *u, KillWho w, int signo, DBusError *error) {
return UNIT_VTABLE(u)->kill(u, w, signo, error);
}
+int unit_kill_common(
+ Unit *u,
+ KillWho who,
+ int signo,
+ pid_t main_pid,
+ pid_t control_pid,
+ DBusError *error) {
+
+ int r = 0;
+
+ if (who == KILL_MAIN && main_pid <= 0) {
+ if (main_pid < 0)
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
+ else
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL && control_pid <= 0) {
+ if (control_pid < 0)
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
+ else
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+ return -ESRCH;
+ }
+
+ if (who == KILL_CONTROL || who == KILL_ALL)
+ if (control_pid > 0)
+ if (kill(control_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_MAIN || who == KILL_ALL)
+ if (main_pid > 0)
+ if (kill(main_pid, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_ALL) {
+ _cleanup_set_free_ Set *pid_set = NULL;
+ int q;
+
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set)
+ return -ENOMEM;
+
+ /* Exclude the control/main pid from being killed via the cgroup */
+ if (control_pid > 0) {
+ q = set_put(pid_set, LONG_TO_PTR(control_pid));
+ if (q < 0)
+ return q;
+ }
+
+ if (main_pid > 0) {
+ q = set_put(pid_set, LONG_TO_PTR(main_pid));
+ if (q < 0)
+ return q;
+ }
+
+ q = cgroup_bonding_kill_list(u->cgroup_bondings, signo, false, false, pid_set, NULL);
+ if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+ r = q;
+ }
+
+ return r;
+}
+
int unit_following_set(Unit *u, Set **s) {
assert(u);
assert(s);
@@ -2699,6 +2900,155 @@ ExecContext *unit_get_exec_context(Unit *u) {
return (ExecContext*) ((uint8_t*) u + offset);
}
+static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) {
+ char *p, *q;
+ int r;
+
+ assert(u);
+ assert(name);
+ assert(_p);
+ assert(_q);
+
+ if (u->manager->running_as == SYSTEMD_USER && runtime)
+ return -ENOTSUP;
+
+ if (!filename_is_safe(name))
+ return -EINVAL;
+
+ if (u->manager->running_as == SYSTEMD_USER) {
+ _cleanup_free_ char *c = NULL;
+
+ r = user_config_home(&c);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ p = strjoin(c, "/", u->id, ".d", NULL);
+ } else if (runtime)
+ p = strjoin("/run/systemd/system/", u->id, ".d", NULL);
+ else
+ p = strjoin("/etc/systemd/system/", u->id, ".d", NULL);
+ if (!p)
+ return -ENOMEM;
+
+ q = strjoin(p, "/50-", name, ".conf", NULL);
+ if (!q) {
+ free(p);
+ return -ENOMEM;
+ }
+
+ *_p = p;
+ *_q = q;
+ return 0;
+}
+
+int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
+
+ assert(u);
+
+ r = drop_in_file(u, runtime, name, &p, &q);
+ if (r < 0)
+ return r;
+
+ mkdir_p(p, 0755);
+ return write_string_file_atomic_label(q, data);
+}
+
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
+
+ assert(u);
+
+ r = drop_in_file(u, runtime, name, &p, &q);
+ if (unlink(q) < 0)
+ r = -errno;
+ else
+ r = 0;
+
+ rmdir(p);
+ return r;
+}
+
+int unit_kill_context(
+ Unit *u,
+ KillContext *c,
+ bool sigkill,
+ pid_t main_pid,
+ pid_t control_pid,
+ bool main_pid_alien) {
+
+ int sig, wait_for_exit = 0, r;
+
+ assert(u);
+ assert(c);
+
+ if (c->kill_mode == KILL_NONE)
+ return 0;
+
+ sig = sigkill ? SIGKILL : c->kill_signal;
+
+ if (main_pid > 0) {
+ r = kill_and_sigcont(main_pid, sig);
+
+ if (r < 0 && r != -ESRCH) {
+ _cleanup_free_ char *comm = NULL;
+ get_process_comm(main_pid, &comm);
+
+ log_warning_unit(u->id, "Failed to kill main process %li (%s): %s",
+ (long) main_pid, strna(comm), strerror(-r));
+ } else
+ wait_for_exit = !main_pid_alien;
+ }
+
+ if (control_pid > 0) {
+ r = kill_and_sigcont(control_pid, sig);
+
+ if (r < 0 && r != -ESRCH) {
+ _cleanup_free_ char *comm = NULL;
+ get_process_comm(control_pid, &comm);
+
+ log_warning_unit(u->id,
+ "Failed to kill control process %li (%s): %s",
+ (long) control_pid, strna(comm), strerror(-r));
+ } else
+ wait_for_exit = true;
+ }
+
+ if (c->kill_mode == KILL_CONTROL_GROUP) {
+ _cleanup_set_free_ Set *pid_set = NULL;
+
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set)
+ return -ENOMEM;
+
+ /* Exclude the main/control pids from being killed via the cgroup */
+ if (main_pid > 0) {
+ r = set_put(pid_set, LONG_TO_PTR(main_pid));
+ if (r < 0)
+ return r;
+ }
+
+ if (control_pid > 0) {
+ r = set_put(pid_set, LONG_TO_PTR(control_pid));
+ if (r < 0)
+ return r;
+ }
+
+ r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL);
+ if (r < 0) {
+ if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+ log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r));
+ } else if (r > 0)
+ wait_for_exit = true;
+ }
+
+ return wait_for_exit;
+}
+
static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = "active",
[UNIT_RELOADING] = "reloading",
diff --git a/src/core/unit.h b/src/core/unit.h
index 702bfeece6..b04475e4fb 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stdlib.h>
+#include <unistd.h>
typedef struct Unit Unit;
typedef struct UnitVTable UnitVTable;
@@ -39,6 +40,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
#include "condition.h"
#include "install.h"
#include "unit-name.h"
+#include "cgroup-semantics.h"
enum UnitActiveState {
UNIT_ACTIVE,
@@ -136,8 +138,10 @@ struct Unit {
char *fragment_path; /* if loaded from a config file this is the primary path to it */
char *source_path; /* if converted, the source file */
+ char **dropin_paths;
usec_t fragment_mtime;
usec_t source_mtime;
+ usec_t dropin_mtime;
/* If there is something to do with this unit, then this is the installed job for it */
Job *job;
@@ -270,6 +274,9 @@ struct UnitVTable {
* ExecContext is found, if the unit type has that */
size_t exec_context_offset;
+ /* The name of the section with the exec settings of ExecContext */
+ const char *exec_section;
+
/* Config file sections this unit type understands, separated
* by NUL chars */
const char *sections;
@@ -345,7 +352,7 @@ struct UnitVTable {
/* Called whenever a process of this unit sends us a message */
void (*notify_message)(Unit *u, pid_t pid, char **tags);
- /* Called whenever a name thus Unit registered for comes or
+ /* Called whenever a name this Unit registered for comes or
* goes away. */
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
@@ -361,6 +368,10 @@ struct UnitVTable {
/* Return the set of units that are following each other */
int (*following_set)(Unit *u, Set **s);
+ /* Invoked each time a unit this unit is triggering changes
+ * state or gains/loses a job */
+ void (*trigger_notify)(Unit *u, Unit *trigger);
+
/* Called whenever CLOCK_REALTIME made a jump */
void (*time_change)(Unit *u);
@@ -410,6 +421,8 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
/* For casting the various unit types into a unit */
#define UNIT(u) (&(u)->meta)
+#define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS]))
+
DEFINE_CAST(SOCKET, Socket);
DEFINE_CAST(TIMER, Timer);
DEFINE_CAST(SERVICE, Service);
@@ -437,11 +450,10 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
-int unit_add_cgroup(Unit *u, CGroupBonding *b);
-int unit_add_cgroup_from_text(Unit *u, const char *name);
+int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret);
int unit_add_default_cgroups(Unit *u);
CGroupBonding* unit_get_default_cgroup(Unit *u);
-int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback);
+int unit_add_cgroup_attribute(Unit *u, const CGroupSemantics *semantics, const char *controller, const char *name, const char *value, CGroupAttribute **ret);
int unit_choose_id(Unit *u, const char *name);
int unit_set_description(Unit *u, const char *description);
@@ -456,13 +468,13 @@ void unit_add_to_gc_queue(Unit *u);
int unit_merge(Unit *u, Unit *other);
int unit_merge_by_name(Unit *u, const char *other);
-Unit *unit_follow_merge(Unit *u);
+Unit *unit_follow_merge(Unit *u) _pure_;
int unit_load_fragment_and_dropin(Unit *u);
int unit_load_fragment_and_dropin_optional(Unit *u);
int unit_load(Unit *unit);
-const char *unit_description(Unit *u);
+const char *unit_description(Unit *u) _pure_;
bool unit_has_name(Unit *u, const char *name);
@@ -472,15 +484,16 @@ const char* unit_sub_state_to_string(Unit *u);
void unit_dump(Unit *u, FILE *f, const char *prefix);
-bool unit_can_reload(Unit *u);
-bool unit_can_start(Unit *u);
-bool unit_can_isolate(Unit *u);
+bool unit_can_reload(Unit *u) _pure_;
+bool unit_can_start(Unit *u) _pure_;
+bool unit_can_isolate(Unit *u) _pure_;
int unit_start(Unit *u);
int unit_stop(Unit *u);
int unit_reload(Unit *u);
int unit_kill(Unit *u, KillWho w, int signo, DBusError *error);
+int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, DBusError *error);
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success);
@@ -505,7 +518,7 @@ char *unit_dbus_path(Unit *u);
int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
int unit_get_related_unit(Unit *u, const char *type, Unit **_found);
-bool unit_can_serialize(Unit *u);
+bool unit_can_serialize(Unit *u) _pure_;
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_attr_(4,5);
void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
@@ -515,7 +528,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants);
int unit_coldplug(Unit *u);
-void unit_status_printf(Unit *u, const char *status, const char *format, ...);
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_attr_(3, 0);
bool unit_need_daemon_reload(Unit *u);
@@ -523,8 +536,9 @@ void unit_reset_failed(Unit *u);
Unit *unit_following(Unit *u);
-bool unit_pending_inactive(Unit *u);
-bool unit_pending_active(Unit *u);
+bool unit_stop_pending(Unit *u) _pure_;
+bool unit_inactive_or_pending(Unit *u) _pure_;
+bool unit_active_or_pending(Unit *u);
int unit_add_default_target_dependency(Unit *u, Unit *target);
@@ -532,7 +546,8 @@ char *unit_default_cgroup_path(Unit *u);
int unit_following_set(Unit *u, Set **s);
-void unit_trigger_on_failure(Unit *u);
+void unit_start_on_failure(Unit *u);
+void unit_trigger_notify(Unit *u);
bool unit_condition_test(Unit *u);
@@ -548,17 +563,26 @@ int unit_add_mount_links(Unit *u);
int unit_exec_context_defaults(Unit *u, ExecContext *c);
-ExecContext *unit_get_exec_context(Unit *u);
+ExecContext *unit_get_exec_context(Unit *u) _pure_;
+
+int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data);
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name);
-const char *unit_active_state_to_string(UnitActiveState i);
-UnitActiveState unit_active_state_from_string(const char *s);
+int unit_kill_context(Unit *u, KillContext *c, bool sigkill, pid_t main_pid, pid_t control_pid, bool main_pid_alien);
-const char *unit_dependency_to_string(UnitDependency i);
-UnitDependency unit_dependency_from_string(const char *s);
+const char *unit_active_state_to_string(UnitActiveState i) _const_;
+UnitActiveState unit_active_state_from_string(const char *s) _pure_;
-#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "UNIT=", unit, __VA_ARGS__)
+const char *unit_dependency_to_string(UnitDependency i) _const_;
+UnitDependency unit_dependency_from_string(const char *s) _pure_;
+
+/* Macros which append UNIT= or USER_UNIT= to the message */
+
+#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, getpid() == 1 ? "UNIT=" : "USER_UNIT=", unit, __VA_ARGS__)
#define log_debug_unit(unit, ...) log_full_unit(LOG_DEBUG, unit, __VA_ARGS__)
#define log_info_unit(unit, ...) log_full_unit(LOG_INFO, unit, __VA_ARGS__)
#define log_notice_unit(unit, ...) log_full_unit(LOG_NOTICE, unit, __VA_ARGS__)
#define log_warning_unit(unit, ...) log_full_unit(LOG_WARNING, unit, __VA_ARGS__)
#define log_error_unit(unit, ...) log_full_unit(LOG_ERR, unit, __VA_ARGS__)
+
+#define log_struct_unit(level, unit, ...) log_struct(level, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__)
diff --git a/src/core/user.conf b/src/core/user.conf
index e02b46bc3a..4252451eb7 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -5,7 +5,7 @@
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
-# See systemd.conf(5) for details
+# See systemd-user.conf(5) for details
[Manager]
#LogLevel=info