summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/audit-fd.c73
-rw-r--r--src/core/audit-fd.h25
-rw-r--r--src/core/automount.c192
-rw-r--r--src/core/build.h20
-rw-r--r--src/core/cgroup.c2
-rw-r--r--src/core/condition.c55
-rw-r--r--src/core/condition.h3
-rw-r--r--src/core/dbus-automount.c3
-rw-r--r--src/core/dbus-device.c3
-rw-r--r--src/core/dbus-job.c70
-rw-r--r--src/core/dbus-manager.c435
-rw-r--r--src/core/dbus-mount.c3
-rw-r--r--src/core/dbus-path.c3
-rw-r--r--src/core/dbus-service.c7
-rw-r--r--src/core/dbus-snapshot.c17
-rw-r--r--src/core/dbus-socket.c9
-rw-r--r--src/core/dbus-swap.c3
-rw-r--r--src/core/dbus-target.c3
-rw-r--r--src/core/dbus-timer.c9
-rw-r--r--src/core/dbus-unit.c259
-rw-r--r--src/core/dbus-unit.h9
-rw-r--r--src/core/dbus.c35
-rw-r--r--src/core/dbus.h2
-rw-r--r--src/core/device.c40
-rw-r--r--src/core/execute.c173
-rw-r--r--src/core/fdset.c167
-rw-r--r--src/core/hostname-setup.c114
-rw-r--r--src/core/job.c172
-rw-r--r--src/core/job.h12
-rw-r--r--src/core/kmod-setup.c30
-rw-r--r--src/core/load-fragment-gperf.gperf.m413
-rw-r--r--src/core/load-fragment.c245
-rw-r--r--src/core/locale-setup.c189
-rw-r--r--src/core/machine-id-setup.c8
-rw-r--r--src/core/macros.systemd.in18
-rw-r--r--src/core/main.c280
-rw-r--r--src/core/manager.c409
-rw-r--r--src/core/manager.h29
-rw-r--r--src/core/mount-setup.c83
-rw-r--r--src/core/mount.c227
-rw-r--r--src/core/mount.h2
-rw-r--r--src/core/namespace.c216
-rw-r--r--src/core/path.c22
-rw-r--r--src/core/selinux-access.c441
-rw-r--r--src/core/selinux-access.h62
-rw-r--r--src/core/selinux-setup.c6
-rw-r--r--src/core/service.c641
-rw-r--r--src/core/service.h9
-rw-r--r--src/core/shutdown.c100
-rw-r--r--src/core/socket.c356
-rw-r--r--src/core/socket.h4
-rw-r--r--src/core/special.h32
-rw-r--r--src/core/swap.c298
-rw-r--r--src/core/switch-root.c42
-rw-r--r--src/core/system.conf2
-rw-r--r--src/core/systemd.pc.in2
-rw-r--r--src/core/target.c24
-rw-r--r--src/core/timer.c249
-rw-r--r--src/core/timer.h16
-rw-r--r--src/core/transaction.c89
-rw-r--r--src/core/umount.c79
-rw-r--r--src/core/unit-printf.c353
-rw-r--r--src/core/unit-printf.h (renamed from src/core/fdset.h)17
-rw-r--r--src/core/unit.c396
-rw-r--r--src/core/unit.h29
65 files changed, 4418 insertions, 2518 deletions
diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c
new file mode 100644
index 0000000000..5955bd846e
--- /dev/null
+++ b/src/core/audit-fd.c
@@ -0,0 +1,73 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 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 <errno.h>
+#include "audit-fd.h"
+
+#ifdef HAVE_AUDIT
+
+#include <stdbool.h>
+#include <libaudit.h>
+
+#include "log.h"
+#include "util.h"
+
+static bool initialized = false;
+static int audit_fd;
+
+int get_audit_fd(void) {
+
+ if (!initialized) {
+ audit_fd = audit_open();
+
+ if (audit_fd < 0) {
+ if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
+ log_error("Failed to connect to audit log: %m");
+
+ audit_fd = errno ? -errno : -EINVAL;
+ }
+
+ initialized = true;
+ }
+
+ return audit_fd;
+}
+
+void close_audit_fd(void) {
+
+ if (initialized && audit_fd >= 0)
+ close_nointr_nofail(audit_fd);
+
+ initialized = true;
+ audit_fd = -ECONNRESET;
+}
+
+#else
+
+int get_audit_fd(void) {
+ return -EAFNOSUPPORT;
+}
+
+void close_audit_fd(void) {
+}
+
+#endif
diff --git a/src/core/audit-fd.h b/src/core/audit-fd.h
new file mode 100644
index 0000000000..8b58289dc5
--- /dev/null
+++ b/src/core/audit-fd.h
@@ -0,0 +1,25 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 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 get_audit_fd(void);
+void close_audit_fd(void);
diff --git a/src/core/automount.c b/src/core/automount.c
index 91162c2a58..4a98540d82 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -31,6 +31,7 @@
#include "unit.h"
#include "automount.h"
+#include "mount.h"
#include "load-fragment.h"
#include "load-dropin.h"
#include "unit-name.h"
@@ -40,6 +41,7 @@
#include "label.h"
#include "mkdir.h"
#include "path-util.h"
+#include "dbus-common.h"
static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
@@ -132,7 +134,8 @@ int automount_add_one_mount_link(Automount *a, Mount *m) {
if (path_equal(a->where, m->where))
return 0;
- if ((r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
+ if (r < 0)
return r;
return 0;
@@ -144,9 +147,11 @@ static int automount_add_mount_links(Automount *a) {
assert(a);
- LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT])
- if ((r = automount_add_one_mount_link(a, MOUNT(other))) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT]) {
+ r = automount_add_one_mount_link(a, MOUNT(other));
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -156,7 +161,7 @@ static int automount_add_default_dependencies(Automount *a) {
assert(a);
- if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
+ if (UNIT(a)->manager->running_as != SYSTEMD_SYSTEM)
return 0;
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -175,18 +180,19 @@ static int automount_verify(Automount *a) {
return 0;
if (path_equal(a->where, "/")) {
- log_error("Cannot have an automount unit for the root directory. Refusing.");
+ log_error_unit(UNIT(a)->id, "Cannot have an automount unit for the root directory. Refusing.");
return -EINVAL;
}
- if (!(e = unit_name_from_path(a->where, ".automount")))
+ e = unit_name_from_path(a->where, ".automount");
+ if (!e)
return -ENOMEM;
b = unit_has_name(UNIT(a), e);
free(e);
if (!b) {
- log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id);
+ log_error_unit(UNIT(a)->id, "%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id);
return -EINVAL;
}
@@ -201,19 +207,23 @@ static int automount_load(Unit *u) {
assert(u->load_state == UNIT_STUB);
/* Load a .automount file */
- if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
+ r = unit_load_fragment_and_dropin_optional(u);
+ if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
Unit *x;
- if (!a->where)
- if (!(a->where = unit_name_to_path(u->id)))
+ if (!a->where) {
+ a->where = unit_name_to_path(u->id);
+ if (!a->where)
return -ENOMEM;
+ }
path_kill_slashes(a->where);
- if ((r = automount_add_mount_links(a)) < 0)
+ r = automount_add_mount_links(a);
+ if (r < 0)
return r;
r = unit_load_related_unit(u, ".mount", &x);
@@ -226,9 +236,11 @@ static int automount_load(Unit *u) {
if (r < 0)
return r;
- if (UNIT(a)->default_dependencies)
- if ((r = automount_add_default_dependencies(a)) < 0)
+ if (UNIT(a)->default_dependencies) {
+ r = automount_add_default_dependencies(a);
+ if (r < 0)
return r;
+ }
}
return automount_verify(a);
@@ -246,10 +258,11 @@ static void automount_set_state(Automount *a, AutomountState state) {
unmount_autofs(a);
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(a)->id,
- automount_state_to_string(old_state),
- automount_state_to_string(state));
+ log_debug_unit(UNIT(a)->id,
+ "%s changed %s -> %s",
+ UNIT(a)->id,
+ automount_state_to_string(old_state),
+ automount_state_to_string(state));
unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -263,7 +276,8 @@ static int automount_coldplug(Unit *u) {
if (a->deserialized_state != a->state) {
- if ((r = open_dev_autofs(u->manager)) < 0)
+ r = open_dev_autofs(u->manager);
+ if (r < 0)
return r;
if (a->deserialized_state == AUTOMOUNT_WAITING ||
@@ -271,7 +285,8 @@ static int automount_coldplug(Unit *u) {
assert(a->pipe_fd >= 0);
- if ((r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch)) < 0)
+ r = unit_watch_fd(UNIT(a), a->pipe_fd, EPOLLIN, &a->pipe_watch);
+ if (r < 0)
return r;
}
@@ -316,7 +331,8 @@ static int open_dev_autofs(Manager *m) {
label_fix("/dev/autofs", false, false);
- if ((m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY)) < 0) {
+ m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY);
+ if (m->dev_autofs_fd < 0) {
log_error("Failed to open /dev/autofs: %s", strerror(errno));
return -errno;
}
@@ -336,15 +352,12 @@ static int open_dev_autofs(Manager *m) {
static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
struct autofs_dev_ioctl *param;
size_t l;
- int r;
assert(dev_autofs_fd >= 0);
assert(where);
l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1;
-
- if (!(param = malloc(l)))
- return -ENOMEM;
+ param = alloca(l);
init_autofs_dev_ioctl(param);
param->size = l;
@@ -352,22 +365,14 @@ static int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
param->openmount.devid = devid;
strcpy(param->path, where);
- if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0) {
- r = -errno;
- goto finish;
- }
+ if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0)
+ return -errno;
- if (param->ioctlfd < 0) {
- r = -EIO;
- goto finish;
- }
+ if (param->ioctlfd < 0)
+ return -EIO;
fd_cloexec(param->ioctlfd, true);
- r = param->ioctlfd;
-
-finish:
- free(param);
- return r;
+ return param->ioctlfd;
}
static int autofs_protocol(int dev_autofs_fd, int ioctl_fd) {
@@ -444,15 +449,16 @@ int automount_send_ready(Automount *a, int status) {
if (set_isempty(a->tokens))
return 0;
- if ((ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id)) < 0) {
+ ioctl_fd = open_ioctl_fd(UNIT(a)->manager->dev_autofs_fd, a->where, a->dev_id);
+ if (ioctl_fd < 0) {
r = ioctl_fd;
goto fail;
}
if (status)
- log_debug("Sending failure: %s", strerror(-status));
+ log_debug_unit(UNIT(a)->id, "Sending failure: %s", strerror(-status));
else
- log_debug("Sending success.");
+ log_debug_unit(UNIT(a)->id, "Sending success.");
r = 0;
@@ -465,10 +471,11 @@ int automount_send_ready(Automount *a, int status) {
* if you pass a positive status code here, the kernel will
* freeze! Yay! */
- if ((k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd,
- ioctl_fd,
- token,
- status)) < 0)
+ k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd,
+ ioctl_fd,
+ token,
+ status);
+ if (k < 0)
r = k;
}
@@ -493,7 +500,8 @@ static void automount_enter_waiting(Automount *a) {
if (a->tokens)
set_clear(a->tokens);
- if ((dev_autofs_fd = open_dev_autofs(UNIT(a)->manager)) < 0) {
+ dev_autofs_fd = open_dev_autofs(UNIT(a)->manager);
+ if (dev_autofs_fd < 0) {
r = dev_autofs_fd;
goto fail;
}
@@ -501,6 +509,8 @@ static void automount_enter_waiting(Automount *a) {
/* We knowingly ignore the results of this call */
mkdir_p_label(a->where, 0555);
+ warn_if_dir_nonempty(a->meta.id, a->where);
+
if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) {
r = -errno;
goto fail;
@@ -527,15 +537,18 @@ static void automount_enter_waiting(Automount *a) {
goto fail;
}
- if ((ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev)) < 0) {
+ ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev);
+ if (ioctl_fd < 0) {
r = ioctl_fd;
goto fail;
}
- if ((r = autofs_protocol(dev_autofs_fd, ioctl_fd)) < 0)
+ r = autofs_protocol(dev_autofs_fd, ioctl_fd);
+ if (r < 0)
goto fail;
- if ((r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300)) < 0)
+ r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300);
+ if (r < 0)
goto fail;
/* Autofs fun fact:
@@ -547,7 +560,8 @@ static void automount_enter_waiting(Automount *a) {
close_nointr_nofail(ioctl_fd);
ioctl_fd = -1;
- if ((r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch)) < 0)
+ r = unit_watch_fd(UNIT(a), p[0], EPOLLIN, &a->pipe_watch);
+ if (r < 0)
goto fail;
a->pipe_fd = p[0];
@@ -566,14 +580,15 @@ fail:
if (mounted)
repeat_unmout(a->where);
- log_error("Failed to initialize automounter: %s", strerror(-r));
+ log_error_unit(UNIT(a)->id,
+ "Failed to initialize automounter: %s", strerror(-r));
automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
}
static void automount_enter_runnning(Automount *a) {
int r;
struct stat st;
- DBusError error;
+ _cleanup_dbus_error_free_ DBusError error;
assert(a);
assert(UNIT_DEREF(a->mount));
@@ -583,7 +598,8 @@ static void automount_enter_runnning(Automount *a) {
/* We don't take mount requests anymore if we are supposed to
* shut down anyway */
if (unit_pending_inactive(UNIT(a))) {
- log_debug("Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
+ log_debug_unit(UNIT(a)->id,
+ "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id);
automount_send_ready(a, -EHOSTDOWN);
return;
}
@@ -592,14 +608,18 @@ static void automount_enter_runnning(Automount *a) {
/* Before we do anything, let's see if somebody is playing games with us? */
if (lstat(a->where, &st) < 0) {
- log_warning("%s failed to stat automount point: %m", UNIT(a)->id);
+ log_warning_unit(UNIT(a)->id,
+ "%s failed to stat automount point: %m", UNIT(a)->id);
goto fail;
}
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
- log_info("%s's automount point already active?", UNIT(a)->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("%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error(&error, r));
+ log_warning_unit(UNIT(a)->id,
+ "%s failed to queue mount startup job: %s",
+ UNIT(a)->id, bus_error(&error, r));
goto fail;
}
@@ -608,18 +628,18 @@ static void automount_enter_runnning(Automount *a) {
fail:
automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
- dbus_error_free(&error);
}
static int automount_start(Unit *u) {
Automount *a = AUTOMOUNT(u);
assert(a);
-
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
if (path_is_mount_point(a->where, false)) {
- log_error("Path %s is already a mount point, refusing start for %s", a->where, u->id);
+ log_error_unit(u->id,
+ "Path %s is already a mount point, refusing start for %s",
+ a->where, u->id);
return -EEXIST;
}
@@ -635,7 +655,6 @@ static int automount_stop(Unit *u) {
Automount *a = AUTOMOUNT(u);
assert(a);
-
assert(a->state == AUTOMOUNT_WAITING || a->state == AUTOMOUNT_RUNNING);
automount_enter_dead(a, AUTOMOUNT_SUCCESS);
@@ -661,7 +680,8 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
if (a->pipe_fd >= 0) {
int copy;
- if ((copy = fdset_put_dup(fds, a->pipe_fd)) < 0)
+ copy = fdset_put_dup(fds, a->pipe_fd);
+ if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
@@ -680,8 +700,9 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
if (streq(key, "state")) {
AutomountState state;
- if ((state = automount_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
+ state = automount_state_from_string(value);
+ if (state < 0)
+ log_debug_unit(u->id, "Failed to parse state value %s", value);
else
a->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -689,7 +710,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
f = automount_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse result value %s", value);
+ log_debug_unit(u->id, "Failed to parse result value %s", value);
else if (f != AUTOMOUNT_SUCCESS)
a->result = f;
@@ -697,27 +718,28 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
unsigned d;
if (safe_atou(value, &d) < 0)
- log_debug("Failed to parse dev-id value %s", value);
+ log_debug_unit(u->id, "Failed to parse dev-id value %s", value);
else
a->dev_id = (unsigned) d;
} else if (streq(key, "token")) {
unsigned token;
if (safe_atou(value, &token) < 0)
- log_debug("Failed to parse token value %s", value);
+ log_debug_unit(u->id, "Failed to parse token value %s", value);
else {
if (!a->tokens)
if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func)))
return -ENOMEM;
- if ((r = set_put(a->tokens, UINT_TO_PTR(token))) < 0)
+ r = set_put(a->tokens, UINT_TO_PTR(token));
+ if (r < 0)
return r;
}
} else if (streq(key, "pipe-fd")) {
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse pipe-fd value %s", value);
+ log_debug_unit(u->id, "Failed to parse pipe-fd value %s", value);
else {
if (a->pipe_fd >= 0)
close_nointr_nofail(a->pipe_fd);
@@ -725,7 +747,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
a->pipe_fd = fdset_remove(fds, fd);
}
} else
- log_debug("Unknown serialization key '%s'", key);
+ log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
@@ -763,12 +785,13 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
assert(fd == a->pipe_fd);
if (events != EPOLLIN) {
- log_error("Got invalid poll event on pipe.");
+ log_error_unit(u->id, "Got invalid poll event on pipe.");
goto fail;
}
- if ((l = loop_read(a->pipe_fd, &packet, sizeof(packet), true)) != sizeof(packet)) {
- log_error("Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read");
+ l = loop_read(a->pipe_fd, &packet, sizeof(packet), true);
+ if (l != sizeof(packet)) {
+ log_error_unit(u->id, "Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read");
goto fail;
}
@@ -777,23 +800,24 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
case autofs_ptype_missing_direct:
if (packet.v5_packet.pid > 0) {
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
get_process_comm(packet.v5_packet.pid, &p);
- log_debug("Got direct mount request for %s, triggered by %lu (%s)", packet.v5_packet.name, (unsigned long) packet.v5_packet.pid, strna(p));
- free(p);
-
+ log_debug_unit(u->id,
+ "Got direct mount request on %s, triggered by %lu (%s)",
+ a->where, (unsigned long) packet.v5_packet.pid, strna(p));
} else
- log_debug("Got direct mount request for %s", packet.v5_packet.name);
+ log_debug_unit(u->id, "Got direct mount request on %s", a->where);
- if (!a->tokens)
- if (!(a->tokens = set_new(trivial_hash_func, trivial_compare_func))) {
- log_error("Failed to allocate token set.");
- goto fail;
- }
+ r = set_ensure_allocated(&a->tokens, trivial_hash_func, trivial_compare_func);
+ if (r < 0) {
+ log_error_unit(u->id, "Failed to allocate token set.");
+ goto fail;
+ }
- if ((r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token))) < 0) {
- log_error("Failed to remember token: %s", strerror(-r));
+ r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
+ if (r < 0) {
+ log_error_unit(u->id, "Failed to remember token: %s", strerror(-r));
goto fail;
}
@@ -801,7 +825,7 @@ static void automount_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
break;
default:
- log_error("Received unknown automount request %i", packet.hdr.type);
+ log_error_unit(u->id, "Received unknown automount request %i", packet.hdr.type);
break;
}
diff --git a/src/core/build.h b/src/core/build.h
index 0b38050bd4..4513a0bad7 100644
--- a/src/core/build.h
+++ b/src/core/build.h
@@ -63,4 +63,22 @@
#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP"
#endif
-#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_
+#ifdef HAVE_GCRYPT
+#define _GCRYPT_FEATURE_ "+GCRYPT"
+#else
+#define _GCRYPT_FEATURE_ "-GCRYPT"
+#endif
+
+#ifdef HAVE_ACL
+#define _ACL_FEATURE_ "+ACL"
+#else
+#define _ACL_FEATURE_ "-ACL"
+#endif
+
+#ifdef HAVE_XZ
+#define _XZ_FEATURE_ "+XZ"
+#else
+#define _XZ_FEATURE_ "-XZ"
+#endif
+
+#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ " " _GCRYPT_FEATURE_ " " _ACL_FEATURE_ " " _XZ_FEATURE_
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 8ddb1118ed..8fc1731485 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -313,7 +313,7 @@ int manager_setup_cgroup(Manager *m) {
goto finish;
}
- if (m->running_as == MANAGER_SYSTEM)
+ if (m->running_as == SYSTEMD_SYSTEM)
strcpy(suffix, "/system");
else {
snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
diff --git a/src/core/condition.c b/src/core/condition.c
index e4080d569d..b3184922b8 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -25,11 +25,13 @@
#include <unistd.h>
#include <sys/capability.h>
#include <sys/statvfs.h>
+#include <fnmatch.h>
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
+#include <systemd/sd-id128.h>
#include "util.h"
#include "condition.h"
#include "virt.h"
@@ -194,6 +196,41 @@ static bool test_capability(const char *parameter) {
return !!(capabilities & (1ULL << value));
}
+static bool test_host(const char *parameter) {
+ sd_id128_t x, y;
+ char *h;
+ int r;
+ bool b;
+
+ if (sd_id128_from_string(parameter, &x) >= 0) {
+
+ r = sd_id128_get_machine(&y);
+ if (r < 0)
+ return false;
+
+ return sd_id128_equal(x, y);
+ }
+
+ h = gethostname_malloc();
+ if (!h)
+ return false;
+
+ b = fnmatch(parameter, h, FNM_CASEFOLD) == 0;
+ free(h);
+
+ return b;
+}
+
+static bool test_ac_power(const char *parameter) {
+ int r;
+
+ r = parse_boolean(parameter);
+ if (r < 0)
+ return true;
+
+ return (on_ac_power() != 0) == !!r;
+}
+
bool condition_test(Condition *c) {
assert(c);
@@ -234,6 +271,15 @@ bool condition_test(Condition *c) {
return !(k == -ENOENT || k > 0) == !c->negate;
}
+ case CONDITION_FILE_NOT_EMPTY: {
+ struct stat st;
+
+ if (stat(c->parameter, &st) < 0)
+ return c->negate;
+
+ return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
+ }
+
case CONDITION_FILE_IS_EXECUTABLE: {
struct stat st;
@@ -255,6 +301,12 @@ bool condition_test(Condition *c) {
case CONDITION_CAPABILITY:
return test_capability(c->parameter) == !c->negate;
+ case CONDITION_HOST:
+ return test_host(c->parameter) == !c->negate;
+
+ case CONDITION_AC_POWER:
+ return test_ac_power(c->parameter) == !c->negate;
+
case CONDITION_NULL:
return !c->negate;
@@ -320,9 +372,12 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
[CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
[CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
+ [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
[CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
[CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
[CONDITION_SECURITY] = "ConditionSecurity",
+ [CONDITION_HOST] = "ConditionHost",
+ [CONDITION_AC_POWER] = "ConditionACPower",
[CONDITION_NULL] = "ConditionNull"
};
diff --git a/src/core/condition.h b/src/core/condition.h
index 3dca432f77..1797385d26 100644
--- a/src/core/condition.h
+++ b/src/core/condition.h
@@ -33,11 +33,14 @@ typedef enum ConditionType {
CONDITION_PATH_IS_MOUNT_POINT,
CONDITION_PATH_IS_READ_WRITE,
CONDITION_DIRECTORY_NOT_EMPTY,
+ CONDITION_FILE_NOT_EMPTY,
CONDITION_FILE_IS_EXECUTABLE,
CONDITION_KERNEL_COMMAND_LINE,
CONDITION_VIRTUALIZATION,
CONDITION_SECURITY,
CONDITION_CAPABILITY,
+ CONDITION_HOST,
+ CONDITION_AC_POWER,
CONDITION_NULL,
_CONDITION_TYPE_MAX,
_CONDITION_TYPE_INVALID = -1
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index b93e3ea35f..060cbf7707 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -24,6 +24,7 @@
#include "dbus-unit.h"
#include "dbus-automount.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_AUTOMOUNT_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Automount\">\n" \
@@ -68,5 +69,7 @@ DBusHandlerResult bus_automount_message_handler(Unit *u, DBusConnection *c, DBus
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c
index dfbbafb66d..dbd91fe3db 100644
--- a/src/core/dbus-device.c
+++ b/src/core/dbus-device.c
@@ -22,6 +22,7 @@
#include "dbus-unit.h"
#include "dbus-device.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_DEVICE_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Device\">\n" \
@@ -61,5 +62,7 @@ DBusHandlerResult bus_device_message_handler(Unit *u, DBusConnection *c, DBusMes
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index 1b01ead2da..fdc1dce177 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -25,6 +25,7 @@
#include "log.h"
#include "dbus-job.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_JOB_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Job\">\n" \
@@ -68,7 +69,8 @@ static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *d
if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
return -ENOMEM;
- if (!(p = unit_dbus_path(j->unit)))
+ p = unit_dbus_path(j->unit);
+ if (!p)
return -ENOMEM;
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
@@ -94,43 +96,39 @@ static const BusProperty bus_job_properties[] = {
};
static DBusHandlerResult bus_job_message_dispatch(Job *j, DBusConnection *connection, DBusMessage *message) {
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Job", "Cancel")) {
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
- job_finish_and_invalidate(j, JOB_CANCELED, true);
+ SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "stop");
+ 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 },
{ NULL, }
};
- return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
- }
- if (reply) {
- if (!dbus_connection_send(connection, reply, NULL))
- goto oom;
+ SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
- dbus_message_unref(reply);
+ return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
}
- return DBUS_HANDLER_RESULT_HANDLED;
-
-oom:
- if (reply)
- dbus_message_unref(reply);
+ if (!dbus_connection_send(connection, reply, NULL))
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
Manager *m = data;
Job *j;
int r;
- DBusMessage *reply;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
assert(connection);
assert(message);
@@ -145,7 +143,10 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
Iterator i;
size_t size;
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
/* We roll our own introspection code here, instead of
@@ -153,7 +154,8 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
* need to generate our introspection string
* dynamically. */
- if (!(f = open_memstream(&introspection, &size)))
+ f = open_memstream(&introspection, &size);
+ if (!f)
goto oom;
fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
@@ -188,36 +190,28 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
if (!dbus_connection_send(connection, reply, NULL))
goto oom;
- dbus_message_unref(reply);
-
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- if ((r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j)) < 0) {
-
- if (r == -ENOMEM)
- return DBUS_HANDLER_RESULT_NEED_MEMORY;
-
- if (r == -ENOENT) {
- DBusError e;
-
- dbus_error_init(&e);
- dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
- return bus_send_error_reply(connection, message, &e, r);
- }
+ r = manager_get_job_from_dbus_path(m, dbus_message_get_path(message), &j);
+ if (r == -ENOMEM)
+ goto oom;
+ if (r == -ENOENT) {
+ DBusError e;
- return bus_send_error_reply(connection, message, NULL, r);
+ dbus_error_init(&e);
+ dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown job");
+ return bus_send_error_reply(connection, message, &e, r);
}
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
return bus_job_message_dispatch(j, connection, message);
oom:
- if (reply)
- dbus_message_unref(reply);
-
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index c341d36a6b..859fa1a906 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -30,9 +30,12 @@
#include "build.h"
#include "dbus-common.h"
#include "install.h"
+#include "selinux-access.h"
#include "watchdog.h"
#include "hwclock.h"
#include "path-util.h"
+#include "dbus-unit.h"
+#include "virt.h"
#define BUS_MANAGER_INTERFACE_BEGIN \
" <interface name=\"org.freedesktop.systemd1.Manager\">\n"
@@ -214,6 +217,8 @@
" <arg name=\"result\" type=\"s\"/>\n" \
" </signal>" \
" <signal name=\"StartupFinished\">\n" \
+ " <arg name=\"firmware\" type=\"t\"/>\n" \
+ " <arg name=\"loader\" type=\"t\"/>\n" \
" <arg name=\"kernel\" type=\"t\"/>\n" \
" <arg name=\"initrd\" type=\"t\"/>\n" \
" <arg name=\"userspace\" type=\"t\"/>\n" \
@@ -223,13 +228,18 @@
#define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
" <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"Distribution\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Features\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Tainted\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"FirmwareTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"FirmwareTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LoaderTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"LoaderTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"KernelTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"KernelTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"InitRDTimestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"InitRDTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"StartupTimestamp\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"StartupTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"UserspaceTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"UserspaceTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n" \
@@ -248,7 +258,8 @@
" <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=\"ShutdownWatchdogUSec\" type=\"s\" access=\"readwrite\"/>\n" \
+ " <property name=\"Virtualization\" type=\"s\" access=\"read\"/>\n"
#define BUS_MANAGER_INTERFACE_END \
" </interface>\n"
@@ -340,17 +351,21 @@ static int bus_manager_set_log_target(DBusMessageIter *i, const char *property,
}
static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) {
- const char *t;
+ char *t;
+ int r;
assert(i);
assert(property);
- t = log_level_to_string(log_get_max_level());
+ r = log_level_to_string_alloc(log_get_max_level(), &t);
+ if (r < 0)
+ return r;
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
- return -ENOMEM;
+ r = -ENOMEM;
- return 0;
+ free(t);
+ return r;
}
static int bus_manager_set_log_level(DBusMessageIter *i, const char *property, void *data) {
@@ -415,18 +430,20 @@ static int bus_manager_append_progress(DBusMessageIter *i, const char *property,
return 0;
}
-static const char *message_get_sender_with_fallback(DBusMessage *m) {
- const char *s;
+static int bus_manager_append_virt(DBusMessageIter *i, const char *property, void *data) {
+ Manager *m = data;
+ const char *id = "";
+ assert(i);
+ assert(property);
assert(m);
- if ((s = dbus_message_get_sender(m)))
- return s;
+ detect_virtualization(&id);
- /* When the message came in from a direct connection the
- * message will have no sender. We fix that here. */
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &id))
+ return -ENOMEM;
- return ":no-sender";
+ return 0;
}
static DBusMessage *message_from_file_changes(
@@ -508,51 +525,55 @@ static int bus_manager_set_runtime_watchdog_usec(DBusMessageIter *i, const char
static const char systemd_property_string[] =
PACKAGE_STRING "\0"
- DISTRIBUTION "\0"
SYSTEMD_FEATURES;
static const BusProperty bus_systemd_properties[] = {
- { "Version", bus_property_append_string, "s", 0 },
- { "Distribution", bus_property_append_string, "s", sizeof(PACKAGE_STRING) },
- { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) + sizeof(DISTRIBUTION) },
+ { "Version", bus_property_append_string, "s", 0 },
+ { "Features", bus_property_append_string, "s", sizeof(PACKAGE_STRING) },
{ NULL, }
};
static const BusProperty bus_manager_properties[] = {
- { "Tainted", bus_manager_append_tainted, "s", 0 },
- { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) },
- { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) },
- { "StartupTimestamp", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.realtime) },
- { "StartupTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, startup_timestamp.monotonic) },
- { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) },
- { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) },
- { "LogLevel", bus_manager_append_log_level, "s", 0, false, bus_manager_set_log_level },
- { "LogTarget", bus_manager_append_log_target, "s", 0, false, bus_manager_set_log_target },
- { "NNames", bus_manager_append_n_names, "u", 0 },
- { "NJobs", bus_manager_append_n_jobs, "u", 0 },
- { "NInstalledJobs",bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) },
- { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) },
- { "Progress", bus_manager_append_progress, "d", 0 },
- { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true },
- { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) },
- { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) },
- { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true },
- { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true },
- { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true },
- { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) },
- { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) },
- { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), false, bus_manager_set_runtime_watchdog_usec },
- { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), false, bus_property_set_usec },
+ { "Tainted", bus_manager_append_tainted, "s", 0 },
+ { "FirmwareTimestamp", bus_property_append_uint64, "t", offsetof(Manager, firmware_timestamp.realtime) },
+ { "FirmwareTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, firmware_timestamp.monotonic) },
+ { "LoaderTimestamp", bus_property_append_uint64, "t", offsetof(Manager, loader_timestamp.realtime) },
+ { "LoaderTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, loader_timestamp.monotonic) },
+ { "KernelTimestamp", bus_property_append_uint64, "t", offsetof(Manager, kernel_timestamp.realtime) },
+ { "KernelTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, kernel_timestamp.monotonic) },
+ { "InitRDTimestamp", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.realtime) },
+ { "InitRDTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, initrd_timestamp.monotonic) },
+ { "UserspaceTimestamp", bus_property_append_uint64, "t", offsetof(Manager, userspace_timestamp.realtime) },
+ { "UserspaceTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, userspace_timestamp.monotonic) },
+ { "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) },
+ { "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) },
+ { "LogLevel", bus_manager_append_log_level, "s", 0, false, bus_manager_set_log_level },
+ { "LogTarget", bus_manager_append_log_target, "s", 0, false, bus_manager_set_log_target },
+ { "NNames", bus_manager_append_n_names, "u", 0 },
+ { "NJobs", bus_manager_append_n_jobs, "u", 0 },
+ { "NInstalledJobs", bus_property_append_uint32, "u", offsetof(Manager, n_installed_jobs) },
+ { "NFailedJobs", bus_property_append_uint32, "u", offsetof(Manager, n_failed_jobs) },
+ { "Progress", bus_manager_append_progress, "d", 0 },
+ { "Environment", bus_property_append_strv, "as", offsetof(Manager, environment), true },
+ { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) },
+ { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) },
+ { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true },
+ { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true },
+ { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true },
+ { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) },
+ { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) },
+ { "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), false, bus_manager_set_runtime_watchdog_usec },
+ { "ShutdownWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, shutdown_watchdog), false, bus_property_set_usec },
+ { "Virtualization", bus_manager_append_virt, "s", 0, },
{ NULL, }
};
static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection, DBusMessage *message, void *data) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ char * path = NULL;
Manager *m = data;
-
int r;
DBusError error;
- DBusMessage *reply = NULL;
- char * path = NULL;
JobType job_type = _JOB_TYPE_INVALID;
bool reload_if_possible = false;
const char *member;
@@ -576,15 +597,20 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if (!(u = manager_get_unit(m, name))) {
+ 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);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
- if (!(path = unit_dbus_path(u)))
+ path = unit_dbus_path(u);
+ if (!path)
goto oom;
if (!dbus_message_append_args(
@@ -603,15 +629,20 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if (!(u = cgroup_unit_by_pid(m, (pid_t) pid))) {
+ u = cgroup_unit_by_pid(m, (pid_t) pid);
+ if (!u) {
dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
return bus_send_error_reply(connection, message, &error, -ENOENT);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
- if (!(path = unit_dbus_path(u)))
+ path = unit_dbus_path(u);
+ if (!path)
goto oom;
if (!dbus_message_append_args(
@@ -630,13 +661,18 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
+ r = manager_load_unit(m, name, NULL, &error, &u);
+ if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
- if (!(path = unit_dbus_path(u)))
+ path = unit_dbus_path(u);
+ if (!path)
goto oom;
if (!dbus_message_append_args(
@@ -695,11 +731,14 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
return bus_send_error_reply(connection, message, &error, -ENOENT);
}
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
r = unit_kill(u, who, signo, &error);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
@@ -713,15 +752,20 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if (!(j = manager_get_job(m, id))) {
+ 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);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_UNIT_ACCESS_CHECK(j->unit, connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
- if (!(path = job_dbus_path(j)))
+ path = job_dbus_path(j);
+ if (!path)
goto oom;
if (!dbus_message_append_args(
@@ -732,16 +776,22 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ClearJobs")) {
+ SELINUX_ACCESS_CHECK(connection, message, "reboot");
+
manager_clear_jobs(m);
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailed")) {
+ SELINUX_ACCESS_CHECK(connection, message, "reload");
+
manager_reset_failed(m);
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ResetFailedUnit")) {
@@ -755,14 +805,18 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if (!(u = manager_get_unit(m, name))) {
+ 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, "reload");
+
unit_reset_failed(u);
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
@@ -771,7 +825,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
Unit *u;
const char *k;
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
dbus_message_iter_init_append(reply, &iter);
@@ -800,7 +857,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
f = unit_following(u);
following = f ? f->id : "";
- if (!(u_path = unit_dbus_path(u)))
+ u_path = unit_dbus_path(u);
+ if (!u_path)
goto oom;
if (u->job) {
@@ -850,7 +908,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
Iterator i;
Job *j;
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
dbus_message_iter_init_append(reply, &iter);
@@ -871,10 +932,12 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
state = job_state_to_string(j->state);
type = job_type_to_string(j->type);
- if (!(j_path = job_dbus_path(j)))
+ j_path = job_dbus_path(j);
+ if (!j_path)
goto oom;
- if (!(u_path = unit_dbus_path(j->unit))) {
+ u_path = unit_dbus_path(j->unit);
+ if (!u_path) {
free(j_path);
goto oom;
}
@@ -904,38 +967,49 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
char *client;
Set *s;
- if (!(s = BUS_CONNECTION_SUBSCRIBED(m, connection))) {
- if (!(s = set_new(string_hash_func, string_compare_func)))
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
+ s = BUS_CONNECTION_SUBSCRIBED(m, connection);
+ if (!s) {
+ s = set_new(string_hash_func, string_compare_func);
+ if (!s)
goto oom;
- if (!(dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL))) {
+ if (!dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL)) {
set_free(s);
goto oom;
}
}
- if (!(client = strdup(message_get_sender_with_fallback(message))))
+ client = strdup(bus_message_get_sender_with_fallback(message));
+ if (!client)
goto oom;
- if ((r = set_put(s, client)) < 0) {
+ r = set_put(s, client);
+ if (r < 0) {
free(client);
return bus_send_error_reply(connection, message, NULL, r);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Unsubscribe")) {
char *client;
- if (!(client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) message_get_sender_with_fallback(message)))) {
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
+ client = set_remove(BUS_CONNECTION_SUBSCRIBED(m, connection), (char*) bus_message_get_sender_with_fallback(message));
+ if (!client) {
dbus_set_error(&error, BUS_ERROR_NOT_SUBSCRIBED, "Client is not subscribed.");
return bus_send_error_reply(connection, message, &error, -ENOENT);
}
free(client);
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Dump")) {
@@ -943,10 +1017,14 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
char *dump = NULL;
size_t size;
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
- if (!(f = open_memstream(&dump, &size)))
+ f = open_memstream(&dump, &size);
+ if (!f)
goto oom;
manager_dump_units(m, f, NULL);
@@ -971,6 +1049,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
dbus_bool_t cleanup;
Snapshot *s;
+ SELINUX_ACCESS_CHECK(connection, message, "start");
+
if (!dbus_message_get_args(
message,
&error,
@@ -979,16 +1059,19 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if (name && name[0] == 0)
+ if (isempty(name))
name = NULL;
- if ((r = snapshot_create(m, name, cleanup, &error, &s)) < 0)
+ r = snapshot_create(m, name, cleanup, &error, &s);
+ if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
- if (!(path = unit_dbus_path(UNIT(s))))
+ path = unit_dbus_path(UNIT(s));
+ if (!path)
goto oom;
if (!dbus_message_append_args(
@@ -1006,7 +1089,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
const char *k;
size_t size;
- if (!(reply = dbus_message_new_method_return(message)))
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
/* We roll our own introspection code here, instead of
@@ -1014,7 +1100,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
* need to generate our introspection string
* dynamically. */
- if (!(f = open_memstream(&introspection, &size)))
+ f = open_memstream(&introspection, &size);
+ if (!f)
goto oom;
fputs(INTROSPECTION_BEGIN, f);
@@ -1025,7 +1112,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (k != u->id)
continue;
- if (!(p = bus_path_escape(k))) {
+ p = bus_path_escape(k);
+ if (!p) {
fclose(f);
free(introspection);
goto oom;
@@ -1060,6 +1148,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
+ SELINUX_ACCESS_CHECK(connection, message, "reload");
+
assert(!m->queued_message);
/* Instead of sending the reply back right away, we
@@ -1067,7 +1157,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
* after the reload is finished. That way the caller
* knows when the reload finished. */
- if (!(m->queued_message = dbus_message_new_method_return(message)))
+ m->queued_message = dbus_message_new_method_return(message);
+ if (!m->queued_message)
goto oom;
m->queued_message_connection = connection;
@@ -1075,6 +1166,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reexecute")) {
+ SELINUX_ACCESS_CHECK(connection, message, "reload");
+
/* We don't send a reply back here, the client should
* just wait for us disconnecting. */
@@ -1082,60 +1175,75 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Exit")) {
- if (m->running_as == MANAGER_SYSTEM) {
+ SELINUX_ACCESS_CHECK(connection, message, "halt");
+
+ if (m->running_as == SYSTEMD_SYSTEM) {
dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
return bus_send_error_reply(connection, message, &error, -ENOTSUP);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
m->exit_code = MANAGER_EXIT;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reboot")) {
- if (m->running_as != MANAGER_SYSTEM) {
+ SELINUX_ACCESS_CHECK(connection, message, "reboot");
+
+ if (m->running_as != SYSTEMD_SYSTEM) {
dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
return bus_send_error_reply(connection, message, &error, -ENOTSUP);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
m->exit_code = MANAGER_REBOOT;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PowerOff")) {
- if (m->running_as != MANAGER_SYSTEM) {
+ SELINUX_ACCESS_CHECK(connection, message, "halt");
+
+ if (m->running_as != SYSTEMD_SYSTEM) {
dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
return bus_send_error_reply(connection, message, &error, -ENOTSUP);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
m->exit_code = MANAGER_POWEROFF;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Halt")) {
- if (m->running_as != MANAGER_SYSTEM) {
+ SELINUX_ACCESS_CHECK(connection, message, "halt");
+
+ if (m->running_as != SYSTEMD_SYSTEM) {
dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Halting is only supported for system managers.");
return bus_send_error_reply(connection, message, &error, -ENOTSUP);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
m->exit_code = MANAGER_HALT;
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KExec")) {
- if (m->running_as != MANAGER_SYSTEM) {
+ SELINUX_ACCESS_CHECK(connection, message, "reboot");
+
+ if (m->running_as != SYSTEMD_SYSTEM) {
dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "kexec is only supported for system managers.");
return bus_send_error_reply(connection, message, &error, -ENOTSUP);
}
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
m->exit_code = MANAGER_KEXEC;
@@ -1145,6 +1253,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
char *u, *v;
int k;
+ SELINUX_ACCESS_CHECK(connection, message, "reboot");
+
if (!dbus_message_get_args(
message,
&error,
@@ -1159,7 +1269,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!isempty(switch_root_init) && !path_is_absolute(switch_root_init))
return bus_send_error_reply(connection, message, NULL, -EINVAL);
- if (m->running_as != MANAGER_SYSTEM) {
+ if (m->running_as != SYSTEMD_SYSTEM) {
dbus_set_error(&error, BUS_ERROR_NOT_SUPPORTED, "Switching root is only supported for system managers.");
return bus_send_error_reply(connection, message, &error, -ENOTSUP);
}
@@ -1207,20 +1317,21 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
char **l = NULL, **e = NULL;
- if ((r = bus_parse_strv(message, &l)) < 0) {
- if (r == -ENOMEM)
- goto oom;
+ SELINUX_ACCESS_CHECK(connection, message, "reboot");
+ r = bus_parse_strv(message, &l);
+ if (r == -ENOMEM)
+ goto oom;
+ if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
- }
e = strv_env_merge(2, m->environment, l);
strv_free(l);
-
if (!e)
goto oom;
- if (!(reply = dbus_message_new_method_return(message))) {
+ reply = dbus_message_new_method_return(message);
+ if (!reply) {
strv_free(e);
goto oom;
}
@@ -1231,12 +1342,13 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
char **l = NULL, **e = NULL;
- if ((r = bus_parse_strv(message, &l)) < 0) {
- if (r == -ENOMEM)
- goto oom;
+ SELINUX_ACCESS_CHECK(connection, message, "reboot");
+ r = bus_parse_strv(message, &l);
+ if (r == -ENOMEM)
+ goto oom;
+ if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
- }
e = strv_env_delete(m->environment, 1, l);
strv_free(l);
@@ -1244,7 +1356,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!e)
goto oom;
- if (!(reply = dbus_message_new_method_return(message))) {
+ reply = dbus_message_new_method_return(message);
+ if (!reply) {
strv_free(e);
goto oom;
}
@@ -1256,16 +1369,16 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
DBusMessageIter iter;
+ SELINUX_ACCESS_CHECK(connection, message, "reboot");
+
if (!dbus_message_iter_init(message, &iter))
goto oom;
r = bus_parse_strv_iter(&iter, &l_unset);
- if (r < 0) {
- if (r == -ENOMEM)
- goto oom;
-
+ if (r == -ENOMEM)
+ goto oom;
+ if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
- }
if (!dbus_message_iter_next(&iter)) {
strv_free(l_unset);
@@ -1296,7 +1409,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!f)
goto oom;
- if (!(reply = dbus_message_new_method_return(message))) {
+ reply = dbus_message_new_method_return(message);
+ if (!reply) {
strv_free(f);
goto oom;
}
@@ -1309,6 +1423,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
Iterator i;
UnitFileList *item;
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
reply = dbus_message_new_method_return(message);
if (!reply)
goto oom;
@@ -1317,10 +1433,9 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!h)
goto oom;
- r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
+ r = unit_file_get_list(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
if (r < 0) {
unit_file_list_free(h);
- dbus_message_unref(reply);
return bus_send_error_reply(connection, message, NULL, r);
}
@@ -1356,6 +1471,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
UnitFileState state;
const char *s;
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
if (!dbus_message_get_args(
message,
&error,
@@ -1363,7 +1480,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- state = unit_file_get_state(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
+ state = unit_file_get_state(m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, name);
if (state < 0)
return bus_send_error_reply(connection, message, NULL, state);
@@ -1387,12 +1504,14 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
char **l = NULL;
DBusMessageIter iter;
- UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
dbus_bool_t runtime, force;
int carries_install_info = -1;
+ SELINUX_ACCESS_CHECK(connection, message, streq(member, "MaskUnitFiles") ? "disable" : "enable");
+
if (!dbus_message_iter_init(message, &iter))
goto oom;
@@ -1446,11 +1565,13 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
char **l = NULL;
DBusMessageIter iter;
- UnitFileScope scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
dbus_bool_t runtime;
+ SELINUX_ACCESS_CHECK(connection, message, streq(member, "UnmaskUnitFiles") ? "enable" : "disable");
+
if (!dbus_message_iter_init(message, &iter))
goto oom;
@@ -1495,16 +1616,17 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
{ "org.freedesktop.systemd1.Manager", bus_manager_properties, m },
{ NULL, }
};
+
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, bps);
}
if (job_type != _JOB_TYPE_INVALID) {
const char *name, *smode, *old_name = NULL;
JobMode mode;
- Job *j;
- JobBusClient *cl;
Unit *u;
- bool b;
+ dbus_bool_t b;
if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartUnitReplace"))
b = dbus_message_get_args(
@@ -1521,86 +1643,37 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &smode,
DBUS_TYPE_INVALID);
-
if (!b)
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if (old_name)
- if (!(u = manager_get_unit(m, old_name)) ||
- !u->job ||
- u->job->type != JOB_START) {
+ if (old_name) {
+ u = manager_get_unit(m, old_name);
+ if (!u || !u->job || u->job->type != JOB_START) {
dbus_set_error(&error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
return bus_send_error_reply(connection, message, &error, -ENOENT);
}
+ }
-
- if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
+ mode = job_mode_from_string(smode);
+ if (mode < 0) {
dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
return bus_send_error_reply(connection, message, &error, -EINVAL);
}
- if ((r = manager_load_unit(m, name, NULL, &error, &u)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (reload_if_possible && unit_can_reload(u)) {
- if (job_type == JOB_RESTART)
- job_type = JOB_RELOAD_OR_START;
- else if (job_type == JOB_TRY_RESTART)
- job_type = JOB_RELOAD;
- }
-
- if (job_type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -EPERM);
- }
-
- if ((job_type == JOB_START && u->refuse_manual_start) ||
- (job_type == JOB_STOP && u->refuse_manual_stop) ||
- ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
- (u->refuse_manual_start || u->refuse_manual_stop))) {
- dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
- return bus_send_error_reply(connection, message, &error, -EPERM);
- }
-
- if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
+ r = manager_load_unit(m, name, NULL, &error, &u);
+ if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
- cl = job_bus_client_new(connection, message_get_sender_with_fallback(message));
- if (!cl)
- goto oom;
-
- LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = job_dbus_path(j)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
+ return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
}
- if (reply) {
+ if (reply)
if (!dbus_connection_send(connection, reply, NULL))
goto oom;
- dbus_message_unref(reply);
- }
-
- free(path);
-
return DBUS_HANDLER_RESULT_HANDLED;
oom:
- free(path);
-
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 93bfa4c35d..d81edeb807 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -26,6 +26,7 @@
#include "dbus-kill.h"
#include "dbus-execute.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_MOUNT_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Mount\">\n" \
@@ -161,5 +162,7 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps );
}
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index b77b5191c9..f7fed1754d 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -25,6 +25,7 @@
#include "dbus-path.h"
#include "dbus-execute.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_PATH_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Path\">\n" \
@@ -115,5 +116,7 @@ DBusHandlerResult bus_path_message_handler(Unit *u, DBusConnection *c, DBusMessa
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index c0fac16d2b..d99058dd46 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -26,6 +26,7 @@
#include "dbus-kill.h"
#include "dbus-service.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_SERVICE_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Service\">\n" \
@@ -115,7 +116,9 @@ static const BusProperty bus_service_properties[] = {
{ "PIDFile", bus_property_append_string, "s", offsetof(Service, pid_file), true },
{ "NotifyAccess", bus_service_append_notify_access, "s", offsetof(Service, notify_access) },
{ "RestartUSec", bus_property_append_usec, "t", offsetof(Service, restart_usec) },
- { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_usec) },
+ { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_start_usec) },
+ { "TimeoutStartUSec", bus_property_append_usec, "t", offsetof(Service, timeout_start_usec) },
+ { "TimeoutStopUSec", bus_property_append_usec, "t", offsetof(Service, timeout_stop_usec) },
{ "WatchdogUSec", bus_property_append_usec, "t", offsetof(Service, watchdog_usec) },
{ "WatchdogTimestamp", bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.realtime) },
{ "WatchdogTimestampMonotonic",bus_property_append_usec, "t", offsetof(Service, watchdog_timestamp.monotonic) },
@@ -152,5 +155,7 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c
index 7ff0bca2c8..435c6df39c 100644
--- a/src/core/dbus-snapshot.c
+++ b/src/core/dbus-snapshot.c
@@ -22,6 +22,7 @@
#include "dbus-unit.h"
#include "dbus-snapshot.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_SNAPSHOT_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Snapshot\">\n" \
@@ -52,17 +53,19 @@ static const BusProperty bus_snapshot_properties[] = {
DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
Snapshot *s = SNAPSHOT(u);
-
- DBusMessage *reply = NULL;
+ _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));
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else {
@@ -71,22 +74,20 @@ DBusHandlerResult bus_snapshot_message_handler(Unit *u, DBusConnection *c, DBusM
{ "org.freedesktop.systemd1.Snapshot", bus_snapshot_properties, s },
{ NULL, }
};
+
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
if (reply) {
if (!dbus_connection_send(c, reply, NULL))
goto oom;
-
- dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
oom:
- if (reply)
- dbus_message_unref(reply);
-
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 b2045225d7..095a031612 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -26,6 +26,7 @@
#include "dbus-execute.h"
#include "dbus-kill.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_SOCKET_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Socket\">\n" \
@@ -62,6 +63,9 @@
" <property name=\"MessageQueueMaxMessages\" type=\"x\" access=\"read\"/>\n" \
" <property name=\"MessageQueueMessageSize\" type=\"x\" 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" \
+ " <property name=\"SmackLabelIPOut\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n" \
#define INTROSPECTION \
@@ -125,6 +129,9 @@ static const BusProperty bus_socket_properties[] = {
{ "MessageQueueMaxMessages", bus_property_append_long, "x", offsetof(Socket, mq_maxmsg) },
{ "MessageQueueMessageSize", bus_property_append_long, "x", offsetof(Socket, mq_msgsize) },
{ "Result", bus_socket_append_socket_result, "s", offsetof(Socket, result) },
+ { "SmackLabel", bus_property_append_string, "s", offsetof(Socket, smack), true },
+ { "SmackLabelIPIn", bus_property_append_string, "s", offsetof(Socket, smack_ip_in), true },
+ { "SmackLabelIPOut",bus_property_append_string, "s", offsetof(Socket, smack_ip_out), true },
{ NULL, }
};
@@ -138,5 +145,7 @@ DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMes
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index cad6ec1aaa..67ea0f24fe 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -27,6 +27,7 @@
#include "dbus-execute.h"
#include "dbus-kill.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_SWAP_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Swap\">\n" \
@@ -108,5 +109,7 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessa
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c
index 67ffff870f..6a775506cc 100644
--- a/src/core/dbus-target.c
+++ b/src/core/dbus-target.c
@@ -24,6 +24,7 @@
#include "dbus-unit.h"
#include "dbus-target.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_TARGET_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Target\">\n" \
@@ -51,5 +52,7 @@ DBusHandlerResult bus_target_message_handler(Unit *u, DBusConnection *c, DBusMes
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index c76c2e0500..11d18cbd83 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -25,6 +25,7 @@
#include "dbus-timer.h"
#include "dbus-execute.h"
#include "dbus-common.h"
+#include "selinux-access.h"
#define BUS_TIMER_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Timer\">\n" \
@@ -78,7 +79,8 @@ static int bus_timer_append_timers(DBusMessageIter *i, const char *property, voi
/* s/Sec/USec/ */
l = strlen(t);
- if (!(buf = new(char, l+2)))
+ buf = new(char, l+2);
+ if (!buf)
return -ENOMEM;
memcpy(buf, t, l-3);
@@ -120,7 +122,8 @@ static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_timer_append_timer_result, timer_resu
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) },
+ { "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) },
{ NULL, }
};
@@ -133,5 +136,7 @@ DBusHandlerResult bus_timer_message_handler(Unit *u, DBusConnection *c, DBusMess
{ NULL, }
};
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index ad817d7ee8..8433a720b2 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -26,6 +26,7 @@
#include "dbus-unit.h"
#include "bus-errors.h"
#include "dbus-common.h"
+#include "selinux-access.h"
const char bus_unit_interface[] _introspect_("Unit") = BUS_UNIT_INTERFACE;
@@ -234,7 +235,7 @@ static int bus_unit_append_can_isolate(DBusMessageIter *i, const char *property,
static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data) {
Unit *u = data;
DBusMessageIter sub;
- char *p;
+ _cleanup_free_ char *p = NULL;
assert(i);
assert(property);
@@ -245,14 +246,13 @@ static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *d
if (u->job) {
- if (!(p = job_dbus_path(u->job)))
+ p = job_dbus_path(u->job);
+ if (!p)
return -ENOMEM;
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->job->id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
return -ENOMEM;
- }
} else {
uint32_t id = 0;
@@ -260,18 +260,15 @@ static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *d
* data. Since we need to fill in a valid path we
* simple point to ourselves. */
- if (!(p = unit_dbus_path(u)))
+ p = unit_dbus_path(u);
+ if (!p)
return -ENOMEM;
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &id) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
return -ENOMEM;
- }
}
- free(p);
-
if (!dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
@@ -288,8 +285,10 @@ static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *proper
assert(property);
assert(u);
- if ((cgb = unit_get_default_cgroup(u))) {
- if (!(t = cgroup_bonding_to_string(cgb)))
+ cgb = unit_get_default_cgroup(u);
+ if (cgb) {
+ t = cgroup_bonding_to_string(cgb);
+ if (!t)
return -ENOMEM;
} else
t = (char*) "";
@@ -311,15 +310,14 @@ static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, voi
return -ENOMEM;
LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
- char *t;
+ char _cleanup_free_ *t = NULL;
bool success;
- if (!(t = cgroup_bonding_to_string(cgb)))
+ t = cgroup_bonding_to_string(cgb);
+ if (!t)
return -ENOMEM;
success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
- free(t);
-
if (!success)
return -ENOMEM;
}
@@ -339,7 +337,7 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property
return -ENOMEM;
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char *v = NULL;
+ char _cleanup_free_ *v = NULL;
bool success;
if (a->map_callback)
@@ -351,9 +349,6 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property
dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
dbus_message_iter_close_container(&sub, &sub2);
-
- free(v);
-
if (!success)
return -ENOMEM;
}
@@ -405,12 +400,11 @@ static int bus_unit_append_load_error(DBusMessageIter *i, const char *property,
}
static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
- DBusMessage *reply = NULL;
- Manager *m = u->manager;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusError error;
JobType job_type = _JOB_TYPE_INVALID;
- char *path = NULL;
bool reload_if_possible = false;
+ int r;
dbus_error_init(&error);
@@ -434,7 +428,6 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
const char *swho;
int32_t signo;
KillWho who;
- int r;
if (!dbus_message_get_args(
message,
@@ -455,6 +448,8 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (signo <= 0 || signo >= _NSIG)
return bus_send_error_reply(connection, message, &error, -EINVAL);
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
+
r = unit_kill(u, who, signo, &error);
if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
@@ -465,9 +460,12 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "reload");
+
unit_reset_failed(u);
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
} else if (UNIT_VTABLE(u)->bus_message_handler)
@@ -478,16 +476,6 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (job_type != _JOB_TYPE_INVALID) {
const char *smode;
JobMode mode;
- Job *j;
- int r;
-
- if ((job_type == JOB_START && u->refuse_manual_start) ||
- (job_type == JOB_STOP && u->refuse_manual_stop) ||
- ((job_type == JOB_RESTART || job_type == JOB_TRY_RESTART) &&
- (u->refuse_manual_start || u->refuse_manual_stop))) {
- dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, may be requested by dependency only.");
- return bus_send_error_reply(connection, message, &error, -EPERM);
- }
if (!dbus_message_get_args(
message,
@@ -496,53 +484,23 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- if (reload_if_possible && unit_can_reload(u)) {
- if (job_type == JOB_RESTART)
- job_type = JOB_RELOAD_OR_START;
- else if (job_type == JOB_TRY_RESTART)
- job_type = JOB_RELOAD;
- }
-
- if ((mode = job_mode_from_string(smode)) == _JOB_MODE_INVALID) {
+ mode = job_mode_from_string(smode);
+ if (mode < 0) {
dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
return bus_send_error_reply(connection, message, &error, -EINVAL);
}
- if ((r = manager_add_job(m, job_type, u, mode, true, &error, &j)) < 0)
- return bus_send_error_reply(connection, message, &error, r);
-
- if (!(reply = dbus_message_new_method_return(message)))
- goto oom;
-
- if (!(path = job_dbus_path(j)))
- goto oom;
-
- if (!dbus_message_append_args(
- reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- goto oom;
+ return bus_unit_queue_job(connection, message, u, job_type, mode, reload_if_possible);
}
- if (reply) {
+ if (reply)
if (!dbus_connection_send(connection, reply, NULL))
goto oom;
- dbus_message_unref(reply);
- }
-
- free(path);
-
return DBUS_HANDLER_RESULT_HANDLED;
oom:
- free(path);
-
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
-
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
@@ -550,7 +508,7 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
Manager *m = data;
Unit *u;
int r;
- DBusMessage *reply = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
DBusError error;
assert(connection);
@@ -562,6 +520,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
if (streq(dbus_message_get_path(message), "/org/freedesktop/systemd1/unit")) {
/* Be nice to gdbus and return introspection data for our mid-level paths */
+ SELINUX_ACCESS_CHECK(connection, message, "status");
+
if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
char *introspection = NULL;
FILE *f;
@@ -569,7 +529,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
const char *k;
size_t size;
- if (!(reply = dbus_message_new_method_return(message)))
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
goto oom;
/* We roll our own introspection code here, instead of
@@ -577,7 +538,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
* need to generate our introspection string
* dynamically. */
- if (!(f = open_memstream(&introspection, &size)))
+ f = open_memstream(&introspection, &size);
+ if (!f)
goto oom;
fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
@@ -592,7 +554,8 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
if (k != u->id)
continue;
- if (!(p = bus_path_escape(k))) {
+ p = bus_path_escape(k);
+ if (!p) {
fclose(f);
free(introspection);
goto oom;
@@ -625,8 +588,6 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
if (!dbus_connection_send(connection, reply, NULL))
goto oom;
- dbus_message_unref(reply);
-
return DBUS_HANDLER_RESULT_HANDLED;
}
@@ -634,19 +595,14 @@ static DBusHandlerResult bus_unit_message_handler(DBusConnection *connection, DB
}
r = manager_load_unit_from_dbus_path(m, dbus_message_get_path(message), &error, &u);
- if (r < 0) {
- if (r == -ENOMEM)
- goto oom;
-
+ if (r == -ENOMEM)
+ goto oom;
+ if (r < 0)
return bus_send_error_reply(connection, message, &error, r);
- }
return bus_unit_message_dispatch(u, connection, message);
oom:
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
@@ -657,8 +613,8 @@ const DBusObjectPathVTable bus_unit_vtable = {
};
void bus_unit_send_change_signal(Unit *u) {
- char *p = NULL;
- DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
assert(u);
@@ -675,20 +631,22 @@ void bus_unit_send_change_signal(Unit *u) {
return;
}
- if (!(p = unit_dbus_path(u)))
+ p = unit_dbus_path(u);
+ if (!p)
goto oom;
if (u->sent_dbus_new_signal) {
/* Send a properties changed signal. First for the
* specific type, then for the generic unit. The
* clients may rely on this order to get atomic
- * behaviour if needed. */
+ * behavior if needed. */
if (UNIT_VTABLE(u)->bus_invalidating_properties) {
- if (!(m = bus_properties_changed_new(p,
- UNIT_VTABLE(u)->bus_interface,
- UNIT_VTABLE(u)->bus_invalidating_properties)))
+ m = bus_properties_changed_new(p,
+ UNIT_VTABLE(u)->bus_interface,
+ UNIT_VTABLE(u)->bus_invalidating_properties);
+ if (!m)
goto oom;
if (bus_broadcast(u->manager, m) < 0)
@@ -697,13 +655,18 @@ void bus_unit_send_change_signal(Unit *u) {
dbus_message_unref(m);
}
- if (!(m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit", INVALIDATING_PROPERTIES)))
+ m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
+ INVALIDATING_PROPERTIES);
+ if (!m)
goto oom;
} else {
/* Send a new signal */
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitNew")))
+ m = dbus_message_new_signal("/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnitNew");
+ if (!m)
goto oom;
if (!dbus_message_append_args(m,
@@ -716,25 +679,17 @@ void bus_unit_send_change_signal(Unit *u) {
if (bus_broadcast(u->manager, m) < 0)
goto oom;
- free(p);
- dbus_message_unref(m);
-
u->sent_dbus_new_signal = true;
return;
oom:
- free(p);
-
- if (m)
- dbus_message_unref(m);
-
- log_error("Failed to allocate unit change/new signal.");
+ log_oom();
}
void bus_unit_send_removed_signal(Unit *u) {
- char *p = NULL;
- DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
assert(u);
@@ -747,10 +702,14 @@ void bus_unit_send_removed_signal(Unit *u) {
if (!u->id)
return;
- if (!(p = unit_dbus_path(u)))
+ p = unit_dbus_path(u);
+ if (!p)
goto oom;
- if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitRemoved")))
+ m = dbus_message_new_signal("/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "UnitRemoved");
+ if (!m)
goto oom;
if (!dbus_message_append_args(m,
@@ -762,18 +721,92 @@ void bus_unit_send_removed_signal(Unit *u) {
if (bus_broadcast(u->manager, m) < 0)
goto oom;
- free(p);
- dbus_message_unref(m);
-
return;
oom:
- free(p);
+ log_oom();
+}
+
+DBusHandlerResult bus_unit_queue_job(
+ DBusConnection *connection,
+ DBusMessage *message,
+ Unit *u,
+ JobType type,
+ JobMode mode,
+ bool reload_if_possible) {
+
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ char *path = NULL;
+ Job *j;
+ JobBusClient *cl;
+ DBusError error;
+ int r;
+
+ assert(connection);
+ assert(message);
+ assert(u);
+ assert(type >= 0 && type < _JOB_TYPE_MAX);
+ assert(mode >= 0 && mode < _JOB_MODE_MAX);
+
+ dbus_error_init(&error);
+
+ if (reload_if_possible && unit_can_reload(u)) {
+ if (type == JOB_RESTART)
+ type = JOB_RELOAD_OR_START;
+ else if (type == JOB_TRY_RESTART)
+ type = JOB_RELOAD;
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message,
+ (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
+ type == JOB_STOP ? "stop" : "reload");
- if (m)
- dbus_message_unref(m);
+ if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
+ return bus_send_error_reply(connection, message, &error, -EPERM);
+ }
+
+ if ((type == JOB_START && u->refuse_manual_start) ||
+ (type == JOB_STOP && u->refuse_manual_stop) ||
+ ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop))) {
+ dbus_set_error(&error, BUS_ERROR_ONLY_BY_DEPENDENCY,
+ "Operation refused, unit %s may be requested by dependency only.", u->id);
+ return bus_send_error_reply(connection, message, &error, -EPERM);
+ }
- log_error("Failed to allocate unit remove signal.");
+ r = manager_add_job(u->manager, type, u, mode, true, &error, &j);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ cl = job_bus_client_new(connection, bus_message_get_sender_with_fallback(message));
+ if (!cl)
+ goto oom;
+
+ LIST_PREPEND(JobBusClient, client, j->bus_client_list, cl);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ path = job_dbus_path(j);
+ if (!path)
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (!dbus_connection_send(connection, reply, NULL))
+ goto oom;
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
const BusProperty bus_unit_properties[] = {
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index 7ab355c279..ac6785a949 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -133,6 +133,15 @@ extern const BusProperty bus_unit_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,
+ Unit *u,
+ JobType type,
+ JobMode mode,
+ bool reload_if_possible);
+
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 9db172b6e6..2a1c66054a 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -30,6 +30,7 @@
#include "strv.h"
#include "cgroup.h"
#include "mkdir.h"
+#include "missing.h"
#include "dbus-unit.h"
#include "dbus-job.h"
#include "dbus-manager.h"
@@ -444,7 +445,7 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, D
log_debug("System D-Bus connection terminated.");
bus_done_system(m);
- } else if (m->running_as != MANAGER_SYSTEM &&
+ } else if (m->running_as != SYSTEMD_SYSTEM &&
dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
const char *cgroup;
@@ -480,7 +481,7 @@ static DBusHandlerResult private_bus_message_filter(DBusConnection *connection,
if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected"))
shutdown_connection(m, connection);
- else if (m->running_as == MANAGER_SYSTEM &&
+ else if (m->running_as == SYSTEMD_SYSTEM &&
dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
const char *cgroup;
@@ -775,7 +776,7 @@ static int init_registered_system_bus(Manager *m) {
if (!dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL))
return log_oom();
- if (m->running_as != MANAGER_SYSTEM) {
+ if (m->running_as != SYSTEMD_SYSTEM) {
DBusError error;
dbus_error_init(&error);
@@ -837,7 +838,7 @@ static int init_registered_api_bus(Manager *m) {
if (r < 0)
return r;
- if (m->running_as == MANAGER_USER) {
+ if (m->running_as == SYSTEMD_USER) {
char *id;
log_debug("Successfully connected to API D-Bus bus %s as %s",
strnull((id = dbus_connection_get_server_id(m->api_bus))),
@@ -888,7 +889,7 @@ static void bus_register_cb(DBusPendingCall *pending, void *userdata) {
if (conn == &m->system_bus) {
r = init_registered_system_bus(m);
- if (r == 0 && m->running_as == MANAGER_SYSTEM)
+ if (r == 0 && m->running_as == SYSTEMD_SYSTEM)
r = init_registered_api_bus(m);
} else
r = init_registered_api_bus(m);
@@ -955,12 +956,12 @@ static DBusConnection* manager_bus_connect_private(Manager *m, DBusBusType type)
switch (type) {
case DBUS_BUS_SYSTEM:
- address = getenv("DBUS_SYSTEM_BUS_ADDRESS");
+ address = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
if (!address || !address[0])
address = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS;
break;
case DBUS_BUS_SESSION:
- address = getenv("DBUS_SESSION_BUS_ADDRESS");
+ address = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
if (!address || !address[0])
address = DBUS_SESSION_BUS_DEFAULT_ADDRESS;
break;
@@ -1018,7 +1019,7 @@ static int bus_init_api(Manager *m) {
if (m->api_bus)
return 0;
- if (m->running_as == MANAGER_SYSTEM) {
+ if (m->running_as == SYSTEMD_SYSTEM) {
m->api_bus = m->system_bus;
/* In this mode there is no distinct connection to the API bus,
* the API is published on the system bus.
@@ -1065,7 +1066,7 @@ static int bus_init_private(Manager *m) {
if (m->private_bus)
return 0;
- if (m->running_as == MANAGER_SYSTEM) {
+ if (m->running_as == SYSTEMD_SYSTEM) {
/* We want the private bus only when running as init */
if (getpid() != 1)
@@ -1077,7 +1078,7 @@ static int bus_init_private(Manager *m) {
const char *e;
char *p;
- e = getenv("XDG_RUNTIME_DIR");
+ e = secure_getenv("XDG_RUNTIME_DIR");
if (!e)
return 0;
@@ -1189,7 +1190,7 @@ static void shutdown_connection(Manager *m, DBusConnection *c) {
dbus_connection_set_dispatch_status_function(c, NULL, NULL, NULL);
/* system manager cannot afford to block on DBus */
- if (m->running_as != MANAGER_SYSTEM)
+ if (m->running_as != SYSTEMD_SYSTEM)
dbus_connection_flush(c);
dbus_connection_close(c);
dbus_connection_unref(c);
@@ -1199,7 +1200,7 @@ static void bus_done_api(Manager *m) {
if (!m->api_bus)
return;
- if (m->running_as == MANAGER_USER)
+ if (m->running_as == SYSTEMD_USER)
shutdown_connection(m, m->api_bus);
m->api_bus = NULL;
@@ -1214,7 +1215,7 @@ static void bus_done_system(Manager *m) {
if (!m->system_bus)
return;
- if (m->running_as == MANAGER_SYSTEM)
+ if (m->running_as == SYSTEMD_SYSTEM)
bus_done_api(m);
shutdown_connection(m, m->system_bus);
@@ -1361,11 +1362,11 @@ int bus_broadcast(Manager *m, DBusMessage *message) {
assert(message);
SET_FOREACH(c, m->bus_connections_for_dispatch, i)
- if (c != m->system_bus || m->running_as == MANAGER_SYSTEM)
+ if (c != m->system_bus || m->running_as == SYSTEMD_SYSTEM)
oom = !dbus_connection_send(c, message, NULL);
SET_FOREACH(c, m->bus_connections, i)
- if (c != m->system_bus || m->running_as == MANAGER_SYSTEM)
+ if (c != m->system_bus || m->running_as == SYSTEMD_SYSTEM)
oom = !dbus_connection_send(c, message, NULL);
return oom ? -ENOMEM : 0;
@@ -1436,6 +1437,8 @@ int bus_fdset_add_all(Manager *m, FDSet *fds) {
void bus_broadcast_finished(
Manager *m,
+ usec_t firmware_usec,
+ usec_t loader_usec,
usec_t kernel_usec,
usec_t initrd_usec,
usec_t userspace_usec,
@@ -1453,6 +1456,8 @@ void bus_broadcast_finished(
assert_cc(sizeof(usec_t) == sizeof(uint64_t));
if (!dbus_message_append_args(message,
+ DBUS_TYPE_UINT64, &firmware_usec,
+ DBUS_TYPE_UINT64, &loader_usec,
DBUS_TYPE_UINT64, &kernel_usec,
DBUS_TYPE_UINT64, &initrd_usec,
DBUS_TYPE_UINT64, &userspace_usec,
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 0502d7c251..c7a058e198 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -42,7 +42,7 @@ bool bus_connection_has_subscriber(Manager *m, DBusConnection *c);
int bus_fdset_add_all(Manager *m, FDSet *fds);
-void bus_broadcast_finished(Manager *m, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
+void bus_broadcast_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
#define BUS_CONNECTION_SUBSCRIBED(m, c) dbus_connection_get_data((c), (m)->subscribed_data_slot)
#define BUS_PENDING_CALL_NAME(m, p) dbus_pending_call_get_data((p), (m)->name_data_slot)
diff --git a/src/core/device.c b/src/core/device.c
index 5307341d70..0b01718ad4 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -92,10 +92,10 @@ static void device_set_state(Device *d, DeviceState state) {
d->state = state;
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(d)->id,
- device_state_to_string(old_state),
- device_state_to_string(state));
+ log_debug_unit(UNIT(d)->id,
+ "%s changed %s -> %s", UNIT(d)->id,
+ device_state_to_string(old_state),
+ device_state_to_string(state));
unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -251,30 +251,46 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p
* interpret for the main object */
const char *wants, *alias;
- if ((alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS"))) {
- if (!is_path(alias))
- log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, alias);
- else {
- if ((r = device_add_escaped_name(u, alias)) < 0)
+ alias = udev_device_get_property_value(dev, "SYSTEMD_ALIAS");
+ if (alias) {
+ char *state, *w;
+ size_t l;
+
+ FOREACH_WORD_QUOTED(w, l, alias, state) {
+ char *e;
+
+ e = strndup(w, l);
+ if (!e) {
+ r = -ENOMEM;
goto fail;
+ }
+
+ if (!is_path(e)) {
+ log_warning("SYSTEMD_ALIAS for %s is not a path, ignoring: %s", sysfs, e);
+ free(e);
+ } else {
+ device_update_unit(m, dev, e, false);
+ free(e);
+ }
}
}
- if ((wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS"))) {
+ wants = udev_device_get_property_value(dev, "SYSTEMD_WANTS");
+ if (wants) {
char *state, *w;
size_t l;
FOREACH_WORD_QUOTED(w, l, wants, state) {
char *e;
- if (!(e = strndup(w, l))) {
+ e = strndup(w, l);
+ if (!e) {
r = -ENOMEM;
goto fail;
}
r = unit_add_dependency_by_name(u, UNIT_WANTS, e, NULL, true);
free(e);
-
if (r < 0)
goto fail;
}
diff --git a/src/core/execute.c b/src/core/execute.c
index fc0edc6cfd..7dc15044b4 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -39,6 +39,7 @@
#include <linux/oom.h>
#include <sys/poll.h>
#include <linux/seccomp-bpf.h>
+#include <glob.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
@@ -50,6 +51,7 @@
#include "capability.h"
#include "util.h"
#include "log.h"
+#include "sd-messages.h"
#include "ioprio.h"
#include "securebits.h"
#include "cgroup.h"
@@ -999,7 +1001,7 @@ int exec_spawn(ExecCommand *command,
int r;
char *line;
int socket_fd;
- char **files_env = NULL;
+ char _cleanup_strv_free_ **files_env = NULL;
assert(command);
assert(context);
@@ -1020,8 +1022,13 @@ int exec_spawn(ExecCommand *command,
} else
socket_fd = -1;
- if ((r = exec_context_load_environment(context, &files_env)) < 0) {
- log_error("Failed to load environment files: %s", strerror(-r));
+ r = exec_context_load_environment(context, &files_env);
+ if (r < 0) {
+ log_struct(LOG_ERR,
+ "UNIT=%s", unit_id,
+ "MESSAGE=Failed to load environment files: %s", strerror(-r),
+ "ERRNO=%d", -r,
+ NULL);
return r;
}
@@ -1029,24 +1036,24 @@ int exec_spawn(ExecCommand *command,
argv = command->argv;
line = exec_command_line(argv);
- if (!line) {
- r = -ENOMEM;
- goto fail_parent;
- }
+ if (!line)
+ return log_oom();
- log_debug("About to execute: %s", line);
+ log_struct(LOG_DEBUG,
+ "UNIT=%s", unit_id,
+ "MESSAGE=About to execute %s", line,
+ NULL);
free(line);
r = cgroup_bonding_realize_list(cgroup_bondings);
if (r < 0)
- goto fail_parent;
+ return r;
cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
- if ((pid = fork()) < 0) {
- r = -errno;
- goto fail_parent;
- }
+ pid = fork();
+ if (pid < 0)
+ return -errno;
if (pid == 0) {
int i, err;
@@ -1054,7 +1061,8 @@ int exec_spawn(ExecCommand *command,
const char *username = NULL, *home = NULL;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
- char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
+ char _cleanup_strv_free_ **our_env = NULL, **pam_env = NULL,
+ **final_env = NULL, **final_argv = NULL;
unsigned n_env = 0;
bool set_access = false;
@@ -1283,7 +1291,7 @@ int exec_spawn(ExecCommand *command,
umask(context->umask);
#ifdef HAVE_PAM
- if (context->pam_name && username) {
+ if (apply_permissions && context->pam_name && username) {
err = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds);
if (err < 0) {
r = EXIT_PAM;
@@ -1304,7 +1312,7 @@ int exec_spawn(ExecCommand *command,
if (strv_length(context->read_write_dirs) > 0 ||
strv_length(context->read_only_dirs) > 0 ||
strv_length(context->inaccessible_dirs) > 0 ||
- context->mount_flags != MS_SHARED ||
+ context->mount_flags != 0 ||
context->private_tmp) {
err = setup_namespace(context->read_write_dirs,
context->read_only_dirs,
@@ -1331,8 +1339,7 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
} else {
-
- char *d;
+ char _cleanup_free_ *d = NULL;
if (asprintf(&d, "%s/%s",
context->root_directory ? context->root_directory : "",
@@ -1344,12 +1351,9 @@ int exec_spawn(ExecCommand *command,
if (chdir(d) < 0) {
err = -errno;
- free(d);
r = EXIT_CHDIR;
goto fail_child;
}
-
- free(d);
}
/* We repeat the fd closing here, to make sure that
@@ -1495,21 +1499,24 @@ int exec_spawn(ExecCommand *command,
fail_child:
if (r != 0) {
log_open();
- log_warning("Failed at step %s spawning %s: %s",
- exit_status_to_string(r, EXIT_STATUS_SYSTEMD),
- command->path, strerror(-err));
+ log_struct(LOG_ERR, MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
+ "EXECUTABLE=%s", command->path,
+ "MESSAGE=Failed at step %s spawning %s: %s",
+ exit_status_to_string(r, EXIT_STATUS_SYSTEMD),
+ command->path, strerror(-err),
+ "ERRNO=%d", -err,
+ NULL);
+ log_close();
}
- strv_free(our_env);
- strv_free(final_env);
- strv_free(pam_env);
- strv_free(files_env);
- strv_free(final_argv);
-
_exit(r);
}
- strv_free(files_env);
+ log_struct(LOG_DEBUG,
+ "UNIT=%s", 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
@@ -1519,17 +1526,10 @@ int exec_spawn(ExecCommand *command,
if (cgroup_bondings)
cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix);
- log_debug("Forked %s as %lu", command->path, (unsigned long) pid);
-
exec_status_start(&command->exec_status, pid);
*ret = pid;
return 0;
-
-fail_parent:
- strv_free(files_env);
-
- return r;
}
void exec_context_init(ExecContext *c) {
@@ -1540,7 +1540,6 @@ void exec_context_init(ExecContext *c) {
c->cpu_sched_policy = SCHED_OTHER;
c->syslog_priority = LOG_DAEMON|LOG_INFO;
c->syslog_level_prefix = true;
- c->mount_flags = MS_SHARED;
c->control_group_persistent = -1;
c->ignore_sigpipe = true;
c->timer_slack_nsec = (nsec_t) -1;
@@ -1659,6 +1658,8 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
int k;
bool ignore = false;
char **p;
+ glob_t pglob;
+ int count, n;
fn = *i;
@@ -1676,29 +1677,55 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
return -EINVAL;
}
- if ((k = load_env_file(fn, &p)) < 0) {
+ /* Filename supports globbing, take all matching files */
+ zero(pglob);
+ errno = 0;
+ if (glob(fn, 0, NULL, &pglob) != 0) {
+ globfree(&pglob);
+ if (ignore)
+ continue;
+ strv_free(r);
+ return errno ? -errno : -EINVAL;
+ }
+ count = pglob.gl_pathc;
+ if (count == 0) {
+ globfree(&pglob);
if (ignore)
continue;
strv_free(r);
- return k;
+ return -EINVAL;
}
+ for (n = 0; n < count; n++) {
+ k = load_env_file(pglob.gl_pathv[n], &p);
+ if (k < 0) {
+ if (ignore)
+ continue;
- if (r == NULL)
- r = p;
- else {
- char **m;
+ strv_free(r);
+ globfree(&pglob);
+ return k;
+ }
- m = strv_env_merge(2, r, p);
- strv_free(r);
- strv_free(p);
+ if (r == NULL)
+ r = p;
+ else {
+ char **m;
+
+ m = strv_env_merge(2, r, p);
+ strv_free(r);
+ strv_free(p);
- if (!m)
- return -ENOMEM;
+ if (!m) {
+ globfree(&pglob);
+ return -ENOMEM;
+ }
- r = m;
+ r = m;
+ }
}
+ globfree(&pglob);
}
*l = r;
@@ -1770,21 +1797,37 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->rlimit[i])
fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
- if (c->ioprio_set)
+ if (c->ioprio_set) {
+ char *class_str;
+ int r;
+
+ r = ioprio_class_to_string_alloc(IOPRIO_PRIO_CLASS(c->ioprio), &class_str);
+ if (r < 0)
+ class_str = NULL;
fprintf(f,
"%sIOSchedulingClass: %s\n"
"%sIOPriority: %i\n",
- prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
+ prefix, strna(class_str),
prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
+ free(class_str);
+ }
- if (c->cpu_sched_set)
+ if (c->cpu_sched_set) {
+ char *policy_str;
+ int r;
+
+ r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
+ if (r < 0)
+ policy_str = NULL;
fprintf(f,
"%sCPUSchedulingPolicy: %s\n"
"%sCPUSchedulingPriority: %i\n"
"%sCPUSchedulingResetOnFork: %s\n",
- prefix, sched_policy_to_string(c->cpu_sched_policy),
+ prefix, strna(policy_str),
prefix, c->cpu_sched_priority,
prefix, yes_no(c->cpu_sched_reset_on_fork));
+ free(policy_str);
+ }
if (c->cpuset) {
fprintf(f, "%sCPUAffinity:", prefix);
@@ -1819,12 +1862,26 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_JOURNAL ||
c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE ||
c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || c->std_error == EXEC_OUTPUT_JOURNAL ||
- c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE)
+ c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) {
+ char *fac_str, *lvl_str;
+ int r;
+
+ r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
+ if (r < 0)
+ fac_str = NULL;
+
+ r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
+ if (r < 0)
+ lvl_str = NULL;
+
fprintf(f,
"%sSyslogFacility: %s\n"
"%sSyslogLevel: %s\n",
- prefix, log_facility_unshifted_to_string(c->syslog_priority >> 3),
- prefix, log_level_to_string(LOG_PRI(c->syslog_priority)));
+ prefix, strna(fac_str),
+ prefix, strna(lvl_str));
+ free(lvl_str);
+ free(fac_str);
+ }
if (c->capabilities) {
char *t;
diff --git a/src/core/fdset.c b/src/core/fdset.c
deleted file mode 100644
index fe918cd8cc..0000000000
--- a/src/core/fdset.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2010 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 <errno.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "set.h"
-#include "util.h"
-#include "macro.h"
-#include "fdset.h"
-
-#define MAKE_SET(s) ((Set*) s)
-#define MAKE_FDSET(s) ((FDSet*) s)
-
-/* Make sure we can distuingish fd 0 and NULL */
-#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
-#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
-
-FDSet *fdset_new(void) {
- return MAKE_FDSET(set_new(trivial_hash_func, trivial_compare_func));
-}
-
-void fdset_free(FDSet *s) {
- void *p;
-
- while ((p = set_steal_first(MAKE_SET(s)))) {
- /* Valgrind's fd might have ended up in this set here,
- * due to fdset_new_fill(). We'll ignore all failures
- * here, so that the EBADFD that valgrind will return
- * us on close() doesn't influence us */
-
- /* When reloading duplicates of the private bus
- * connection fds and suchlike are closed here, which
- * has no effect at all, since they are only
- * duplicates. So don't be surprised about these log
- * messages. */
-
- log_debug("Closing left-over fd %i", PTR_TO_FD(p));
- close_nointr(PTR_TO_FD(p));
- }
-
- set_free(MAKE_SET(s));
-}
-
-int fdset_put(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return set_put(MAKE_SET(s), FD_TO_PTR(fd));
-}
-
-int fdset_put_dup(FDSet *s, int fd) {
- int copy, r;
-
- assert(s);
- assert(fd >= 0);
-
- if ((copy = fcntl(fd, F_DUPFD_CLOEXEC, 3)) < 0)
- return -errno;
-
- if ((r = fdset_put(s, copy)) < 0) {
- close_nointr_nofail(copy);
- return r;
- }
-
- return copy;
-}
-
-bool fdset_contains(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return !!set_get(MAKE_SET(s), FD_TO_PTR(fd));
-}
-
-int fdset_remove(FDSet *s, int fd) {
- assert(s);
- assert(fd >= 0);
-
- return set_remove(MAKE_SET(s), FD_TO_PTR(fd)) ? fd : -ENOENT;
-}
-
-int fdset_new_fill(FDSet **_s) {
- DIR *d;
- struct dirent *de;
- int r = 0;
- FDSet *s;
-
- assert(_s);
-
- /* Creates an fdsets and fills in all currently open file
- * descriptors. */
-
- if (!(d = opendir("/proc/self/fd")))
- return -errno;
-
- if (!(s = fdset_new())) {
- r = -ENOMEM;
- goto finish;
- }
-
- while ((de = readdir(d))) {
- int fd = -1;
-
- if (ignore_file(de->d_name))
- continue;
-
- if ((r = safe_atoi(de->d_name, &fd)) < 0)
- goto finish;
-
- if (fd < 3)
- continue;
-
- if (fd == dirfd(d))
- continue;
-
- if ((r = fdset_put(s, fd)) < 0)
- goto finish;
- }
-
- r = 0;
- *_s = s;
- s = NULL;
-
-finish:
- closedir(d);
-
- /* We won't close the fds here! */
- if (s)
- set_free(MAKE_SET(s));
-
- return r;
-}
-
-int fdset_cloexec(FDSet *fds, bool b) {
- Iterator i;
- void *p;
- int r;
-
- assert(fds);
-
- SET_FOREACH(p, MAKE_SET(fds), i)
- if ((r = fd_cloexec(PTR_TO_FD(p), b)) < 0)
- return r;
-
- return 0;
-}
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 25ea09c733..7894f8a5f2 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -30,16 +30,6 @@
#include "util.h"
#include "log.h"
-#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-#define FILENAME "/etc/sysconfig/network"
-#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE)
-#define FILENAME "/etc/HOSTNAME"
-#elif defined(TARGET_ARCH)
-#define FILENAME "/etc/rc.conf"
-#elif defined(TARGET_GENTOO)
-#define FILENAME "/etc/conf.d/hostname"
-#endif
-
static int read_and_strip_hostname(const char *path, char **hn) {
char *s;
int r;
@@ -59,104 +49,23 @@ static int read_and_strip_hostname(const char *path, char **hn) {
}
*hn = s;
-
- return 0;
-}
-
-static int read_distro_hostname(char **hn) {
-
-#if defined(TARGET_FEDORA) || defined(TARGET_ARCH) || defined(TARGET_GENTOO) || defined(TARGET_ALTLINUX) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
- int r;
- FILE *f;
-
- assert(hn);
-
- f = fopen(FILENAME, "re");
- if (!f)
- return -errno;
-
- for (;;) {
- char line[LINE_MAX];
- char *s, *k;
-
- if (!fgets(line, sizeof(line), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- goto finish;
- }
-
- s = strstrip(line);
-
- if (!startswith_no_case(s, "HOSTNAME="))
- continue;
-
- k = strdup(s+9);
- if (!k) {
- r = -ENOMEM;
- goto finish;
- }
-
- hostname_cleanup(k);
-
- if (isempty(k)) {
- free(k);
- r = -ENOENT;
- goto finish;
- }
-
- *hn = k;
- r = 0;
- goto finish;
- }
-
- r = -ENOENT;
-
-finish:
- fclose(f);
- return r;
-
-#elif defined(TARGET_SUSE) || defined(TARGET_SLACKWARE)
- return read_and_strip_hostname(FILENAME, hn);
-#else
- return -ENOENT;
-#endif
-}
-
-static int read_hostname(char **hn) {
- int r;
-
- assert(hn);
-
- /* First, try to load the generic hostname configuration file,
- * that we support on all distributions */
-
- r = read_and_strip_hostname("/etc/hostname", hn);
- if (r < 0) {
- if (r == -ENOENT)
- return read_distro_hostname(hn);
-
- return r;
- }
-
return 0;
}
int hostname_setup(void) {
int r;
- char *b = NULL;
- const char *hn = NULL;
+ _cleanup_free_ char *b = NULL;
+ const char *hn;
bool enoent = false;
- r = read_hostname(&b);
+ r = read_and_strip_hostname("/etc/hostname", &b);
if (r < 0) {
- hn = NULL;
-
if (r == -ENOENT)
enoent = true;
else
log_warning("Failed to read configured hostname: %s", strerror(-r));
+
+ hn = NULL;
} else
hn = b;
@@ -164,7 +73,7 @@ int hostname_setup(void) {
/* Don't override the hostname if it is already set
* and not explicitly configured */
if (hostname_is_set())
- goto finish;
+ return 0;
if (enoent)
log_info("No hostname configured.");
@@ -174,12 +83,9 @@ int hostname_setup(void) {
if (sethostname(hn, strlen(hn)) < 0) {
log_warning("Failed to set hostname to <%s>: %m", hn);
- r = -errno;
- } else
- log_info("Set hostname to <%s>.", hn);
-
-finish:
- free(b);
+ return -errno;
+ }
- return r;
+ log_info("Set hostname to <%s>.", hn);
+ return 0;
}
diff --git a/src/core/job.c b/src/core/job.c
index 8d51aa3fdd..5ff95f5c90 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -24,6 +24,8 @@
#include <sys/timerfd.h>
#include <sys/epoll.h>
+#include "systemd/sd-id128.h"
+#include "systemd/sd-messages.h"
#include "set.h"
#include "unit.h"
#include "macro.h"
@@ -178,15 +180,16 @@ Job* job_install(Job *j) {
if (uj) {
if (j->type != JOB_NOP && job_type_is_conflicting(uj->type, j->type))
- job_finish_and_invalidate(uj, JOB_CANCELED, true);
+ job_finish_and_invalidate(uj, JOB_CANCELED, false);
else {
/* not conflicting, i.e. mergeable */
if (j->type == JOB_NOP || uj->state == JOB_WAITING ||
(job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
job_merge_into_installed(uj, j);
- log_debug("Merged into installed job %s/%s as %u",
- uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+ log_debug_unit(uj->unit->id,
+ "Merged into installed job %s/%s as %u",
+ uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
return uj;
} else {
/* already running and not safe to merge into */
@@ -194,8 +197,9 @@ Job* job_install(Job *j) {
/* XXX It should be safer to queue j to run after uj finishes, but it is
* not currently possible to have more than one installed job per unit. */
job_merge_into_installed(uj, j);
- log_debug("Merged into running job, re-running: %s/%s as %u",
- uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
+ log_debug_unit(uj->unit->id,
+ "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;
return uj;
}
@@ -206,7 +210,9 @@ Job* job_install(Job *j) {
*pj = j;
j->installed = true;
j->manager->n_installed_jobs ++;
- log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+ log_debug_unit(j->unit->id,
+ "Installed new job %s/%s as %u",
+ j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
return j;
}
@@ -223,12 +229,16 @@ int job_install_deserialized(Job *j) {
pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
if (*pj) {
- log_debug("Unit %s already has a job installed. Not installing deserialized job.", j->unit->id);
+ log_debug_unit(j->unit->id,
+ "Unit %s already has a job installed. Not installing deserialized job.",
+ j->unit->id);
return -EEXIST;
}
*pj = j;
j->installed = true;
- log_debug("Reinstalled deserialized job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
+ log_debug_unit(j->unit->id,
+ "Reinstalled deserialized job %s/%s as %u",
+ j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
return 0;
}
@@ -455,9 +465,10 @@ bool job_is_runnable(Job *j) {
}
static void job_change_type(Job *j, JobType newtype) {
- log_debug("Converting job %s/%s -> %s/%s",
- j->unit->id, job_type_to_string(j->type),
- j->unit->id, job_type_to_string(newtype));
+ log_debug_unit(j->unit->id,
+ "Converting job %s/%s -> %s/%s",
+ j->unit->id, job_type_to_string(j->type),
+ j->unit->id, job_type_to_string(newtype));
j->type = newtype;
}
@@ -549,17 +560,74 @@ int job_run_and_invalidate(Job *j) {
return r;
}
-static void job_print_status_message(Unit *u, JobType t, JobResult result) {
+static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
const UnitStatusMessageFormats *format_table;
- const char *format;
+
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
format_table = &UNIT_VTABLE(u)->status_message_formats;
if (!format_table)
- return;
+ return NULL;
+ if (t == JOB_START)
+ return format_table->finished_start_job[result];
+ else if (t == JOB_STOP || t == JOB_RESTART)
+ return format_table->finished_stop_job[result];
+
+ return NULL;
+}
+
+static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
+ const char *format;
+
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ format = job_get_status_message_format(u, t, result);
+ if (format)
+ return format;
+
+ /* Return generic strings */
if (t == JOB_START) {
+ if (result == JOB_DONE)
+ return "Started %s.";
+ else if (result == JOB_FAILED)
+ return "Failed to start %s.";
+ else if (result == JOB_DEPENDENCY)
+ return "Dependency failed for %s.";
+ else if (result == JOB_TIMEOUT)
+ return "Timed out starting %s.";
+ } else if (t == JOB_STOP || t == JOB_RESTART) {
+ if (result == JOB_DONE)
+ return "Stopped %s.";
+ else if (result == JOB_FAILED)
+ return "Stopped (with error) %s.";
+ else if (result == JOB_TIMEOUT)
+ return "Timed out stoppping %s.";
+ } else if (t == JOB_RELOAD) {
+ if (result == JOB_DONE)
+ return "Reloaded %s.";
+ else if (result == JOB_FAILED)
+ return "Reload failed for %s.";
+ else if (result == JOB_TIMEOUT)
+ return "Timed out reloading %s.";
+ }
+
+ return NULL;
+}
- format = format_table->finished_start_job[result];
+static void job_print_status_message(Unit *u, JobType t, JobResult result) {
+ const char *format;
+
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ if (t == JOB_START) {
+ format = job_get_status_message_format(u, t, result);
if (!format)
return;
@@ -572,7 +640,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
case JOB_FAILED:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
- unit_status_printf(u, "", "See 'systemctl status %s' for details.", u->id);
+ unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
break;
case JOB_DEPENDENCY:
@@ -589,7 +657,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
} else if (t == JOB_STOP || t == JOB_RESTART) {
- format = format_table->finished_stop_job[result];
+ format = job_get_status_message_format(u, t, result);
if (!format)
return;
@@ -618,6 +686,58 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
}
}
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+static void job_log_status_message(Unit *u, JobType t, JobResult result) {
+ const char *format;
+ char buf[LINE_MAX];
+
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ /* Skip this if it goes to the console. since we already print
+ * to the console anyway... */
+
+ if (log_on_console())
+ return;
+
+ format = job_get_status_message_format_try_harder(u, t, result);
+ if (!format)
+ return;
+
+ snprintf(buf, sizeof(buf), format, unit_description(u));
+ char_array_0(buf);
+
+ if (t == JOB_START) {
+ 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,
+ 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,
+ 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,
+ MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
+ "UNIT=%s", u->id,
+ "RESULT=%s", job_result_to_string(result),
+ "MESSAGE=%s", buf,
+ NULL);
+}
+#pragma GCC diagnostic pop
+
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
Unit *u;
Unit *other;
@@ -633,9 +753,11 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
j->result = result;
- log_debug("Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
+ log_debug_unit(u->id, "Job %s/%s finished, result=%s",
+ u->id, job_type_to_string(t), job_result_to_string(result));
job_print_status_message(u, t, result);
+ job_log_status_message(u, t, result);
job_add_to_dbus_queue(j);
@@ -692,14 +814,19 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
}
/* Trigger OnFailure dependencies that are not generated by
- * the unit itself. We don't tread JOB_CANCELED as failure in
+ * the unit itself. We don't treat JOB_CANCELED as failure in
* this context. And JOB_FAILURE is already handled by the
* unit itself. */
if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
- log_notice("Job %s/%s failed with result '%s'.",
+ log_struct(LOG_NOTICE,
+ "UNIT=%s", u->id,
+ "JOB_TYPE=%s", job_type_to_string(t),
+ "JOB_RESULT=%s", job_result_to_string(t),
+ "Job %s/%s failed with result '%s'.",
u->id,
job_type_to_string(t),
- job_result_to_string(result));
+ job_result_to_string(result),
+ NULL);
unit_trigger_on_failure(u);
}
@@ -806,7 +933,8 @@ void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) {
assert(j);
assert(w == &j->timer_watch);
- log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
+ log_warning_unit(j->unit->id, "Job %s/%s timed out.",
+ j->unit->id, job_type_to_string(j->type));
job_finish_and_invalidate(j, JOB_TIMEOUT, true);
}
diff --git a/src/core/job.h b/src/core/job.h
index 349fb687cf..3aa49d4b46 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -91,12 +91,12 @@ enum JobMode {
};
enum JobResult {
- JOB_DONE,
- JOB_CANCELED,
- JOB_TIMEOUT,
- JOB_FAILED,
- JOB_DEPENDENCY,
- JOB_SKIPPED,
+ JOB_DONE, /* Job completed successfully */
+ JOB_CANCELED, /* Job canceled by a conflicting job installation or by explicit cancel request */
+ JOB_TIMEOUT, /* JobTimeout elapsed */
+ JOB_FAILED, /* Job failed */
+ JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */
+ JOB_SKIPPED, /* JOB_RELOAD of inactive unit; negative result of JOB_VERIFY_ACTIVE */
_JOB_RESULT_MAX,
_JOB_RESULT_INVALID = -1
};
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index cc2a2d94b2..20ab232374 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -30,10 +30,16 @@
#include "kmod-setup.h"
-static const char * const kmod_table[] = {
- "autofs4", "/sys/class/misc/autofs",
- "ipv6", "/sys/module/ipv6",
- "unix", "/proc/net/unix"
+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
@@ -41,7 +47,8 @@ static const char * const kmod_table[] = {
static void systemd_kmod_log(void *data, int priority, const char *file, int line,
const char *fn, const char *format, va_list args)
{
- log_metav(priority, file, line, fn, format, args);
+ /* library logging is enabled at debug only */
+ log_metav(LOG_DEBUG, file, line, fn, format, args);
}
#pragma GCC diagnostic pop
@@ -52,13 +59,15 @@ int kmod_setup(void) {
int err;
for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) {
+ if (kmod_table[i].condition_fn && !kmod_table[i].condition_fn())
+ continue;
- if (access(kmod_table[i+1], F_OK) >= 0)
+ if (access(kmod_table[i].directory, 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]);
+ kmod_table[i].name);
if (!ctx) {
ctx = kmod_new(NULL, NULL);
@@ -68,13 +77,12 @@ int kmod_setup(void) {
}
kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
-
kmod_load_resources(ctx);
}
- err = kmod_module_new_from_name(ctx, kmod_table[i], &mod);
+ err = kmod_module_new_from_name(ctx, kmod_table[i].name, &mod);
if (err < 0) {
- log_error("Failed to load module '%s'", kmod_table[i]);
+ log_error("Failed to lookup module '%s'", kmod_table[i].name);
continue;
}
@@ -84,7 +92,7 @@ int kmod_setup(void) {
else if (err == KMOD_PROBE_APPLY_BLACKLIST)
log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
else
- log_error("Failed to insert '%s'", kmod_module_get_name(mod));
+ log_error("Failed to insert module '%s'", kmod_module_get_name(mod));
kmod_module_unref(mod);
}
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 2b1cfa073c..7fba0cfb77 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -130,11 +130,14 @@ Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_P
Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, 0
Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, 0
Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, 0
+Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, 0
Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, 0
Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, 0
Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, 0
Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, 0
Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, 0
+Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, 0
+Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, 0
Unit.ConditionNull, config_parse_unit_condition_null, 0, 0
m4_dnl
Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file)
@@ -145,7 +148,9 @@ Service.ExecReload, config_parse_exec, SERVICE_EXE
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.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_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.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst)
@@ -156,6 +161,8 @@ Service.PermissionsStartOnly, config_parse_bool, 0,
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit)
Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid)
+Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_ignore_status)
+Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status)
m4_ifdef(`HAVE_SYSV_COMPAT',
`Service.SysVStartPriority, config_parse_sysv_priority, 0, offsetof(Service, sysv_start_priority)',
`Service.SysVStartPriority, config_parse_warn_compat, 0, 0')
@@ -203,6 +210,9 @@ Socket.TCPCongestion, config_parse_string, 0,
Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg)
Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize)
Socket.Service, config_parse_socket_service, 0, 0
+Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack)
+Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in)
+Socket.SmackLabelIPOut, config_parse_string, 0, offsetof(Socket, smack_ip_out)
EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
m4_dnl
@@ -225,6 +235,7 @@ Swap.TimeoutSec, config_parse_usec, 0,
EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
m4_dnl
+Timer.OnCalendar, config_parse_timer, 0, 0
Timer.OnActiveSec, config_parse_timer, 0, 0
Timer.OnBootSec, config_parse_timer, 0, 0
Timer.OnStartupSec, config_parse_timer, 0, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index bbd82b9d24..e35fdbc5ec 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -4,6 +4,7 @@
This file is part of systemd.
Copyright 2010 Lennart Poettering
+ Copyright 2012 Holger Hans Peter Freyther
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
@@ -42,6 +43,7 @@
#include "securebits.h"
#include "missing.h"
#include "unit-name.h"
+#include "unit-printf.h"
#include "bus-errors.h"
#include "utf8.h"
#include "path-util.h"
@@ -84,7 +86,7 @@ int config_parse_unit_deps(
assert(rvalue);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t, *k;
+ char _cleanup_free_ *t = NULL, *k = NULL;
int r;
t = strndup(w, l);
@@ -92,15 +94,13 @@ int config_parse_unit_deps(
return -ENOMEM;
k = unit_name_printf(u, t);
- free(t);
if (!k)
return -ENOMEM;
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", filename, line, k, strerror(-r));
-
- free(k);
+ log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s",
+ filename, line, k, strerror(-r));
}
return 0;
@@ -185,7 +185,7 @@ int config_parse_unit_path_printf(
k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
+ return log_oom();
r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
free(k);
@@ -325,10 +325,12 @@ int config_parse_socket_bind(
s = SOCKET(data);
- if ((b = socket_address_bind_ipv6_only_from_string(rvalue)) < 0) {
+ b = socket_address_bind_ipv6_only_from_string(rvalue);
+ if (b < 0) {
int r;
- if ((r = parse_boolean(rvalue)) < 0) {
+ r = parse_boolean(rvalue);
+ if (r < 0) {
log_error("[%s:%u] Failed to parse bind IPv6 only value, ignoring: %s", filename, line, rvalue);
return 0;
}
@@ -435,6 +437,7 @@ int config_parse_exec(
e += ltype;
for (;;) {
+ int i;
char *w;
size_t l;
char *state;
@@ -449,18 +452,21 @@ int config_parse_exec(
if (rvalue[0] == 0)
break;
- if (rvalue[0] == '-') {
- ignore = true;
- rvalue ++;
- }
+ for (i = 0; i < 2; i++) {
+ if (rvalue[0] == '-' && !ignore) {
+ ignore = true;
+ rvalue ++;
+ }
- if (rvalue[0] == '@') {
- honour_argv0 = true;
- rvalue ++;
+ if (rvalue[0] == '@' && !honour_argv0) {
+ honour_argv0 = true;
+ rvalue ++;
+ }
}
if (*rvalue != '/') {
- log_error("[%s:%u] Invalid executable path in command line, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Executable path is not absolute, ignoring: %s",
+ filename, line, rvalue);
return 0;
}
@@ -480,6 +486,8 @@ int config_parse_exec(
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
if (strncmp(w, ";", MAX(l, 1U)) == 0)
break;
+ else if (strncmp(w, "\\;", MAX(l, 1U)) == 0)
+ w ++;
if (honour_argv0 && w == rvalue) {
assert(!path);
@@ -613,7 +621,8 @@ int config_parse_exec_io_class(
assert(rvalue);
assert(data);
- if ((x = ioprio_class_from_string(rvalue)) < 0) {
+ x = ioprio_class_from_string(rvalue);
+ if (x < 0) {
log_error("[%s:%u] Failed to parse IO scheduling class, ignoring: %s", filename, line, rvalue);
return 0;
}
@@ -672,12 +681,15 @@ int config_parse_exec_cpu_sched_policy(
assert(rvalue);
assert(data);
- if ((x = sched_policy_from_string(rvalue)) < 0) {
+ x = sched_policy_from_string(rvalue);
+ if (x < 0) {
log_error("[%s:%u] Failed to parse CPU scheduling policy, ignoring: %s", filename, line, rvalue);
return 0;
}
c->cpu_sched_policy = x;
+ /* Moving to or from real-time policy? We need to adjust the priority */
+ c->cpu_sched_priority = CLAMP(c->cpu_sched_priority, sched_get_priority_min(x), sched_get_priority_max(x));
c->cpu_sched_set = true;
return 0;
@@ -694,19 +706,28 @@ int config_parse_exec_cpu_sched_prio(
void *userdata) {
ExecContext *c = data;
- int i;
+ int i, min, max;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- /* On Linux RR/FIFO have the same range */
- if (safe_atoi(rvalue, &i) < 0 || i < sched_get_priority_min(SCHED_RR) || i > sched_get_priority_max(SCHED_RR)) {
+ if (safe_atoi(rvalue, &i) < 0) {
log_error("[%s:%u] Failed to parse CPU scheduling priority, ignoring: %s", filename, line, 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);
+ return 0;
+ }
+
c->cpu_sched_priority = i;
c->cpu_sched_set = true;
@@ -734,22 +755,25 @@ int config_parse_exec_cpu_affinity(
assert(data);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t;
+ char _cleanup_free_ *t = NULL;
int r;
unsigned cpu;
- if (!(t = strndup(w, l)))
+ t = strndup(w, l);
+ if (!t)
return -ENOMEM;
r = safe_atou(t, &cpu);
- free(t);
- if (!(c->cpuset))
- if (!(c->cpuset = cpu_set_malloc(&c->cpuset_ncpus)))
+ if (!c->cpuset) {
+ c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
+ if (!c->cpuset)
return -ENOMEM;
+ }
if (r < 0 || cpu >= c->cpuset_ncpus) {
- log_error("[%s:%u] Failed to parse CPU affinity, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse CPU affinity %s, ignoring: %s",
+ filename, line, t, rvalue);
return 0;
}
@@ -826,7 +850,8 @@ int config_parse_exec_secure_bits(
else if (first_word(w, "noroot-locked"))
c->secure_bits |= SECURE_NOROOT_LOCKED;
else {
- log_error("[%s:%u] Failed to parse secure bits, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse secure bits, ignoring: %s",
+ filename, line, rvalue);
return 0;
}
}
@@ -867,7 +892,7 @@ int config_parse_bounding_set(
* interface. */
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t;
+ char _cleanup_free_ *t = NULL;
int r;
cap_value_t cap;
@@ -876,10 +901,9 @@ int config_parse_bounding_set(
return -ENOMEM;
r = cap_from_name(t, &cap);
- free(t);
-
if (r < 0) {
- log_error("[%s:%u] Failed to parse capability bounding set, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse capability in bounding set, ignoring: %s",
+ filename, line, t);
continue;
}
@@ -945,7 +969,7 @@ int config_parse_unit_cgroup(
char *state;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t, *k;
+ char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL;
int r;
t = strndup(w, l);
@@ -953,22 +977,17 @@ int config_parse_unit_cgroup(
return -ENOMEM;
k = unit_full_printf(u, t);
- free(t);
-
if (!k)
return -ENOMEM;
- t = cunescape(k);
- free(k);
-
- if (!t)
+ ku = cunescape(k);
+ if (!ku)
return -ENOMEM;
- r = unit_add_cgroup_from_text(u, t);
- free(t);
-
+ r = unit_add_cgroup_from_text(u, ku);
if (r < 0) {
- log_error("[%s:%u] Failed to parse cgroup value, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse cgroup value %s, ignoring: %s",
+ filename, line, k, rvalue);
return 0;
}
}
@@ -1082,15 +1101,22 @@ int config_parse_exec_mount_flags(
assert(rvalue);
assert(data);
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- if (strncmp(w, "shared", MAX(l, 6U)) == 0)
+ FOREACH_WORD_SEPARATOR(w, l, rvalue, ", ", state) {
+ char _cleanup_free_ *t;
+
+ t = strndup(w, l);
+ if (!t)
+ return -ENOMEM;
+
+ if (streq(t, "shared"))
flags |= MS_SHARED;
- else if (strncmp(w, "slave", MAX(l, 5U)) == 0)
+ else if (streq(t, "slave"))
flags |= MS_SLAVE;
- else if (strncmp(w, "private", MAX(l, 7U)) == 0)
+ else if (streq(w, "private"))
flags |= MS_PRIVATE;
else {
- log_error("[%s:%u] Failed to parse mount flags, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse mount flag %s, ignoring: %s",
+ filename, line, t, rvalue);
return 0;
}
}
@@ -1110,30 +1136,47 @@ int config_parse_timer(
void *userdata) {
Timer *t = data;
- usec_t u;
+ usec_t u = 0;
TimerValue *v;
TimerBase b;
+ CalendarSpec *c = NULL;
+ clockid_t id;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((b = timer_base_from_string(lvalue)) < 0) {
+ b = timer_base_from_string(lvalue);
+ if (b < 0) {
log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
return 0;
}
- if (parse_usec(rvalue, &u) < 0) {
- log_error("[%s:%u] Failed to parse timer value, ignoring: %s", filename, line, rvalue);
- 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);
+ 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);
+ return 0;
+ }
+
+ id = CLOCK_MONOTONIC;
}
- if (!(v = new0(TimerValue, 1)))
+ v = new0(TimerValue, 1);
+ if (!v)
return -ENOMEM;
v->base = b;
+ v->clock_id = id;
v->value = u;
+ v->calendar_spec = c;
LIST_PREPEND(TimerValue, value, t->values, v);
@@ -1192,32 +1235,36 @@ int config_parse_path_spec(
Path *p = data;
PathSpec *s;
PathType b;
+ char *k;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((b = path_type_from_string(lvalue)) < 0) {
+ b = path_type_from_string(lvalue);
+ if (b < 0) {
log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
return 0;
}
- if (!path_is_absolute(rvalue)) {
- log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, rvalue);
+ k = unit_full_printf(UNIT(p), rvalue);
+ if (!k)
+ return log_oom();
+
+ if (!path_is_absolute(k)) {
+ log_error("[%s:%u] Path is not absolute, ignoring: %s", filename, line, k);
+ free(k);
return 0;
}
- if (!(s = new0(PathSpec, 1)))
- return -ENOMEM;
-
- if (!(s->path = strdup(rvalue))) {
- free(s);
- return -ENOMEM;
+ s = new0(PathSpec, 1);
+ if (!s) {
+ free(k);
+ return log_oom();
}
- path_kill_slashes(s->path);
-
+ s->path = path_kill_slashes(k);
s->type = b;
s->inotify_fd = -1;
@@ -1324,33 +1371,30 @@ int config_parse_service_sockets(
assert(data);
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *t, *k;
+ char _cleanup_free_ *t = NULL, *k = NULL;
t = strndup(w, l);
if (!t)
return -ENOMEM;
k = unit_name_printf(UNIT(s), t);
- free(t);
-
if (!k)
return -ENOMEM;
if (!endswith(k, ".socket")) {
- log_error("[%s:%u] Unit must be of type socket, ignoring: %s", filename, line, rvalue);
- free(k);
+ log_error("[%s:%u] Unit must be of type socket, ignoring: %s",
+ filename, line, 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_error("[%s:%u] Failed to add dependency on %s, ignoring: %s",
+ filename, line, k, strerror(-r));
r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
if (r < 0)
return r;
-
- free(k);
}
return 0;
@@ -1376,10 +1420,16 @@ int config_parse_service_timeout(
r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata);
- if (!r)
- s->timeout_defined = true;
+ if (r)
+ return r;
- return r;
+ if (streq(lvalue, "TimeoutSec")) {
+ s->start_timeout_defined = true;
+ s->timeout_stop_usec = s->timeout_start_usec;
+ } else if (streq(lvalue, "TimeoutStartSec"))
+ s->start_timeout_defined = true;
+
+ return 0;
}
int config_parse_unit_env_file(
@@ -1439,11 +1489,11 @@ int config_parse_ip_tos(
assert(rvalue);
assert(data);
- if ((x = ip_tos_from_string(rvalue)) < 0)
- if (safe_atoi(rvalue, &x) < 0) {
- log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
+ x = ip_tos_from_string(rvalue);
+ if (x < 0) {
+ log_error("[%s:%u] Failed to parse IP TOS value, ignoring: %s", filename, line, rvalue);
+ return 0;
+ }
*ip_tos = x;
return 0;
@@ -1463,6 +1513,7 @@ int config_parse_unit_condition_path(
Unit *u = data;
bool trigger, negate;
Condition *c;
+ _cleanup_free_ char *p = NULL;
assert(filename);
assert(lvalue);
@@ -1477,12 +1528,16 @@ int config_parse_unit_condition_path(
if (negate)
rvalue++;
- if (!path_is_absolute(rvalue)) {
- log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, rvalue);
+ p = unit_full_printf(u, rvalue);
+ if (!p)
+ return -ENOMEM;
+
+ if (!path_is_absolute(p)) {
+ log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, p);
return 0;
}
- c = condition_new(cond, rvalue, trigger, negate);
+ c = condition_new(cond, p, trigger, negate);
if (!c)
return -ENOMEM;
@@ -1504,21 +1559,29 @@ int config_parse_unit_condition_string(
Unit *u = data;
bool trigger, negate;
Condition *c;
+ _cleanup_free_ char *s = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if ((trigger = rvalue[0] == '|'))
+ trigger = rvalue[0] == '|';
+ if (trigger)
rvalue++;
- if ((negate = rvalue[0] == '!'))
+ negate = rvalue[0] == '!';
+ if (negate)
rvalue++;
- if (!(c = condition_new(cond, rvalue, trigger, negate)))
+ s = unit_full_printf(u, rvalue);
+ if (!s)
return -ENOMEM;
+ c = condition_new(cond, s, trigger, negate);
+ if (!c)
+ return log_oom();
+
LIST_PREPEND(Condition, conditions, u->conditions, c);
return 0;
}
@@ -2022,7 +2085,7 @@ int config_parse_syscall_filter(
ExecContext *c = data;
Unit *u = userdata;
- bool invert;
+ bool invert = false;
char *w;
size_t l;
char *state;
@@ -2059,17 +2122,17 @@ int config_parse_syscall_filter(
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
int id;
- char *t;
+ char _cleanup_free_ *t = NULL;
t = strndup(w, l);
if (!t)
return -ENOMEM;
id = syscall_from_name(t);
- free(t);
if (id < 0) {
- log_error("[%s:%u] Failed to parse syscall, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse syscall, ignoring: %s",
+ filename, line, t);
continue;
}
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index 25eb13474a..48b59bf448 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -72,166 +72,61 @@ int locale_setup(void) {
zero(variables);
- if (detect_container(NULL) <= 0)
- if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
-#if defined(TARGET_FEDORA)
- "LANG", &variables[VARIABLE_LANG],
-#endif
- "locale.LANG", &variables[VARIABLE_LANG],
- "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
- "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
- "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
- "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
- }
+ if (detect_container(NULL) <= 0) {
+ r = parse_env_file("/proc/cmdline", WHITESPACE,
+ "locale.LANG", &variables[VARIABLE_LANG],
+ "locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
+ "locale.LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "locale.LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "locale.LC_TIME", &variables[VARIABLE_LC_TIME],
+ "locale.LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "locale.LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "locale.LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "locale.LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "locale.LC_NAME", &variables[VARIABLE_LC_NAME],
+ "locale.LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "locale.LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "locale.LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "locale.LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+ NULL);
+
+ if (r < 0 && r != -ENOENT)
+ log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
+ }
/* Hmm, nothing set on the kernel cmd line? Then let's
* try /etc/locale.conf */
- if (r <= 0 &&
- (r = parse_env_file("/etc/locale.conf", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- "LANGUAGE", &variables[VARIABLE_LANGUAGE],
- "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "LC_TIME", &variables[VARIABLE_LC_TIME],
- "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "LC_NAME", &variables[VARIABLE_LC_NAME],
- "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
+ if (r <= 0) {
+ r = parse_env_file("/etc/locale.conf", NEWLINE,
+ "LANG", &variables[VARIABLE_LANG],
+ "LANGUAGE", &variables[VARIABLE_LANGUAGE],
+ "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
+ "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
+ "LC_TIME", &variables[VARIABLE_LC_TIME],
+ "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
+ "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
+ "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
+ "LC_PAPER", &variables[VARIABLE_LC_PAPER],
+ "LC_NAME", &variables[VARIABLE_LC_NAME],
+ "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
+ "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
+ "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
+ "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
+ NULL);
+
+ if (r < 0 && r != -ENOENT)
log_warning("Failed to read /etc/locale.conf: %s", strerror(-r));
}
-#if defined(TARGET_FEDORA) || defined(TARGET_ALTLINUX)
- if (r <= 0 &&
- (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
- }
-
-#elif defined(TARGET_SUSE)
- if (r <= 0 &&
- (r = parse_env_file("/etc/sysconfig/language", NEWLINE,
- "RC_LANG", &variables[VARIABLE_LANG],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/sysconfig/language: %s", strerror(-r));
- }
-
-#elif defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- if (r <= 0 &&
- (r = parse_env_file("/etc/default/locale", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "LC_TIME", &variables[VARIABLE_LC_TIME],
- "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "LC_NAME", &variables[VARIABLE_LC_NAME],
- "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/default/locale: %s", strerror(-r));
- }
-
-#elif defined(TARGET_ARCH)
- if (r <= 0 &&
- (r = parse_env_file("/etc/rc.conf", NEWLINE,
- "LOCALE", &variables[VARIABLE_LANG],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
- }
-
-#elif defined(TARGET_GENTOO)
- /* Gentoo's openrc expects locale variables in /etc/env.d/
- * These files are later compiled by env-update into shell
- * export commands at /etc/profile.env, with variables being
- * exported by openrc's runscript (so /etc/init.d/)
- */
- if (r <= 0 &&
- (r = parse_env_file("/etc/profile.env", NEWLINE,
- "export LANG", &variables[VARIABLE_LANG],
- "export LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "export LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "export LC_TIME", &variables[VARIABLE_LC_TIME],
- "export LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "export LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "export LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "export LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "export LC_NAME", &variables[VARIABLE_LC_NAME],
- "export LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "export LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "export LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "export LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/profile.env: %s", strerror(-r));
- }
-#elif defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA )
- if (r <= 0 &&
- (r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
- "LANG", &variables[VARIABLE_LANG],
- "LC_CTYPE", &variables[VARIABLE_LC_CTYPE],
- "LC_NUMERIC", &variables[VARIABLE_LC_NUMERIC],
- "LC_TIME", &variables[VARIABLE_LC_TIME],
- "LC_COLLATE", &variables[VARIABLE_LC_COLLATE],
- "LC_MONETARY", &variables[VARIABLE_LC_MONETARY],
- "LC_MESSAGES", &variables[VARIABLE_LC_MESSAGES],
- "LC_PAPER", &variables[VARIABLE_LC_PAPER],
- "LC_NAME", &variables[VARIABLE_LC_NAME],
- "LC_ADDRESS", &variables[VARIABLE_LC_ADDRESS],
- "LC_TELEPHONE", &variables[VARIABLE_LC_TELEPHONE],
- "LC_MEASUREMENT", &variables[VARIABLE_LC_MEASUREMENT],
- "LC_IDENTIFICATION", &variables[VARIABLE_LC_IDENTIFICATION],
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
- }
-
-#endif
-
if (!variables[VARIABLE_LANG]) {
- if (!(variables[VARIABLE_LANG] = strdup("C"))) {
+ variables[VARIABLE_LANG] = strdup("C");
+ if (!variables[VARIABLE_LANG]) {
r = -ENOMEM;
goto finish;
}
}
for (i = 0; i < _VARIABLE_MAX; i++) {
-
if (variables[i]) {
if (setenv(variable_names[i], variables[i], 1) < 0) {
r = -errno;
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index c6fd77ac8d..7f4c23b130 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -226,13 +226,17 @@ int machine_id_setup(void) {
}
/* And now, let's mount it over */
- r = mount("/run/machine-id", "/etc/machine-id", "bind", MS_BIND|MS_RDONLY, NULL) < 0 ? -errno : 0;
+ r = mount("/run/machine-id", "/etc/machine-id", NULL, MS_BIND, NULL) < 0 ? -errno : 0;
if (r < 0) {
unlink("/run/machine-id");
log_error("Failed to mount /etc/machine-id: %s", strerror(-r));
- } else
+ } 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);
+ }
+
finish:
if (fd >= 0)
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index 86e869307c..647cce6913 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -20,6 +20,12 @@
# RPM macros for packages installing systemd unit files
%_unitdir @systemunitdir@
+%_presetdir @systempresetdir@
+%_udevhwdbdir @udevhwdbdir@
+%_udevrulesdir @udevrulesdir@
+%_journalcatalogdir @catalogdir@
+%_tmpfilesdir @tmpfilesdir@
+%_sysctldir @sysctldir@
%systemd_requires \
Requires(post): systemd \
@@ -53,3 +59,15 @@ if [ $1 -ge 1 ] ; then \
@rootbindir@/systemctl try-restart %{?*} >/dev/null 2>&1 || : \
fi \
%{nil}
+
+%udev_hwdb_update() \
+@bindir@/udevadm hwdb --update >/dev/null 2>&1 || : \
+%{nil}
+
+%udev_rules_update() \
+@bindir@/udevadm control --reload >/dev/null 2>&1 || : \
+%{nil}
+
+%journal_catalog_update() \
+@rootbindir@/journalctl --update-catalog >/dev/null 2>&1 || : \
+%{nil}
diff --git a/src/core/main.c b/src/core/main.c
index 1326b94f68..1ee3c9c0e8 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -55,13 +55,16 @@
#include "mount-setup.h"
#include "loopback-setup.h"
+#ifdef HAVE_KMOD
#include "kmod-setup.h"
+#endif
#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"
static enum {
ACTION_RUN,
@@ -73,7 +76,7 @@ static enum {
} arg_action = ACTION_RUN;
static char *arg_default_unit = NULL;
-static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
+static SystemdRunningAs arg_running_as = _SYSTEMD_RUNNING_AS_INVALID;
static bool arg_dump_core = true;
static bool arg_crash_shell = false;
@@ -168,12 +171,12 @@ _noreturn_ static void crash(int sig) {
pid = fork();
if (pid < 0)
- log_error("Failed to fork off crash shell: %s", strerror(errno));
+ log_error("Failed to fork off crash shell: %m");
else if (pid == 0) {
make_console_stdio();
execl("/bin/sh", "/bin/sh", NULL);
- log_error("execl() failed: %s", strerror(errno));
+ log_error("execl() failed: %m");
_exit(1);
}
@@ -350,12 +353,12 @@ static int parse_proc_cmdline_word(const char *word) {
if (!eq) {
r = unsetenv(cenv);
if (r < 0)
- log_warning("unsetenv failed %s. Ignoring.", strerror(errno));
+ log_warning("unsetenv failed %m. Ignoring.");
} else {
*eq = 0;
r = setenv(cenv, eq + 1, 1);
if (r < 0)
- log_warning("setenv failed %s. Ignoring.", strerror(errno));
+ log_warning("setenv failed %m. Ignoring.");
}
free(cenv);
@@ -495,14 +498,14 @@ static int config_parse_cpu_affinity2(
unsigned cpu;
if (!(t = strndup(w, l)))
- return -ENOMEM;
+ return log_oom();
r = safe_atou(t, &cpu);
free(t);
if (!c)
if (!(c = cpu_set_malloc(&ncpus)))
- return -ENOMEM;
+ return log_oom();
if (r < 0 || cpu >= ncpus) {
log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
@@ -568,7 +571,7 @@ static int config_parse_join_controllers(
s = strndup(w, length);
if (!s)
- return -ENOMEM;
+ return log_oom();
l = strv_split(s, ",");
free(s);
@@ -584,7 +587,7 @@ static int config_parse_join_controllers(
arg_join_controllers = new(char**, 2);
if (!arg_join_controllers) {
strv_free(l);
- return -ENOMEM;
+ return log_oom();
}
arg_join_controllers[0] = l;
@@ -598,7 +601,7 @@ static int config_parse_join_controllers(
t = new0(char**, n+2);
if (!t) {
strv_free(l);
- return -ENOMEM;
+ return log_oom();
}
n = 0;
@@ -612,7 +615,7 @@ static int config_parse_join_controllers(
if (!c) {
strv_free(l);
strv_free_free(t);
- return -ENOMEM;
+ return log_oom();
}
strv_free(l);
@@ -624,7 +627,7 @@ static int config_parse_join_controllers(
if (!c) {
strv_free(l);
strv_free_free(t);
- return -ENOMEM;
+ return log_oom();
}
t[n++] = c;
@@ -684,7 +687,7 @@ static int parse_config_file(void) {
const char *fn;
int r;
- fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
+ fn = arg_running_as == SYSTEMD_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
@@ -727,10 +730,13 @@ static int parse_proc_cmdline(void) {
}
r = parse_proc_cmdline_word(word);
- free(word);
-
- if (r < 0)
+ if (r < 0) {
+ log_error("Failed on cmdline argument %s: %s", word, strerror(-r));
+ free(word);
goto finish;
+ }
+
+ free(word);
}
r = 0;
@@ -869,11 +875,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_SYSTEM:
- arg_running_as = MANAGER_SYSTEM;
+ arg_running_as = SYSTEMD_SYSTEM;
break;
case ARG_USER:
- arg_running_as = MANAGER_USER;
+ arg_running_as = SYSTEMD_USER;
break;
case ARG_TEST:
@@ -928,14 +934,18 @@ static int parse_argv(int argc, char *argv[]) {
int fd;
FILE *f;
- if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) {
+ r = safe_atoi(optarg, &fd);
+ if (r < 0 || fd < 0) {
log_error("Failed to parse deserialize option %s.", optarg);
- return r;
+ return r < 0 ? r : -EINVAL;
}
- if (!(f = fdopen(fd, "r"))) {
+ fd_cloexec(fd, true);
+
+ f = fdopen(fd, "r");
+ if (!f) {
log_error("Failed to open serialization fd: %m");
- return r;
+ return -errno;
}
if (serialization)
@@ -1017,8 +1027,10 @@ static int parse_argv(int argc, char *argv[]) {
* instead. */
for (a = argv; a < argv + argc; a++)
- if ((r = parse_proc_cmdline_word(*a)) < 0)
+ if ((r = parse_proc_cmdline_word(*a)) < 0) {
+ log_error("Failed on cmdline argument %s: %s", *a, strerror(-r));
return r;
+ }
}
return 0;
@@ -1052,7 +1064,6 @@ static int help(void) {
static int version(void) {
puts(PACKAGE_STRING);
- puts(DISTRIBUTION);
puts(SYSTEMD_FEATURES);
return 0;
@@ -1120,6 +1131,42 @@ fail:
return r;
}
+static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
+ struct rlimit nl;
+ int r;
+
+ assert(saved_rlimit);
+
+ /* Save the original RLIMIT_NOFILE so that we can reset it
+ * later when transitioning from the initrd to the main
+ * systemd or suchlike. */
+ if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) {
+ log_error("Reading RLIMIT_NOFILE failed: %m");
+ return -errno;
+ }
+
+ /* Make sure forked processes get the default kernel setting */
+ if (!arg_default_rlimit[RLIMIT_NOFILE]) {
+ struct rlimit *rl;
+
+ rl = newdup(struct rlimit, saved_rlimit, 1);
+ if (!rl)
+ return log_oom();
+
+ arg_default_rlimit[RLIMIT_NOFILE] = rl;
+ }
+
+ /* Bump up the resource limit for ourselves substantially */
+ nl.rlim_cur = nl.rlim_max = 64*1024;
+ r = setrlimit_closest(RLIMIT_NOFILE, &nl);
+ if (r < 0) {
+ log_error("Setting RLIMIT_NOFILE failed: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}
+
static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) {
const char *e;
unsigned long long a, b;
@@ -1186,6 +1233,28 @@ static void test_cgroups(void) {
sleep(10);
}
+static int initialize_join_controllers(void) {
+ /* By default, mount "cpu" + "cpuacct" together, and "net_cls"
+ * + "net_prio". We'd like to add "cpuset" to the mix, but
+ * "cpuset" does't really work for groups with no initialized
+ * attributes. */
+
+ arg_join_controllers = new(char**, 3);
+ if (!arg_join_controllers)
+ 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])
+ return -ENOMEM;
+
+ arg_join_controllers[2] = NULL;
+ return 0;
+}
+
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r, retval = EXIT_FAILURE;
@@ -1202,6 +1271,7 @@ int main(int argc, char *argv[]) {
bool arm_reboot_watchdog = false;
bool queue_default_job = false;
char *switch_root_dir = NULL, *switch_root_init = NULL;
+ static struct rlimit saved_rlimit_nofile = { 0, 0 };
#ifdef HAVE_SYSV_COMPAT
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
@@ -1243,10 +1313,15 @@ int main(int argc, char *argv[]) {
saved_argc = argc;
log_show_color(isatty(STDERR_FILENO) > 0);
- log_show_location(false);
- log_set_max_level(LOG_INFO);
- if (getpid() == 1) {
+ if (getpid() == 1 && detect_container(NULL) <= 0) {
+
+ /* Running outside of a container as PID 1 */
+ arg_running_as = SYSTEMD_SYSTEM;
+ make_null_stdio();
+ log_set_target(LOG_TARGET_KMSG);
+ log_open();
+
if (in_initrd()) {
char *rd_timestamp = NULL;
@@ -1260,9 +1335,6 @@ int main(int argc, char *argv[]) {
}
}
- arg_running_as = MANAGER_SYSTEM;
- log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_JOURNAL : LOG_TARGET_JOURNAL_OR_KMSG);
-
if (!skip_setup) {
if (selinux_setup(&loaded_policy) < 0)
goto finish;
@@ -1270,41 +1342,72 @@ int main(int argc, char *argv[]) {
goto finish;
}
- log_open();
-
if (label_init(NULL) < 0)
goto finish;
- if (!skip_setup)
+ if (!skip_setup) {
if (hwclock_is_localtime() > 0) {
int min;
- r = hwclock_apply_localtime_delta(&min);
+ /* The first-time call to settimeofday() does a time warp in the kernel */
+ r = hwclock_set_timezone(&min);
if (r < 0)
log_error("Failed to apply local time delta, ignoring: %s", strerror(-r));
else
log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min);
+ } else if (!in_initrd()) {
+ /*
+ * Do dummy first-time call to seal the kernel's time warp magic
+ *
+ * Do not call this this from inside the initrd. The initrd might not
+ * carry /etc/adjtime with LOCAL, but the real system could be set up
+ * that way. In such case, we need to delay the time-warp or the sealing
+ * until we reach the real system.
+ */
+ hwclock_reset_timezone();
+
+ /* Tell the kernel our time zone */
+ r = hwclock_set_timezone(NULL);
+ if (r < 0)
+ log_error("Failed to set the kernel's time zone, ignoring: %s", strerror(-r));
}
+ }
+
+ /* Set the default for later on, but don't actually
+ * open the logs like this for now. Note that if we
+ * are transitioning from the initrd there might still
+ * be journal fd open, and we shouldn't attempt
+ * opening that before we parsed /proc/cmdline which
+ * might redirect output elsewhere. */
+ 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);
+ log_open();
+
+ /* For the later on, see above... */
+ log_set_target(LOG_TARGET_JOURNAL);
} else {
- arg_running_as = MANAGER_USER;
+
+ /* Running as user instance */
+ arg_running_as = SYSTEMD_USER;
log_set_target(LOG_TARGET_AUTO);
log_open();
}
/* Initialize default unit */
- if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0)
- goto finish;
-
- /* By default, mount "cpu" and "cpuacct" together */
- arg_join_controllers = new(char**, 2);
- if (!arg_join_controllers)
+ r = set_default_unit(SPECIAL_DEFAULT_TARGET);
+ if (r < 0) {
+ log_error("Failed to set default unit %s: %s", SPECIAL_DEFAULT_TARGET, strerror(-r));
goto finish;
+ }
- arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
- arg_join_controllers[1] = NULL;
-
- if (!arg_join_controllers[0])
+ r = initialize_join_controllers();
+ if (r < 0)
goto finish;
/* Mount /proc, /sys and friends, so that /proc/cmdline and
@@ -1324,7 +1427,7 @@ int main(int argc, char *argv[]) {
if (parse_config_file() < 0)
goto finish;
- if (arg_running_as == MANAGER_SYSTEM)
+ if (arg_running_as == SYSTEMD_SYSTEM)
if (parse_proc_cmdline() < 0)
goto finish;
@@ -1333,12 +1436,20 @@ int main(int argc, char *argv[]) {
if (parse_argv(argc, argv) < 0)
goto finish;
- if (arg_action == ACTION_TEST && geteuid() == 0) {
+ if (arg_action == ACTION_TEST &&
+ geteuid() == 0) {
log_error("Don't run test mode as root.");
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM &&
+ if (arg_running_as == SYSTEMD_USER &&
+ arg_action == ACTION_RUN &&
+ sd_booted() <= 0) {
+ log_error("Trying to run as user instance, but the system has not been booted with systemd.");
+ goto finish;
+ }
+
+ if (arg_running_as == SYSTEMD_SYSTEM &&
arg_action == ACTION_RUN &&
running_in_chroot() > 0) {
log_error("Cannot be run in a chroot() environment.");
@@ -1366,16 +1477,15 @@ int main(int argc, char *argv[]) {
log_close();
/* Remember open file descriptors for later deserialization */
- if (serialization) {
- r = fdset_new_fill(&fds);
- if (r < 0) {
- log_error("Failed to allocate fd set: %s", strerror(-r));
- goto finish;
- }
+ r = fdset_new_fill(&fds);
+ if (r < 0) {
+ log_error("Failed to allocate fd set: %s", strerror(-r));
+ goto finish;
+ } else
+ fdset_cloexec(fds, true);
+ if (serialization)
assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
- } else
- close_all_fds(NULL, 0);
/* Set up PATH unless it is already set */
setenv("PATH",
@@ -1384,9 +1494,9 @@ int main(int argc, char *argv[]) {
#else
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
#endif
- arg_running_as == MANAGER_SYSTEM);
+ arg_running_as == SYSTEMD_SYSTEM);
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_running_as == SYSTEMD_SYSTEM) {
/* Parse the data passed to us. We leave this
* variables set, but the manager later on will not
* pass them on to our children. */
@@ -1410,6 +1520,12 @@ int main(int argc, char *argv[]) {
unsetenv("USER");
unsetenv("LOGNAME");
+ /* We suppress the socket activation env vars, as
+ * we'll try to match *any* open fd to units if
+ * possible. */
+ unsetenv("LISTEN_FDS");
+ unsetenv("LISTEN_PID");
+
/* All other variables are left as is, so that clients
* can still read them via /proc/1/environ */
}
@@ -1417,7 +1533,7 @@ int main(int argc, char *argv[]) {
/* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0);
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_running_as == SYSTEMD_SYSTEM) {
/* Become a session leader if we aren't one yet. */
setsid();
@@ -1430,10 +1546,8 @@ int main(int argc, char *argv[]) {
/* Reset the console, but only if this is really init and we
* are freshly booted */
- if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) {
+ if (arg_running_as == SYSTEMD_SYSTEM && arg_action == ACTION_RUN)
console_setup(getpid() == 1 && !skip_setup);
- make_null_stdio();
- }
/* Open the logging devices, if possible and necessary */
log_open();
@@ -1449,10 +1563,10 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_running_as == SYSTEMD_SYSTEM) {
const char *virtualization = NULL;
- log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")");
+ log_info(PACKAGE_STRING " running in system mode. (" SYSTEMD_FEATURES ")");
detect_virtualization(&virtualization);
if (virtualization)
@@ -1462,15 +1576,17 @@ int main(int argc, char *argv[]) {
log_info("Running in initial RAM disk.");
} else
- log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")");
+ log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES ")");
- if (arg_running_as == MANAGER_SYSTEM && !skip_setup) {
+ if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) {
locale_setup();
if (arg_show_status || plymouth_running())
status_welcome();
+#ifdef HAVE_KMOD
kmod_setup();
+#endif
hostname_setup();
machine_id_setup();
loopback_setup();
@@ -1480,7 +1596,7 @@ int main(int argc, char *argv[]) {
test_cgroups();
}
- if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0)
+ if (arg_running_as == SYSTEMD_SYSTEM && arg_runtime_watchdog > 0)
watchdog_set_timeout(&arg_runtime_watchdog);
if (arg_timer_slack_nsec != (nsec_t) -1)
@@ -1500,15 +1616,18 @@ int main(int argc, char *argv[]) {
}
}
- if (arg_running_as == MANAGER_USER) {
+ if (arg_running_as == SYSTEMD_USER) {
/* Become reaper of our children */
- r = prctl(PR_SET_CHILD_SUBREAPER, 1);
- if (r < 0)
- log_error("Failed to prctl(PR_SET_CHILD_SUBREAPER): %s", strerror(-r));
- if (r == -EINVAL)
- log_error("Perhaps the kernel version is too old (< 3.4?)");
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
+ log_warning("Failed to make us a subreaper: %m");
+ if (errno == EINVAL)
+ log_info("Perhaps the kernel version is too old (< 3.4?)");
+ }
}
+ if (arg_running_as == SYSTEMD_SYSTEM)
+ bump_rlimit_nofile(&saved_rlimit_nofile);
+
r = manager_new(arg_running_as, &m);
if (r < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r));
@@ -1542,10 +1661,7 @@ int main(int argc, char *argv[]) {
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
- if (fds) {
- fdset_free(fds);
- fds = NULL;
- }
+ fdset_free(fds);
if (serialization) {
fclose(serialization);
@@ -1687,7 +1803,7 @@ finish:
manager_free(m);
for (j = 0; j < RLIMIT_NLIMITS; j++)
- free (arg_default_rlimit[j]);
+ free(arg_default_rlimit[j]);
free(arg_default_unit);
strv_free(arg_default_controllers);
@@ -1705,6 +1821,12 @@ finish:
* rebooted while we do that */
watchdog_close(true);
+ /* Reset the RLIMIT_NOFILE to the kernel default, so
+ * that the new systemd can pass the kernel default to
+ * its child processes */
+ if (saved_rlimit_nofile.rlim_cur > 0)
+ setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile);
+
if (switch_root_dir) {
/* Kill all remaining processes from the
* initrd, but don't wait for them, so that we
@@ -1739,7 +1861,7 @@ finish:
args[i++] = SYSTEMD_BINARY_PATH;
if (switch_root_dir)
args[i++] = "--switched-root";
- args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user";
+ args[i++] = arg_running_as == SYSTEMD_SYSTEM ? "--system" : "--user";
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
diff --git a/src/core/manager.c b/src/core/manager.c
index bcaf913b5a..df0fd63e87 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -36,12 +36,15 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
+#include <sys/timerfd.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
#endif
-#include <systemd/sd-daemon.h>
+#include "systemd/sd-daemon.h"
+#include "systemd/sd-id128.h"
+#include "systemd/sd-messages.h"
#include "manager.h"
#include "transaction.h"
@@ -66,6 +69,7 @@
#include "watchdog.h"
#include "cgroup-util.h"
#include "path-util.h"
+#include "audit-fd.h"
/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_ENTRIES_MAX 16
@@ -117,27 +121,75 @@ static int manager_setup_notify(Manager *m) {
ev.events = EPOLLIN;
ev.data.ptr = &m->notify_watch;
- if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
+ 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");
return -errno;
+ }
sa.un.sun_path[0] = '@';
m->notify_socket = strdup(sa.un.sun_path);
if (!m->notify_socket)
- return -ENOMEM;
+ return log_oom();
log_debug("Using notification socket %s", m->notify_socket);
return 0;
}
+static int manager_setup_time_change(Manager *m) {
+ struct epoll_event ev;
+ struct itimerspec its;
+
+ assert(m);
+ assert(m->time_change_watch.type == WATCH_INVALID);
+
+ /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever
+ * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */
+
+ m->time_change_watch.type = WATCH_TIME_CHANGE;
+ m->time_change_watch.fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (m->time_change_watch.fd < 0) {
+ log_error("Failed to create timerfd: %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);
+ watch_init(&m->time_change_watch);
+ 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;
+ }
+
+ log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd.");
+
+ return 0;
+}
+
static int enable_special_signals(Manager *m) {
int fd;
assert(m);
/* Enable that we get SIGINT on control-alt-del. In containers
- * this will fail with EPERM, so ignore that. */
- if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM)
+ * this will fail with EPERM (older) or EINVAL (newer), so
+ * ignore that. */
+ if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL)
log_warning("Failed to enable ctrl-alt-del handling: %m");
fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
@@ -195,6 +247,7 @@ static int manager_setup_signals(Manager *m) {
SIGRTMIN+21, /* systemd: disable status messages */
SIGRTMIN+22, /* systemd: set log level to LOG_DEBUG */
SIGRTMIN+23, /* systemd: set log level to LOG_INFO */
+ SIGRTMIN+24, /* systemd: Immediate exit (--user only) */
SIGRTMIN+26, /* systemd: set log target to journal-or-kmsg */
SIGRTMIN+27, /* systemd: set log target to console */
SIGRTMIN+28, /* systemd: set log target to kmsg */
@@ -203,7 +256,8 @@ static int manager_setup_signals(Manager *m) {
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
m->signal_watch.type = WATCH_SIGNAL;
- if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
+ m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
+ if (m->signal_watch.fd < 0)
return -errno;
zero(ev);
@@ -213,7 +267,7 @@ static int manager_setup_signals(Manager *m) {
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
return -errno;
- if (m->running_as == MANAGER_SYSTEM)
+ if (m->running_as == SYSTEMD_SYSTEM)
return enable_special_signals(m);
return 0;
@@ -234,18 +288,20 @@ static void manager_strip_environment(Manager *m) {
strv_remove_prefix(m->environment, "RD_");
}
-int manager_new(ManagerRunningAs running_as, Manager **_m) {
+int manager_new(SystemdRunningAs running_as, Manager **_m) {
Manager *m;
int r = -ENOMEM;
assert(_m);
assert(running_as >= 0);
- assert(running_as < _MANAGER_RUNNING_AS_MAX);
+ assert(running_as < _SYSTEMD_RUNNING_AS_MAX);
- if (!(m = new0(Manager, 1)))
+ m = new0(Manager, 1);
+ if (!m)
return -ENOMEM;
- dual_timestamp_get(&m->startup_timestamp);
+ 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;
@@ -253,11 +309,13 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
m->pin_cgroupfs_fd = -1;
m->idle_pipe[0] = m->idle_pipe[1] = -1;
-#ifdef HAVE_AUDIT
- m->audit_fd = -1;
-#endif
+ watch_init(&m->signal_watch);
+ watch_init(&m->mount_watch);
+ watch_init(&m->swap_watch);
+ watch_init(&m->udev_watch);
+ watch_init(&m->time_change_watch);
- m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = m->swap_watch.fd = -1;
+ 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 */
m->environment = strv_copy(environ);
@@ -266,7 +324,7 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
manager_strip_environment(m);
- if (running_as == MANAGER_SYSTEM) {
+ if (running_as == SYSTEMD_SYSTEM) {
m->default_controllers = strv_new("cpu", NULL);
if (!m->default_controllers)
goto fail;
@@ -287,29 +345,30 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
goto fail;
- if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
+ m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (m->epoll_fd < 0)
goto fail;
- if ((r = manager_setup_signals(m)) < 0)
+ r = manager_setup_signals(m);
+ if (r < 0)
goto fail;
- if ((r = manager_setup_cgroup(m)) < 0)
+ r = manager_setup_cgroup(m);
+ if (r < 0)
goto fail;
- if ((r = manager_setup_notify(m)) < 0)
+ r = manager_setup_notify(m);
+ if (r < 0)
goto fail;
- /* Try to connect to the busses, if possible. */
- if ((r = bus_init(m, running_as != MANAGER_SYSTEM)) < 0)
+ r = manager_setup_time_change(m);
+ if (r < 0)
goto fail;
-#ifdef HAVE_AUDIT
- if ((m->audit_fd = audit_open()) < 0 &&
- /* If the kernel lacks netlink or audit support,
- * don't worry about it. */
- errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
- log_error("Failed to connect to audit log: %m");
-#endif
+ /* Try to connect to the busses, if possible. */
+ r = bus_init(m, running_as != SYSTEMD_SYSTEM);
+ if (r < 0)
+ goto fail;
m->taint_usr = dir_is_empty("/usr") > 0;
@@ -429,7 +488,7 @@ static unsigned manager_dispatch_gc_queue(Manager *m) {
if (u->gc_marker == gc_marker + GC_OFFSET_BAD ||
u->gc_marker == gc_marker + GC_OFFSET_UNSURE) {
- log_debug("Collecting %s", u->id);
+ log_debug_unit(u->id, "Collecting %s", u->id);
u->gc_marker = gc_marker + GC_OFFSET_BAD;
unit_add_to_cleanup_queue(u);
}
@@ -493,11 +552,8 @@ void manager_free(Manager *m) {
close_nointr_nofail(m->signal_watch.fd);
if (m->notify_watch.fd >= 0)
close_nointr_nofail(m->notify_watch.fd);
-
-#ifdef HAVE_AUDIT
- if (m->audit_fd >= 0)
- audit_close(m->audit_fd);
-#endif
+ if (m->time_change_watch.fd >= 0)
+ close_nointr_nofail(m->time_change_watch.fd);
free(m->notify_socket);
@@ -579,7 +635,8 @@ static void manager_build_unit_path_cache(Manager *m) {
STRV_FOREACH(i, m->lookup_paths.unit_path) {
struct dirent *de;
- if (!(d = opendir(*i))) {
+ d = opendir(*i);
+ if (!d) {
log_error("Failed to open directory: %m");
continue;
}
@@ -651,6 +708,16 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
r = q;
}
+ /* Any fds left? Find some unit which wants them. This is
+ * useful to allow container managers to pass some file
+ * descriptors to us pre-initialized. This enables
+ * socket-based activation of entire containers. */
+ if (fdset_size(fds) > 0) {
+ q = manager_distribute_fds(m, fds);
+ if (q < 0)
+ r = q;
+ }
+
/* Third, fire things up! */
q = manager_coldplug(m);
if (q < 0)
@@ -683,7 +750,9 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
return -EPERM;
}
- log_debug("Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
+ log_debug_unit(unit->id,
+ "Trying to enqueue job %s/%s/%s", unit->id,
+ job_type_to_string(type), job_mode_to_string(mode));
job_type_collapse(&type, unit);
@@ -707,7 +776,9 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
if (r < 0)
goto tr_abort;
- log_debug("Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) tr->anchor_job->id);
+ log_debug_unit(unit->id,
+ "Enqueued job %s/%s as %u", unit->id,
+ job_type_to_string(type), (unsigned) tr->anchor_job->id);
if (_ret)
*_ret = tr->anchor_job;
@@ -730,7 +801,8 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode
assert(name);
assert(mode < _JOB_MODE_MAX);
- if ((r = manager_load_unit(m, name, NULL, NULL, &unit)) < 0)
+ r = manager_load_unit(m, name, NULL, NULL, &unit);
+ if (r < 0)
return r;
return manager_add_job(m, type, unit, mode, override, e, _ret);
@@ -842,7 +914,8 @@ int manager_load_unit(Manager *m, const char *name, const char *path, DBusError
/* This will load the service information files, but not actually
* start any services or anything. */
- if ((r = manager_load_unit_prepare(m, name, path, e, _ret)) != 0)
+ r = manager_load_unit_prepare(m, name, path, e, _ret);
+ if (r != 0)
return r;
manager_dispatch_load_queue(m);
@@ -966,7 +1039,8 @@ static int manager_process_notify_fd(Manager *m) {
msghdr.msg_control = &control;
msghdr.msg_controllen = sizeof(control);
- if ((n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT)) <= 0) {
+ n = recvmsg(m->notify_watch.fd, &msghdr, MSG_DONTWAIT);
+ if (n <= 0) {
if (n >= 0)
return -EIO;
@@ -986,18 +1060,22 @@ static int manager_process_notify_fd(Manager *m) {
ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
- if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid))))
- if (!(u = cgroup_unit_by_pid(m, ucred->pid))) {
+ u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid));
+ if (!u) {
+ u = cgroup_unit_by_pid(m, ucred->pid);
+ if (!u) {
log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid);
continue;
}
+ }
assert((size_t) n < sizeof(buf));
buf[n] = 0;
- if (!(tags = strv_split(buf, "\n\r")))
- return -ENOMEM;
+ tags = strv_split(buf, "\n\r");
+ if (!tags)
+ return log_oom();
- log_debug("Got notification message for unit %s", u->id);
+ log_debug_unit(u->id, "Got notification message for unit %s", u->id);
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, ucred->pid, tags);
@@ -1036,22 +1114,23 @@ 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 *name = NULL;
+ char _cleanup_free_ *name = NULL;
get_process_comm(si.si_pid, &name);
log_debug("Got SIGCHLD for process %lu (%s)", (unsigned long) si.si_pid, strna(name));
- free(name);
}
/* Let's flush any message the dying child might still
* have queued for us. This ensures that the process
* still exists in /proc so that we can figure out
* which cgroup and hence unit it belongs to. */
- if ((r = manager_process_notify_fd(m)) < 0)
+ r = manager_process_notify_fd(m);
+ if (r < 0)
return r;
/* And now figure out the unit this belongs to */
- if (!(u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid))))
+ u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid));
+ if (!u)
u = cgroup_unit_by_pid(m, si.si_pid);
/* And now, we actually reap the zombie. */
@@ -1076,7 +1155,8 @@ static int manager_dispatch_sigchld(Manager *m) {
if (!u)
continue;
- log_debug("Child %lu belongs to %s", (long unsigned) si.si_pid, u->id);
+ log_debug_unit(u->id,
+ "Child %lu belongs to %s", (long unsigned) si.si_pid, u->id);
hashmap_remove(m->watch_pids, LONG_TO_PTR(si.si_pid));
UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status);
@@ -1091,10 +1171,12 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) {
dbus_error_init(&error);
- log_debug("Activating special unit %s", name);
+ log_debug_unit(name, "Activating special unit %s", name);
- if ((r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL)) < 0)
- log_error("Failed to enqueue %s job: %s", name, bus_error(&error, r));
+ r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL);
+ if (r < 0)
+ log_error_unit(name,
+ "Failed to enqueue %s job: %s", name, bus_error(&error, r));
dbus_error_free(&error);
@@ -1109,7 +1191,8 @@ static int manager_process_signal_fd(Manager *m) {
assert(m);
for (;;) {
- if ((n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
+ n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi));
+ if (n != sizeof(sfsi)) {
if (n >= 0)
return -EIO;
@@ -1139,7 +1222,7 @@ static int manager_process_signal_fd(Manager *m) {
break;
case SIGTERM:
- if (m->running_as == MANAGER_SYSTEM) {
+ if (m->running_as == SYSTEMD_SYSTEM) {
/* This is for compatibility with the
* original sysvinit */
m->exit_code = MANAGER_REEXECUTE;
@@ -1149,7 +1232,7 @@ static int manager_process_signal_fd(Manager *m) {
/* Fall through */
case SIGINT:
- if (m->running_as == MANAGER_SYSTEM) {
+ if (m->running_as == SYSTEMD_SYSTEM) {
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE);
break;
}
@@ -1163,14 +1246,14 @@ static int manager_process_signal_fd(Manager *m) {
break;
case SIGWINCH:
- if (m->running_as == MANAGER_SYSTEM)
+ if (m->running_as == SYSTEMD_SYSTEM)
manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
break;
case SIGPWR:
- if (m->running_as == MANAGER_SYSTEM)
+ if (m->running_as == SYSTEMD_SYSTEM)
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
@@ -1282,6 +1365,15 @@ static int manager_process_signal_fd(Manager *m) {
log_notice("Setting log level to info.");
break;
+ case 24:
+ if (m->running_as == SYSTEMD_USER) {
+ m->exit_code = MANAGER_EXIT;
+ return 0;
+ }
+
+ /* This is a nop on init */
+ break;
+
case 26:
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
log_notice("Setting log target to journal-or-kmsg.");
@@ -1363,11 +1455,13 @@ static int process_event(Manager *m, struct epoll_event *ev) {
ssize_t k;
/* Some timer event, to be dispatched to the units */
- if ((k = read(w->fd, &v, sizeof(v))) != sizeof(v)) {
+ k = read(w->fd, &v, sizeof(v));
+ if (k != sizeof(v)) {
if (k < 0 && (errno == EINTR || errno == EAGAIN))
break;
+ log_error("Failed to read timer event counter: %s", k < 0 ? strerror(-k) : "Short read");
return k < 0 ? -errno : -EIO;
}
@@ -1401,6 +1495,28 @@ static int process_event(Manager *m, struct epoll_event *ev) {
bus_timeout_event(m, w, ev->events);
break;
+ case WATCH_TIME_CHANGE: {
+ Unit *u;
+ Iterator i;
+
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_TIME_CHANGE),
+ "MESSAGE=Time has been changed",
+ NULL);
+
+ /* Restart the watch */
+ close_nointr_nofail(m->time_change_watch.fd);
+ watch_init(&m->time_change_watch);
+ manager_setup_time_change(m);
+
+ HASHMAP_FOREACH(u, m->units, i) {
+ if (UNIT_VTABLE(u)->time_change)
+ UNIT_VTABLE(u)->time_change(u);
+ }
+
+ break;
+ }
+
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
@@ -1434,7 +1550,7 @@ int manager_loop(Manager *m) {
int n;
int wait_msec = -1;
- if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM)
+ if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM)
watchdog_ping();
if (!ratelimit_test(&rl)) {
@@ -1466,7 +1582,7 @@ int manager_loop(Manager *m) {
continue;
/* Sleep for half the watchdog time */
- if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) {
+ if (m->runtime_watchdog > 0 && m->running_as == SYSTEMD_SYSTEM) {
wait_msec = (int) (m->runtime_watchdog / 2 / USEC_PER_MSEC);
if (wait_msec <= 0)
wait_msec = 1;
@@ -1532,10 +1648,12 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
if (!startswith(s, "/org/freedesktop/systemd1/job/"))
return -EINVAL;
- if ((r = safe_atou(s + 30, &id)) < 0)
+ r = safe_atou(s + 30, &id);
+ if (r < 0)
return r;
- if (!(j = manager_get_job(m, id)))
+ j = manager_get_job(m, id);
+ if (!j)
return -ENOENT;
*_j = j;
@@ -1547,8 +1665,10 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
#ifdef HAVE_AUDIT
char *p;
+ int audit_fd;
- if (m->audit_fd < 0)
+ audit_fd = get_audit_fd();
+ if (audit_fd < 0)
return;
/* Don't generate audit events if the service was already
@@ -1556,23 +1676,24 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
if (m->n_reloading > 0)
return;
- if (m->running_as != MANAGER_SYSTEM)
+ if (m->running_as != SYSTEMD_SYSTEM)
return;
if (u->type != UNIT_SERVICE)
return;
- if (!(p = unit_name_to_prefix_and_instance(u->id))) {
- log_error("Failed to allocate unit name for audit message: %s", strerror(ENOMEM));
+ p = unit_name_to_prefix_and_instance(u->id);
+ if (!p) {
+ log_error_unit(u->id,
+ "Failed to allocate unit name for audit message: %s", strerror(ENOMEM));
return;
}
- if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
+ if (audit_log_user_comm_message(audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
if (errno == EPERM) {
/* We aren't allowed to send audit messages?
* Then let's not retry again. */
- audit_close(m->audit_fd);
- m->audit_fd = -1;
+ close_audit_fd();
} else
log_warning("Failed to send audit message: %m");
}
@@ -1593,7 +1714,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
if (m->n_reloading > 0)
return;
- if (m->running_as != MANAGER_SYSTEM)
+ if (m->running_as != SYSTEMD_SYSTEM)
return;
if (u->type != UNIT_SERVICE &&
@@ -1692,7 +1813,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
assert(_f);
- if (m->running_as == MANAGER_SYSTEM)
+ if (m->running_as == SYSTEMD_SYSTEM)
asprintf(&path, "/run/systemd/dump-%lu-XXXXXX", (unsigned long) getpid());
else
asprintf(&path, "/tmp/systemd-dump-%lu-XXXXXX", (unsigned long) getpid());
@@ -1714,7 +1835,8 @@ int manager_open_serialization(Manager *m, FILE **_f) {
log_debug("Serializing state to %s", path);
free(path);
- if (!(f = fdopen(fd, "w+")))
+ f = fdopen(fd, "w+");
+ if (!f)
return -errno;
*_f = f;
@@ -1739,10 +1861,13 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs) {
fprintf(f, "n-installed-jobs=%u\n", m->n_installed_jobs);
fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs);
+ dual_timestamp_serialize(f, "firmware-timestamp", &m->firmware_timestamp);
+ dual_timestamp_serialize(f, "kernel-timestamp", &m->kernel_timestamp);
+ dual_timestamp_serialize(f, "loader-timestamp", &m->loader_timestamp);
dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp);
if (!in_initrd()) {
- dual_timestamp_serialize(f, "startup-timestamp", &m->startup_timestamp);
+ dual_timestamp_serialize(f, "userspace-timestamp", &m->userspace_timestamp);
dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
}
@@ -1834,10 +1959,16 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
log_debug("Failed to parse taint /usr flag %s", l+10);
else
m->taint_usr = m->taint_usr || b;
- } else if (startswith(l, "initrd-timestamp="))
+ } else if (startswith(l, "firmware-timestamp="))
+ dual_timestamp_deserialize(l+19, &m->firmware_timestamp);
+ else if (startswith(l, "loader-timestamp="))
+ dual_timestamp_deserialize(l+17, &m->loader_timestamp);
+ else if (startswith(l, "kernel-timestamp="))
+ dual_timestamp_deserialize(l+17, &m->kernel_timestamp);
+ else if (startswith(l, "initrd-timestamp="))
dual_timestamp_deserialize(l+17, &m->initrd_timestamp);
- else if (startswith(l, "startup-timestamp="))
- dual_timestamp_deserialize(l+18, &m->startup_timestamp);
+ else if (startswith(l, "userspace-timestamp="))
+ dual_timestamp_deserialize(l+20, &m->userspace_timestamp);
else if (startswith(l, "finish-timestamp="))
dual_timestamp_deserialize(l+17, &m->finish_timestamp);
else
@@ -1860,10 +1991,12 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
char_array_0(name);
- if ((r = manager_load_unit(m, strstrip(name), NULL, NULL, &u)) < 0)
+ r = manager_load_unit(m, strstrip(name), NULL, NULL, &u);
+ if (r < 0)
goto finish;
- if ((r = unit_deserialize(u, f, fds)) < 0)
+ r = unit_deserialize(u, f, fds);
+ if (r < 0)
goto finish;
}
@@ -1879,6 +2012,28 @@ finish:
return r;
}
+int manager_distribute_fds(Manager *m, FDSet *fds) {
+ Unit *u;
+ Iterator i;
+ int r;
+
+ assert(m);
+
+ HASHMAP_FOREACH(u, m->units, i) {
+
+ if (fdset_size(fds) <= 0)
+ break;
+
+ if (UNIT_VTABLE(u)->distribute_fds) {
+ r = UNIT_VTABLE(u)->distribute_fds(u, fds);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
int manager_reload(Manager *m) {
int r, q;
FILE *f;
@@ -1994,7 +2149,8 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) {
assert(name);
/* Returns true if the unit is inactive or going down */
- if (!(u = manager_get_unit(m, name)))
+ u = manager_get_unit(m, name);
+ if (!u)
return true;
return unit_pending_inactive(u);
@@ -2002,7 +2158,7 @@ bool manager_unit_pending_inactive(Manager *m, const char *name) {
void manager_check_finished(Manager *m) {
char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX];
- usec_t kernel_usec, initrd_usec, userspace_usec, total_usec;
+ usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec;
assert(m);
@@ -2020,39 +2176,64 @@ void manager_check_finished(Manager *m) {
dual_timestamp_get(&m->finish_timestamp);
- if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) {
+ if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) {
- userspace_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
- total_usec = m->finish_timestamp.monotonic;
+ /* Note that m->kernel_usec.monotonic is always at 0,
+ * and m->firmware_usec.monotonic and
+ * m->loader_usec.monotonic should be considered
+ * negative values. */
- if (dual_timestamp_is_set(&m->initrd_timestamp)) {
+ firmware_usec = m->firmware_timestamp.monotonic - m->loader_timestamp.monotonic;
+ loader_usec = m->loader_timestamp.monotonic - m->kernel_timestamp.monotonic;
+ userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
+ total_usec = m->firmware_timestamp.monotonic + m->finish_timestamp.monotonic;
- kernel_usec = m->initrd_timestamp.monotonic;
- initrd_usec = m->startup_timestamp.monotonic - m->initrd_timestamp.monotonic;
+ if (dual_timestamp_is_set(&m->initrd_timestamp)) {
- log_info("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));
+ kernel_usec = m->initrd_timestamp.monotonic - m->kernel_timestamp.monotonic;
+ initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic;
+
+ if (!log_on_console())
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+ "KERNEL_USEC=%llu", (unsigned long long) kernel_usec,
+ "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),
+ NULL);
} else {
- kernel_usec = m->startup_timestamp.monotonic;
+ kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic;
initrd_usec = 0;
- log_info("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));
+ if (!log_on_console())
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED),
+ "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),
+ NULL);
}
} else {
- userspace_usec = initrd_usec = kernel_usec = 0;
- total_usec = m->finish_timestamp.monotonic - m->startup_timestamp.monotonic;
-
- log_debug("Startup finished in %s.",
- format_timespan(sum, sizeof(sum), total_usec));
+ firmware_usec = loader_usec = initrd_usec = kernel_usec = 0;
+ total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic;
+
+ if (!log_on_console())
+ log_struct(LOG_INFO,
+ 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),
+ NULL);
}
- bus_broadcast_finished(m, kernel_usec, initrd_usec, userspace_usec, total_usec);
+ bus_broadcast_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec);
sd_notifyf(false,
"READY=1\nSTATUS=Startup finished in %s.",
@@ -2070,7 +2251,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name)
if (*generator)
return 0;
- if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
+ if (m->running_as == SYSTEMD_SYSTEM && getpid() == 1) {
p = strappend("/run/systemd/", name);
if (!p)
@@ -2122,7 +2303,7 @@ void manager_run_generators(Manager *m) {
assert(m);
- generator_path = m->running_as == MANAGER_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
+ generator_path = m->running_as == SYSTEMD_SYSTEM ? SYSTEM_GENERATOR_PATH : USER_GENERATOR_PATH;
d = opendir(generator_path);
if (!d) {
if (errno == ENOENT)
@@ -2224,7 +2405,7 @@ void manager_recheck_journal(Manager *m) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (m->running_as != SYSTEMD_SYSTEM)
return;
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
@@ -2247,7 +2428,7 @@ void manager_recheck_journal(Manager *m) {
void manager_set_show_status(Manager *m, bool b) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (m->running_as != SYSTEMD_SYSTEM)
return;
m->show_status = b;
@@ -2261,7 +2442,7 @@ void manager_set_show_status(Manager *m, bool b) {
bool manager_get_show_status(Manager *m) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (m->running_as != SYSTEMD_SYSTEM)
return false;
if (m->show_status)
@@ -2273,9 +2454,9 @@ bool manager_get_show_status(Manager *m) {
return plymouth_running();
}
-static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
- [MANAGER_SYSTEM] = "system",
- [MANAGER_USER] = "user"
-};
+void watch_init(Watch *w) {
+ assert(w);
-DEFINE_STRING_TABLE_LOOKUP(manager_running_as, ManagerRunningAs);
+ w->type = WATCH_INVALID;
+ w->fd = -1;
+}
diff --git a/src/core/manager.h b/src/core/manager.h
index 603c2dc318..cc4edf8f1e 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -49,13 +49,6 @@ typedef enum ManagerExitCode {
_MANAGER_EXIT_CODE_INVALID = -1
} ManagerExitCode;
-typedef enum ManagerRunningAs {
- MANAGER_SYSTEM,
- MANAGER_USER,
- _MANAGER_RUNNING_AS_MAX,
- _MANAGER_RUNNING_AS_INVALID = -1
-} ManagerRunningAs;
-
enum WatchType {
WATCH_INVALID,
WATCH_SIGNAL,
@@ -67,7 +60,8 @@ enum WatchType {
WATCH_SWAP,
WATCH_UDEV,
WATCH_DBUS_WATCH,
- WATCH_DBUS_TIMEOUT
+ WATCH_DBUS_TIMEOUT,
+ WATCH_TIME_CHANGE
};
struct Watch {
@@ -132,6 +126,7 @@ struct Manager {
Watch notify_watch;
Watch signal_watch;
+ Watch time_change_watch;
int epoll_fd;
@@ -146,8 +141,11 @@ struct Manager {
usec_t runtime_watchdog;
usec_t shutdown_watchdog;
+ dual_timestamp firmware_timestamp;
+ dual_timestamp loader_timestamp;
+ dual_timestamp kernel_timestamp;
dual_timestamp initrd_timestamp;
- dual_timestamp startup_timestamp;
+ dual_timestamp userspace_timestamp;
dual_timestamp finish_timestamp;
char *generator_unit_path;
@@ -204,13 +202,8 @@ struct Manager {
* file system */
int pin_cgroupfs_fd;
- /* Audit fd */
-#ifdef HAVE_AUDIT
- int audit_fd;
-#endif
-
/* Flags */
- ManagerRunningAs running_as;
+ SystemdRunningAs running_as;
ManagerExitCode exit_code:5;
bool dispatching_load_queue:1;
@@ -239,7 +232,7 @@ struct Manager {
char *switch_root_init;
};
-int manager_new(ManagerRunningAs running_as, Manager **m);
+int manager_new(SystemdRunningAs running_as, Manager **m);
void manager_free(Manager *m);
int manager_enumerate(Manager *m);
@@ -279,6 +272,7 @@ int manager_open_serialization(Manager *m, FILE **_f);
int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool serialize_jobs);
int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
+int manager_distribute_fds(Manager *m, FDSet *fds);
int manager_reload(Manager *m);
@@ -301,5 +295,4 @@ void manager_recheck_journal(Manager *m);
void manager_set_show_status(Manager *m, bool b);
bool manager_get_show_status(Manager *m);
-const char *manager_running_as_to_string(ManagerRunningAs i);
-ManagerRunningAs manager_running_as_from_string(const char *s);
+void watch_init(Watch *w);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 07794df049..98614d0c3e 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -40,18 +40,26 @@
#include "mkdir.h"
#include "path-util.h"
#include "missing.h"
+#include "virt.h"
#ifndef TTY_GID
#define TTY_GID 5
#endif
+typedef enum MountMode {
+ MNT_NONE = 0,
+ MNT_FATAL = 1 << 0,
+ MNT_IN_CONTAINER = 1 << 1,
+} MountMode;
+
typedef struct MountPoint {
const char *what;
const char *where;
const char *type;
const char *options;
unsigned long flags;
- bool fatal;
+ bool (*condition_fn)(void);
+ MountMode mode;
} MountPoint;
/* The first three entries we might need before SELinux is up. The
@@ -60,15 +68,26 @@ typedef struct MountPoint {
#define N_EARLY_MOUNT 4
static const MountPoint mount_table[] = {
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
- { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
- { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
- { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
- { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
- { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false },
- { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
- { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false },
- { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME,
+ 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 },
+ { "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,
+ NULL, MNT_IN_CONTAINER },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+ 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 },
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_IN_CONTAINER },
};
/* These are API file systems that might be mounted by other software,
@@ -86,10 +105,7 @@ static const char ignore_paths[] =
/* Container bind mounts */
"/proc/sys\0"
"/dev/console\0"
- "/proc/kmsg\0"
- "/etc/localtime\0"
- "/etc/timezone\0"
- "/etc/machine-id\0";
+ "/proc/kmsg\0";
bool mount_point_is_api(const char *path) {
unsigned i;
@@ -119,16 +135,24 @@ static int mount_one(const MountPoint *p, bool relabel) {
assert(p);
+ if (p->condition_fn && !p->condition_fn())
+ return 0;
+
/* Relabel first, just in case */
if (relabel)
label_fix(p->where, true, true);
- if ((r = path_is_mount_point(p->where, true)) < 0)
+ r = path_is_mount_point(p->where, true);
+ if (r < 0)
return r;
if (r > 0)
return 0;
+ /* Skip securityfs in a container */
+ if (!(p->mode & MNT_IN_CONTAINER) && detect_container(NULL) > 0)
+ return 0;
+
/* The access mode here doesn't really matter too much, since
* the mounted file system will take precedence anyway. */
mkdir_p_label(p->where, 0755);
@@ -144,8 +168,8 @@ static int mount_one(const MountPoint *p, bool relabel) {
p->type,
p->flags,
p->options) < 0) {
- log_full(p->fatal ? LOG_ERR : LOG_DEBUG, "Failed to mount %s: %s", p->where, strerror(errno));
- return p->fatal ? -errno : 0;
+ log_full((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, "Failed to mount %s: %s", p->where, strerror(errno));
+ return (p->mode & MNT_FATAL) ? -errno : 0;
}
/* Relabel again, since we now mounted something fresh here */
@@ -190,8 +214,7 @@ int mount_cgroup_controllers(char ***join_controllers) {
controllers = set_new(string_hash_func, string_compare_func);
if (!controllers) {
- r = -ENOMEM;
- log_error("Failed to allocate controller set.");
+ r = log_oom();
goto finish;
}
@@ -262,9 +285,8 @@ int mount_cgroup_controllers(char ***join_controllers) {
options = strv_join(*k, ",");
if (!options) {
- log_error("Failed to join options");
free(controller);
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
@@ -275,9 +297,8 @@ int mount_cgroup_controllers(char ***join_controllers) {
where = strappend("/sys/fs/cgroup/", options);
if (!where) {
- log_error("Failed to build path");
free(options);
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
@@ -287,7 +308,6 @@ int mount_cgroup_controllers(char ***join_controllers) {
p.type = "cgroup";
p.options = options;
p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
- p.fatal = false;
r = mount_one(&p, true);
free(controller);
@@ -306,8 +326,7 @@ int mount_cgroup_controllers(char ***join_controllers) {
t = strappend("/sys/fs/cgroup/", *i);
if (!t) {
- log_error("Failed to build path");
- r = -ENOMEM;
+ r = log_oom();
free(options);
goto finish;
}
@@ -402,7 +421,17 @@ int mount_setup(bool loaded_policy) {
/* Create a few default symlinks, which are normally created
* by udevd, but some scripts might need them before we start
* udevd. */
- dev_setup();
+ dev_setup(NULL);
+
+ /* Mark the root directory as shared in regards to mount
+ * propagation. The kernel defaults to "private", but we think
+ * it makes more sense to have a default of "shared" so that
+ * nspawn and the container tools work out of the box. If
+ * specific setups need other settings they can reset the
+ * propagation mode to private if needed. */
+ if (detect_container(NULL) <= 0)
+ 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 */
mkdir_label("/run/systemd", 0755);
diff --git a/src/core/mount.c b/src/core/mount.c
index 83e51a7aba..5d2b010013 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -30,6 +30,7 @@
#include "load-fragment.h"
#include "load-dropin.h"
#include "log.h"
+#include "sd-messages.h"
#include "strv.h"
#include "mkdir.h"
#include "path-util.h"
@@ -221,9 +222,11 @@ static int mount_add_swap_links(Mount *m) {
assert(m);
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP])
- if ((r = swap_add_one_mount_link(SWAP(other), m)) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP]) {
+ r = swap_add_one_mount_link(SWAP(other), m);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -234,9 +237,11 @@ static int mount_add_path_links(Mount *m) {
assert(m);
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH])
- if ((r = path_add_one_mount_link(PATH(other), m)) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH]) {
+ r = path_add_one_mount_link(PATH(other), m);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -247,9 +252,11 @@ static int mount_add_automount_links(Mount *m) {
assert(m);
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT])
- if ((r = automount_add_one_mount_link(AUTOMOUNT(other), m)) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT]) {
+ r = automount_add_one_mount_link(AUTOMOUNT(other), m);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -260,9 +267,11 @@ static int mount_add_socket_links(Mount *m) {
assert(m);
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET])
- if ((r = socket_add_one_mount_link(SOCKET(other), m)) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET]) {
+ r = socket_add_one_mount_link(SOCKET(other), m);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -352,17 +361,21 @@ static int mount_add_device_links(Mount *m) {
if (!p->what)
return 0;
- if (!mount_is_bind(p) &&
- !path_equal(m->where, "/")) {
- r = unit_add_node_link(UNIT(m), p->what, false);
- if (r < 0)
- return r;
- }
+ if (mount_is_bind(p))
+ return 0;
+
+ if (!is_device_path(p->what))
+ return 0;
+
+ if (path_equal(m->where, "/"))
+ return 0;
+
+ r = unit_add_node_link(UNIT(m), p->what, false);
+ if (r < 0)
+ return r;
if (p->passno > 0 &&
- !mount_is_bind(p) &&
- !path_equal(m->where, "/") &&
- UNIT(m)->manager->running_as == MANAGER_SYSTEM) {
+ UNIT(m)->manager->running_as == SYSTEMD_SYSTEM) {
char *name;
Unit *fsck;
/* Let's add in the fsck service */
@@ -374,7 +387,8 @@ static int mount_add_device_links(Mount *m) {
r = manager_load_unit_prepare(UNIT(m)->manager, name, NULL, NULL, &fsck);
if (r < 0) {
- log_warning("Failed to prepare unit %s: %s", name, strerror(-r));
+ log_warning_unit(name,
+ "Failed to prepare unit %s: %s", name, strerror(-r));
free(name);
return r;
}
@@ -396,7 +410,7 @@ static int mount_add_quota_links(Mount *m) {
assert(m);
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
return 0;
p = get_mount_parameters_fragment(m);
@@ -424,7 +438,7 @@ static int mount_add_default_dependencies(Mount *m) {
assert(m);
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
return 0;
p = get_mount_parameters_fragment(m);
@@ -485,7 +499,9 @@ static int mount_fix_timeouts(Mount *m) {
free(t);
if (r < 0) {
- log_warning("Failed to parse timeout for %s, ignoring: %s", m->where, timeout);
+ log_warning_unit(UNIT(m)->id,
+ "Failed to parse timeout for %s, ignoring: %s",
+ m->where, timeout);
return r;
}
@@ -517,22 +533,29 @@ static int mount_verify(Mount *m) {
free(e);
if (!b) {
- log_error("%s's Where setting doesn't match unit name. Refusing.", UNIT(m)->id);
+ log_error_unit(UNIT(m)->id,
+ "%s's Where setting doesn't match unit name. Refusing.",
+ UNIT(m)->id);
return -EINVAL;
}
if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) {
- log_error("Cannot create mount unit for API file system %s. Refusing.", m->where);
+ log_error_unit(UNIT(m)->id,
+ "Cannot create mount unit for API file system %s. Refusing.",
+ m->where);
return -EINVAL;
}
if (UNIT(m)->fragment_path && !m->parameters_fragment.what) {
- log_error("%s's What setting is missing. Refusing.", UNIT(m)->id);
+ log_error_unit(UNIT(m)->id,
+ "%s's What setting is missing. Refusing.", UNIT(m)->id);
return -EBADMSG;
}
if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(m)->id);
+ log_error_unit(UNIT(m)->id,
+ "%s has PAM enabled. Kill mode must be set to control-group'. Refusing.",
+ UNIT(m)->id);
return -EINVAL;
}
@@ -543,10 +566,6 @@ static int mount_add_extras(Mount *m) {
Unit *u = UNIT(m);
int r;
- r = unit_add_exec_dependencies(u, &m->exec_context);
- if (r < 0)
- return r;
-
if (UNIT(m)->fragment_path)
m->from_fragment = true;
@@ -558,6 +577,10 @@ static int mount_add_extras(Mount *m) {
path_kill_slashes(m->where);
+ r = unit_add_exec_dependencies(u, &m->exec_context);
+ if (r < 0)
+ return r;
+
if (!UNIT(m)->description) {
r = unit_set_description(u, m->where);
if (r < 0)
@@ -699,10 +722,11 @@ static void mount_set_state(Mount *m, MountState state) {
}
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(m)->id,
- mount_state_to_string(old_state),
- mount_state_to_string(state));
+ log_debug_unit(UNIT(m)->id,
+ "%s changed %s -> %s",
+ UNIT(m)->id,
+ mount_state_to_string(old_state),
+ mount_state_to_string(state));
unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS);
m->reload_result = MOUNT_SUCCESS;
@@ -737,10 +761,12 @@ static int mount_coldplug(Unit *u) {
if (m->control_pid <= 0)
return -EBADMSG;
- if ((r = unit_watch_pid(UNIT(m), m->control_pid)) < 0)
+ r = unit_watch_pid(UNIT(m), m->control_pid);
+ if (r < 0)
return r;
- if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch);
+ if (r < 0)
return r;
}
@@ -796,7 +822,8 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
assert(c);
assert(_pid);
- if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch);
+ if (r < 0)
goto fail;
if ((r = exec_spawn(c,
@@ -866,7 +893,9 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
if (m->control_pid > 0) {
if (kill_and_sigcont(m->control_pid, sig) < 0 && errno != ESRCH)
- log_warning("Failed to kill control process %li: %m", (long) m->control_pid);
+ log_warning_unit(UNIT(m)->id,
+ "Failed to kill control process %li: %m",
+ (long) m->control_pid);
else
wait_for_exit = true;
}
@@ -886,7 +915,9 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
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("Failed to kill control group: %s", strerror(-r));
+ log_warning_unit(UNIT(m)->id,
+ "Failed to kill control group: %s",
+ strerror(-r));
} else if (r > 0)
wait_for_exit = true;
@@ -896,7 +927,8 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
}
if (wait_for_exit) {
- if ((r = unit_watch_timer(UNIT(m), m->timeout_usec, &m->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch);
+ if (r < 0)
goto fail;
mount_set_state(m, state);
@@ -908,7 +940,8 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
return;
fail:
- log_warning("%s failed to kill processes: %s", UNIT(m)->id, strerror(-r));
+ log_warning_unit(UNIT(m)->id,
+ "%s failed to kill processes: %s", UNIT(m)->id, strerror(-r));
if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
@@ -919,6 +952,18 @@ fail:
set_free(pid_set);
}
+void warn_if_dir_nonempty(const char *unit, const char* where) {
+ if (dir_is_empty(where) > 0)
+ return;
+ log_struct(LOG_NOTICE,
+ "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);
+}
+
static void mount_enter_unmounting(Mount *m) {
int r;
@@ -944,7 +989,9 @@ static void mount_enter_unmounting(Mount *m) {
return;
fail:
- log_warning("%s failed to run 'umount' task: %s", UNIT(m)->id, strerror(-r));
+ log_warning_unit(UNIT(m)->id,
+ "%s failed to run 'umount' task: %s",
+ UNIT(m)->id, strerror(-r));
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
}
@@ -959,6 +1006,8 @@ static void mount_enter_mounting(Mount *m) {
mkdir_p_label(m->where, m->directory_mode);
+ warn_if_dir_nonempty(m->meta.id, m->where);
+
/* Create the source directory for bind-mounts if needed */
p = get_mount_parameters_fragment(m);
if (p && mount_is_bind(p))
@@ -981,7 +1030,8 @@ static void mount_enter_mounting(Mount *m) {
mount_unwatch_control_pid(m);
- if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
+ r = mount_spawn(m, m->control_command, &m->control_pid);
+ if (r < 0)
goto fail;
mount_set_state(m, MOUNT_MOUNTING);
@@ -989,7 +1039,9 @@ static void mount_enter_mounting(Mount *m) {
return;
fail:
- log_warning("%s failed to run 'mount' task: %s", UNIT(m)->id, strerror(-r));
+ log_warning_unit(UNIT(m)->id,
+ "%s failed to run 'mount' task: %s",
+ UNIT(m)->id, strerror(-r));
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
}
@@ -1047,7 +1099,9 @@ static void mount_enter_remounting(Mount *m) {
return;
fail:
- log_warning("%s failed to run 'remount' task: %s", UNIT(m)->id, strerror(-r));
+ log_warning_unit(UNIT(m)->id,
+ "%s failed to run 'remount' task: %s",
+ UNIT(m)->id, strerror(-r));
m->reload_result = MOUNT_FAILURE_RESOURCES;
mount_enter_mounted(m, MOUNT_SUCCESS);
}
@@ -1149,7 +1203,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
MountState state;
if ((state = mount_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
+ log_debug_unit(u->id, "Failed to parse state value %s", value);
else
m->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -1157,7 +1211,8 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
f = mount_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse result value %s", value);
+ log_debug_unit(UNIT(m)->id,
+ "Failed to parse result value %s", value);
else if (f != MOUNT_SUCCESS)
m->result = f;
@@ -1166,7 +1221,8 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
f = mount_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse reload result value %s", value);
+ log_debug_unit(UNIT(m)->id,
+ "Failed to parse reload result value %s", value);
else if (f != MOUNT_SUCCESS)
m->reload_result = f;
@@ -1174,21 +1230,24 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
+ log_debug_unit(UNIT(m)->id,
+ "Failed to parse control-pid value %s", value);
else
m->control_pid = pid;
} else if (streq(key, "control-command")) {
MountExecCommand id;
if ((id = mount_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
+ log_debug_unit(UNIT(m)->id,
+ "Failed to parse exec-command value %s", value);
else {
m->control_command_id = id;
m->control_command = m->exec_command + id;
}
} else
- log_debug("Unknown serialization key '%s'", key);
+ log_debug_unit(UNIT(m)->id,
+ "Unknown serialization key '%s'", key);
return 0;
}
@@ -1225,7 +1284,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = MOUNT_SUCCESS;
else if (code == CLD_EXITED)
f = MOUNT_FAILURE_EXIT_CODE;
@@ -1246,8 +1305,9 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
- log_full(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s mount process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+ log_full_unit(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, u->id,
+ "%s mount process exited, code=%s status=%i",
+ u->id, sigchld_code_to_string(code), status);
/* Note that mount(8) returning and the kernel sending us a
* mount table change event might happen out-of-order. If an
@@ -1314,27 +1374,33 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
case MOUNT_MOUNTING:
case MOUNT_MOUNTING_DONE:
- log_warning("%s mounting timed out. Stopping.", u->id);
+ log_warning_unit(u->id,
+ "%s mounting timed out. Stopping.", u->id);
mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
break;
case MOUNT_REMOUNTING:
- log_warning("%s remounting timed out. Stopping.", u->id);
+ log_warning_unit(u->id,
+ "%s remounting timed out. Stopping.", u->id);
m->reload_result = MOUNT_FAILURE_TIMEOUT;
mount_enter_mounted(m, MOUNT_SUCCESS);
break;
case MOUNT_UNMOUNTING:
- log_warning("%s unmounting timed out. Stopping.", u->id);
+ log_warning_unit(u->id,
+ "%s unmounting timed out. Stopping.", u->id);
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT);
break;
case MOUNT_MOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_warning("%s mounting timed out. Killing.", u->id);
+ log_warning_unit(u->id,
+ "%s mounting timed out. Killing.", u->id);
mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
- log_warning("%s mounting timed out. Skipping SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s mounting timed out. Skipping SIGKILL. Ignoring.",
+ u->id);
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1345,10 +1411,13 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
case MOUNT_REMOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_warning("%s remounting timed out. Killing.", u->id);
+ log_warning_unit(u->id,
+ "%s remounting timed out. Killing.", u->id);
mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
- log_warning("%s remounting timed out. Skipping SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s remounting timed out. Skipping SIGKILL. Ignoring.",
+ u->id);
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1359,10 +1428,13 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
case MOUNT_UNMOUNTING_SIGTERM:
if (m->kill_context.send_sigkill) {
- log_warning("%s unmounting timed out. Killing.", u->id);
+ log_warning_unit(u->id,
+ "%s unmounting timed out. Killing.", u->id);
mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT);
} else {
- log_warning("%s unmounting timed out. Skipping SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s unmounting timed out. Skipping SIGKILL. Ignoring.",
+ u->id);
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1374,7 +1446,9 @@ static void mount_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
case MOUNT_MOUNTING_SIGKILL:
case MOUNT_REMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGKILL:
- log_warning("%s mount process still around after SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s mount process still around after SIGKILL. Ignoring.",
+ u->id);
if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, MOUNT_FAILURE_TIMEOUT);
@@ -1400,6 +1474,7 @@ static int mount_add_one(
bool delete;
char *e, *w = NULL, *o = NULL, *f = NULL;
MountParameters *p;
+ bool load_extras = false;
assert(m);
assert(what);
@@ -1450,12 +1525,21 @@ static int mount_add_one(
delete = false;
free(e);
+ if (!MOUNT(u)->where) {
+ MOUNT(u)->where = strdup(where);
+ if (!MOUNT(u)->where) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
if (u->load_state == UNIT_ERROR) {
u->load_state = UNIT_LOADED;
u->load_error = 0;
- r = mount_add_extras(MOUNT(u));
- if (r < 0)
- goto fail;
+
+ /* Load in the extras later on, after we
+ * finished initialization of the unit */
+ load_extras = true;
}
}
@@ -1486,6 +1570,12 @@ static int mount_add_one(
p->passno = passno;
+ if (load_extras) {
+ r = mount_add_extras(MOUNT(u));
+ if (r < 0)
+ goto fail;
+ }
+
unit_add_to_dbus_queue(u);
return 0;
@@ -1630,7 +1720,8 @@ void mount_fd_event(Manager *m, int events) {
* /proc/self/mountinfo file, which informs us about mounting
* table changes */
- if ((r = mount_load_proc_self_mountinfo(m, true)) < 0) {
+ r = mount_load_proc_self_mountinfo(m, true);
+ if (r < 0) {
log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r));
/* Reset flags, just in case, for later calls */
@@ -1797,6 +1888,8 @@ 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"
diff --git a/src/core/mount.h b/src/core/mount.h
index 67d6132a5d..30c6d9b249 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -119,3 +119,5 @@ MountExecCommand mount_exec_command_from_string(const char *s);
const char* mount_result_to_string(MountResult i);
MountResult mount_result_from_string(const char *s);
+
+void warn_if_dir_nonempty(const char *unit, const char* where);
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 4bef15fdf5..ba18ddc5b0 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -41,13 +41,15 @@ typedef enum PathMode {
/* This is ordered by priority! */
INACCESSIBLE,
READONLY,
- PRIVATE,
+ PRIVATE_TMP,
+ PRIVATE_VAR_TMP,
READWRITE
} PathMode;
typedef struct Path {
const char *path;
PathMode mode;
+ bool done;
} Path;
static int append_paths(Path **p, char **strv, PathMode mode) {
@@ -91,25 +93,22 @@ static int path_compare(const void *a, const void *b) {
return 0;
}
-static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) {
+static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) {
Path *f, *t, *previous;
assert(p);
assert(n);
assert(need_inaccessible);
- assert(need_private);
for (f = p, t = p, previous = NULL; f < p+*n; f++) {
+ /* The first one wins */
if (previous && path_equal(f->path, previous->path))
continue;
t->path = f->path;
t->mode = f->mode;
- if (t->mode == PRIVATE)
- *need_private = true;
-
if (t->mode == INACCESSIBLE)
*need_inaccessible = true;
@@ -121,65 +120,62 @@ static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool
*n = t - p;
}
-static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) {
+static int apply_mount(
+ Path *p,
+ const char *tmp_dir,
+ const char *var_tmp_dir,
+ const char *inaccessible_dir) {
+
const char *what;
- char *where;
int r;
assert(p);
- assert(root_dir);
- assert(inaccessible_dir);
- assert(private_dir);
-
- where = strappend(root_dir, p->path);
- if (!where)
- return -ENOMEM;
switch (p->mode) {
case INACCESSIBLE:
what = inaccessible_dir;
- flags |= MS_RDONLY;
break;
case READONLY:
- flags |= MS_RDONLY;
- /* Fall through */
-
case READWRITE:
what = p->path;
break;
- case PRIVATE:
- what = private_dir;
+ case PRIVATE_TMP:
+ what = tmp_dir;
+ break;
+
+ case PRIVATE_VAR_TMP:
+ what = var_tmp_dir;
break;
default:
assert_not_reached("Unknown mode");
}
- r = mount(what, where, NULL, MS_BIND|MS_REC, NULL);
- if (r >= 0) {
- log_debug("Successfully mounted %s to %s", what, where);
+ assert(what);
- /* The bind mount will always inherit the original
- * flags. If we want to set any flag we need
- * to do so in a second independent step. */
- if (flags)
- r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL);
+ r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL);
+ if (r >= 0)
+ log_debug("Successfully mounted %s to %s", what, p->path);
- /* Avoid exponential growth of trees */
- if (r >= 0 && path_equal(p->path, "/"))
- r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL);
+ return r;
+}
- if (r < 0) {
- r = -errno;
- umount2(where, MNT_DETACH);
- }
- }
+static int make_read_only(Path *p) {
+ int r;
- free(where);
- return r;
+ assert(p);
+
+ if (p->mode != INACCESSIBLE && p->mode != READONLY)
+ return 0;
+
+ r = mount(NULL, p->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
+ if (r < 0)
+ return -errno;
+
+ return 0;
}
int setup_namespace(
@@ -190,30 +186,26 @@ int setup_namespace(
unsigned long flags) {
char
- tmp_dir[] = "/tmp/systemd-namespace-XXXXXX",
- root_dir[] = "/tmp/systemd-namespace-XXXXXX/root",
- old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX",
- inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible",
- private_dir[] = "/tmp/systemd-namespace-XXXXXX/private";
+ 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_private = false, need_inaccessible = false;
- bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false;
+ bool need_inaccessible = false;
+ bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false;
int r;
- const char *t;
+
+ if (!flags)
+ flags = MS_SHARED;
n =
strv_length(writable) +
strv_length(readable) +
strv_length(inaccessible) +
- (private_tmp ? 3 : 1);
+ (private_tmp ? 2 : 0);
- paths = new(Path, n);
- if (!paths)
- return -ENOMEM;
-
- p = paths;
+ 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)
@@ -221,60 +213,70 @@ int setup_namespace(
if (private_tmp) {
p->path = "/tmp";
- p->mode = PRIVATE;
+ p->mode = PRIVATE_TMP;
p++;
p->path = "/var/tmp";
- p->mode = PRIVATE;
+ p->mode = PRIVATE_VAR_TMP;
p++;
}
- p->path = "/";
- p->mode = READWRITE;
- p++;
-
assert(paths + n == p);
qsort(paths, n, sizeof(Path), path_compare);
- drop_duplicates(paths, &n, &need_inaccessible, &need_private);
+ drop_duplicates(paths, &n, &need_inaccessible);
- if (!mkdtemp(tmp_dir)) {
- r = -errno;
- goto fail;
- }
- remove_tmp = true;
+ if (need_inaccessible) {
+ mode_t u;
+ char *d;
- memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1);
- if (mkdir(root_dir, 0777) < 0) {
- r = -errno;
- goto fail;
- }
- remove_root = true;
+ u = umask(0777);
+ d = mkdtemp(inaccessible_dir);
+ umask(u);
- if (need_inaccessible) {
- memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1);
- if (mkdir(inaccessible_dir, 0) < 0) {
+ if (!d) {
r = -errno;
goto fail;
}
+
remove_inaccessible = true;
}
- if (need_private) {
+ if (private_tmp) {
mode_t u;
-
- memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1);
+ char *d;
u = umask(0000);
- if (mkdir(private_dir, 0777 + S_ISVTX) < 0) {
- umask(u);
+ d = mkdtemp(tmp_dir);
+ umask(u);
+ if (!d) {
r = -errno;
goto fail;
}
+ remove_tmp = true;
+
+ u = umask(0000);
+ d = mkdtemp(var_tmp_dir);
umask(u);
- remove_private = true;
+
+ 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;
+ }
}
if (unshare(CLONE_NEWNS) < 0) {
@@ -282,7 +284,7 @@ int setup_namespace(
goto fail;
}
- /* Remount / as SLAVE so that nothing mounted in the namespace
+ /* 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;
@@ -290,67 +292,39 @@ int setup_namespace(
}
for (p = paths; p < paths + n; p++) {
- r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags);
+ r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir);
if (r < 0)
goto undo_mounts;
}
- memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1);
- if (!mkdtemp(old_root_dir)) {
- r = -errno;
- goto undo_mounts;
- }
- remove_old_root = true;
-
- if (chdir(root_dir) < 0) {
- r = -errno;
- goto undo_mounts;
+ for (p = paths; p < paths + n; p++) {
+ r = make_read_only(p);
+ if (r < 0)
+ goto undo_mounts;
}
- if (pivot_root(root_dir, old_root_dir) < 0) {
+ /* Remount / as the desired mode */
+ if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) {
r = -errno;
goto undo_mounts;
}
- t = old_root_dir + sizeof(root_dir) - 1;
- if (umount2(t, MNT_DETACH) < 0)
- /* At this point it's too late to turn anything back,
- * since we are already in the new root. */
- return -errno;
-
- if (rmdir(t) < 0)
- return -errno;
-
return 0;
undo_mounts:
-
- for (p--; p >= paths; p--) {
- char full_path[PATH_MAX];
-
- snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path);
- char_array_0(full_path);
-
- umount2(full_path, MNT_DETACH);
- }
+ for (p = paths; p < paths + n; p++)
+ if (p->done)
+ umount2(p->path, MNT_DETACH);
fail:
- if (remove_old_root)
- rmdir(old_root_dir);
-
if (remove_inaccessible)
rmdir(inaccessible_dir);
- if (remove_private)
- rmdir(private_dir);
-
- if (remove_root)
- rmdir(root_dir);
-
if (remove_tmp)
rmdir(tmp_dir);
- free(paths);
+ if (remove_var_tmp)
+ rmdir(var_tmp_dir);
return r;
}
diff --git a/src/core/path.c b/src/core/path.c
index 42dd5da8e1..767620ba75 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -73,14 +73,16 @@ int path_spec_watch(PathSpec *s, Unit *u) {
goto fail;
}
- if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0)
+ s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type]);
+ if (s->primary_wd >= 0)
exists = true;
do {
int flags;
/* This assumes the path was passed through path_kill_slashes()! */
- if (!(slash = strrchr(k, '/')))
+ slash = strrchr(k, '/');
+ if (!slash)
break;
/* Trim the path at the last slash. Keep the slash if it's the root dir. */
@@ -122,7 +124,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) {
int r = 0;
if (events != EPOLLIN) {
- log_error("Got Invalid poll event on inotify.");
+ log_error("Got invalid poll event on inotify.");
r = -EINVAL;
goto out;
}
@@ -135,13 +137,15 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) {
assert(l > 0);
- if (!(buf = malloc(l))) {
+ buf = malloc(l);
+ if (!buf) {
log_error("Failed to allocate buffer: %m");
r = -errno;
goto out;
}
- if ((k = read(s->inotify_fd, buf, l)) < 0) {
+ k = read(s->inotify_fd, buf, l);
+ if (k < 0) {
log_error("Failed to read inotify event: %m");
r = -errno;
goto out;
@@ -215,7 +219,8 @@ static void path_spec_mkdir(PathSpec *s, mode_t mode) {
if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
return;
- if ((r = mkdir_p_label(s->path, mode)) < 0)
+ r = mkdir_p_label(s->path, mode);
+ if (r < 0)
log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
}
@@ -302,7 +307,8 @@ static int path_verify(Path *p) {
return 0;
if (!p->specs) {
- log_error("%s lacks path setting. Refusing.", UNIT(p)->id);
+ log_error_unit(UNIT(p)->id,
+ "%s lacks path setting. Refusing.", UNIT(p)->id);
return -EINVAL;
}
@@ -314,7 +320,7 @@ static int path_add_default_dependencies(Path *p) {
assert(p);
- if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) {
+ 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;
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
new file mode 100644
index 0000000000..6dfe8b45f3
--- /dev/null
+++ b/src/core/selinux-access.c
@@ -0,0 +1,441 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Dan Walsh
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "selinux-access.h"
+
+#ifdef HAVE_SELINUX
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#ifdef HAVE_AUDIT
+#include <libaudit.h>
+#endif
+#include <dbus.h>
+
+#include "util.h"
+#include "log.h"
+#include "bus-errors.h"
+#include "dbus-common.h"
+#include "audit.h"
+#include "selinux-util.h"
+#include "audit-fd.h"
+
+static bool initialized = false;
+
+struct auditstruct {
+ const char *path;
+ char *cmdline;
+ uid_t loginuid;
+ uid_t uid;
+ gid_t gid;
+};
+
+static int bus_get_selinux_security_context(
+ DBusConnection *connection,
+ const char *name,
+ char **scon,
+ DBusError *error) {
+
+ _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
+ DBusMessageIter iter, sub;
+ const char *bytes;
+ char *b;
+ int nbytes;
+
+ m = dbus_message_new_method_call(
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetConnectionSELinuxSecurityContext");
+ if (!m) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_append_args(
+ m,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID)) {
+ dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, NULL);
+ return -ENOMEM;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(connection, m, -1, error);
+ if (!reply)
+ return -EIO;
+
+ if (dbus_set_error_from_message(error, reply))
+ return -EIO;
+
+ if (!dbus_message_iter_init(reply, &iter))
+ return -EIO;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return -EIO;
+
+ dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_fixed_array(&sub, &bytes, &nbytes);
+
+ b = strndup(bytes, nbytes);
+ if (!b)
+ return -ENOMEM;
+
+ *scon = b;
+
+ log_debug("GetConnectionSELinuxSecurityContext %s (pid %ld)", *scon, (long) bus_get_unix_process_id(connection, name, error));
+
+ return 0;
+}
+
+static int bus_get_audit_data(
+ DBusConnection *connection,
+ const char *name,
+ struct auditstruct *audit,
+ DBusError *error) {
+
+ pid_t pid;
+ int r;
+
+ pid = bus_get_unix_process_id(connection, name, error);
+ if (pid <= 0)
+ return -EIO;
+
+ r = audit_loginuid_from_pid(pid, &audit->loginuid);
+ if (r < 0)
+ return r;
+
+ r = get_process_uid(pid, &audit->uid);
+ if (r < 0)
+ return r;
+
+ r = get_process_gid(pid, &audit->gid);
+ if (r < 0)
+ return r;
+
+ r = get_process_cmdline(pid, LINE_MAX, true, &audit->cmdline);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/*
+ Any time an access gets denied this callback will be called
+ with the aduit data. We then need to just copy the audit data into the msgbuf.
+*/
+static int audit_callback(
+ void *auditdata,
+ security_class_t cls,
+ char *msgbuf,
+ size_t msgbufsize) {
+
+ struct auditstruct *audit = (struct auditstruct *) auditdata;
+
+ snprintf(msgbuf, msgbufsize,
+ "auid=%d uid=%d gid=%d%s%s%s%s%s%s",
+ audit->loginuid,
+ audit->uid,
+ audit->gid,
+ (audit->path ? " path=\"" : ""),
+ strempty(audit->path),
+ (audit->path ? "\"" : ""),
+ (audit->cmdline ? " cmdline=\"" : ""),
+ strempty(audit->cmdline),
+ (audit->cmdline ? "\"" : ""));
+
+ msgbuf[msgbufsize-1] = 0;
+
+ return 0;
+}
+
+/*
+ Any time an access gets denied this callback will be called
+ code copied from dbus. If audit is turned on the messages will go as
+ 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, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+
+#ifdef HAVE_AUDIT
+ if (get_audit_fd() >= 0) {
+ char buf[LINE_MAX];
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0);
+ va_end(ap);
+
+ return 0;
+ }
+#endif
+ log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
+ va_end(ap);
+
+ return 0;
+}
+
+/*
+ Function must be called once to initialize the SELinux AVC environment.
+ Sets up callbacks.
+ If you want to cleanup memory you should need to call selinux_access_finish.
+*/
+static int access_init(void) {
+ int r;
+
+ if (avc_open(NULL, 0)) {
+ log_error("avc_open() failed: %m");
+ return -errno;
+ }
+
+ selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
+ selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
+
+ if (security_getenforce() >= 0)
+ return 0;
+
+ r = -errno;
+ avc_destroy();
+
+ return r;
+}
+
+static int selinux_access_init(DBusError *error) {
+ int r;
+
+ if (initialized)
+ return 0;
+
+ if (use_selinux()) {
+ r = access_init();
+ if (r < 0) {
+ dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
+ return r;
+ }
+ }
+
+ initialized = true;
+ return 0;
+}
+
+void selinux_access_free(void) {
+ if (!initialized)
+ return;
+
+ avc_destroy();
+ initialized = false;
+}
+
+static int get_audit_data(
+ DBusConnection *connection,
+ DBusMessage *message,
+ struct auditstruct *audit,
+ DBusError *error) {
+
+ const char *sender;
+ int r, fd;
+ struct ucred ucred;
+ socklen_t len;
+
+ sender = dbus_message_get_sender(message);
+ if (sender)
+ return bus_get_audit_data(connection, sender, audit, error);
+
+ if (!dbus_connection_get_unix_fd(connection, &fd))
+ return -EINVAL;
+
+ r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
+ if (r < 0) {
+ log_error("Failed to determine peer credentials: %m");
+ return -errno;
+ }
+
+ audit->uid = ucred.uid;
+ audit->gid = ucred.gid;
+
+ r = audit_loginuid_from_pid(ucred.pid, &audit->loginuid);
+ if (r < 0)
+ return r;
+
+ r = get_process_cmdline(ucred.pid, LINE_MAX, true, &audit->cmdline);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/*
+ This function returns the security context of the remote end of the dbus
+ connections. Whether it is on the bus or a local connection.
+*/
+static int get_calling_context(
+ DBusConnection *connection,
+ DBusMessage *message,
+ security_context_t *scon,
+ DBusError *error) {
+
+ const char *sender;
+ int r;
+ int fd;
+
+ /*
+ If sender exists then
+ if sender is NULL this indicates a local connection. Grab the fd
+ from dbus and do an getpeercon to peers process 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;
+
+ log_error("bus_get_selinux_security_context failed: %m");
+ 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;
+ }
+
+ r = getpeercon(fd, scon);
+ if (r < 0) {
+ log_error("getpeercon failed %m");
+ return -errno;
+ }
+
+ return 0;
+}
+
+/*
+ This function communicates with the kernel to check whether or not it should
+ allow the access.
+ If the machine is in permissive mode it will return ok. Audit messages will
+ still be generated if the access would be denied in enforcing mode.
+*/
+int selinux_access_check(
+ DBusConnection *connection,
+ DBusMessage *message,
+ const char *path,
+ const char *permission,
+ DBusError *error) {
+
+ security_context_t scon = NULL, fcon = NULL;
+ int r = 0;
+ const char *tclass = NULL;
+ struct auditstruct audit;
+
+ assert(connection);
+ assert(message);
+ assert(permission);
+ assert(error);
+
+ if (!use_selinux())
+ return 0;
+
+ r = selinux_access_init(error);
+ 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;
+ audit.path = path;
+
+ r = get_calling_context(connection, message, &scon, error);
+ if (r < 0) {
+ log_error("Failed to get caller's security context on: %m");
+ goto finish;
+ }
+
+ if (path) {
+ tclass = "service";
+ /* get the file context of the unit file */
+ r = getfilecon(path, &fcon);
+ if (r < 0) {
+ dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
+ r = -errno;
+ log_error("Failed to get security context on %s: %m",path);
+ goto finish;
+ }
+
+ } else {
+ tclass = "system";
+ r = getcon(&fcon);
+ if (r < 0) {
+ dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
+ r = -errno;
+ log_error("Failed to get current process context on: %m");
+ goto finish;
+ }
+ }
+
+ (void) get_audit_data(connection, message, &audit, error);
+
+ errno = 0;
+ r = selinux_check_access(scon, fcon, tclass, permission, &audit);
+ if (r < 0) {
+ dbus_set_error(error, DBUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
+ r = -errno;
+ log_error("SELinux policy denies access.");
+ }
+
+ log_debug("SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %i", scon, fcon, tclass, permission, path, audit.cmdline, r);
+
+finish:
+ free(audit.cmdline);
+ freecon(scon);
+ freecon(fcon);
+
+ if (r && security_getenforce() != 1) {
+ dbus_error_init(error);
+ r = 0;
+ }
+
+ return r;
+}
+
+#else
+
+int selinux_access_check(
+ DBusConnection *connection,
+ DBusMessage *message,
+ const char *path,
+ const char *permission,
+ DBusError *error) {
+
+ return 0;
+}
+
+void selinux_access_free(void) {
+}
+
+#endif
diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h
new file mode 100644
index 0000000000..9183cbc9a6
--- /dev/null
+++ b/src/core/selinux-access.h
@@ -0,0 +1,62 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Dan Walsh
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus.h>
+
+void selinux_access_free(void);
+
+int selinux_access_check(DBusConnection *connection, DBusMessage *message, const char *path, const char *permission, DBusError *error);
+
+#ifdef HAVE_SELINUX
+
+#define SELINUX_ACCESS_CHECK(connection, message, permission) \
+ do { \
+ DBusError _error; \
+ int _r; \
+ DBusConnection *_c = (connection); \
+ DBusMessage *_m = (message); \
+ dbus_error_init(&_error); \
+ _r = selinux_access_check(_c, _m, NULL, (permission), &_error); \
+ if (_r < 0) \
+ return bus_send_error_reply(_c, _m, &_error, _r); \
+ } while (false)
+
+#define SELINUX_UNIT_ACCESS_CHECK(unit, connection, message, permission) \
+ do { \
+ DBusError _error; \
+ int _r; \
+ DBusConnection *_c = (connection); \
+ DBusMessage *_m = (message); \
+ Unit *_u = (unit); \
+ dbus_error_init(&_error); \
+ _r = selinux_access_check(_c, _m, _u->source_path ?: _u->fragment_path, (permission), &_error); \
+ if (_r < 0) \
+ return bus_send_error_reply(_c, _m, &_error, _r); \
+ } while (false)
+
+#else
+
+#define SELINUX_ACCESS_CHECK(connection, message, permission) do { } while (false)
+#define SELINUX_UNIT_ACCESS_CHECK(unit, connection, message, permission) do { } while (false)
+
+#endif
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
index fa025b7268..e9c0de92f1 100644
--- a/src/core/selinux-setup.c
+++ b/src/core/selinux-setup.c
@@ -30,11 +30,12 @@
#endif
#include "selinux-setup.h"
+#include "selinux-util.h"
+#include "label.h"
#include "mount-setup.h"
#include "macro.h"
#include "util.h"
#include "log.h"
-#include "label.h"
#ifdef HAVE_SELINUX
static int null_log(int type, const char *fmt, ...) {
@@ -79,12 +80,11 @@ int selinux_setup(bool *loaded_policy) {
/* Now load the policy */
before_load = now(CLOCK_MONOTONIC);
r = selinux_init_load_policy(&enforce);
-
if (r == 0) {
char timespan[FORMAT_TIMESPAN_MAX];
char *label;
- label_retest_selinux();
+ retest_selinux();
/* Transition to the new context */
r = label_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label);
diff --git a/src/core/service.c b/src/core/service.c
index 1c127bdbcb..2a4e691e78 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -33,6 +33,7 @@
#include "log.h"
#include "strv.h"
#include "unit-name.h"
+#include "unit-printf.h"
#include "dbus-service.h"
#include "special.h"
#include "bus-errors.h"
@@ -48,8 +49,7 @@
typedef enum RunlevelType {
RUNLEVEL_UP,
- RUNLEVEL_DOWN,
- RUNLEVEL_SYSINIT
+ RUNLEVEL_DOWN
} RunlevelType;
static const struct {
@@ -64,16 +64,6 @@ static const struct {
{ "rc4.d", SPECIAL_RUNLEVEL4_TARGET, RUNLEVEL_UP },
{ "rc5.d", SPECIAL_RUNLEVEL5_TARGET, RUNLEVEL_UP },
-#ifdef TARGET_SUSE
- /* SUSE style boot.d */
- { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
-#endif
-
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- /* Debian style rcS.d */
- { "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
-#endif
-
/* Standard SysV runlevels for shutdown */
{ "rc0.d", SPECIAL_POWEROFF_TARGET, RUNLEVEL_DOWN },
{ "rc6.d", SPECIAL_REBOOT_TARGET, RUNLEVEL_DOWN }
@@ -82,12 +72,10 @@ static const struct {
directories in this order, and we want to make sure that
sysv_start_priority is known when we first load the
unit. And that value we only know from S links. Hence
- UP/SYSINIT must be read before DOWN */
+ UP must be read before DOWN */
};
#define RUNLEVELS_UP "12345"
-/* #define RUNLEVELS_DOWN "06" */
-#define RUNLEVELS_BOOT "bBsS"
#endif
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
@@ -134,13 +122,14 @@ static void service_init(Unit *u) {
assert(u);
assert(u->load_state == UNIT_STUB);
- s->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ s->timeout_start_usec = DEFAULT_TIMEOUT_USEC;
+ s->timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
s->restart_usec = DEFAULT_RESTART_USEC;
s->type = _SERVICE_TYPE_INVALID;
- s->watchdog_watch.type = WATCH_INVALID;
+ watch_init(&s->watchdog_watch);
+ watch_init(&s->timer_watch);
- s->timer_watch.type = WATCH_INVALID;
#ifdef HAVE_SYSV_COMPAT
s->sysv_start_priority = -1;
s->sysv_start_priority_from_rcnd = -1;
@@ -180,7 +169,8 @@ static void service_unwatch_pid_file(Service *s) {
if (!s->pid_file_pathspec)
return;
- log_debug("Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
+ log_debug_unit(UNIT(s)->id, "Stopping watch for %s's PID file %s",
+ UNIT(s)->id, s->pid_file_pathspec->path);
path_spec_unwatch(s->pid_file_pathspec, UNIT(s));
path_spec_done(s->pid_file_pathspec);
free(s->pid_file_pathspec);
@@ -202,8 +192,9 @@ static int service_set_main_pid(Service *s, pid_t pid) {
s->main_pid_known = true;
if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
- log_warning("%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.",
- UNIT(s)->id, (unsigned long) pid);
+ log_warning_unit(UNIT(s)->id,
+ "%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.",
+ UNIT(s)->id, (unsigned long) pid);
s->main_pid_alien = true;
} else
@@ -255,14 +246,16 @@ static void service_handle_watchdog(Service *s) {
offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic;
if (offset >= s->watchdog_usec) {
- log_error("%s watchdog timeout!", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id, "%s watchdog timeout!", UNIT(s)->id);
service_enter_dead(s, SERVICE_FAILURE_WATCHDOG, true);
return;
}
- r = unit_watch_timer(UNIT(s), s->watchdog_usec - offset, &s->watchdog_watch);
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->watchdog_usec - offset, &s->watchdog_watch);
if (r < 0)
- log_warning("%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to install watchdog timer: %s",
+ UNIT(s)->id, strerror(-r));
}
static void service_reset_watchdog(Service *s) {
@@ -293,6 +286,16 @@ static void service_done(Unit *u) {
s->control_command = NULL;
s->main_command = NULL;
+ set_free(s->restart_ignore_status.code);
+ s->restart_ignore_status.code = NULL;
+ set_free(s->restart_ignore_status.signal);
+ s->restart_ignore_status.signal = NULL;
+
+ set_free(s->success_status.code);
+ s->success_status.code = NULL;
+ set_free(s->success_status.signal);
+ s->success_status.signal = NULL;
+
/* This will leak a process, but at least no memory or any of
* our resources */
service_unwatch_main_pid(s);
@@ -322,21 +325,12 @@ static char *sysv_translate_name(const char *name) {
if (!(r = new(char, strlen(name) + sizeof(".service"))))
return NULL;
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
if (endswith(name, ".sh"))
/* Drop Debian-style .sh suffix */
strcpy(stpcpy(r, name) - 3, ".service");
-#endif
-#ifdef TARGET_SUSE
- if (startswith(name, "boot."))
- /* Drop SuSE-style boot. prefix */
- strcpy(stpcpy(r, name + 5), ".service");
-#endif
-#ifdef TARGET_FRUGALWARE
if (startswith(name, "rc."))
/* Drop Frugalware-style rc. prefix */
strcpy(stpcpy(r, name + 3), ".service");
-#endif
else
/* Normal init scripts */
strcpy(stpcpy(r, name), ".service");
@@ -355,14 +349,11 @@ 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,
-#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-#else
/* 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 */
"network", SPECIAL_NETWORK_TARGET,
-#endif
"named", SPECIAL_NSS_LOOKUP_TARGET,
"portmap", SPECIAL_RPCBIND_TARGET,
"remote_fs", SPECIAL_REMOTE_FS_TARGET,
@@ -373,20 +364,8 @@ static int sysv_translate_facility(const char *name, const char *filename, char
"mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
"x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE,
"null", NULL,
-
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
"mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
-#endif
-
-#ifdef TARGET_FEDORA
- "MTA", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "smtpdaemon", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
- "httpd", SPECIAL_HTTP_DAEMON_TARGET,
-#endif
-
-#ifdef TARGET_SUSE
"smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
-#endif
};
unsigned i;
@@ -514,7 +493,7 @@ static ExecCommand *exec_command_new(const char *path, const char *arg1) {
return c;
}
-static int sysv_exec_commands(Service *s) {
+static int sysv_exec_commands(Service *s, const bool supports_reload) {
ExecCommand *c;
assert(s);
@@ -531,14 +510,25 @@ static int sysv_exec_commands(Service *s) {
return -ENOMEM;
exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c);
- c = exec_command_new(UNIT(s)->source_path, "reload");
- if (!c)
- return -ENOMEM;
- exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c);
+ if (supports_reload) {
+ c = exec_command_new(UNIT(s)->source_path, "reload");
+ if (!c)
+ return -ENOMEM;
+ exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c);
+ }
return 0;
}
+static bool usage_contains_reload(const char *line) {
+ return (strcasestr(line, "{reload|") ||
+ strcasestr(line, "{reload}") ||
+ strcasestr(line, "{reload\"") ||
+ strcasestr(line, "|reload|") ||
+ strcasestr(line, "|reload}") ||
+ strcasestr(line, "|reload\""));
+}
+
static int service_load_sysv_path(Service *s, const char *path) {
FILE *f;
Unit *u;
@@ -548,10 +538,12 @@ static int service_load_sysv_path(Service *s, const char *path) {
NORMAL,
DESCRIPTION,
LSB,
- LSB_DESCRIPTION
+ LSB_DESCRIPTION,
+ USAGE_CONTINUATION
} state = NORMAL;
char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
struct stat st;
+ bool supports_reload = false;
assert(s);
assert(path);
@@ -593,15 +585,32 @@ static int service_load_sysv_path(Service *s, const char *path) {
break;
r = -errno;
- log_error("Failed to read configuration file '%s': %s", path, strerror(-r));
+ log_error_unit(u->id,
+ "Failed to read configuration file '%s': %s",
+ path, strerror(-r));
goto finish;
}
line++;
t = strstrip(l);
- if (*t != '#')
+ if (*t != '#') {
+ /* Try to figure out whether this init script supports
+ * the reload operation. This heuristic looks for
+ * "Usage" lines which include the reload option. */
+ if ( state == USAGE_CONTINUATION ||
+ (state == NORMAL && strcasestr(t, "usage"))) {
+ if (usage_contains_reload(t)) {
+ supports_reload = true;
+ state = NORMAL;
+ } else if (t[strlen(t)-1] == '\\')
+ state = USAGE_CONTINUATION;
+ else
+ state = NORMAL;
+ }
+
continue;
+ }
if (state == NORMAL && streq(t, "### BEGIN INIT INFO")) {
state = LSB;
@@ -631,7 +640,9 @@ static int service_load_sysv_path(Service *s, const char *path) {
runlevels,
&start_priority) != 2) {
- log_warning("[%s:%u] Failed to parse chkconfig line. Ignoring.", path, line);
+ log_warning_unit(u->id,
+ "[%s:%u] Failed to parse chkconfig line. Ignoring.",
+ path, line);
continue;
}
@@ -639,7 +650,9 @@ static int service_load_sysv_path(Service *s, const char *path) {
* symlink farms is preferred over the
* data from the LSB header. */
if (start_priority < 0 || start_priority > 99)
- log_warning("[%s:%u] Start priority out of range. Ignoring.", path, line);
+ log_warning_unit(u->id,
+ "[%s:%u] Start priority out of range. Ignoring.",
+ path, line);
else
s->sysv_start_priority = start_priority;
@@ -688,7 +701,9 @@ static int service_load_sysv_path(Service *s, const char *path) {
fn = strstrip(t+8);
if (!path_is_absolute(fn)) {
- log_warning("[%s:%u] PID file not absolute. Ignoring.", path, line);
+ log_warning_unit(u->id,
+ "[%s:%u] PID file not absolute. Ignoring.",
+ path, line);
continue;
}
@@ -775,7 +790,9 @@ static int service_load_sysv_path(Service *s, const char *path) {
r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_WANTS, m, NULL, true);
if (r < 0)
- log_error("[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", path, line, m, strerror(-r));
+ log_error_unit(u->id,
+ "[%s:%u] Failed to add LSB Provides name %s, ignoring: %s",
+ path, line, m, strerror(-r));
free(m);
}
@@ -800,7 +817,9 @@ 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("[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", path, line, n, strerror(-r));
+ log_error_unit(u->id,
+ "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s",
+ path, line, n, strerror(-r));
free(n);
continue;
}
@@ -813,7 +832,8 @@ static int service_load_sysv_path(Service *s, const char *path) {
r = unit_add_dependency_by_name(u, startswith_no_case(t, "X-Start-Before:") ? UNIT_BEFORE : UNIT_AFTER, m, NULL, true);
if (r < 0)
- log_error("[%s:%u] Failed to add dependency on %s, ignoring: %s", path, line, m, strerror(-r));
+ log_error_unit(u->id, "[%s:%u] Failed to add dependency on %s, ignoring: %s",
+ path, line, m, strerror(-r));
free(m);
}
@@ -894,15 +914,8 @@ static int service_load_sysv_path(Service *s, const char *path) {
}
}
- if ((r = sysv_exec_commands(s)) < 0)
+ if ((r = sysv_exec_commands(s, supports_reload)) < 0)
goto finish;
- if (s->sysv_runlevels &&
- chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) &&
- chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
- /* Service has both boot and "up" runlevels
- configured. Kill the "up" ones. */
- delete_chars(s->sysv_runlevels, RUNLEVELS_UP);
- }
if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) {
/* If there a runlevels configured for this service
@@ -914,9 +927,12 @@ static int service_load_sysv_path(Service *s, const char *path) {
UNIT(s)->default_dependencies = false;
/* Don't timeout special services during boot (like fsck) */
- s->timeout_usec = 0;
- } else
- s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC;
+ s->timeout_start_usec = 0;
+ s->timeout_stop_usec = 0;
+ } else {
+ s->timeout_start_usec = DEFAULT_SYSV_TIMEOUT_USEC;
+ s->timeout_stop_usec = DEFAULT_SYSV_TIMEOUT_USEC;
+ }
/* Special setting for all SysV services */
s->type = SERVICE_FORKING;
@@ -976,22 +992,11 @@ static int service_load_sysv_name(Service *s, const char *name) {
assert(s);
assert(name);
- /* For SysV services we strip the boot.*, rc.* and *.sh
+ /* For SysV services we strip the rc.* and *.sh
* prefixes/suffixes. */
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
- if (endswith(name, ".sh.service"))
+ if (startswith(name, "rc.") ||
+ endswith(name, ".sh.service"))
return -ENOENT;
-#endif
-
-#ifdef TARGET_SUSE
- if (startswith(name, "boot."))
- return -ENOENT;
-#endif
-
-#ifdef TARGET_FRUGALWARE
- if (startswith(name, "rc."))
- return -ENOENT;
-#endif
STRV_FOREACH(p, UNIT(s)->manager->lookup_paths.sysvinit_path) {
char *path;
@@ -1006,31 +1011,13 @@ static int service_load_sysv_name(Service *s, const char *name) {
r = service_load_sysv_path(s, path);
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM)
if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
/* Try Debian style *.sh source'able init scripts */
strcat(path, ".sh");
r = service_load_sysv_path(s, path);
}
-#endif
free(path);
-#ifdef TARGET_SUSE
- if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
- /* Try SUSE style boot.* init scripts */
-
- path = strjoin(*p, "/boot.", name, NULL);
- if (!path)
- return -ENOMEM;
-
- /* Drop .service suffix */
- path[strlen(path)-8] = 0;
- r = service_load_sysv_path(s, path);
- free(path);
- }
-#endif
-
-#ifdef TARGET_FRUGALWARE
if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
/* Try Frugalware style rc.* init scripts */
@@ -1043,12 +1030,11 @@ static int service_load_sysv_name(Service *s, const char *name) {
r = service_load_sysv_path(s, path);
free(path);
}
-#endif
if (r < 0)
return r;
- if ((UNIT(s)->load_state != UNIT_STUB))
+ if (UNIT(s)->load_state != UNIT_STUB)
break;
}
@@ -1136,32 +1122,31 @@ static int service_verify(Service *s) {
return 0;
if (!s->exec_command[SERVICE_EXEC_START]) {
- log_error("%s lacks ExecStart setting. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s lacks ExecStart setting. Refusing.", UNIT(s)->id);
return -EINVAL;
}
if (s->type != SERVICE_ONESHOT &&
s->exec_command[SERVICE_EXEC_START]->command_next) {
- log_error("%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
- return -EINVAL;
- }
-
- if (s->type == SERVICE_ONESHOT &&
- s->exec_command[SERVICE_EXEC_RELOAD]) {
- log_error("%s has an ExecReload setting, which is not allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s has more than one ExecStart setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
return -EINVAL;
}
if (s->type == SERVICE_DBUS && !s->bus_name) {
- log_error("%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id);
return -EINVAL;
}
if (s->bus_name && s->type != SERVICE_DBUS)
- log_warning("%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id);
+ log_warning_unit(UNIT(s)->id,
+ "%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id);
if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
return -EINVAL;
}
@@ -1177,12 +1162,12 @@ static int service_add_default_dependencies(Service *s) {
* majority of services. */
/* First, pull in base system */
- if (UNIT(s)->manager->running_as == MANAGER_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)
return r;
- } else if (UNIT(s)->manager->running_as == MANAGER_USER) {
+ } else if (UNIT(s)->manager->running_as == SYSTEMD_USER) {
if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
return r;
@@ -1241,9 +1226,9 @@ static int service_load(Unit *u) {
if (s->type == _SERVICE_TYPE_INVALID)
s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE;
- /* Oneshot services have disabled timeout by default */
- if (s->type == SERVICE_ONESHOT && !s->timeout_defined)
- s->timeout_usec = 0;
+ /* Oneshot services have disabled start timeout by default */
+ if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
+ s->timeout_start_usec = 0;
service_fix_output(s);
@@ -1403,8 +1388,9 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if ((r = read_one_line_file(s->pid_file, &k)) < 0) {
if (may_warn)
- log_info("PID file %s not readable (yet?) after %s.",
- s->pid_file, service_state_to_string(s->state));
+ log_info_unit(UNIT(s)->id,
+ "PID file %s not readable (yet?) after %s.",
+ s->pid_file, service_state_to_string(s->state));
return r;
}
@@ -1416,8 +1402,9 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (kill(pid, 0) < 0 && errno != EPERM) {
if (may_warn)
- log_info("PID %lu read from file %s does not exist.",
- (unsigned long) pid, s->pid_file);
+ log_info_unit(UNIT(s)->id,
+ "PID %lu read from file %s does not exist.",
+ (unsigned long) pid, s->pid_file);
return -ESRCH;
}
@@ -1425,12 +1412,14 @@ static int service_load_pid_file(Service *s, bool may_warn) {
if (pid == s->main_pid)
return 0;
- log_debug("Main PID changing: %lu -> %lu",
- (unsigned long) s->main_pid, (unsigned long) pid);
+ log_debug_unit(UNIT(s)->id,
+ "Main PID changing: %lu -> %lu",
+ (unsigned long) s->main_pid, (unsigned long) pid);
service_unwatch_main_pid(s);
s->main_pid_known = false;
} else
- log_debug("Main PID loaded: %lu", (unsigned long) pid);
+ log_debug_unit(UNIT(s)->id,
+ "Main PID loaded: %lu", (unsigned long) pid);
if ((r = service_set_main_pid(s, pid)) < 0)
return r;
@@ -1461,7 +1450,8 @@ static int service_search_main_pid(Service *s) {
if ((pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings)) <= 0)
return -ENOENT;
- log_debug("Main PID guessed: %lu", (unsigned long) pid);
+ log_debug_unit(UNIT(s)->id,
+ "Main PID guessed: %lu", (unsigned long) pid);
if ((r = service_set_main_pid(s, pid)) < 0)
return r;
@@ -1577,7 +1567,10 @@ static void service_set_state(Service *s, ServiceState state) {
cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true);
if (old_state != state)
- log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
+ log_debug_unit(UNIT(s)->id,
+ "%s changed %s -> %s", UNIT(s)->id,
+ service_state_to_string(old_state),
+ service_state_to_string(state));
unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
s->reload_result = SERVICE_SUCCESS;
@@ -1603,13 +1596,13 @@ static int service_coldplug(Unit *u) {
s->deserialized_state == SERVICE_FINAL_SIGTERM ||
s->deserialized_state == SERVICE_FINAL_SIGKILL ||
s->deserialized_state == SERVICE_AUTO_RESTART) {
-
- if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_usec > 0) {
+ if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_start_usec > 0) {
usec_t k;
- k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_usec;
+ k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_start_usec;
- if ((r = unit_watch_timer(UNIT(s), k, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, k, &s->timer_watch);
+ if (r < 0)
return r;
}
}
@@ -1753,8 +1746,9 @@ static int service_spawn(
}
}
- if (timeout && s->timeout_usec) {
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ if (timeout && s->timeout_start_usec) {
+ 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);
@@ -1764,7 +1758,8 @@ static int service_spawn(
goto fail;
}
- if (!(our_env = new0(char*, 4))) {
+ our_env = new0(char*, 5);
+ if (!our_env) {
r = -ENOMEM;
goto fail;
}
@@ -1787,10 +1782,14 @@ static int service_spawn(
goto fail;
}
- if (!(final_env = strv_env_merge(2,
- UNIT(s)->manager->environment,
- our_env,
- NULL))) {
+ if (s->meta.manager->running_as != SYSTEMD_SYSTEM)
+ if (asprintf(our_env + n_env++, "MANAGERPID=%lu", (unsigned long) getpid()) < 0) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
+ if (!final_env) {
r = -ENOMEM;
goto fail;
}
@@ -1814,7 +1813,6 @@ static int service_spawn(
if (r < 0)
goto fail;
-
if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
/* FIXME: we need to do something here */
goto fail;
@@ -1897,9 +1895,14 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
(s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
- s->result == SERVICE_FAILURE_CORE_DUMP)))) {
-
- r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
+ s->result == SERVICE_FAILURE_CORE_DUMP))) &&
+ (s->result != SERVICE_FAILURE_EXIT_CODE ||
+ !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) &&
+ (s->result != SERVICE_FAILURE_SIGNAL ||
+ !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status)))
+ ) {
+
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->restart_usec, &s->timer_watch);
if (r < 0)
goto fail;
@@ -1911,7 +1914,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
return;
fail:
- log_warning("%s failed to run install restart timer: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run install restart timer: %s",
+ UNIT(s)->id, strerror(-r));
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
}
@@ -1950,7 +1955,9 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
return;
fail:
- log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'stop-post' task: %s",
+ UNIT(s)->id, strerror(-r));
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
@@ -1969,14 +1976,16 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
if (s->main_pid > 0) {
if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH)
- log_warning("Failed to kill main process %li: %m", (long) s->main_pid);
+ 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("Failed to kill control process %li: %m", (long) s->control_pid);
+ log_warning_unit(UNIT(s)->id,
+ "Failed to kill control process %li: %m", (long) s->control_pid);
else
wait_for_exit = true;
}
@@ -2001,7 +2010,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
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("Failed to kill control group: %s", strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "Failed to kill control group: %s", strerror(-r));
} else if (r > 0)
wait_for_exit = true;
@@ -2011,9 +2021,11 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
}
if (wait_for_exit) {
- if (s->timeout_usec > 0)
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ if (s->timeout_stop_usec > 0) {
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
+ }
service_set_state(s, state);
} else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL)
@@ -2024,7 +2036,8 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
return;
fail:
- log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL)
service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
@@ -2068,7 +2081,8 @@ static void service_enter_stop(Service *s, ServiceResult f) {
return;
fail:
- log_warning("%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r));
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
@@ -2123,7 +2137,8 @@ static void service_enter_start_post(Service *s) {
return;
fail:
- log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
}
@@ -2206,7 +2221,8 @@ static void service_enter_start(Service *s) {
return;
fail:
- log_warning("%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r));
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
@@ -2245,7 +2261,8 @@ static void service_enter_start_pre(Service *s) {
return;
fail:
- log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
}
@@ -2258,9 +2275,10 @@ static void service_enter_restart(Service *s) {
if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) {
/* Don't restart things if we are going down anyway */
- log_info("Stop job pending for unit, delaying automatic restart.");
+ log_info_unit(UNIT(s)->id,
+ "Stop job pending for unit, delaying automatic restart.");
- r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->restart_usec, &s->timer_watch);
if (r < 0)
goto fail;
@@ -2279,11 +2297,14 @@ static void service_enter_restart(Service *s) {
* it will be canceled as part of the service_stop() call that
* is executed as part of JOB_RESTART. */
- log_debug("%s scheduled restart job.", UNIT(s)->id);
+ log_debug_unit(UNIT(s)->id,
+ "%s scheduled restart job.", UNIT(s)->id);
return;
fail:
- log_warning("%s failed to schedule restart job: %s", UNIT(s)->id, bus_error(&error, -r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to schedule restart job: %s",
+ UNIT(s)->id, bus_error(&error, -r));
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
dbus_error_free(&error);
@@ -2319,7 +2340,9 @@ static void service_enter_reload(Service *s) {
return;
fail:
- log_warning("%s failed to run 'reload' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'reload' task: %s",
+ UNIT(s)->id, strerror(-r));
s->reload_result = SERVICE_FAILURE_RESOURCES;
service_enter_running(s, SERVICE_SUCCESS);
}
@@ -2353,7 +2376,9 @@ static void service_run_next_control(Service *s) {
return;
fail:
- log_warning("%s failed to run next control task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run next control task: %s",
+ UNIT(s)->id, strerror(-r));
if (s->state == SERVICE_START_PRE)
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
@@ -2398,7 +2423,8 @@ static void service_run_next_main(Service *s) {
return;
fail:
- log_warning("%s failed to run next main task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run next main task: %s", UNIT(s)->id, strerror(-r));
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
}
@@ -2411,7 +2437,9 @@ static int service_start_limit_test(Service *s) {
switch (s->start_limit_action) {
case SERVICE_START_LIMIT_NONE:
- log_warning("%s start request repeated too quickly, refusing to start.", UNIT(s)->id);
+ log_warning_unit(UNIT(s)->id,
+ "%s start request repeated too quickly, refusing to start.",
+ UNIT(s)->id);
break;
case SERVICE_START_LIMIT_REBOOT: {
@@ -2420,11 +2448,13 @@ static int service_start_limit_test(Service *s) {
dbus_error_init(&error);
- log_warning("%s start request repeated too quickly, rebooting.", UNIT(s)->id);
+ 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);
if (r < 0) {
- log_error("Failed to reboot: %s.", bus_error(&error, r));
+ log_error_unit(UNIT(s)->id,
+ "Failed to reboot: %s.", bus_error(&error, r));
dbus_error_free(&error);
}
@@ -2432,17 +2462,21 @@ static int service_start_limit_test(Service *s) {
}
case SERVICE_START_LIMIT_REBOOT_FORCE:
- log_warning("%s start request repeated too quickly, forcibly rebooting.", UNIT(s)->id);
+ log_warning_unit(UNIT(s)->id,
+ "%s start request repeated too quickly, forcibly rebooting.", UNIT(s)->id);
UNIT(s)->manager->exit_code = MANAGER_REBOOT;
break;
case SERVICE_START_LIMIT_REBOOT_IMMEDIATE:
- log_warning("%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id);
+ log_warning_unit(UNIT(s)->id,
+ "%s start request repeated too quickly, rebooting immediately.", UNIT(s)->id);
+ sync();
reboot(RB_AUTOBOOT);
break;
default:
- log_error("start limit action=%i", s->start_limit_action);
+ log_error_unit(UNIT(s)->id,
+ "start limit action=%i", s->start_limit_action);
assert_not_reached("Unknown StartLimitAction.");
}
@@ -2486,7 +2520,7 @@ static int service_start(Unit *u) {
/* Make sure we don't enter a busy loop of some kind. */
r = service_start_limit_test(s);
if (r < 0) {
- service_notify_sockets_dead(s, true);
+ service_enter_dead(s, SERVICE_FAILURE_START_LIMIT, false);
return r;
}
@@ -2624,7 +2658,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
ServiceState state;
if ((state = service_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
+ log_debug_unit(u->id, "Failed to parse state value %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -2632,7 +2666,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
f = service_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse result value %s", value);
+ log_debug_unit(u->id, "Failed to parse result value %s", value);
else if (f != SERVICE_SUCCESS)
s->result = f;
@@ -2641,7 +2675,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
f = service_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse reload result value %s", value);
+ log_debug_unit(u->id, "Failed to parse reload result value %s", value);
else if (f != SERVICE_SUCCESS)
s->reload_result = f;
@@ -2649,21 +2683,21 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
+ log_debug_unit(u->id, "Failed to parse control-pid value %s", value);
else
s->control_pid = pid;
} else if (streq(key, "main-pid")) {
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse main-pid value %s", value);
+ log_debug_unit(u->id, "Failed to parse main-pid value %s", value);
else
service_set_main_pid(s, (pid_t) pid);
} else if (streq(key, "main-pid-known")) {
int b;
if ((b = parse_boolean(value)) < 0)
- log_debug("Failed to parse main-pid-known value %s", value);
+ 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")) {
@@ -2678,7 +2712,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
ServiceExecCommand id;
if ((id = service_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
+ log_debug_unit(u->id, "Failed to parse exec-command value %s", value);
else {
s->control_command_id = id;
s->control_command = s->exec_command[id];
@@ -2687,7 +2721,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
int fd;
if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse socket-fd value %s", value);
+ log_debug_unit(u->id, "Failed to parse socket-fd value %s", value);
else {
if (s->socket_fd >= 0)
@@ -2698,21 +2732,21 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse main-exec-status-pid value %s", value);
+ log_debug_unit(u->id, "Failed to parse main-exec-status-pid value %s", value);
else
s->main_exec_status.pid = pid;
} else if (streq(key, "main-exec-status-code")) {
int i;
if (safe_atoi(value, &i) < 0)
- log_debug("Failed to parse main-exec-status-code value %s", value);
+ log_debug_unit(u->id, "Failed to parse main-exec-status-code value %s", value);
else
s->main_exec_status.code = i;
} else if (streq(key, "main-exec-status-status")) {
int i;
if (safe_atoi(value, &i) < 0)
- log_debug("Failed to parse main-exec-status-status value %s", value);
+ log_debug_unit(u->id, "Failed to parse main-exec-status-status value %s", value);
else
s->main_exec_status.status = i;
} else if (streq(key, "main-exec-status-start"))
@@ -2722,7 +2756,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
else if (streq(key, "watchdog-timestamp"))
dual_timestamp_deserialize(value, &s->watchdog_timestamp);
else
- log_debug("Unknown serialization key '%s'", key);
+ log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
@@ -2790,7 +2824,9 @@ static int service_retry_pid_file(Service *s) {
static int service_watch_pid_file(Service *s) {
int r;
- log_debug("Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path);
+ log_debug_unit(UNIT(s)->id,
+ "Setting watch for %s's PID file %s",
+ UNIT(s)->id, s->pid_file_pathspec->path);
r = path_spec_watch(s->pid_file_pathspec, UNIT(s));
if (r < 0)
goto fail;
@@ -2800,8 +2836,9 @@ static int service_watch_pid_file(Service *s) {
return 0;
fail:
- log_error("Failed to set a watch for %s's PID file %s: %s",
- UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r));
+ log_error_unit(UNIT(s)->id,
+ "Failed to set a watch for %s's PID file %s: %s",
+ UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r));
service_unwatch_pid_file(s);
return r;
}
@@ -2843,7 +2880,7 @@ static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
assert(s->pid_file_pathspec);
assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd));
- log_debug("inotify event for %s", u->id);
+ log_debug_unit(u->id, "inotify event for %s", u->id);
if (path_spec_fd_event(s->pid_file_pathspec, events) < 0)
goto fail;
@@ -2867,7 +2904,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
- if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status))
+ if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) :
+ is_clean_exit_lsb(code, status, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)
f = SERVICE_FAILURE_EXIT_CODE;
@@ -2899,8 +2937,16 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SERVICE_SUCCESS;
}
- log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s: main process exited, code=%s, status=%i", u->id, sigchld_code_to_string(code), status);
+ log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ "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);
if (f != SERVICE_SUCCESS)
s->result = f;
@@ -2912,7 +2958,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* There is another command to *
* execute, so let's do that. */
- log_debug("%s running next main command for state %s", u->id, service_state_to_string(s->state));
+ log_debug_unit(u->id,
+ "%s running next main command for state %s",
+ u->id, service_state_to_string(s->state));
service_run_next_main(s);
} else {
@@ -2938,12 +2986,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
break;
- } else {
- assert(s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY);
-
- /* Fall through */
}
+ /* Fall through */
+
case SERVICE_RUNNING:
service_enter_running(s, f);
break;
@@ -2973,8 +3019,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SERVICE_SUCCESS;
}
- log_full(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s: control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+ log_full_unit(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, u->id,
+ "%s: control process exited, code=%s status=%i",
+ u->id, sigchld_code_to_string(code), status);
if (f != SERVICE_SUCCESS)
s->result = f;
@@ -2991,7 +3038,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* There is another command to *
* execute, so let's do that. */
- log_debug("%s running next control command for state %s", u->id, service_state_to_string(s->state));
+ log_debug_unit(u->id,
+ "%s running next control command for state %s",
+ u->id, service_state_to_string(s->state));
service_run_next_control(s);
} else {
@@ -3001,7 +3050,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command = NULL;
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
- log_debug("%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state));
+ log_debug_unit(u->id,
+ "%s got final SIGCHLD for state %s",
+ u->id, service_state_to_string(s->state));
switch (s->state) {
@@ -3013,7 +3064,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
case SERVICE_START:
- assert(s->type == SERVICE_FORKING);
+ if (s->type != SERVICE_FORKING)
+ /* Maybe spurious event due to a reload that changed the type? */
+ break;
if (f != SERVICE_SUCCESS) {
service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
@@ -3121,32 +3174,38 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
case SERVICE_START_PRE:
case SERVICE_START:
- log_warning("%s operation timed out. Terminating.", u->id);
+ log_warning_unit(u->id,
+ "%s operation timed out. Terminating.", u->id);
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_START_POST:
- log_warning("%s operation timed out. Stopping.", u->id);
+ log_warning_unit(u->id,
+ "%s operation timed out. Stopping.", u->id);
service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_RELOAD:
- log_warning("%s operation timed out. Stopping.", u->id);
+ log_warning_unit(u->id,
+ "%s operation timed out. Stopping.", u->id);
s->reload_result = SERVICE_FAILURE_TIMEOUT;
service_enter_running(s, SERVICE_SUCCESS);
break;
case SERVICE_STOP:
- log_warning("%s stopping timed out. Terminating.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out. Terminating.", u->id);
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_STOP_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_warning("%s stopping timed out. Killing.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out. Killing.", u->id);
service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
} else {
- log_warning("%s stopping timed out. Skipping SIGKILL.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out. Skipping SIGKILL.", u->id);
service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
}
@@ -3157,33 +3216,40 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
* Must be something we cannot kill, so let's just be
* weirded out and continue */
- log_warning("%s still around after SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s still around after SIGKILL. Ignoring.", u->id);
service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_STOP_POST:
- log_warning("%s stopping timed out (2). Terminating.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out (2). Terminating.", u->id);
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_FINAL_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_warning("%s stopping timed out (2). Killing.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out (2). Killing.", u->id);
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
} else {
- log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.",
+ u->id);
service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false);
}
break;
case SERVICE_FINAL_SIGKILL:
- log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id);
+ log_warning_unit(u->id,
+ "%s still around after SIGKILL (2). Entering failed mode.", u->id);
service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true);
break;
case SERVICE_AUTO_RESTART:
- log_info("%s holdoff time over, scheduling restart.", u->id);
+ log_info_unit(u->id,
+ "%s holdoff time over, scheduling restart.", u->id);
service_enter_restart(s);
break;
@@ -3197,7 +3263,8 @@ static void service_cgroup_notify_event(Unit *u) {
assert(u);
- log_debug("%s: cgroup is empty", u->id);
+ log_debug_unit(u->id,
+ "%s: cgroup is empty", u->id);
switch (s->state) {
@@ -3212,7 +3279,8 @@ static void service_cgroup_notify_event(Unit *u) {
/* If we were hoping for the daemon to write its PID file,
* we can give up now. */
if (s->pid_file_pathspec) {
- log_warning("%s never wrote its PID file. Failing.", UNIT(s)->id);
+ log_warning_unit(u->id,
+ "%s never wrote its PID file. Failing.", UNIT(s)->id);
service_unwatch_pid_file(s);
if (s->state == SERVICE_START)
service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES);
@@ -3253,18 +3321,21 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
assert(u);
if (s->notify_access == NOTIFY_NONE) {
- log_warning("%s: Got notification message from PID %lu, but reception is disabled.",
- u->id, (unsigned long) pid);
+ log_warning_unit(u->id,
+ "%s: Got notification message from PID %lu, but reception is disabled.",
+ u->id, (unsigned long) pid);
return;
}
if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) {
- log_warning("%s: Got notification message from PID %lu, but reception only permitted for PID %lu",
- u->id, (unsigned long) pid, (unsigned long) s->main_pid);
+ log_warning_unit(u->id,
+ "%s: Got notification message from PID %lu, but reception only permitted for PID %lu",
+ u->id, (unsigned long) pid, (unsigned long) s->main_pid);
return;
}
- log_debug("%s: Got message", u->id);
+ log_debug_unit(u->id,
+ "%s: Got message", u->id);
/* Interpret MAINPID= */
if ((e = strv_find_prefix(tags, "MAINPID=")) &&
@@ -3274,9 +3345,11 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
s->state == SERVICE_RELOAD)) {
if (parse_pid(e + 8, &pid) < 0)
- log_warning("Failed to parse notification message %s", e);
+ log_warning_unit(u->id,
+ "Failed to parse notification message %s", e);
else {
- log_debug("%s: got %s", u->id, e);
+ log_debug_unit(u->id,
+ "%s: got %s", u->id, e);
service_set_main_pid(s, pid);
}
}
@@ -3285,7 +3358,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
if (s->type == SERVICE_NOTIFY &&
s->state == SERVICE_START &&
strv_find(tags, "READY=1")) {
- log_debug("%s: got READY=1", u->id);
+ log_debug_unit(u->id,
+ "%s: got READY=1", u->id);
service_enter_start_post(s);
}
@@ -3298,17 +3372,20 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
if (e[7]) {
if (!utf8_is_valid(e+7)) {
- log_warning("Status message in notification is not UTF-8 clean.");
+ log_warning_unit(u->id,
+ "Status message in notification is not UTF-8 clean.");
return;
}
t = strdup(e+7);
if (!t) {
- log_error("Failed to allocate string.");
+ log_error_unit(u->id,
+ "Failed to allocate string.");
return;
}
- log_debug("%s: got %s", u->id, e);
+ log_debug_unit(u->id,
+ "%s: got %s", u->id, e);
free(s->status_text);
s->status_text = t;
@@ -3319,7 +3396,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
}
if (strv_find(tags, "WATCHDOG=1")) {
- log_debug("%s: got WATCHDOG=1", u->id);
+ log_debug_unit(u->id,
+ "%s: got WATCHDOG=1", u->id);
service_reset_watchdog(s);
}
@@ -3329,71 +3407,6 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
#ifdef HAVE_SYSV_COMPAT
-#ifdef TARGET_SUSE
-static void sysv_facility_in_insserv_conf(Manager *mgr) {
- FILE *f=NULL;
- int r;
-
- if (!(f = fopen("/etc/insserv.conf", "re"))) {
- r = errno == ENOENT ? 0 : -errno;
- goto finish;
- }
-
- while (!feof(f)) {
- char l[LINE_MAX], *t;
- char **parsed = NULL;
-
- if (!fgets(l, sizeof(l), f)) {
- if (feof(f))
- break;
-
- r = -errno;
- log_error("Failed to read configuration file '/etc/insserv.conf': %s", strerror(-r));
- goto finish;
- }
-
- t = strstrip(l);
- if (*t != '$' && *t != '<')
- continue;
-
- parsed = strv_split(t,WHITESPACE);
- /* we ignore <interactive>, not used, equivalent to X-Interactive */
- if (parsed && !startswith_no_case (parsed[0], "<interactive>")) {
- char *facility;
- Unit *u;
- if (sysv_translate_facility(parsed[0], NULL, &facility) < 0)
- continue;
- if ((u = manager_get_unit(mgr, facility)) && (u->type == UNIT_TARGET)) {
- UnitDependency e;
- char *dep = NULL, *name, **j;
-
- STRV_FOREACH (j, parsed+1) {
- if (*j[0]=='+') {
- e = UNIT_WANTS;
- name = *j+1;
- }
- else {
- e = UNIT_REQUIRES;
- name = *j;
- }
- if (sysv_translate_facility(name, NULL, &dep) < 0)
- continue;
-
- r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, e, dep, NULL, true);
- free(dep);
- }
- }
- free(facility);
- }
- strv_free(parsed);
- }
-finish:
- if (f)
- fclose(f);
-
-}
-#endif
-
static int service_enumerate(Manager *m) {
char **p;
unsigned i;
@@ -3406,7 +3419,7 @@ static int service_enumerate(Manager *m) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (m->running_as != SYSTEMD_SYSTEM)
return 0;
zero(runlevel_services);
@@ -3471,14 +3484,15 @@ static int service_enumerate(Manager *m) {
goto finish;
}
- if ((r = manager_load_unit_prepare(m, name, NULL, NULL, &service)) < 0) {
+ r = manager_load_unit_prepare(m, name, NULL, NULL, &service);
+ if (r < 0) {
log_warning("Failed to prepare unit %s: %s", name, strerror(-r));
continue;
}
if (de->d_name[0] == 'S') {
- if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) {
+ if (rcnd_table[i].type == RUNLEVEL_UP) {
SERVICE(service)->sysv_start_priority_from_rcnd =
MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd);
@@ -3492,8 +3506,7 @@ static int service_enumerate(Manager *m) {
goto finish;
} else if (de->d_name[0] == 'K' &&
- (rcnd_table[i].type == RUNLEVEL_DOWN ||
- rcnd_table[i].type == RUNLEVEL_SYSINIT)) {
+ (rcnd_table[i].type == RUNLEVEL_DOWN)) {
if ((r = set_ensure_allocated(&shutdown_services, trivial_hash_func, trivial_compare_func)) < 0)
goto finish;
@@ -3527,9 +3540,7 @@ static int service_enumerate(Manager *m) {
* runlevels we assume the stop jobs will be implicitly added
* by the core logic. Also, we don't really distinguish here
* between the runlevels 0 and 6 and just add them to the
- * special shutdown target. On SUSE the boot.d/ runlevel is
- * also used for shutdown, so we add links for that too to the
- * shutdown target.*/
+ * special shutdown target. */
SET_FOREACH(service, shutdown_services, j) {
service = unit_follow_merge(service);
@@ -3542,10 +3553,6 @@ static int service_enumerate(Manager *m) {
r = 0;
-#ifdef TARGET_SUSE
- sysv_facility_in_insserv_conf (m);
-#endif
-
finish:
free(path);
free(fpath);
@@ -3577,11 +3584,17 @@ static void service_bus_name_owner_change(
assert(old_owner || new_owner);
if (old_owner && new_owner)
- log_debug("%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner);
+ log_debug_unit(u->id,
+ "%s's D-Bus name %s changed owner from %s to %s",
+ u->id, name, old_owner, new_owner);
else if (old_owner)
- log_debug("%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner);
+ log_debug_unit(u->id,
+ "%s's D-Bus name %s no longer registered by %s",
+ u->id, name, old_owner);
else
- log_debug("%s's D-Bus name %s now registered by %s", u->id, name, new_owner);
+ log_debug_unit(u->id,
+ "%s's D-Bus name %s now registered by %s",
+ u->id, name, new_owner);
s->bus_name_good = !!new_owner;
@@ -3602,7 +3615,8 @@ static void service_bus_name_owner_change(
s->state == SERVICE_RELOAD)) {
/* Try to acquire PID from bus service */
- log_debug("Trying to acquire PID from D-Bus name...");
+ log_debug_unit(u->id,
+ "Trying to acquire PID from D-Bus name...");
bus_query_pid(u->manager, name);
}
@@ -3618,7 +3632,9 @@ static void service_bus_query_pid_done(
assert(s);
assert(name);
- log_debug("%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid);
+ log_debug_unit(u->id,
+ "%s's D-Bus name %s is now owned by process %u",
+ u->id, name, (unsigned) pid);
if (s->main_pid <= 0 &&
(s->state == SERVICE_START ||
@@ -3798,7 +3814,8 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
[SERVICE_FAILURE_EXIT_CODE] = "exit-code",
[SERVICE_FAILURE_SIGNAL] = "signal",
[SERVICE_FAILURE_CORE_DUMP] = "core-dump",
- [SERVICE_FAILURE_WATCHDOG] = "watchdog"
+ [SERVICE_FAILURE_WATCHDOG] = "watchdog",
+ [SERVICE_FAILURE_START_LIMIT] = "start-limit"
};
DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
@@ -3813,6 +3830,8 @@ 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"
diff --git a/src/core/service.h b/src/core/service.h
index cc63347c76..d1e53bf727 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -28,6 +28,7 @@ typedef struct Service Service;
#include "ratelimit.h"
#include "service.h"
#include "kill.h"
+#include "exit-status.h"
typedef enum ServiceState {
SERVICE_DEAD,
@@ -97,6 +98,7 @@ typedef enum ServiceResult {
SERVICE_FAILURE_SIGNAL,
SERVICE_FAILURE_CORE_DUMP,
SERVICE_FAILURE_WATCHDOG,
+ SERVICE_FAILURE_START_LIMIT,
_SERVICE_RESULT_MAX,
_SERVICE_RESULT_INVALID = -1
} ServiceResult;
@@ -115,12 +117,15 @@ struct Service {
ServiceType type;
ServiceRestart restart;
+ ExitStatusSet restart_ignore_status;
+ ExitStatusSet success_status;
/* If set we'll read the main daemon PID from this file */
char *pid_file;
usec_t restart_usec;
- usec_t timeout_usec;
+ usec_t timeout_start_usec;
+ usec_t timeout_stop_usec;
dual_timestamp watchdog_timestamp;
usec_t watchdog_usec;
@@ -166,7 +171,7 @@ struct Service {
bool bus_name_good:1;
bool forbid_restart:1;
bool got_socket_fd:1;
- bool timeout_defined:1;
+ bool start_timeout_defined:1;
#ifdef HAVE_SYSV_COMPAT
bool is_sysv:1;
bool sysv_has_lsb:1;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 105a604542..0b0e0c3d47 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -104,15 +104,14 @@ static int pivot_to_new_root(void) {
return -errno;
}
- /*
- In case some evil process made "/" MS_SHARED
- It works for pivot_root, but the ref count for the root device
- is not decreasing :-/
- */
- if (mount(NULL, "/", NULL, MS_PRIVATE, NULL) < 0) {
- log_error("Failed to make \"/\" private mount %m");
- return -errno;
- }
+ /* Work-around for a kernel bug: for some reason the kernel
+ * refuses switching root if any file systems are mounted
+ * MS_SHARED. Hence remount them MS_PRIVATE here as a
+ * work-around.
+ *
+ * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
+ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
+ log_warning("Failed to make \"/\" private mount: %m");
if (pivot_root(".", "oldroot") < 0) {
log_error("pivot failed: %m");
@@ -198,31 +197,34 @@ int main(int argc, char *argv[]) {
if (need_umount) {
log_info("Unmounting file systems.");
r = umount_all(&changed);
- if (r == 0)
+ if (r == 0) {
need_umount = false;
- else if (r > 0)
+ log_info("All filesystems unmounted.");
+ } else if (r > 0)
log_info("Not all file systems unmounted, %d left.", r);
else
log_error("Failed to unmount file systems: %s", strerror(-r));
}
if (need_swapoff) {
- log_info("Disabling swaps.");
+ log_info("Deactivating swaps.");
r = swapoff_all(&changed);
- if (r == 0)
+ if (r == 0) {
need_swapoff = false;
- else if (r > 0)
- log_info("Not all swaps are turned off, %d left.", r);
+ log_info("All swaps deactivated.");
+ } else if (r > 0)
+ log_info("Not all swaps deactivated, %d left.", r);
else
- log_error("Failed to turn off swaps: %s", strerror(-r));
+ log_error("Failed to deactivate swaps: %s", strerror(-r));
}
if (need_loop_detach) {
log_info("Detaching loop devices.");
r = loopback_detach_all(&changed);
- if (r == 0)
+ if (r == 0) {
need_loop_detach = false;
- else if (r > 0)
+ log_info("All loop devices detached.");
+ } else if (r > 0)
log_info("Not all loop devices detached, %d left.", r);
else
log_error("Failed to detach loop devices: %s", strerror(-r));
@@ -231,10 +233,11 @@ int main(int argc, char *argv[]) {
if (need_dm_detach) {
log_info("Detaching DM devices.");
r = dm_detach_all(&changed);
- if (r == 0)
+ if (r == 0) {
need_dm_detach = false;
- else if (r > 0)
- log_warning("Not all DM devices detached, %d left.", r);
+ log_info("All DM devices detached.");
+ } else if (r > 0)
+ log_info("Not all DM devices detached, %d left.", r);
else
log_error("Failed to detach DM devices: %s", strerror(-r));
}
@@ -264,14 +267,8 @@ int main(int argc, char *argv[]) {
arguments[2] = NULL;
execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments);
- /* If we are in a container, just exit, this will kill our
- * container for good. */
- if (in_container) {
- log_error("Exiting container.");
- exit(0);
- }
-
- if (access("/run/initramfs/shutdown", X_OK) == 0) {
+ if (!in_container && !in_initrd() &&
+ access("/run/initramfs/shutdown", X_OK) == 0) {
if (prepare_new_root() >= 0 &&
pivot_to_new_root() >= 0) {
@@ -280,28 +277,45 @@ int main(int argc, char *argv[]) {
}
}
- sync();
+ /* The kernel will automaticall flush ATA disks and suchlike
+ * on reboot(), but the file systems need to be synce'd
+ * explicitly in advance. So let's do this here, but not
+ * needlessly slow down containers. */
+ if (!in_container)
+ sync();
if (cmd == LINUX_REBOOT_CMD_KEXEC) {
- /* We cheat and exec kexec to avoid doing all its work */
- pid_t pid = fork();
-
- if (pid < 0)
- log_error("Could not fork: %m. Falling back to normal reboot.");
- else if (pid > 0) {
- wait_for_terminate_and_warn("kexec", pid);
- log_warning("kexec failed. Falling back to normal reboot.");
- } else {
- /* Child */
- const char *args[3] = { "/sbin/kexec", "-e", NULL };
- execv(args[0], (char * const *) args);
- return EXIT_FAILURE;
+
+ if (!in_container) {
+ /* We cheat and exec kexec to avoid doing all its work */
+ pid_t pid = fork();
+
+ if (pid < 0)
+ log_error("Could not fork: %m. Falling back to normal reboot.");
+ else if (pid > 0) {
+ wait_for_terminate_and_warn("kexec", pid);
+ log_warning("kexec failed. Falling back to normal reboot.");
+ } else {
+ /* Child */
+ const char *args[3] = { "/sbin/kexec", "-e", NULL };
+ execv(args[0], (char * const *) args);
+ return EXIT_FAILURE;
+ }
}
cmd = RB_AUTOBOOT;
}
reboot(cmd);
+
+ if (errno == EPERM && in_container) {
+ /* If we are in a container, and we lacked
+ * CAP_SYS_BOOT just exit, this will kill our
+ * container for good. */
+ log_error("Exiting container.");
+ exit(0);
+ }
+
log_error("Failed to invoke reboot(): %m");
r = -errno;
diff --git a/src/core/socket.c b/src/core/socket.c
index 837b166e3b..fcbcdbe192 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -28,6 +28,9 @@
#include <signal.h>
#include <arpa/inet.h>
#include <mqueue.h>
+#ifdef HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
#include "unit.h"
#include "socket.h"
@@ -39,6 +42,7 @@
#include "mkdir.h"
#include "path-util.h"
#include "unit-name.h"
+#include "unit-printf.h"
#include "dbus-socket.h"
#include "missing.h"
#include "special.h"
@@ -130,6 +134,10 @@ static void socket_done(Unit *u) {
free(s->bind_to_device);
s->bind_to_device = NULL;
+ free(s->smack);
+ free(s->smack_ip_in);
+ free(s->smack_ip_out);
+
unit_unwatch_timer(u, &s->timer_watch);
}
@@ -205,27 +213,35 @@ static int socket_verify(Socket *s) {
return 0;
if (!s->ports) {
- log_error("%s lacks Listen setting. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s lacks Listen setting. Refusing.", UNIT(s)->id);
return -EINVAL;
}
if (s->accept && have_non_accept_socket(s)) {
- log_error("%s configured for accepting sockets, but sockets are non-accepting. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s configured for accepting sockets, but sockets are non-accepting. Refusing.",
+ UNIT(s)->id);
return -EINVAL;
}
if (s->accept && s->max_connections <= 0) {
- log_error("%s's MaxConnection setting too small. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s's MaxConnection setting too small. Refusing.", UNIT(s)->id);
return -EINVAL;
}
if (s->accept && UNIT_DEREF(s->service)) {
- log_error("Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "Explicit service configuration for accepting sockets not supported on %s. Refusing.",
+ UNIT(s)->id);
return -EINVAL;
}
if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.",
+ UNIT(s)->id);
return -EINVAL;
}
@@ -264,7 +280,8 @@ int socket_add_one_mount_link(Socket *s, Mount *m) {
if (!socket_needs_mount(s, m->where))
return 0;
- if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
+ if (r < 0)
return r;
return 0;
@@ -276,9 +293,11 @@ static int socket_add_mount_links(Socket *s) {
assert(s);
- LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT])
- if ((r = socket_add_one_mount_link(s, MOUNT(other))) < 0)
+ LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) {
+ r = socket_add_one_mount_link(s, MOUNT(other));
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -305,7 +324,7 @@ static int socket_add_default_dependencies(Socket *s) {
int r;
assert(s);
- if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+ 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;
@@ -504,6 +523,21 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sMessageQueueMessageSize: %li\n",
prefix, s->mq_msgsize);
+ if (s->smack)
+ fprintf(f,
+ "%sSmackLabel: %s\n",
+ prefix, s->smack);
+
+ if (s->smack_ip_in)
+ fprintf(f,
+ "%sSmackLabelIPIn: %s\n",
+ prefix, s->smack_ip_in);
+
+ if (s->smack_ip_out)
+ fprintf(f,
+ "%sSmackLabelIPOut: %s\n",
+ prefix, s->smack_ip_out);
+
LIST_FOREACH(port, p, s->ports) {
if (p->type == SOCKET_SOCKET) {
@@ -584,7 +618,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
}
case AF_INET6: {
- static const char ipv4_prefix[] = {
+ static const unsigned char ipv4_prefix[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
};
@@ -674,30 +708,30 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (s->keep_alive) {
int b = s->keep_alive;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0)
- log_warning("SO_KEEPALIVE failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_KEEPALIVE failed: %m");
}
if (s->broadcast) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0)
- log_warning("SO_BROADCAST failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_BROADCAST failed: %m");
}
if (s->pass_cred) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
- log_warning("SO_PASSCRED failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_PASSCRED failed: %m");
}
if (s->pass_sec) {
int one = 1;
if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0)
- log_warning("SO_PASSSEC failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_PASSSEC failed: %m");
}
if (s->priority >= 0)
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0)
- log_warning("SO_PRIORITY failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_PRIORITY failed: %m");
if (s->receive_buffer > 0) {
int value = (int) s->receive_buffer;
@@ -706,23 +740,23 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0)
- log_warning("SO_RCVBUF failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_RCVBUF failed: %m");
}
if (s->send_buffer > 0) {
int value = (int) s->send_buffer;
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0)
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0)
- log_warning("SO_SNDBUF failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_SNDBUF failed: %m");
}
if (s->mark >= 0)
if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0)
- log_warning("SO_MARK failed: %m");
+ log_warning_unit(UNIT(s)->id, "SO_MARK failed: %m");
if (s->ip_tos >= 0)
if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0)
- log_warning("IP_TOS failed: %m");
+ log_warning_unit(UNIT(s)->id, "IP_TOS failed: %m");
if (s->ip_ttl >= 0) {
int r, x;
@@ -737,12 +771,25 @@ static void socket_apply_socket_options(Socket *s, int fd) {
}
if (r < 0 && x < 0)
- log_warning("IP_TTL/IPV6_UNICAST_HOPS failed: %m");
+ log_warning_unit(UNIT(s)->id,
+ "IP_TTL/IPV6_UNICAST_HOPS failed: %m");
}
if (s->tcp_congestion)
if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
- log_warning("TCP_CONGESTION failed: %m");
+ log_warning_unit(UNIT(s)->id, "TCP_CONGESTION failed: %m");
+
+#ifdef HAVE_ATTR_XATTR_H
+ 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,
+ "fsetxattr(\"security.SMACK64IPIN\"): %m");
+
+ if (s->smack_ip_out)
+ if (fsetxattr(fd, "security.SMACK64IPOUT", s->smack_ip_out, strlen(s->smack_ip_out), 0) < 0)
+ log_error_unit(UNIT(s)->id,
+ "fsetxattr(\"security.SMACK64IPOUT\"): %m");
+#endif
}
static void socket_apply_fifo_options(Socket *s, int fd) {
@@ -751,7 +798,15 @@ static void socket_apply_fifo_options(Socket *s, int fd) {
if (s->pipe_size > 0)
if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0)
- log_warning("F_SETPIPE_SZ: %m");
+ log_warning_unit(UNIT(s)->id,
+ "F_SETPIPE_SZ: %m");
+
+#ifdef HAVE_ATTR_XATTR_H
+ if (s->smack)
+ if (fsetxattr(fd, "security.SMACK64", s->smack, strlen(s->smack), 0) < 0)
+ log_error_unit(UNIT(s)->id,
+ "fsetxattr(\"security.SMACK64\"): %m");
+#endif
}
static int fifo_address_create(
@@ -1075,10 +1130,10 @@ static void socket_set_state(Socket *s, SocketState state) {
socket_close_fds(s);
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(s)->id,
- socket_state_to_string(old_state),
- socket_state_to_string(state));
+ log_debug_unit(UNIT(s)->id,
+ "%s changed %s -> %s", UNIT(s)->id,
+ socket_state_to_string(old_state),
+ socket_state_to_string(state));
unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -1104,10 +1159,12 @@ static int socket_coldplug(Unit *u) {
if (s->control_pid <= 0)
return -EBADMSG;
- if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ r = unit_watch_pid(UNIT(s), s->control_pid);
+ if (r < 0)
return r;
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
+ if (r < 0)
return r;
}
@@ -1139,10 +1196,12 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
assert(c);
assert(_pid);
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
- 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;
}
@@ -1214,7 +1273,9 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
return;
fail:
- log_warning("%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'stop-post' task: %s",
+ UNIT(s)->id, strerror(-r));
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES);
}
@@ -1234,7 +1295,9 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
if (s->control_pid > 0) {
if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
- log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
+ log_warning_unit(UNIT(s)->id,
+ "Failed to kill control process %li: %m",
+ (long) s->control_pid);
else
wait_for_exit = true;
}
@@ -1254,7 +1317,9 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
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("Failed to kill control group: %s", strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "Failed to kill control group: %s",
+ strerror(-r));
} else if (r > 0)
wait_for_exit = true;
@@ -1264,7 +1329,8 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
}
if (wait_for_exit) {
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
socket_set_state(s, state);
@@ -1276,7 +1342,9 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
return;
fail:
- log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to kill processes: %s",
+ UNIT(s)->id, strerror(-r));
if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL)
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
@@ -1309,7 +1377,9 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
return;
fail:
- log_warning("%s failed to run 'stop-pre' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'stop-pre' task: %s",
+ UNIT(s)->id, strerror(-r));
socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES);
}
@@ -1319,7 +1389,9 @@ static void socket_enter_listening(Socket *s) {
r = socket_watch_fds(s);
if (r < 0) {
- log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to watch sockets: %s",
+ UNIT(s)->id, strerror(-r));
goto fail;
}
@@ -1336,7 +1408,9 @@ static void socket_enter_start_post(Socket *s) {
r = socket_open_fds(s);
if (r < 0) {
- log_warning("%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to listen on sockets: %s",
+ UNIT(s)->id, strerror(-r));
goto fail;
}
@@ -1347,7 +1421,9 @@ static void socket_enter_start_post(Socket *s) {
if ((s->control_command = s->exec_command[SOCKET_EXEC_START_POST])) {
r = socket_spawn(s, s->control_command, &s->control_pid);
if (r < 0) {
- log_warning("%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'start-post' task: %s",
+ UNIT(s)->id, strerror(-r));
goto fail;
}
@@ -1380,7 +1456,9 @@ static void socket_enter_start_pre(Socket *s) {
return;
fail:
- log_warning("%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'start-pre' task: %s",
+ UNIT(s)->id, strerror(-r));
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
}
@@ -1394,7 +1472,9 @@ 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))) {
- log_debug("Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id);
+ log_debug_unit(UNIT(s)->id,
+ "Suppressing connection request on %s since unit stop is scheduled.",
+ UNIT(s)->id);
if (cfd >= 0)
close_nointr_nofail(cfd);
@@ -1404,7 +1484,9 @@ static void socket_enter_running(Socket *s, int cfd) {
r = socket_watch_fds(s);
if (r < 0) {
- log_warning("%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to watch sockets: %s",
+ UNIT(s)->id, strerror(-r));
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
}
}
@@ -1437,7 +1519,9 @@ static void socket_enter_running(Socket *s, int cfd) {
Service *service;
if (s->n_connections >= s->max_connections) {
- log_warning("Too many incoming connections (%u)", s->n_connections);
+ log_warning_unit(UNIT(s)->id,
+ "%s: Too many incoming connections (%u)",
+ UNIT(s)->id, s->n_connections);
close_nointr_nofail(cfd);
return;
}
@@ -1506,7 +1590,11 @@ static void socket_enter_running(Socket *s, int cfd) {
return;
fail:
- log_warning("%s failed to queue socket startup job: %s", UNIT(s)->id, bus_error(&error, r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s",
+ UNIT(s)->id,
+ cfd >= 0 ? "template" : "non-template",
+ bus_error(&error, r));
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
if (cfd >= 0)
@@ -1532,7 +1620,9 @@ static void socket_run_next(Socket *s) {
return;
fail:
- log_warning("%s failed to run next task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run next task: %s",
+ UNIT(s)->id, strerror(-r));
if (s->state == SOCKET_START_POST)
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
@@ -1568,7 +1658,9 @@ static int socket_start(Unit *u) {
service = SERVICE(UNIT_DEREF(s->service));
if (UNIT(service)->load_state != UNIT_LOADED) {
- log_error("Socket service %s not loaded, refusing.", UNIT(service)->id);
+ log_error_unit(UNIT(service)->id,
+ "Socket service %s not loaded, refusing.",
+ UNIT(service)->id);
return -ENOENT;
}
@@ -1577,13 +1669,16 @@ static int socket_start(Unit *u) {
if (service->state != SERVICE_DEAD &&
service->state != SERVICE_FAILED &&
service->state != SERVICE_AUTO_RESTART) {
- log_error("Socket service %s already active, refusing.", UNIT(service)->id);
+ log_error_unit(UNIT(service)->id,
+ "Socket service %s already active, refusing.",
+ UNIT(service)->id);
return -EBUSY;
}
#ifdef HAVE_SYSV_COMPAT
if (service->is_sysv) {
- log_error("Using SysV services for socket activation is not supported. Refusing.");
+ log_error_unit(UNIT(s)->id,
+ "Using SysV services for socket activation is not supported. Refusing.");
return -ENOENT;
}
#endif
@@ -1655,7 +1750,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
if (p->type == SOCKET_SOCKET) {
char *t;
- if ((r = socket_address_print(&p->address, &t)) < 0)
+ r = socket_address_print(&p->address, &t);
+ if (r < 0)
return r;
if (socket_address_family(&p->address) == AF_NETLINK)
@@ -1665,6 +1761,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
free(t);
} else if (p->type == SOCKET_SPECIAL)
unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path);
+ else if (p->type == SOCKET_MQUEUE)
+ unit_serialize_item_format(u, f, "mqueue", "%i %s", copy, p->path);
else {
assert(p->type == SOCKET_FIFO);
unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
@@ -1685,8 +1783,10 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (streq(key, "state")) {
SocketState state;
- if ((state = socket_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
+ state = socket_state_from_string(value);
+ if (state < 0)
+ log_debug_unit(u->id,
+ "Failed to parse state value %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -1694,7 +1794,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
f = socket_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse result value %s", value);
+ log_debug_unit(u->id,
+ "Failed to parse result value %s", value);
else if (f != SOCKET_SUCCESS)
s->result = f;
@@ -1702,21 +1803,25 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
unsigned k;
if (safe_atou(value, &k) < 0)
- log_debug("Failed to parse n-accepted value %s", value);
+ log_debug_unit(u->id,
+ "Failed to parse n-accepted value %s", value);
else
s->n_accepted += k;
} else if (streq(key, "control-pid")) {
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
+ log_debug_unit(u->id,
+ "Failed to parse control-pid value %s", value);
else
s->control_pid = pid;
} else if (streq(key, "control-command")) {
SocketExecCommand id;
- if ((id = socket_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
+ id = socket_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;
s->control_command = s->exec_command[id];
@@ -1726,7 +1831,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse fifo value %s", value);
+ log_debug_unit(u->id,
+ "Failed to parse fifo value %s", value);
else {
LIST_FOREACH(port, p, s->ports)
@@ -1746,7 +1852,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse special value %s", value);
+ log_debug_unit(u->id,
+ "Failed to parse special value %s", value);
else {
LIST_FOREACH(port, p, s->ports)
@@ -1761,12 +1868,34 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
}
}
+ } else if (streq(key, "mqueue")) {
+ int fd, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug_unit(u->id,
+ "Failed to parse mqueue value %s", value);
+ else {
+
+ LIST_FOREACH(port, p, s->ports)
+ if (p->type == SOCKET_MQUEUE &&
+ streq_ptr(p->path, value+skip))
+ break;
+
+ if (p) {
+ if (p->fd >= 0)
+ close_nointr_nofail(p->fd);
+ p->fd = fdset_remove(fds, fd);
+ }
+ }
+
} else if (streq(key, "socket")) {
int fd, type, skip = 0;
SocketPort *p;
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse socket value %s", value);
+ log_debug_unit(u->id,
+ "Failed to parse socket value %s", value);
else {
LIST_FOREACH(port, p, s->ports)
@@ -1785,7 +1914,8 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
SocketPort *p;
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse socket value %s", value);
+ log_debug_unit(u->id,
+ "Failed to parse socket value %s", value);
else {
LIST_FOREACH(port, p, s->ports)
@@ -1800,7 +1930,36 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
}
} else
- log_debug("Unknown serialization key '%s'", key);
+ log_debug_unit(UNIT(s)->id,
+ "Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static int socket_distribute_fds(Unit *u, FDSet *fds) {
+ Socket *s = SOCKET(u);
+ SocketPort *p;
+
+ assert(u);
+
+ LIST_FOREACH(port, p, s->ports) {
+ Iterator i;
+ int fd;
+
+ if (p->type != SOCKET_SOCKET)
+ continue;
+
+ if (p->fd >= 0)
+ continue;
+
+ FDSET_FOREACH(fd, fds, i) {
+ if (socket_address_matches_fd(&p->address, fd)) {
+ p->fd = fdset_remove(fds, fd);
+ s->deserialized_state = SOCKET_LISTENING;
+ break;
+ }
+ }
+ }
return 0;
}
@@ -1835,14 +1994,18 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
if (s->state != SOCKET_LISTENING)
return;
- log_debug("Incoming traffic on %s", u->id);
+ log_debug_unit(u->id, "Incoming traffic on %s", u->id);
if (events != EPOLLIN) {
if (events & EPOLLHUP)
- log_error("%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", u->id);
+ log_error_unit(u->id,
+ "%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.",
+ u->id);
else
- log_error("%s: Got unexpected poll event (0x%x) on socket.", u->id, events);
+ log_error_unit(u->id,
+ "%s: Got unexpected poll event (0x%x) on socket.",
+ u->id, events);
goto fail;
}
@@ -1850,12 +2013,14 @@ static void socket_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
if (w->socket_accept) {
for (;;) {
- if ((cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK)) < 0) {
+ cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK);
+ if (cfd < 0) {
if (errno == EINTR)
continue;
- log_error("Failed to accept socket: %m");
+ log_error_unit(u->id,
+ "Failed to accept socket: %m");
goto fail;
}
@@ -1884,7 +2049,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = SOCKET_SUCCESS;
else if (code == CLD_EXITED)
f = SOCKET_FAILURE_EXIT_CODE;
@@ -1902,8 +2067,10 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
f = SOCKET_SUCCESS;
}
- log_full(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+ log_full_unit(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ u->id,
+ "%s control process exited, code=%s status=%i",
+ u->id, sigchld_code_to_string(code), status);
if (f != SOCKET_SUCCESS)
s->result = f;
@@ -1912,7 +2079,9 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command->command_next &&
f == SOCKET_SUCCESS) {
- log_debug("%s running next command for state %s", u->id, socket_state_to_string(s->state));
+ log_debug_unit(u->id,
+ "%s running next command for state %s",
+ u->id, socket_state_to_string(s->state));
socket_run_next(s);
} else {
s->control_command = NULL;
@@ -1921,7 +2090,9 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* No further commands for this step, so let's figure
* out what to do next */
- log_debug("%s got final SIGCHLD for state %s", u->id, socket_state_to_string(s->state));
+ log_debug_unit(u->id,
+ "%s got final SIGCHLD for state %s",
+ u->id, socket_state_to_string(s->state));
switch (s->state) {
@@ -1970,52 +2141,65 @@ static void socket_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
switch (s->state) {
case SOCKET_START_PRE:
- log_warning("%s starting timed out. Terminating.", u->id);
+ log_warning_unit(u->id,
+ "%s starting timed out. Terminating.", u->id);
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_START_POST:
- log_warning("%s starting timed out. Stopping.", u->id);
+ log_warning_unit(u->id,
+ "%s starting timed out. Stopping.", u->id);
socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE:
- log_warning("%s stopping timed out. Terminating.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out. Terminating.", u->id);
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_PRE_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_warning("%s stopping timed out. Killing.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out. Killing.", u->id);
socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
- log_warning("%s stopping timed out. Skipping SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out. Skipping SIGKILL. Ignoring.",
+ u->id);
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_STOP_PRE_SIGKILL:
- log_warning("%s still around after SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s still around after SIGKILL. Ignoring.", u->id);
socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_STOP_POST:
- log_warning("%s stopping timed out (2). Terminating.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out (2). Terminating.", u->id);
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT);
break;
case SOCKET_FINAL_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_warning("%s stopping timed out (2). Killing.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out (2). Killing.", u->id);
socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT);
} else {
- log_warning("%s stopping timed out (2). Skipping SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id,
+ "%s stopping timed out (2). Skipping SIGKILL. Ignoring.",
+ u->id);
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
}
break;
case SOCKET_FINAL_SIGKILL:
- log_warning("%s still around after SIGKILL (2). Entering failed mode.", u->id);
+ log_warning_unit(u->id,
+ "%s still around after SIGKILL (2). Entering failed mode.",
+ u->id);
socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT);
break;
@@ -2071,7 +2255,9 @@ void socket_notify_service_dead(Socket *s, bool failed_permanent) {
* services. */
if (s->state == SOCKET_RUNNING) {
- log_debug("%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent));
+ log_debug_unit(UNIT(s)->id,
+ "%s got notified about service death (failed permanently: %s)",
+ UNIT(s)->id, yes_no(failed_permanent));
if (failed_permanent)
socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT);
else
@@ -2090,7 +2276,8 @@ void socket_connection_unref(Socket *s) {
assert(s->n_connections > 0);
s->n_connections--;
- log_debug("%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections);
+ log_debug_unit(UNIT(s)->id,
+ "%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections);
}
static void socket_reset_failed(Unit *u) {
@@ -2194,6 +2381,8 @@ 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"
@@ -2214,6 +2403,7 @@ const UnitVTable socket_vtable = {
.serialize = socket_serialize,
.deserialize_item = socket_deserialize_item,
+ .distribute_fds = socket_distribute_fds,
.active_state = socket_active_state,
.sub_state_to_string = socket_sub_state_to_string,
diff --git a/src/core/socket.h b/src/core/socket.h
index a06b3eae94..f099520dce 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -144,6 +144,10 @@ struct Socket {
/* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
SocketAddressBindIPv6Only bind_ipv6_only;
+
+ char *smack;
+ char *smack_ip_in;
+ char *smack_ip_out;
};
/* Called from the service code when collecting fds */
diff --git a/src/core/special.h b/src/core/special.h
index e3004a518d..ef72260ecd 100644
--- a/src/core/special.h
+++ b/src/core/special.h
@@ -37,6 +37,7 @@
#define SPECIAL_EXIT_TARGET "exit.target"
#define SPECIAL_SUSPEND_TARGET "suspend.target"
#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
+#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
/* Special boot targets */
#define SPECIAL_RESCUE_TARGET "rescue.target"
@@ -58,9 +59,34 @@
#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" /* Debian's $x-display-manager */
-#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Debian's $mail-{transport|transfer-agent */
-#define SPECIAL_HTTP_DAEMON_TARGET "http-daemon.target"
+#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:
+ *
+ * - Be conservative, only add more of these when we really need
+ * them. We need strong usecases for further additions.
+ *
+ * - When there can be multiple implementations running side-by-side,
+ * it needs to be a .target unit which can pull in all
+ * implementations.
+ *
+ * - If something can be implemented with socket activation, and
+ * without, it needs to be a .target unit, so that it can pull in
+ * the appropriate unit.
+ *
+ * - Otherwise, it should be a .service unit.
+ *
+ * - In some cases it is OK to have both a .service and a .target
+ * unit, i.e. if there can be multiple parallel implementations, but
+ * only one is the "system" one. Example: syslog.
+ *
+ * Or to put this in other words: .service symlinks can be used to
+ * arbitrate between multiple implementations if there can be only one
+ * of a kind. .target units can be used to support multiple
+ * implementations that can run side-by-side.
+ */
/* Magic early boot services */
#define SPECIAL_FSCK_SERVICE "systemd-fsck@.service"
diff --git a/src/core/swap.c b/src/core/swap.c
index 458e00efe5..c8e25d0665 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -39,6 +39,7 @@
#include "exit-status.h"
#include "def.h"
#include "path-util.h"
+#include "virt.h"
static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = UNIT_INACTIVE,
@@ -54,6 +55,7 @@ static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
static void swap_unset_proc_swaps(Swap *s) {
Swap *first;
+ Hashmap *swaps;
assert(s);
@@ -62,14 +64,17 @@ static void swap_unset_proc_swaps(Swap *s) {
/* Remove this unit from the chain of swaps which share the
* same kernel swap device. */
-
- first = hashmap_get(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what);
+ swaps = UNIT(s)->manager->swaps_by_proc_swaps;
+ first = hashmap_get(swaps, s->parameters_proc_swaps.what);
LIST_REMOVE(Swap, same_proc_swaps, first, s);
if (first)
- hashmap_remove_and_replace(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what, first->parameters_proc_swaps.what, first);
+ hashmap_remove_and_replace(swaps,
+ s->parameters_proc_swaps.what,
+ first->parameters_proc_swaps.what,
+ first);
else
- hashmap_remove(UNIT(s)->manager->swaps_by_proc_swaps, s->parameters_proc_swaps.what);
+ hashmap_remove(swaps, s->parameters_proc_swaps.what);
free(s->parameters_proc_swaps.what);
s->parameters_proc_swaps.what = NULL;
@@ -145,7 +150,8 @@ int swap_add_one_mount_link(Swap *s, Mount *m) {
if (!path_startswith(s->what, m->where))
return 0;
- if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
+ r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
+ if (r < 0)
return r;
return 0;
@@ -164,21 +170,6 @@ static int swap_add_mount_links(Swap *s) {
return 0;
}
-static int swap_add_target_links(Swap *s) {
- Unit *tu;
- int r;
-
- assert(s);
-
- if (!s->from_fragment)
- return 0;
-
- if ((r = manager_load_unit(UNIT(s)->manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0)
- return r;
-
- return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true);
-}
-
static int swap_add_device_links(Swap *s) {
SwapParameters *p;
@@ -195,7 +186,7 @@ static int swap_add_device_links(Swap *s) {
if (is_device_path(s->what))
return unit_add_node_link(UNIT(s), s->what,
!p->noauto && p->nofail &&
- UNIT(s)->manager->running_as == MANAGER_SYSTEM);
+ UNIT(s)->manager->running_as == SYSTEMD_SYSTEM);
else
/* File based swap devices need to be ordered after
* systemd-remount-fs.service, since they might need a
@@ -208,7 +199,10 @@ static int swap_add_default_dependencies(Swap *s) {
assert(s);
- if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
+ if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM)
+ return 0;
+
+ if (detect_container(NULL) > 0)
return 0;
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -220,24 +214,27 @@ static int swap_add_default_dependencies(Swap *s) {
static int swap_verify(Swap *s) {
bool b;
- char *e;
+ char _cleanup_free_ *e = NULL;
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- if (!(e = unit_name_from_path(s->what, ".swap")))
- return -ENOMEM;
+ e = unit_name_from_path(s->what, ".swap");
+ if (e == NULL)
+ return log_oom();
b = unit_has_name(UNIT(s), e);
- free(e);
-
if (!b) {
- log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s: Value of \"What\" and unit name do not match, not loading.",
+ UNIT(s)->id);
return -EINVAL;
}
if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
- log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
+ log_error_unit(UNIT(s)->id,
+ "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.",
+ UNIT(s)->id);
return -EINVAL;
}
@@ -252,11 +249,13 @@ static int swap_load(Unit *u) {
assert(u->load_state == UNIT_STUB);
/* Load a .swap file */
- if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
+ r = unit_load_fragment_and_dropin_optional(u);
+ if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
- 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 (UNIT(s)->fragment_path)
@@ -280,21 +279,23 @@ static int swap_load(Unit *u) {
if ((r = unit_set_description(u, s->what)) < 0)
return r;
- if ((r = swap_add_device_links(s)) < 0)
- return r;
-
- if ((r = swap_add_mount_links(s)) < 0)
+ r = swap_add_device_links(s);
+ if (r < 0)
return r;
- if ((r = swap_add_target_links(s)) < 0)
+ r = swap_add_mount_links(s);
+ if (r < 0)
return r;
- if ((r = unit_add_default_cgroups(u)) < 0)
+ r = unit_add_default_cgroups(u);
+ if (r < 0)
return r;
- if (UNIT(s)->default_dependencies)
- if ((r = swap_add_default_dependencies(s)) < 0)
+ if (UNIT(s)->default_dependencies) {
+ r = swap_add_default_dependencies(s);
+ if (r < 0)
return r;
+ }
r = unit_exec_context_defaults(u, &s->exec_context);
if (r < 0)
@@ -314,7 +315,8 @@ static int swap_add_one(
bool set_flags) {
Unit *u = NULL;
- char *e = NULL, *wp = NULL;
+ char _cleanup_free_ *e = NULL;
+ char *wp = NULL;
bool delete = false;
int r;
SwapParameters *p;
@@ -326,7 +328,7 @@ static int swap_add_one(
e = unit_name_from_path(what, ".swap");
if (!e)
- return -ENOMEM;
+ return log_oom();
u = manager_get_unit(m, e);
@@ -339,10 +341,8 @@ static int swap_add_one(
delete = true;
u = unit_new(m, sizeof(Swap));
- if (!u) {
- free(e);
- return -ENOMEM;
- }
+ if (!u)
+ return log_oom();
r = unit_add_name(u, e);
if (r < 0)
@@ -350,7 +350,7 @@ static int swap_add_one(
SWAP(u)->what = strdup(what);
if (!SWAP(u)->what) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
@@ -361,16 +361,19 @@ static int swap_add_one(
p = &SWAP(u)->parameters_proc_swaps;
if (!p->what) {
- if (!(wp = strdup(what_proc_swaps))) {
- r = -ENOMEM;
+ wp = strdup(what_proc_swaps);
+ if (!wp) {
+ r = log_oom();
goto fail;
}
- if (!m->swaps_by_proc_swaps)
- if (!(m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func))) {
- r = -ENOMEM;
+ if (!m->swaps_by_proc_swaps) {
+ m->swaps_by_proc_swaps = hashmap_new(string_hash_func, string_compare_func);
+ if (!m->swaps_by_proc_swaps) {
+ r = log_oom();
goto fail;
}
+ }
free(p->what);
p->what = wp;
@@ -378,7 +381,8 @@ static int swap_add_one(
first = hashmap_get(m->swaps_by_proc_swaps, wp);
LIST_PREPEND(Swap, same_proc_swaps, first, SWAP(u));
- if ((r = hashmap_replace(m->swaps_by_proc_swaps, wp, first)) < 0)
+ r = hashmap_replace(m->swaps_by_proc_swaps, wp, first);
+ if (r < 0)
goto fail;
}
@@ -395,15 +399,12 @@ static int swap_add_one(
unit_add_to_dbus_queue(u);
- free(e);
-
return 0;
fail:
- log_warning("Failed to load swap unit: %s", strerror(-r));
+ log_warning_unit(e, "Failed to load swap unit: %s", strerror(-r));
free(wp);
- free(e);
if (delete && u)
unit_free(u);
@@ -425,11 +426,13 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
/* So this is a proper swap device. Create swap units
* for all names this swap device is known under */
- if (!(d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev)))
- return -ENOMEM;
+ d = udev_device_new_from_devnum(m->udev, 'b', st.st_rdev);
+ if (!d)
+ return log_oom();
dn = udev_device_get_devnode(d);
- if (dn)
+ /* Skip dn==device, since that case will be handled below */
+ if (dn && !streq(dn, device))
r = swap_add_one(m, dn, device, prio, false, false, set_flags);
/* Add additional units for all symlinks */
@@ -444,7 +447,8 @@ static int swap_process_new_swap(Manager *m, const char *device, int prio, bool
continue;
if (stat(p, &st) >= 0)
- if ((!S_ISBLK(st.st_mode)) || st.st_rdev != udev_device_get_devnum(d))
+ if ((!S_ISBLK(st.st_mode)) ||
+ st.st_rdev != udev_device_get_devnum(d))
continue;
k = swap_add_one(m, p, device, prio, false, false, set_flags);
@@ -483,12 +487,14 @@ static void swap_set_state(Swap *s, SwapState state) {
}
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(s)->id,
- swap_state_to_string(old_state),
- swap_state_to_string(state));
-
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
+ log_debug_unit(UNIT(s)->id,
+ "%s changed %s -> %s",
+ UNIT(s)->id,
+ swap_state_to_string(old_state),
+ swap_state_to_string(state));
+
+ unit_notify(UNIT(s), state_translation_table[old_state],
+ state_translation_table[state], true);
}
static int swap_coldplug(Unit *u) {
@@ -516,10 +522,12 @@ static int swap_coldplug(Unit *u) {
if (s->control_pid <= 0)
return -EBADMSG;
- if ((r = unit_watch_pid(UNIT(s), s->control_pid)) < 0)
+ r = unit_watch_pid(UNIT(s), s->control_pid);
+ if (r < 0)
return r;
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
+ if (r < 0)
return r;
}
@@ -581,27 +589,30 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
assert(c);
assert(_pid);
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
- if ((r = exec_spawn(c,
- NULL,
- &s->exec_context,
- NULL, 0,
- UNIT(s)->manager->environment,
- true,
- true,
- true,
- UNIT(s)->manager->confirm_spawn,
- UNIT(s)->cgroup_bondings,
- UNIT(s)->cgroup_attributes,
- NULL,
- UNIT(s)->id,
- NULL,
- &pid)) < 0)
+ r = exec_spawn(c,
+ NULL,
+ &s->exec_context,
+ NULL, 0,
+ UNIT(s)->manager->environment,
+ true,
+ true,
+ true,
+ UNIT(s)->manager->confirm_spawn,
+ UNIT(s)->cgroup_bondings,
+ UNIT(s)->cgroup_attributes,
+ NULL,
+ UNIT(s)->id,
+ 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;
@@ -650,27 +661,33 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
if (s->control_pid > 0) {
if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH)
- log_warning("Failed to kill control process %li: %m", (long) s->control_pid);
+ 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;
+ 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)
- if ((r = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0)
+ 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("Failed to kill control group: %s", strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "Failed to kill control group: %s", strerror(-r));
} else if (r > 0)
wait_for_exit = true;
@@ -680,7 +697,8 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
}
if (wait_for_exit) {
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
swap_set_state(s, state);
@@ -690,7 +708,8 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
return;
fail:
- log_warning("%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
@@ -736,7 +755,8 @@ static void swap_enter_activating(Swap *s) {
swap_unwatch_control_pid(s);
- if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0)
+ r = swap_spawn(s, s->control_command, &s->control_pid);
+ if (r < 0)
goto fail;
swap_set_state(s, SWAP_ACTIVATING);
@@ -744,7 +764,9 @@ static void swap_enter_activating(Swap *s) {
return;
fail:
- log_warning("%s failed to run 'swapon' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'swapon' task: %s",
+ UNIT(s)->id, strerror(-r));
swap_enter_dead(s, SWAP_FAILURE_RESOURCES);
}
@@ -756,16 +778,17 @@ static void swap_enter_deactivating(Swap *s) {
s->control_command_id = SWAP_EXEC_DEACTIVATE;
s->control_command = s->exec_command + SWAP_EXEC_DEACTIVATE;
- if ((r = exec_command_set(
- s->control_command,
+ r = exec_command_set(s->control_command,
"/sbin/swapoff",
s->what,
- NULL)) < 0)
+ NULL);
+ if (r < 0)
goto fail;
swap_unwatch_control_pid(s);
- if ((r = swap_spawn(s, s->control_command, &s->control_pid)) < 0)
+ r = swap_spawn(s, s->control_command, &s->control_pid);
+ if (r < 0)
goto fail;
swap_set_state(s, SWAP_DEACTIVATING);
@@ -773,7 +796,9 @@ static void swap_enter_deactivating(Swap *s) {
return;
fail:
- log_warning("%s failed to run 'swapoff' task: %s", UNIT(s)->id, strerror(-r));
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to run 'swapoff' task: %s",
+ UNIT(s)->id, strerror(-r));
swap_enter_active(s, SWAP_FAILURE_RESOURCES);
}
@@ -797,6 +822,9 @@ static int swap_start(Unit *u) {
assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED);
+ if (detect_container(NULL) > 0)
+ return -EPERM;
+
s->result = SWAP_SUCCESS;
swap_enter_activating(s);
return 0;
@@ -817,6 +845,9 @@ static int swap_stop(Unit *u) {
assert(s->state == SWAP_ACTIVATING ||
s->state == SWAP_ACTIVE);
+ if (detect_container(NULL) > 0)
+ return -EPERM;
+
swap_enter_deactivating(s);
return 0;
}
@@ -849,8 +880,9 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
if (streq(key, "state")) {
SwapState state;
- if ((state = swap_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
+ state = swap_state_from_string(value);
+ if (state < 0)
+ log_debug_unit(u->id, "Failed to parse state value %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -858,29 +890,30 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
f = swap_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse result value %s", value);
+ log_debug_unit(u->id, "Failed to parse result value %s", value);
else if (f != SWAP_SUCCESS)
s->result = f;
} else if (streq(key, "control-pid")) {
pid_t pid;
if (parse_pid(value, &pid) < 0)
- log_debug("Failed to parse control-pid value %s", value);
+ log_debug_unit(u->id, "Failed to parse control-pid value %s", value);
else
s->control_pid = pid;
} else if (streq(key, "control-command")) {
SwapExecCommand id;
- if ((id = swap_exec_command_from_string(value)) < 0)
- log_debug("Failed to parse exec-command value %s", value);
+ id = swap_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;
s->control_command = s->exec_command + id;
}
} else
- log_debug("Unknown serialization key '%s'", key);
+ log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
@@ -917,7 +950,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = SWAP_SUCCESS;
else if (code == CLD_EXITED)
f = SWAP_FAILURE_EXIT_CODE;
@@ -938,8 +971,10 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_command_id = _SWAP_EXEC_COMMAND_INVALID;
}
- log_full(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
- "%s swap process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status);
+ log_full_unit(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+ u->id,
+ "%s swap process exited, code=%s status=%i",
+ u->id, sigchld_code_to_string(code), status);
switch (s->state) {
@@ -985,38 +1020,38 @@ static void swap_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
switch (s->state) {
case SWAP_ACTIVATING:
- log_warning("%s activation timed out. Stopping.", u->id);
+ log_warning_unit(u->id, "%s activation timed out. Stopping.", u->id);
swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
break;
case SWAP_DEACTIVATING:
- log_warning("%s deactivation timed out. Stopping.", u->id);
+ log_warning_unit(u->id, "%s deactivation timed out. Stopping.", u->id);
swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT);
break;
case SWAP_ACTIVATING_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_warning("%s activation timed out. Killing.", u->id);
+ log_warning_unit(u->id, "%s activation timed out. Killing.", u->id);
swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
} else {
- log_warning("%s activation timed out. Skipping SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id, "%s activation timed out. Skipping SIGKILL. Ignoring.", u->id);
swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
}
break;
case SWAP_DEACTIVATING_SIGTERM:
if (s->kill_context.send_sigkill) {
- log_warning("%s deactivation timed out. Killing.", u->id);
+ log_warning_unit(u->id, "%s deactivation timed out. Killing.", u->id);
swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT);
} else {
- log_warning("%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", u->id);
swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
}
break;
case SWAP_ACTIVATING_SIGKILL:
case SWAP_DEACTIVATING_SIGKILL:
- log_warning("%s swap process still around after SIGKILL. Ignoring.", u->id);
+ log_warning_unit(u->id, "%s swap process still around after SIGKILL. Ignoring.", u->id);
swap_enter_dead(s, SWAP_FAILURE_TIMEOUT);
break;
@@ -1039,18 +1074,18 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
char *dev = NULL, *d;
int prio = 0, k;
- if ((k = fscanf(m->proc_swaps,
- "%ms " /* device/file */
- "%*s " /* type of swap */
- "%*s " /* swap size */
- "%*s " /* used */
- "%i\n", /* priority */
- &dev, &prio)) != 2) {
-
+ k = fscanf(m->proc_swaps,
+ "%ms " /* device/file */
+ "%*s " /* type of swap */
+ "%*s " /* swap size */
+ "%*s " /* used */
+ "%i\n", /* priority */
+ &dev, &prio);
+ if (k != 2) {
if (k == EOF)
break;
- log_warning("Failed to parse /proc/swaps:%u.", i);
+ log_warning("Failed to parse /proc/swaps:%u", i);
free(dev);
continue;
}
@@ -1089,7 +1124,8 @@ int swap_fd_event(Manager *m, int events) {
assert(m);
assert(events & EPOLLPRI);
- if ((r = swap_load_proc_swaps(m, true)) < 0) {
+ r = swap_load_proc_swaps(m, true);
+ if (r < 0) {
log_error("Failed to reread /proc/swaps: %s", strerror(-r));
/* Reset flags, just in case, for late calls */
@@ -1229,7 +1265,8 @@ static int swap_enumerate(Manager *m) {
assert(m);
if (!m->proc_swaps) {
- if (!(m->proc_swaps = fopen("/proc/swaps", "re")))
+ m->proc_swaps = fopen("/proc/swaps", "re");
+ if (!m->proc_swaps)
return (errno == ENOENT) ? 0 : -errno;
m->swap_watch.type = WATCH_SWAP;
@@ -1243,7 +1280,8 @@ static int swap_enumerate(Manager *m) {
return -errno;
}
- if ((r = swap_load_proc_swaps(m, false)) < 0)
+ r = swap_load_proc_swaps(m, false);
+ if (r < 0)
swap_shutdown(m);
return r;
@@ -1344,6 +1382,8 @@ 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"
diff --git a/src/core/switch-root.c b/src/core/switch-root.c
index 9832a520e4..ce0e41d510 100644
--- a/src/core/switch-root.c
+++ b/src/core/switch-root.c
@@ -30,6 +30,7 @@
#include "util.h"
#include "path-util.h"
#include "switch-root.h"
+#include "missing.h"
int switch_root(const char *new_root) {
@@ -44,10 +45,21 @@ int switch_root(const char *new_root) {
struct stat new_root_stat;
bool old_root_remove;
const char *i;
+ _cleanup_free_ char *temporary_old_root = NULL;
if (path_equal(new_root, "/"))
return 0;
+ /* When using pivot_root() we assume that /mnt exists as place
+ * we can temporarily move the old root to. As we immediately
+ * unmount it from there it doesn't matter much which
+ * directory we choose for this, but it should be more likely
+ * than not that /mnt exists and is suitable as mount point
+ * and is on the same fs as the old root dir */
+ temporary_old_root = strappend(new_root, "/mnt");
+ if (!temporary_old_root)
+ return -ENOMEM;
+
old_root_remove = in_initrd();
if (stat(new_root, &new_root_stat) < 0) {
@@ -56,6 +68,15 @@ int switch_root(const char *new_root) {
goto fail;
}
+ /* Work-around for a kernel bug: for some reason the kernel
+ * refuses switching root if any file systems are mounted
+ * MS_SHARED. Hence remount them MS_PRIVATE here as a
+ * work-around.
+ *
+ * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
+ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
+ log_warning("Failed to make \"/\" private mount: %m");
+
NULSTR_FOREACH(i, move_mounts) {
char new_mount[PATH_MAX];
struct stat sb;
@@ -94,7 +115,20 @@ int switch_root(const char *new_root) {
log_warning("Failed to open root directory: %m");
}
- if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) {
+ /* We first try a pivot_root() so that we can umount the old
+ * root dir. In many cases (i.e. where rootfs is /), that's
+ * not possible however, and hence we simply overmount root */
+ if (pivot_root(new_root, temporary_old_root) >= 0) {
+
+ /* Immediately get rid of the old root. Since we are
+ * running off it we need to do this lazily. */
+ if (umount2(temporary_old_root, MNT_DETACH) < 0) {
+ r = -errno;
+ log_error("Failed to umount old root dir %s: %m", temporary_old_root);
+ goto fail;
+ }
+
+ } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) {
r = -errno;
log_error("Failed to mount moving %s to /: %m", new_root);
goto fail;
@@ -106,6 +140,12 @@ int switch_root(const char *new_root) {
goto fail;
}
+ if (chdir("/") < 0) {
+ r = -errno;
+ log_error("Failed to change directory: %m");
+ goto fail;
+ }
+
if (old_root_fd >= 0) {
struct stat rb;
diff --git a/src/core/system.conf b/src/core/system.conf
index d5208186a0..68076d9735 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -20,7 +20,7 @@
#DefaultControllers=cpu
#DefaultStandardOutput=journal
#DefaultStandardError=inherit
-#JoinControllers=cpu,cpuacct
+#JoinControllers=cpu,cpuacct,cpuset net_cls,net_prio
#RuntimeWatchdogSec=0
#ShutdownWatchdogSec=10min
#CapabilityBoundingSet=
diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
index 49f65b2a65..2f49d5df52 100644
--- a/src/core/systemd.pc.in
+++ b/src/core/systemd.pc.in
@@ -9,7 +9,9 @@ prefix=@prefix@
exec_prefix=@exec_prefix@
systemdutildir=@rootlibexecdir@
systemdsystemunitdir=@systemunitdir@
+systemdsystempresetdir=@systempresetdir@
systemduserunitdir=@userunitdir@
+systemduserpresetdir=@userpresetdir@
systemdsystemconfdir=@pkgsysconfdir@/system
systemduserconfdir=@pkgsysconfdir@/user
systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system
diff --git a/src/core/target.c b/src/core/target.c
index 55f9fc6fc4..981424132b 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -53,13 +53,15 @@ static void target_set_state(Target *t, TargetState state) {
}
static int target_add_default_dependencies(Target *t) {
+
static const UnitDependency deps[] = {
UNIT_REQUIRES,
UNIT_REQUIRES_OVERRIDABLE,
UNIT_REQUISITE,
UNIT_REQUISITE_OVERRIDABLE,
UNIT_WANTS,
- UNIT_BINDS_TO
+ UNIT_BINDS_TO,
+ UNIT_PART_OF
};
Iterator i;
@@ -75,9 +77,11 @@ static int target_add_default_dependencies(Target *t) {
* sure we don't create a loop. */
for (k = 0; k < ELEMENTSOF(deps); k++)
- SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i)
- if ((r = unit_add_default_target_dependency(other, UNIT(t))) < 0)
+ SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i) {
+ r = unit_add_default_target_dependency(other, UNIT(t));
+ if (r < 0)
return r;
+ }
/* Make sure targets are unloaded on shutdown */
return unit_add_dependency_by_name(UNIT(t), UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
@@ -89,14 +93,15 @@ static int target_load(Unit *u) {
assert(t);
- if ((r = unit_load_fragment_and_dropin(u)) < 0)
+ r = unit_load_fragment_and_dropin(u);
+ if (r < 0)
return r;
/* This is a new unit? Then let's add in some extras */
- if (u->load_state == UNIT_LOADED) {
- if (u->default_dependencies)
- if ((r = target_add_default_dependencies(t)) < 0)
- return r;
+ if (u->load_state == UNIT_LOADED && u->default_dependencies) {
+ r = target_add_default_dependencies(t);
+ if (r < 0)
+ return r;
}
return 0;
@@ -167,7 +172,8 @@ static int target_deserialize_item(Unit *u, const char *key, const char *value,
if (streq(key, "state")) {
TargetState state;
- if ((state = target_state_from_string(value)) < 0)
+ state = target_state_from_string(value);
+ if (state < 0)
log_debug("Failed to parse state value %s", value);
else
s->deserialized_state = state;
diff --git a/src/core/timer.c b/src/core/timer.c
index 0b3c5ce077..31ef176e7e 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -42,7 +42,10 @@ static void timer_init(Unit *u) {
assert(u);
assert(u->load_state == UNIT_STUB);
- t->next_elapse = (usec_t) -1;
+ t->next_elapse_monotonic = (usec_t) -1;
+ t->next_elapse_realtime = (usec_t) -1;
+ watch_init(&t->monotonic_watch);
+ watch_init(&t->realtime_watch);
}
static void timer_done(Unit *u) {
@@ -53,10 +56,15 @@ static void timer_done(Unit *u) {
while ((v = t->values)) {
LIST_REMOVE(TimerValue, value, t->values, v);
+
+ if (v->calendar_spec)
+ calendar_spec_free(v->calendar_spec);
+
free(v);
}
- unit_unwatch_timer(u, &t->timer_watch);
+ unit_unwatch_timer(u, &t->monotonic_watch);
+ unit_unwatch_timer(u, &t->realtime_watch);
unit_ref_unset(&t->unit);
}
@@ -68,7 +76,8 @@ static int timer_verify(Timer *t) {
return 0;
if (!t->values) {
- log_error("%s lacks value setting. Refusing.", UNIT(t)->id);
+ log_error_unit(UNIT(t)->id,
+ "%s lacks value setting. Refusing.", UNIT(t)->id);
return -EINVAL;
}
@@ -80,11 +89,13 @@ static int timer_add_default_dependencies(Timer *t) {
assert(t);
- if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
- if ((r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
+ 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;
- if ((r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (r < 0)
return r;
}
@@ -98,7 +109,8 @@ static int timer_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) {
@@ -117,9 +129,11 @@ static int timer_load(Unit *u) {
if (r < 0)
return r;
- if (UNIT(t)->default_dependencies)
- if ((r = timer_add_default_dependencies(t)) < 0)
+ if (UNIT(t)->default_dependencies) {
+ r = timer_add_default_dependencies(t);
+ if (r < 0)
return r;
+ }
}
return timer_verify(t);
@@ -128,8 +142,6 @@ static int timer_load(Unit *u) {
static void timer_dump(Unit *u, FILE *f, const char *prefix) {
Timer *t = TIMER(u);
TimerValue *v;
- char
- timespan1[FORMAT_TIMESPAN_MAX];
fprintf(f,
"%sTimer State: %s\n"
@@ -139,12 +151,28 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
prefix, timer_result_to_string(t->result),
prefix, UNIT_DEREF(t->unit)->id);
- LIST_FOREACH(value, v, t->values)
- fprintf(f,
- "%s%s: %s\n",
- prefix,
- timer_base_to_string(v->base),
- strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
+ LIST_FOREACH(value, v, t->values) {
+
+ if (v->base == TIMER_CALENDAR) {
+ _cleanup_free_ char *p = NULL;
+
+ calendar_spec_to_string(v->calendar_spec, &p);
+
+ fprintf(f,
+ "%s%s: %s\n",
+ prefix,
+ timer_base_to_string(v->base),
+ strna(p));
+ } else {
+ char timespan1[FORMAT_TIMESPAN_MAX];
+
+ fprintf(f,
+ "%s%s: %s\n",
+ prefix,
+ timer_base_to_string(v->base),
+ strna(format_timespan(timespan1, sizeof(timespan1), v->value)));
+ }
+ }
}
static void timer_set_state(Timer *t, TimerState state) {
@@ -154,14 +182,16 @@ static void timer_set_state(Timer *t, TimerState state) {
old_state = t->state;
t->state = state;
- if (state != TIMER_WAITING)
- unit_unwatch_timer(UNIT(t), &t->timer_watch);
+ if (state != TIMER_WAITING) {
+ unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
+ unit_unwatch_timer(UNIT(t), &t->realtime_watch);
+ }
if (state != old_state)
- log_debug("%s changed %s -> %s",
- UNIT(t)->id,
- timer_state_to_string(old_state),
- timer_state_to_string(state));
+ log_debug_unit(UNIT(t)->id,
+ "%s changed %s -> %s", UNIT(t)->id,
+ timer_state_to_string(old_state),
+ timer_state_to_string(state));
unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
}
@@ -196,85 +226,131 @@ static void timer_enter_dead(Timer *t, TimerResult f) {
static void timer_enter_waiting(Timer *t, bool initial) {
TimerValue *v;
- usec_t base = 0, delay, n;
- bool found = false;
+ usec_t base = 0;
+ dual_timestamp ts;
+ bool found_monotonic = false, found_realtime = false;
int r;
- n = now(CLOCK_MONOTONIC);
+ dual_timestamp_get(&ts);
+ t->next_elapse_monotonic = t->next_elapse_realtime = 0;
LIST_FOREACH(value, v, t->values) {
if (v->disabled)
continue;
- switch (v->base) {
+ if (v->base == TIMER_CALENDAR) {
- case TIMER_ACTIVE:
- if (state_translation_table[t->state] == UNIT_ACTIVE)
- base = UNIT(t)->inactive_exit_timestamp.monotonic;
+ r = calendar_spec_next_usec(v->calendar_spec, ts.realtime, &v->next_elapse);
+ 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
- base = n;
- break;
+ t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
- case TIMER_BOOT:
- /* CLOCK_MONOTONIC equals the uptime on Linux */
- base = 0;
- break;
+ found_realtime = true;
- case TIMER_STARTUP:
- base = UNIT(t)->manager->startup_timestamp.monotonic;
- break;
+ } else {
+ switch (v->base) {
- case TIMER_UNIT_ACTIVE:
+ case TIMER_ACTIVE:
+ if (state_translation_table[t->state] == UNIT_ACTIVE)
+ base = UNIT(t)->inactive_exit_timestamp.monotonic;
+ else
+ base = ts.monotonic;
+ break;
- if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
- continue;
+ case TIMER_BOOT:
+ /* CLOCK_MONOTONIC equals the uptime on Linux */
+ base = 0;
+ break;
- base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
- break;
+ case TIMER_STARTUP:
+ base = UNIT(t)->manager->userspace_timestamp.monotonic;
+ break;
- case TIMER_UNIT_INACTIVE:
+ case TIMER_UNIT_ACTIVE:
- if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
- continue;
+ if (UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic <= 0)
+ continue;
- base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
- break;
+ base = UNIT_DEREF(t->unit)->inactive_exit_timestamp.monotonic;
+ break;
- default:
- assert_not_reached("Unknown timer base");
- }
+ case TIMER_UNIT_INACTIVE:
- v->next_elapse = base + v->value;
+ if (UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic <= 0)
+ continue;
- if (!initial && v->next_elapse < n) {
- v->disabled = true;
- continue;
- }
+ base = UNIT_DEREF(t->unit)->inactive_enter_timestamp.monotonic;
+ break;
- if (!found)
- t->next_elapse = v->next_elapse;
- else
- t->next_elapse = MIN(t->next_elapse, v->next_elapse);
+ default:
+ assert_not_reached("Unknown timer base");
+ }
+
+ v->next_elapse = base + v->value;
+
+ if (!initial && v->next_elapse < ts.monotonic) {
+ v->disabled = true;
+ continue;
+ }
+
+ if (!found_monotonic)
+ t->next_elapse_monotonic = v->next_elapse;
+ else
+ t->next_elapse_monotonic = MIN(t->next_elapse_monotonic, v->next_elapse);
- found = true;
+ found_monotonic = true;
+ }
}
- if (!found) {
+ if (!found_monotonic && !found_realtime) {
+ log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id);
timer_set_state(t, TIMER_ELAPSED);
return;
}
- delay = n < t->next_elapse ? t->next_elapse - n : 0;
+ if (found_monotonic) {
+ char buf[FORMAT_TIMESPAN_MAX];
+ log_debug_unit(UNIT(t)->id,
+ "%s: Monotonic timer elapses in %s the next time.",
+ UNIT(t)->id,
+ format_timespan(buf, sizeof(buf), t->next_elapse_monotonic - ts.monotonic));
- if ((r = unit_watch_timer(UNIT(t), delay, &t->timer_watch)) < 0)
- goto fail;
+ r = unit_watch_timer(UNIT(t), CLOCK_MONOTONIC, false, t->next_elapse_monotonic, &t->monotonic_watch);
+ if (r < 0)
+ goto fail;
+ } else
+ unit_unwatch_timer(UNIT(t), &t->monotonic_watch);
+
+ if (found_realtime) {
+ char buf[FORMAT_TIMESTAMP_MAX];
+ log_debug_unit(UNIT(t)->id,
+ "%s: Realtime timer elapses at %s the next time.",
+ UNIT(t)->id,
+ format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
+
+ r = unit_watch_timer(UNIT(t), CLOCK_REALTIME, false, t->next_elapse_realtime, &t->realtime_watch);
+ if (r < 0)
+ goto fail;
+ } else
+ unit_unwatch_timer(UNIT(t), &t->realtime_watch);
timer_set_state(t, TIMER_WAITING);
return;
fail:
- log_warning("%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r));
+ log_warning_unit(UNIT(t)->id,
+ "%s failed to enter waiting state: %s",
+ UNIT(t)->id, strerror(-r));
timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
}
@@ -289,14 +365,17 @@ static void timer_enter_running(Timer *t) {
if (UNIT(t)->job && UNIT(t)->job->type == JOB_STOP)
return;
- if ((r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL)) < 0)
+ r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_DEREF(t->unit), JOB_REPLACE, true, &error, NULL);
+ if (r < 0)
goto fail;
timer_set_state(t, TIMER_RUNNING);
return;
fail:
- log_warning("%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error(&error, r));
+ log_warning_unit(UNIT(t)->id,
+ "%s failed to queue unit startup job: %s",
+ UNIT(t)->id, bus_error(&error, r));
timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
dbus_error_free(&error);
@@ -350,8 +429,9 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
if (streq(key, "state")) {
TimerState state;
- if ((state = timer_state_from_string(value)) < 0)
- log_debug("Failed to parse state value %s", value);
+ state = timer_state_from_string(value);
+ if (state < 0)
+ log_debug_unit(u->id, "Failed to parse state value %s", value);
else
t->deserialized_state = state;
} else if (streq(key, "result")) {
@@ -359,12 +439,12 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F
f = timer_result_from_string(value);
if (f < 0)
- log_debug("Failed to parse result value %s", value);
+ log_debug_unit(u->id, "Failed to parse result value %s", value);
else if (f != TIMER_SUCCESS)
t->result = f;
} else
- log_debug("Unknown serialization key '%s'", key);
+ log_debug_unit(u->id, "Unknown serialization key '%s'", key);
return 0;
}
@@ -390,7 +470,7 @@ static void timer_timer_event(Unit *u, uint64_t elapsed, Watch *w) {
if (t->state != TIMER_WAITING)
return;
- log_debug("Timer elapsed on %s", u->id);
+ log_debug_unit(u->id, "Timer elapsed on %s", u->id);
timer_enter_running(t);
}
@@ -431,7 +511,9 @@ void timer_unit_notify(Unit *u, UnitActiveState new_state) {
case TIMER_RUNNING:
if (UNIT_IS_INACTIVE_OR_FAILED(new_state)) {
- log_debug("%s got notified about unit deactivation.", UNIT(t)->id);
+ log_debug_unit(UNIT(t)->id,
+ "%s got notified about unit deactivation.",
+ UNIT(t)->id);
timer_enter_waiting(t, false);
}
@@ -458,6 +540,19 @@ static void timer_reset_failed(Unit *u) {
t->result = TIMER_SUCCESS;
}
+static void timer_time_change(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(u);
+
+ if (t->state != TIMER_WAITING)
+ return;
+
+ log_info_unit(u->id,
+ "%s: time change, recalculating next elapse.", u->id);
+ timer_enter_waiting(t, false);
+}
+
static const char* const timer_state_table[_TIMER_STATE_MAX] = {
[TIMER_DEAD] = "dead",
[TIMER_WAITING] = "waiting",
@@ -473,7 +568,8 @@ static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_BOOT] = "OnBootSec",
[TIMER_STARTUP] = "OnStartupSec",
[TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
- [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec"
+ [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
+ [TIMER_CALENDAR] = "OnCalendar"
};
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
@@ -512,6 +608,7 @@ const UnitVTable timer_vtable = {
.timer_event = timer_timer_event,
.reset_failed = timer_reset_failed,
+ .time_change = timer_time_change,
.bus_interface = "org.freedesktop.systemd1.Timer",
.bus_message_handler = bus_timer_message_handler,
diff --git a/src/core/timer.h b/src/core/timer.h
index c6d1d42e44..57a514a68c 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -24,6 +24,7 @@
typedef struct Timer Timer;
#include "unit.h"
+#include "calendarspec.h"
typedef enum TimerState {
TIMER_DEAD,
@@ -41,18 +42,21 @@ typedef enum TimerBase {
TIMER_STARTUP,
TIMER_UNIT_ACTIVE,
TIMER_UNIT_INACTIVE,
+ TIMER_CALENDAR,
_TIMER_BASE_MAX,
_TIMER_BASE_INVALID = -1
} TimerBase;
typedef struct TimerValue {
+ TimerBase base;
+ bool disabled;
+ clockid_t clock_id;
+
usec_t value;
+ CalendarSpec *calendar_spec;
usec_t next_elapse;
LIST_FIELDS(struct TimerValue, value);
-
- TimerBase base;
- bool disabled;
} TimerValue;
typedef enum TimerResult {
@@ -66,12 +70,14 @@ struct Timer {
Unit meta;
LIST_HEAD(TimerValue, values);
- usec_t next_elapse;
+ usec_t next_elapse_monotonic;
+ usec_t next_elapse_realtime;
TimerState state, deserialized_state;
UnitRef unit;
- Watch timer_watch;
+ Watch monotonic_watch;
+ Watch realtime_watch;
TimerResult result;
};
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 1f8d803aeb..1854047afd 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -186,8 +186,14 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
* another unit in which case we
* rather remove the start. */
- log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
- log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
+ log_debug_unit(j->unit->id,
+ "Looking at job %s/%s conflicted_by=%s",
+ j->unit->id, job_type_to_string(j->type),
+ yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
+ log_debug_unit(k->unit->id,
+ "Looking at job %s/%s conflicted_by=%s",
+ k->unit->id, job_type_to_string(k->type),
+ yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
if (j->type == JOB_STOP) {
@@ -213,7 +219,9 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
return -ENOEXEC;
/* Ok, we can drop one, so let's do so. */
- log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type));
+ log_debug_unit(d->unit->id,
+ "Fixing conflicting jobs by deleting job %s/%s",
+ d->unit->id, job_type_to_string(d->type));
transaction_delete_job(tr, d, true);
return 0;
}
@@ -352,12 +360,17 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
* job to remove. We use the marker to find our way
* back, since smart how we are we stored our way back
* in there. */
- log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type));
+ log_warning_unit(j->unit->id,
+ "Found ordering cycle on %s/%s",
+ j->unit->id, job_type_to_string(j->type));
delete = NULL;
for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
- log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type));
+ /* logging for j not k here here to provide consistent narrative */
+ log_info_unit(j->unit->id,
+ "Walked on cycle path to %s/%s",
+ k->unit->id, job_type_to_string(k->type));
if (!delete &&
!unit_matters_to_anchor(k->unit, k)) {
@@ -374,14 +387,24 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
if (delete) {
- log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type));
+ /* logging for j not k here here to provide consistent narrative */
+ log_warning_unit(j->unit->id,
+ "Breaking ordering cycle by deleting job %s/%s",
+ delete->unit->id, job_type_to_string(delete->type));
+ log_error_unit(delete->unit->id,
+ "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));
transaction_delete_unit(tr, delete->unit);
return -EAGAIN;
}
log_error("Unable to break cycle");
- dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
+ dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
+ "Transaction order is cyclic. See system logs for details.");
return -ENOEXEC;
}
@@ -392,7 +415,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
j->marker = from ? from : j;
j->generation = generation;
- /* We assume that the the dependencies are bidirectional, and
+ /* We assume that the dependencies are bidirectional, and
* hence can ignore UNIT_AFTER */
SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
Job *o;
@@ -524,13 +547,19 @@ rescan:
continue;
if (stops_running_service)
- log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
+ log_debug_unit(j->unit->id,
+ "%s/%s would stop a running service.",
+ j->unit->id, job_type_to_string(j->type));
if (changes_existing_job)
- log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
+ log_debug_unit(j->unit->id,
+ "%s/%s would change existing job.",
+ j->unit->id, job_type_to_string(j->type));
/* Ok, let's get rid of this */
- log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
+ log_debug_unit(j->unit->id,
+ "Deleting %s/%s to minimize impact.",
+ j->unit->id, job_type_to_string(j->type));
transaction_delete_job(tr, j, true);
goto rescan;
@@ -782,9 +811,10 @@ static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependen
job_dependency_free(j->object_list);
if (other && delete_dependencies) {
- log_debug("Deleting job %s/%s as dependency of job %s/%s",
- other->unit->id, job_type_to_string(other->type),
- j->unit->id, job_type_to_string(j->type));
+ log_debug_unit(other->unit->id,
+ "Deleting job %s/%s as dependency of job %s/%s",
+ other->unit->id, job_type_to_string(other->type),
+ j->unit->id, job_type_to_string(j->type));
transaction_delete_job(tr, other, delete_dependencies);
}
}
@@ -870,7 +900,9 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, following, i) {
r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
if (r < 0) {
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+ log_warning_unit(dep->id,
+ "Cannot add dependency job for unit %s, ignoring: %s",
+ dep->id, bus_error(e, r));
if (e)
dbus_error_free(e);
@@ -907,8 +939,9 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
if (r < 0) {
- log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
- "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+ log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
+ "Cannot add dependency job for unit %s, ignoring: %s",
+ dep->id, bus_error(e, r));
if (e)
dbus_error_free(e);
@@ -918,8 +951,9 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
if (r < 0) {
- log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
- "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+ log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
+ "Cannot add dependency job for unit %s, ignoring: %s",
+ dep->id, bus_error(e, r));
if (e)
dbus_error_free(e);
@@ -940,8 +974,9 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
if (r < 0) {
- log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
- "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+ log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
+ "Cannot add dependency job for unit %s, ignoring: %s",
+ dep->id, bus_error(e, r));
if (e)
dbus_error_free(e);
@@ -962,7 +997,9 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
if (r < 0) {
- log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+ log_warning_unit(dep->id,
+ "Cannot add dependency job for unit %s, ignoring: %s",
+ dep->id, bus_error(e, r));
if (e)
dbus_error_free(e);
@@ -1013,7 +1050,9 @@ int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
if (r < 0) {
- log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
+ log_warning_unit(dep->id,
+ "Cannot add dependency reload job for unit %s, ignoring: %s",
+ dep->id, bus_error(e, r));
if (e)
dbus_error_free(e);
@@ -1058,7 +1097,9 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
if (r < 0)
- log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r));
+ log_warning_unit(u->id,
+ "Cannot add isolate job for unit %s, ignoring: %s",
+ u->id, strerror(-r));
}
return 0;
diff --git a/src/core/umount.c b/src/core/umount.c
index a5a215b2b5..96232d38db 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -34,6 +34,7 @@
#include "umount.h"
#include "path-util.h"
#include "util.h"
+#include "virt.h"
typedef struct MountPoint {
char *path;
@@ -406,18 +407,44 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
assert(head);
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+
+ /* If we are in a container, don't attempt to
+ read-only mount anything as that brings no real
+ benefits, but might confuse the host, as we remount
+ the superblock here, not the bind mound. */
+ if (detect_container(NULL) <= 0) {
+ /* We always try to remount directories
+ * read-only first, before we go on and umount
+ * them.
+ *
+ * Mount points can be stacked. If a mount
+ * point is stacked below / or /usr, we
+ * cannnot 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
+ * do something about it if it is aliased
+ * somehwere else via a bind mount. If we
+ * explicitly remount the super block of that
+ * alias read-only we hence should be
+ * relatively safe regarding keeping the fs we
+ * can otherwise not see dirty. */
+ mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
+ }
+
+ /* Skip / and /usr since we cannot unmount that
+ * anyway, since we are running from it. They have
+ * already been remounted ro. */
if (path_equal(m->path, "/")
#ifndef HAVE_SPLIT_USR
|| path_equal(m->path, "/usr")
#endif
- ) {
- n_failed++;
+ )
continue;
- }
/* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+ log_info("Unmounting %s.", m->path);
if (umount2(m->path, MNT_FORCE) == 0) {
- log_info("Unmounted %s.", m->path);
if (changed)
*changed = true;
@@ -431,29 +458,6 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
return n_failed;
}
-static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
- MountPoint *m, *n;
- int n_failed = 0;
-
- assert(head);
-
- LIST_FOREACH_SAFE(mount_point, m, n, *head) {
-
- /* Trying to remount read-only */
- if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
- if (changed)
- *changed = true;
-
- mount_point_free(head, m);
- } else {
- log_warning("Could not remount as read-only %s: %m", m->path);
- n_failed++;
- }
- }
-
- return n_failed;
-}
-
static int swap_points_list_off(MountPoint **head, bool *changed) {
MountPoint *m, *n;
int n_failed = 0;
@@ -461,6 +465,7 @@ static int swap_points_list_off(MountPoint **head, bool *changed) {
assert(head);
LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+ log_info("Deactivating swap %s.", m->path);
if (swapoff(m->path) == 0) {
if (changed)
*changed = true;
@@ -496,14 +501,15 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) {
continue;
}
- if ((r = delete_loopback(m->path)) >= 0) {
-
+ log_info("Detaching loopback %s.", m->path);
+ r = delete_loopback(m->path);
+ if (r >= 0) {
if (r > 0 && changed)
*changed = true;
mount_point_free(head, m);
} else {
- log_warning("Could not delete loopback %s: %m", m->path);
+ log_warning("Could not detach loopback %s: %m", m->path);
n_failed++;
}
}
@@ -530,14 +536,15 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) {
continue;
}
- if ((r = delete_dm(m->devnum)) >= 0) {
-
- if (r > 0 && changed)
+ log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
+ r = delete_dm(m->devnum);
+ if (r >= 0) {
+ if (changed)
*changed = true;
mount_point_free(head, m);
} else {
- log_warning("Could not delete dm %s: %m", m->path);
+ log_warning("Could not detach DM %s: %m", m->path);
n_failed++;
}
}
@@ -548,11 +555,9 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) {
int umount_all(bool *changed) {
int r;
bool umount_changed;
-
LIST_HEAD(MountPoint, mp_list_head);
LIST_HEAD_INIT(MountPoint, mp_list_head);
-
r = mount_points_list_get(&mp_list_head);
if (r < 0)
goto end;
@@ -572,8 +577,6 @@ int umount_all(bool *changed) {
if (r <= 0)
goto end;
- r = mount_points_list_remount_read_only(&mp_list_head, changed);
-
end:
mount_points_list_free(&mp_list_head);
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
new file mode 100644
index 0000000000..a58c96c238
--- /dev/null
+++ b/src/core/unit-printf.c
@@ -0,0 +1,353 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 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 "systemd/sd-id128.h"
+#include "unit.h"
+#include "specifier.h"
+#include "path-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+
+static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ return unit_name_to_prefix_and_instance(u->id);
+}
+
+static char *specifier_prefix(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ return unit_name_to_prefix(u->id);
+}
+
+static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ char *p, *r;
+
+ assert(u);
+
+ p = unit_name_to_prefix(u->id);
+ if (!p)
+ return NULL;
+
+ r = unit_name_unescape(p);
+ free(p);
+
+ return r;
+}
+
+static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ if (u->instance)
+ return unit_name_unescape(u->instance);
+
+ return strdup("");
+}
+
+static char *specifier_filename(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ if (u->instance)
+ return unit_name_path_unescape(u->instance);
+
+ return unit_name_to_path(u->id);
+}
+
+static char *specifier_cgroup(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ return unit_default_cgroup_path(u);
+}
+
+static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ char *p;
+ assert(u);
+
+ if (specifier == 'r')
+ return strdup(u->manager->cgroup_hierarchy);
+
+ if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0)
+ return strdup("");
+
+ if (streq(p, "/")) {
+ free(p);
+ return strdup("");
+ }
+
+ return p;
+}
+
+static char *specifier_runtime(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ assert(u);
+
+ if (u->manager->running_as == SYSTEMD_USER) {
+ const char *e;
+
+ e = getenv("XDG_RUNTIME_DIR");
+ if (e)
+ return strdup(e);
+ }
+
+ return strdup("/run");
+}
+
+static char *specifier_user_name(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ ExecContext *c;
+ int r;
+ const char *username;
+ uid_t uid;
+ char *printed = NULL;
+
+ assert(u);
+
+ c = unit_get_exec_context(u);
+
+ /* get USER env from our own env if set */
+ if (!c || !c->user)
+ return getusername_malloc();
+
+ /* fish username from passwd */
+ username = c->user;
+ r = get_user_creds(&username, &uid, NULL, NULL, NULL);
+ if (r < 0)
+ return NULL;
+
+ switch (specifier) {
+ case 'U':
+ if (asprintf(&printed, "%d", uid) < 0)
+ return NULL;
+ break;
+ case 'u':
+ printed = strdup(username);
+ break;
+ }
+
+ return printed;
+}
+
+static char *specifier_user_home(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ ExecContext *c;
+ int r;
+ const char *username, *home;
+
+ assert(u);
+
+ c = unit_get_exec_context(u);
+
+ /* return HOME if set, otherwise from passwd */
+ if (!c || !c->user) {
+ char *h;
+
+ r = get_home_dir(&h);
+ if (r < 0)
+ return NULL;
+
+ return h;
+ }
+
+ username = c->user;
+ r = get_user_creds(&username, NULL, NULL, &home, NULL);
+ if (r < 0)
+ return NULL;
+
+ return strdup(home);
+}
+
+static char *specifier_user_shell(char specifier, void *data, void *userdata) {
+ Unit *u = userdata;
+ ExecContext *c;
+ int r;
+ const char *username, *shell;
+
+ 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;
+ }
+
+ username = c->user;
+ 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)
+ return NULL;
+
+ buf = new(char, 33);
+ if (!buf)
+ return NULL;
+
+ return sd_id128_to_string(id, buf);
+}
+
+static char *specifier_host_name(char specifier, void *data, void *userdata) {
+ return gethostname_malloc();
+}
+
+char *unit_name_printf(Unit *u, const char* format) {
+
+ /*
+ * This will use the passed string as format string and
+ * replace the following specifiers:
+ *
+ * %n: the full id of the unit (foo@bar.waldo)
+ * %N: the id of the unit without the suffix (foo@bar)
+ * %p: the prefix (foo)
+ * %i: the instance (bar)
+ */
+
+ const Specifier table[] = {
+ { 'n', specifier_string, u->id },
+ { 'N', specifier_prefix_and_instance, NULL },
+ { 'p', specifier_prefix, NULL },
+ { 'i', specifier_string, u->instance },
+ { 0, NULL, NULL }
+ };
+
+ assert(u);
+ assert(format);
+
+ return specifier_printf(format, table, u);
+}
+
+char *unit_full_printf(Unit *u, const char *format) {
+
+ /* This is similar to unit_name_printf() but also supports
+ * unescaping. Also, adds a couple of additional codes:
+ *
+ * %f the the instance if set, otherwise the id
+ * %c cgroup path of unit
+ * %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 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
+ */
+
+ const Specifier table[] = {
+ { 'n', specifier_string, u->id },
+ { 'N', specifier_prefix_and_instance, NULL },
+ { 'p', specifier_prefix, NULL },
+ { 'P', specifier_prefix_unescaped, NULL },
+ { 'i', specifier_string, u->instance },
+ { 'I', specifier_instance_unescaped, NULL },
+
+ { 'f', specifier_filename, NULL },
+ { 'c', specifier_cgroup, NULL },
+ { 'r', specifier_cgroup_root, NULL },
+ { 'R', specifier_cgroup_root, NULL },
+ { 't', specifier_runtime, NULL },
+ { 'U', specifier_user_name, NULL },
+ { 'u', specifier_user_name, NULL },
+ { 'h', specifier_user_home, NULL },
+ { 's', specifier_user_shell, NULL },
+
+ { 'm', specifier_machine_id, NULL },
+ { 'H', specifier_host_name, NULL },
+ { 'b', specifier_boot_id, NULL },
+ { 0, NULL, NULL }
+ };
+
+ assert(u);
+ assert(format);
+
+ return specifier_printf(format, table, u);
+}
+
+char **unit_full_printf_strv(Unit *u, char **l) {
+ size_t n;
+ char **r, **i, **j;
+
+ /* Applies unit_full_printf to every entry in l */
+
+ assert(u);
+
+ n = strv_length(l);
+ r = new(char*, n+1);
+ if (!r)
+ return NULL;
+
+ for (i = l, j = r; *i; i++, j++) {
+ *j = unit_full_printf(u, *i);
+ if (!*j)
+ goto fail;
+ }
+
+ *j = NULL;
+ return r;
+
+fail:
+ for (j--; j >= r; j--)
+ free(*j);
+
+ free(r);
+
+ return NULL;
+}
diff --git a/src/core/fdset.h b/src/core/unit-printf.h
index c3e408c8ad..d2f4ccd178 100644
--- a/src/core/fdset.h
+++ b/src/core/unit-printf.h
@@ -21,17 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-typedef struct FDSet FDSet;
+#include "unit.h"
-FDSet* fdset_new(void);
-void fdset_free(FDSet *s);
-
-int fdset_put(FDSet *s, int fd);
-int fdset_put_dup(FDSet *s, int fd);
-
-bool fdset_contains(FDSet *s, int fd);
-int fdset_remove(FDSet *s, int fd);
-
-int fdset_new_fill(FDSet **_s);
-
-int fdset_cloexec(FDSet *fds, bool b);
+char *unit_name_printf(Unit *u, const char* text);
+char *unit_full_printf(Unit *u, const char *text);
+char **unit_full_printf_strv(Unit *u, char **l);
diff --git a/src/core/unit.c b/src/core/unit.c
index 0d5d15ef90..45453dce64 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -29,6 +29,8 @@
#include <unistd.h>
#include <sys/stat.h>
+#include "systemd/sd-id128.h"
+#include "systemd/sd-messages.h"
#include "set.h"
#include "unit.h"
#include "macro.h"
@@ -38,7 +40,6 @@
#include "load-dropin.h"
#include "log.h"
#include "unit-name.h"
-#include "specifier.h"
#include "dbus-unit.h"
#include "special.h"
#include "cgroup-util.h"
@@ -256,6 +257,9 @@ bool unit_check_gc(Unit *u) {
if (unit_active_state(u) != UNIT_INACTIVE)
return true;
+ if (u->refs)
+ return true;
+
if (UNIT_VTABLE(u)->check_gc)
if (UNIT_VTABLE(u)->check_gc(u))
return true;
@@ -609,7 +613,7 @@ 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 == MANAGER_SYSTEM)
+ 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)
return r;
@@ -936,21 +940,96 @@ bool unit_condition_test(Unit *u) {
return u->condition_result;
}
-static void unit_status_print_starting_stopping(Unit *u, bool stopping) {
+static const char* unit_get_status_message_format(Unit *u, JobType t) {
const UnitStatusMessageFormats *format_table;
- const char *format;
+
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ if (t != JOB_START && t != JOB_STOP)
+ return NULL;
format_table = &UNIT_VTABLE(u)->status_message_formats;
if (!format_table)
- return;
+ return NULL;
+
+ return format_table->starting_stopping[t == JOB_STOP];
+}
- format = format_table->starting_stopping[stopping];
+static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
+ const char *format;
+
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ format = unit_get_status_message_format(u, t);
+ if (format)
+ return format;
+
+ /* Return generic strings */
+ if (t == JOB_START)
+ return "Starting %s.";
+ else if (t == JOB_STOP)
+ return "Stopping %s.";
+ else if (t == JOB_RELOAD)
+ return "Reloading %s.";
+
+ return NULL;
+}
+
+static void unit_status_print_starting_stopping(Unit *u, JobType t) {
+ const char *format;
+
+ assert(u);
+
+ /* We only print status messages for selected units on
+ * selected operations. */
+
+ format = unit_get_status_message_format(u, t);
if (!format)
return;
unit_status_printf(u, "", format, unit_description(u));
}
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
+ const char *format;
+ char buf[LINE_MAX];
+ sd_id128_t mid;
+
+ assert(u);
+
+ if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD)
+ return;
+
+ if (log_on_console())
+ return;
+
+ /* We log status messages for all units and all operations. */
+
+ format = unit_get_status_message_format_try_harder(u, t);
+ if (!format)
+ return;
+
+ snprintf(buf, sizeof(buf), format, unit_description(u));
+ char_array_0(buf);
+
+ mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING :
+ 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);
+}
+#pragma GCC diagnostic pop
+
/* Errors:
* -EBADR: This unit type does not support starting.
* -EALREADY: Unit is already started.
@@ -990,7 +1069,8 @@ int unit_start(Unit *u) {
return unit_start(following);
}
- unit_status_print_starting_stopping(u, false);
+ unit_status_log_starting_stopping_reloading(u, JOB_START);
+ unit_status_print_starting_stopping(u, JOB_START);
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
@@ -1040,7 +1120,8 @@ int unit_stop(Unit *u) {
return unit_stop(following);
}
- unit_status_print_starting_stopping(u, true);
+ unit_status_log_starting_stopping_reloading(u, JOB_STOP);
+ unit_status_print_starting_stopping(u, JOB_STOP);
if (!UNIT_VTABLE(u)->stop)
return -EBADR;
@@ -1079,6 +1160,8 @@ int unit_reload(Unit *u) {
return unit_reload(following);
}
+ unit_status_log_starting_stopping_reloading(u, JOB_RELOAD);
+
unit_add_to_dbus_queue(u);
return UNIT_VTABLE(u)->reload(u);
}
@@ -1243,7 +1326,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
/* Note that this is called for all low-level state changes,
* even if they might map to the same high-level
* UnitActiveState! That means that ns == os is OK an expected
- * behaviour here. For example: if a mount point is remounted
+ * behavior here. For example: if a mount point is remounted
* this function will be called too! */
if (u->manager->n_reloading <= 0) {
@@ -1355,7 +1438,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
check_unneeded_dependencies(u);
if (ns != os && ns == UNIT_FAILED) {
- log_notice("Unit %s entered failed state.", u->id);
+ log_struct(LOG_NOTICE,
+ "MESSAGE=Unit %s entered failed state", u->id,
+ "UNIT=%s", u->id,
+ NULL);
unit_trigger_on_failure(u);
}
}
@@ -1474,7 +1560,7 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
hashmap_remove_value(u->manager->watch_pids, LONG_TO_PTR(pid), u);
}
-int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
+int unit_watch_timer(Unit *u, clockid_t clock_id, bool relative, usec_t usec, Watch *w) {
struct itimerspec its;
int flags, fd;
bool ours;
@@ -1494,14 +1580,15 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
} else if (w->type == WATCH_INVALID) {
ours = true;
- if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
+ fd = timerfd_create(clock_id, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (fd < 0)
return -errno;
} else
assert_not_reached("Invalid watch type");
zero(its);
- if (delay <= 0) {
+ if (usec <= 0) {
/* Set absolute time in the past, but not 0, since we
* don't want to disarm the timer */
its.it_value.tv_sec = 0;
@@ -1509,8 +1596,8 @@ int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
flags = TFD_TIMER_ABSTIME;
} else {
- timespec_store(&its.it_value, delay);
- flags = 0;
+ timespec_store(&its.it_value, usec);
+ flags = relative ? 0 : TFD_TIMER_ABSTIME;
}
/* This will also flush the elapse counter */
@@ -1881,7 +1968,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
return 0;
}
-static char *default_cgroup_path(Unit *u) {
+char *unit_default_cgroup_path(Unit *u) {
char *p;
assert(u);
@@ -1914,7 +2001,7 @@ int unit_add_cgroup_from_text(Unit *u, const char *name) {
return r;
if (!path) {
- path = default_cgroup_path(u);
+ path = unit_default_cgroup_path(u);
ours = true;
}
@@ -1976,7 +2063,8 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
if (!(b->controller = strdup(controller)))
goto fail;
- if (!(b->path = default_cgroup_path(u)))
+ b->path = unit_default_cgroup_path(u);
+ if (!b->path)
goto fail;
b->ours = true;
@@ -2133,253 +2221,6 @@ int unit_get_related_unit(Unit *u, const char *type, Unit **_found) {
return 0;
}
-static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- return unit_name_to_prefix_and_instance(u->id);
-}
-
-static char *specifier_prefix(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- return unit_name_to_prefix(u->id);
-}
-
-static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- char *p, *r;
-
- assert(u);
-
- if (!(p = unit_name_to_prefix(u->id)))
- return NULL;
-
- r = unit_name_unescape(p);
- free(p);
-
- return r;
-}
-
-static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- if (u->instance)
- return unit_name_unescape(u->instance);
-
- return strdup("");
-}
-
-static char *specifier_filename(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- if (u->instance)
- return unit_name_path_unescape(u->instance);
-
- return unit_name_to_path(u->instance);
-}
-
-static char *specifier_cgroup(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- return default_cgroup_path(u);
-}
-
-static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- char *p;
- assert(u);
-
- if (specifier == 'r')
- return strdup(u->manager->cgroup_hierarchy);
-
- if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0)
- return strdup("");
-
- if (streq(p, "/")) {
- free(p);
- return strdup("");
- }
-
- return p;
-}
-
-static char *specifier_runtime(char specifier, void *data, void *userdata) {
- Unit *u = userdata;
- assert(u);
-
- if (u->manager->running_as == MANAGER_USER) {
- const char *e;
-
- e = getenv("XDG_RUNTIME_DIR");
- if (e)
- return strdup(e);
- }
-
- return strdup("/run");
-}
-
-static char *specifier_user_name(char specifier, void *data, void *userdata) {
- Service *s = userdata;
- int r;
- const char *username;
-
- /* get USER env from our own env if set */
- if (!s->exec_context.user)
- return getusername_malloc();
-
- /* fish username from passwd */
- username = s->exec_context.user;
- r = get_user_creds(&username, NULL, NULL, NULL, NULL);
- if (r < 0)
- return NULL;
-
- return strdup(username);
-}
-
-static char *specifier_user_home(char specifier, void *data, void *userdata) {
- Service *s = userdata;
- int r;
- const char *username, *home;
-
- /* return HOME if set, otherwise from passwd */
- if (!s->exec_context.user) {
- char *h;
-
- r = get_home_dir(&h);
- if (r < 0)
- return NULL;
-
- return h;
- }
-
- username = s->exec_context.user;
- r = get_user_creds(&username, NULL, NULL, &home, NULL);
- if (r < 0)
- return NULL;
-
- return strdup(home);
-}
-
-static char *specifier_user_shell(char specifier, void *data, void *userdata) {
- Service *s = userdata;
- int r;
- const char *username, *shell;
-
- /* return HOME if set, otherwise from passwd */
- if (!s->exec_context.user) {
- char *sh;
-
- r = get_shell(&sh);
- if (r < 0)
- return strdup("/bin/sh");
-
- return sh;
- }
-
- username = s->exec_context.user;
- r = get_user_creds(&username, NULL, NULL, NULL, &shell);
- if (r < 0)
- return strdup("/bin/sh");
-
- return strdup(shell);
-}
-
-char *unit_name_printf(Unit *u, const char* format) {
-
- /*
- * This will use the passed string as format string and
- * replace the following specifiers:
- *
- * %n: the full id of the unit (foo@bar.waldo)
- * %N: the id of the unit without the suffix (foo@bar)
- * %p: the prefix (foo)
- * %i: the instance (bar)
- */
-
- const Specifier table[] = {
- { 'n', specifier_string, u->id },
- { 'N', specifier_prefix_and_instance, NULL },
- { 'p', specifier_prefix, NULL },
- { 'i', specifier_string, u->instance },
- { 0, NULL, NULL }
- };
-
- assert(u);
- assert(format);
-
- return specifier_printf(format, table, u);
-}
-
-char *unit_full_printf(Unit *u, const char *format) {
-
- /* This is similar to unit_name_printf() but also supports
- * unescaping. Also, adds a couple of additional codes:
- *
- * %c cgroup path of unit
- * %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 username of the configured User or running user
- * %h the homedir of the configured User or running user
- */
-
- const Specifier table[] = {
- { 'n', specifier_string, u->id },
- { 'N', specifier_prefix_and_instance, NULL },
- { 'p', specifier_prefix, NULL },
- { 'P', specifier_prefix_unescaped, NULL },
- { 'i', specifier_string, u->instance },
- { 'I', specifier_instance_unescaped, NULL },
- { 'f', specifier_filename, NULL },
- { 'c', specifier_cgroup, NULL },
- { 'r', specifier_cgroup_root, NULL },
- { 'R', specifier_cgroup_root, NULL },
- { 't', specifier_runtime, NULL },
- { 'u', specifier_user_name, NULL },
- { 'h', specifier_user_home, NULL },
- { 's', specifier_user_shell, NULL },
- { 0, NULL, NULL }
- };
-
- assert(u);
- assert(format);
-
- return specifier_printf(format, table, u);
-}
-
-char **unit_full_printf_strv(Unit *u, char **l) {
- size_t n;
- char **r, **i, **j;
-
- /* Applies unit_full_printf to every entry in l */
-
- assert(u);
-
- n = strv_length(l);
- if (!(r = new(char*, n+1)))
- return NULL;
-
- for (i = l, j = r; *i; i++, j++)
- if (!(*j = unit_full_printf(u, *i)))
- goto fail;
-
- *j = NULL;
- return r;
-
-fail:
- for (j--; j >= r; j--)
- free(*j);
-
- free(r);
-
- return NULL;
-}
-
int unit_watch_bus_name(Unit *u, const char *name) {
assert(u);
assert(name);
@@ -2591,16 +2432,18 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
r = manager_load_unit(u->manager, e, NULL, NULL, &device);
free(e);
-
if (r < 0)
return r;
- if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true)) < 0)
+ r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BINDS_TO, device, true);
+ if (r < 0)
return r;
- if (wants)
- if ((r = unit_add_dependency(device, UNIT_WANTS, u, false)) < 0)
+ if (wants) {
+ r = unit_add_dependency(device, UNIT_WANTS, u, false);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -2751,7 +2594,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) {
if (u->unit_file_state < 0 && u->fragment_path)
u->unit_file_state = unit_file_get_state(
- u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+ u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
NULL, path_get_file_name(u->fragment_path));
return u->unit_file_state;
@@ -2834,7 +2677,7 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c) {
return -ENOMEM;
}
- if (u->manager->running_as == MANAGER_USER &&
+ if (u->manager->running_as == SYSTEMD_USER &&
!c->working_directory) {
r = get_home_dir(&c->working_directory);
@@ -2845,6 +2688,17 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c) {
return 0;
}
+ExecContext *unit_get_exec_context(Unit *u) {
+ size_t offset;
+ assert(u);
+
+ offset = UNIT_VTABLE(u)->exec_context_offset;
+ if (offset <= 0)
+ return NULL;
+
+ return (ExecContext*) ((uint8_t*) u + offset);
+}
+
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 89bd61c2d8..702bfeece6 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -266,6 +266,10 @@ struct UnitVTable {
/* How much memory does an object of this unit type need */
size_t object_size;
+ /* If greater than 0, the offset into the object where
+ * ExecContext is found, if the unit type has that */
+ size_t exec_context_offset;
+
/* Config file sections this unit type understands, separated
* by NUL chars */
const char *sections;
@@ -285,7 +289,7 @@ struct UnitVTable {
* UNIT_STUB if no configuration could be found. */
int (*load)(Unit *u);
- /* If a a lot of units got created via enumerate(), this is
+ /* If a lot of units got created via enumerate(), this is
* where to actually set the state and call unit_notify(). */
int (*coldplug)(Unit *u);
@@ -306,6 +310,9 @@ struct UnitVTable {
/* Restore one item from the serialization */
int (*deserialize_item)(Unit *u, const char *key, const char *data, FDSet *fds);
+ /* Try to match up fds with what we need for this unit */
+ int (*distribute_fds)(Unit *u, FDSet *fds);
+
/* Boils down the more complex internal state of this unit to
* a simpler one that the engine can understand */
UnitActiveState (*active_state)(Unit *u);
@@ -354,6 +361,9 @@ struct UnitVTable {
/* Return the set of units that are following each other */
int (*following_set)(Unit *u, Set **s);
+ /* Called whenever CLOCK_REALTIME made a jump */
+ void (*time_change)(Unit *u);
+
/* This is called for each unit type and should be used to
* enumerate existing devices and load them. However,
* everything that is loaded here should still stay in
@@ -480,7 +490,7 @@ void unit_unwatch_fd(Unit *u, Watch *w);
int unit_watch_pid(Unit *u, pid_t pid);
void unit_unwatch_pid(Unit *u, pid_t pid);
-int unit_watch_timer(Unit *u, usec_t delay, Watch *w);
+int unit_watch_timer(Unit *u, clockid_t, bool relative, usec_t usec, Watch *w);
void unit_unwatch_timer(Unit *u, Watch *w);
int unit_watch_bus_name(Unit *u, const char *name);
@@ -495,10 +505,6 @@ 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);
-char *unit_name_printf(Unit *u, const char* text);
-char *unit_full_printf(Unit *u, const char *text);
-char **unit_full_printf_strv(Unit *u, char **l);
-
bool unit_can_serialize(Unit *u);
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);
@@ -522,6 +528,8 @@ bool unit_pending_active(Unit *u);
int unit_add_default_target_dependency(Unit *u, Unit *target);
+char *unit_default_cgroup_path(Unit *u);
+
int unit_following_set(Unit *u, Set **s);
void unit_trigger_on_failure(Unit *u);
@@ -540,8 +548,17 @@ int unit_add_mount_links(Unit *u);
int unit_exec_context_defaults(Unit *u, ExecContext *c);
+ExecContext *unit_get_exec_context(Unit *u);
+
const char *unit_active_state_to_string(UnitActiveState i);
UnitActiveState unit_active_state_from_string(const char *s);
const char *unit_dependency_to_string(UnitDependency i);
UnitDependency unit_dependency_from_string(const char *s);
+
+#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "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__)