summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/.gitignore1
-rw-r--r--src/core/audit-fd.c6
-rw-r--r--src/core/audit-fd.h2
-rw-r--r--src/core/automount.c338
-rw-r--r--src/core/automount.h18
-rw-r--r--src/core/bus-endpoint.c134
-rw-r--r--src/core/bus-endpoint.h44
-rw-r--r--src/core/bus-policy.c7
-rw-r--r--src/core/bus-policy.h4
-rw-r--r--src/core/busname.c164
-rw-r--r--src/core/busname.h23
-rw-r--r--src/core/cgroup.c1488
-rw-r--r--src/core/cgroup.h93
-rw-r--r--src/core/dbus-automount.c59
-rw-r--r--src/core/dbus-automount.h4
-rw-r--r--src/core/dbus-busname.c7
-rw-r--r--src/core/dbus-busname.h2
-rw-r--r--src/core/dbus-cgroup.c712
-rw-r--r--src/core/dbus-cgroup.h3
-rw-r--r--src/core/dbus-device.c6
-rw-r--r--src/core/dbus-device.h2
-rw-r--r--src/core/dbus-execute.c878
-rw-r--r--src/core/dbus-execute.h3
-rw-r--r--src/core/dbus-job.c17
-rw-r--r--src/core/dbus-job.h3
-rw-r--r--src/core/dbus-kill.c19
-rw-r--r--src/core/dbus-kill.h5
-rw-r--r--src/core/dbus-manager.c858
-rw-r--r--src/core/dbus-manager.h2
-rw-r--r--src/core/dbus-mount.c18
-rw-r--r--src/core/dbus-mount.h3
-rw-r--r--src/core/dbus-path.c9
-rw-r--r--src/core/dbus-path.h2
-rw-r--r--src/core/dbus-scope.c23
-rw-r--r--src/core/dbus-scope.h3
-rw-r--r--src/core/dbus-service.c92
-rw-r--r--src/core/dbus-service.h3
-rw-r--r--src/core/dbus-slice.c6
-rw-r--r--src/core/dbus-slice.h3
-rw-r--r--src/core/dbus-snapshot.c55
-rw-r--r--src/core/dbus-snapshot.h28
-rw-r--r--src/core/dbus-socket.c40
-rw-r--r--src/core/dbus-socket.h3
-rw-r--r--src/core/dbus-swap.c13
-rw-r--r--src/core/dbus-swap.h3
-rw-r--r--src/core/dbus-target.c4
-rw-r--r--src/core/dbus-target.h3
-rw-r--r--src/core/dbus-timer.c60
-rw-r--r--src/core/dbus-timer.h3
-rw-r--r--src/core/dbus-unit.c590
-rw-r--r--src/core/dbus-unit.h14
-rw-r--r--src/core/dbus.c377
-rw-r--r--src/core/dbus.h12
-rw-r--r--src/core/device.c95
-rw-r--r--src/core/device.h15
-rw-r--r--src/core/dynamic-user.c796
-rw-r--r--src/core/dynamic-user.h66
-rw-r--r--src/core/execute.c1804
-rw-r--r--src/core/execute.h97
-rw-r--r--src/core/failure-action.c41
-rw-r--r--src/core/failure-action.h2
-rw-r--r--src/core/hostname-setup.c19
-rw-r--r--src/core/hostname-setup.h2
-rw-r--r--src/core/ima-setup.c10
-rw-r--r--src/core/ima-setup.h4
-rw-r--r--src/core/job.c477
-rw-r--r--src/core/job.h23
-rw-r--r--src/core/kill.c12
-rw-r--r--src/core/kill.h5
-rw-r--r--src/core/killall.c48
-rw-r--r--src/core/killall.h2
-rw-r--r--src/core/kmod-setup.c13
-rw-r--r--src/core/kmod-setup.h2
-rw-r--r--src/core/load-dropin.c22
-rw-r--r--src/core/load-dropin.h6
-rw-r--r--src/core/load-fragment-gperf.gperf.m4104
-rw-r--r--src/core/load-fragment.c2057
-rw-r--r--src/core/load-fragment.h22
-rw-r--r--src/core/locale-setup.c15
-rw-r--r--src/core/locale-setup.h2
-rw-r--r--src/core/loopback-setup.c13
-rw-r--r--src/core/loopback-setup.h2
-rw-r--r--src/core/machine-id-setup.c264
-rw-r--r--src/core/machine-id-setup.h4
-rw-r--r--src/core/macros.systemd.in22
-rw-r--r--src/core/main.c1040
-rw-r--r--src/core/manager.c1566
-rw-r--r--src/core/manager.h115
-rw-r--r--src/core/mount-setup.c120
-rw-r--r--src/core/mount-setup.h2
-rw-r--r--src/core/mount.c561
-rw-r--r--src/core/mount.h30
-rw-r--r--src/core/namespace.c760
-rw-r--r--src/core/namespace.h12
-rw-r--r--src/core/org.freedesktop.systemd1.conf40
-rw-r--r--src/core/path.c99
-rw-r--r--src/core/path.h15
-rw-r--r--src/core/scope.c201
-rw-r--r--src/core/scope.h19
-rw-r--r--src/core/selinux-access.c125
-rw-r--r--src/core/selinux-access.h9
-rw-r--r--src/core/selinux-setup.c22
-rw-r--r--src/core/selinux-setup.h2
-rw-r--r--src/core/service.c1170
-rw-r--r--src/core/service.h54
-rw-r--r--src/core/show-status.c86
-rw-r--r--src/core/show-status.h9
-rw-r--r--src/core/shutdown.c90
-rw-r--r--src/core/slice.c85
-rw-r--r--src/core/slice.h13
-rw-r--r--src/core/smack-setup.c126
-rw-r--r--src/core/smack-setup.h2
-rw-r--r--src/core/snapshot.c308
-rw-r--r--src/core/snapshot.h48
-rw-r--r--src/core/socket.c1182
-rw-r--r--src/core/socket.h59
-rw-r--r--src/core/swap.c237
-rw-r--r--src/core/swap.h25
-rw-r--r--src/core/system.conf11
-rw-r--r--src/core/target.c26
-rw-r--r--src/core/target.h13
-rw-r--r--src/core/timer.c194
-rw-r--r--src/core/timer.h18
-rw-r--r--src/core/transaction.c139
-rw-r--r--src/core/transaction.h9
-rw-r--r--src/core/triggers.systemd.in66
-rw-r--r--src/core/umount.c52
-rw-r--r--src/core/umount.h2
-rw-r--r--src/core/unit-printf.c199
-rw-r--r--src/core/unit-printf.h2
-rw-r--r--src/core/unit.c1632
-rw-r--r--src/core/unit.h177
-rw-r--r--src/core/user.conf2
133 files changed, 15121 insertions, 7988 deletions
diff --git a/src/core/.gitignore b/src/core/.gitignore
index f293bbdc93..465b4fcc20 100644
--- a/src/core/.gitignore
+++ b/src/core/.gitignore
@@ -1,2 +1,3 @@
/macros.systemd
+/triggers.systemd
/systemd.pc
diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c
index 5a18e263a8..76afe3fe15 100644
--- a/src/core/audit-fd.c
+++ b/src/core/audit-fd.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,13 +19,15 @@
#include <errno.h>
+
#include "audit-fd.h"
#ifdef HAVE_AUDIT
-#include <stdbool.h>
#include <libaudit.h>
+#include <stdbool.h>
+#include "fd-util.h"
#include "log.h"
#include "util.h"
diff --git a/src/core/audit-fd.h b/src/core/audit-fd.h
index 8b58289dc5..0eccb59210 100644
--- a/src/core/audit-fd.h
+++ b/src/core/audit-fd.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/automount.c b/src/core/automount.c
index d847dc1629..7d7a0a6e46 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,29 +18,37 @@
***/
#include <errno.h>
-#include <limits.h>
-#include <sys/mount.h>
-#include <unistd.h>
#include <fcntl.h>
+#include <limits.h>
+#include <linux/auto_dev-ioctl.h>
+#include <linux/auto_fs4.h>
#include <sys/epoll.h>
+#include <sys/mount.h>
#include <sys/stat.h>
-#include <linux/auto_fs4.h>
-#include <linux/auto_dev-ioctl.h>
+#include <unistd.h>
-#include "unit.h"
+#include "alloc-util.h"
+#include "async.h"
#include "automount.h"
-#include "mount.h"
-#include "unit-name.h"
-#include "special.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "dbus-automount.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "io-util.h"
#include "label.h"
#include "mkdir.h"
+#include "mount-util.h"
+#include "mount.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "dbus-automount.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "formats-util.h"
#include "process-util.h"
-#include "async.h"
+#include "special.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "unit-name.h"
+#include "unit.h"
static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = UNIT_INACTIVE,
@@ -69,6 +75,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free);
static int open_dev_autofs(Manager *m);
static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata);
+static int automount_start_expire(Automount *a);
+static void automount_stop_expire(Automount *a);
+static int automount_send_ready(Automount *a, Set *tokens, int status);
static void automount_init(Unit *u) {
Automount *a = AUTOMOUNT(u);
@@ -81,34 +90,14 @@ static void automount_init(Unit *u) {
UNIT(a)->ignore_on_isolate = true;
}
-static void repeat_unmount(const char *path) {
- assert(path);
-
- for (;;) {
- /* If there are multiple mounts on a mount point, this
- * removes them all */
-
- if (umount2(path, MNT_DETACH) >= 0)
- continue;
-
- if (errno != EINVAL)
- log_error_errno(errno, "Failed to unmount: %m");
-
- break;
- }
-}
-
-static int automount_send_ready(Automount *a, Set *tokens, int status);
-
static void unmount_autofs(Automount *a) {
+ int r;
+
assert(a);
if (a->pipe_fd < 0)
return;
- automount_send_ready(a, a->tokens, -EHOSTDOWN);
- automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
-
a->pipe_event_source = sd_event_source_unref(a->pipe_event_source);
a->pipe_fd = safe_close(a->pipe_fd);
@@ -116,8 +105,14 @@ static void unmount_autofs(Automount *a) {
* around */
if (a->where &&
(UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
- UNIT(a)->manager->exit_code != MANAGER_REEXECUTE))
- repeat_unmount(a->where);
+ UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) {
+ automount_send_ready(a, a->tokens, -EHOSTDOWN);
+ automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
+
+ r = repeat_unmount(a->where, MNT_DETACH);
+ if (r < 0)
+ log_error_errno(r, "Failed to unmount: %m");
+ }
}
static void automount_done(Unit *u) {
@@ -127,26 +122,22 @@ static void automount_done(Unit *u) {
unmount_autofs(a);
- free(a->where);
- a->where = NULL;
+ a->where = mfree(a->where);
- set_free(a->tokens);
- a->tokens = NULL;
- set_free(a->expire_tokens);
- a->expire_tokens = NULL;
+ a->tokens = set_free(a->tokens);
+ a->expire_tokens = set_free(a->expire_tokens);
a->expire_event_source = sd_event_source_unref(a->expire_event_source);
}
static int automount_add_mount_links(Automount *a) {
_cleanup_free_ char *parent = NULL;
- int r;
assert(a);
- r = path_get_parent(a->where, &parent);
- if (r < 0)
- return r;
+ parent = dirname_malloc(a->where);
+ if (!parent)
+ return -ENOMEM;
return unit_require_mounts_for(UNIT(a), parent);
}
@@ -156,7 +147,10 @@ static int automount_add_default_dependencies(Automount *a) {
assert(a);
- if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
+ if (!UNIT(a)->default_dependencies)
+ return 0;
+
+ if (!MANAGER_IS_SYSTEM(UNIT(a)->manager))
return 0;
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -227,11 +221,9 @@ static int automount_load(Unit *u) {
if (r < 0)
return r;
- if (UNIT(a)->default_dependencies) {
- r = automount_add_default_dependencies(a);
- if (r < 0)
- return r;
- }
+ r = automount_add_default_dependencies(a);
+ if (r < 0)
+ return r;
}
return automount_verify(a);
@@ -244,6 +236,9 @@ static void automount_set_state(Automount *a, AutomountState state) {
old_state = a->state;
a->state = state;
+ if (state != AUTOMOUNT_RUNNING)
+ automount_stop_expire(a);
+
if (state != AUTOMOUNT_WAITING &&
state != AUTOMOUNT_RUNNING)
unmount_autofs(a);
@@ -276,6 +271,11 @@ static int automount_coldplug(Unit *u) {
return r;
(void) sd_event_source_set_description(a->pipe_event_source, "automount-io");
+ if (a->deserialized_state == AUTOMOUNT_RUNNING) {
+ r = automount_start_expire(a);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
+ }
}
automount_set_state(a, a->deserialized_state);
@@ -306,7 +306,7 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) {
static void automount_enter_dead(Automount *a, AutomountResult f) {
assert(a);
- if (f != AUTOMOUNT_SUCCESS)
+ if (a->result == AUTOMOUNT_SUCCESS)
a->result = f;
automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD);
@@ -417,7 +417,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in
init_autofs_dev_ioctl(&param);
param.ioctlfd = ioctl_fd;
- if (status) {
+ if (status != 0) {
param.fail.token = token;
param.fail.status = status;
} else
@@ -444,7 +444,7 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) {
if (ioctl_fd < 0)
return ioctl_fd;
- if (status)
+ if (status != 0)
log_unit_debug_errno(UNIT(a), status, "Sending failure: %m");
else
log_unit_debug(UNIT(a), "Sending success.");
@@ -471,51 +471,68 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) {
return r;
}
-int automount_update_mount(Automount *a, MountState old_state, MountState state) {
+static void automount_trigger_notify(Unit *u, Unit *other) {
+ Automount *a = AUTOMOUNT(u);
+ int r;
+
assert(a);
+ assert(other);
- switch (state) {
- case MOUNT_MOUNTED:
- case MOUNT_REMOUNTING:
- automount_send_ready(a, a->tokens, 0);
- break;
- case MOUNT_DEAD:
- case MOUNT_UNMOUNTING:
- case MOUNT_MOUNTING_SIGTERM:
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_REMOUNTING_SIGTERM:
- case MOUNT_REMOUNTING_SIGKILL:
- case MOUNT_UNMOUNTING_SIGTERM:
- case MOUNT_UNMOUNTING_SIGKILL:
- case MOUNT_FAILED:
- if (old_state != state)
- automount_send_ready(a, a->tokens, -ENODEV);
- break;
- default:
- break;
+ /* Filter out invocations with bogus state */
+ if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT)
+ return;
+
+ /* Don't propagate state changes from the mount if we are already down */
+ if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING))
+ return;
+
+ /* Propagate start limit hit state */
+ if (other->start_limit_hit) {
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT);
+ return;
}
- switch (state) {
- case MOUNT_DEAD:
- automount_send_ready(a, a->expire_tokens, 0);
- break;
- case MOUNT_MOUNTING:
- case MOUNT_MOUNTING_DONE:
- case MOUNT_MOUNTING_SIGTERM:
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_REMOUNTING_SIGTERM:
- case MOUNT_REMOUNTING_SIGKILL:
- case MOUNT_UNMOUNTING_SIGTERM:
- case MOUNT_UNMOUNTING_SIGKILL:
- case MOUNT_FAILED:
- if (old_state != state)
- automount_send_ready(a, a->expire_tokens, -ENODEV);
- break;
- default:
- break;
+ /* Don't propagate anything if there's still a job queued */
+ if (other->job)
+ return;
+
+ /* The mount is successfully established */
+ if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) {
+ (void) automount_send_ready(a, a->tokens, 0);
+
+ r = automount_start_expire(a);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
+
+ automount_set_state(a, AUTOMOUNT_RUNNING);
}
- return 0;
+ if (IN_SET(MOUNT(other)->state,
+ MOUNT_MOUNTING, MOUNT_MOUNTING_DONE,
+ MOUNT_MOUNTED, MOUNT_REMOUNTING,
+ MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL,
+ MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_FAILED)) {
+
+ (void) automount_send_ready(a, a->expire_tokens, -ENODEV);
+ }
+
+ if (MOUNT(other)->state == MOUNT_DEAD)
+ (void) automount_send_ready(a, a->expire_tokens, 0);
+
+ /* The mount is in some unhappy state now, let's unfreeze any waiting clients */
+ if (IN_SET(MOUNT(other)->state,
+ MOUNT_DEAD, MOUNT_UNMOUNTING,
+ MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL,
+ MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_FAILED)) {
+
+ (void) automount_send_ready(a, a->tokens, -ENODEV);
+
+ automount_set_state(a, AUTOMOUNT_WAITING);
+ }
}
static void automount_enter_waiting(Automount *a) {
@@ -603,12 +620,16 @@ static void automount_enter_waiting(Automount *a) {
return;
fail:
+ log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");
+
safe_close_pair(p);
- if (mounted)
- repeat_unmount(a->where);
+ if (mounted) {
+ r = repeat_unmount(a->where, MNT_DETACH);
+ if (r < 0)
+ log_error_errno(r, "Failed to unmount, ignoring: %m");
+ }
- log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");
automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
}
@@ -633,8 +654,6 @@ static void *expire_thread(void *p) {
return NULL;
}
-static int automount_start_expire(Automount *a);
-
static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) {
Automount *a = AUTOMOUNT(userdata);
_cleanup_(expire_data_freep) struct expire_data *data = NULL;
@@ -672,7 +691,10 @@ static int automount_start_expire(Automount *a) {
assert(a);
- timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/10, USEC_PER_SEC);
+ if (a->timeout_idle_usec == 0)
+ return 0;
+
+ timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/3, USEC_PER_SEC);
if (a->expire_event_source) {
r = sd_event_source_set_time(a->expire_event_source, timeout);
@@ -695,8 +717,17 @@ static int automount_start_expire(Automount *a) {
return 0;
}
+static void automount_stop_expire(Automount *a) {
+ assert(a);
+
+ if (!a->expire_event_source)
+ return;
+
+ (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+}
+
static void automount_enter_runnning(Automount *a) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
struct stat st;
int r;
@@ -722,18 +753,21 @@ static void automount_enter_runnning(Automount *a) {
if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
log_unit_info(UNIT(a), "Automount point already active?");
else {
- r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)),
- JOB_REPLACE, true, &error, NULL);
+ Unit *trigger;
+
+ trigger = UNIT_TRIGGER(UNIT(a));
+ if (!trigger) {
+ log_unit_error(UNIT(a), "Unit to trigger vanished.");
+ goto fail;
+ }
+
+ r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL);
if (r < 0) {
log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r));
goto fail;
}
}
- r = automount_start_expire(a);
- if (r < 0)
- log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
-
automount_set_state(a, AUTOMOUNT_RUNNING);
return;
@@ -743,6 +777,8 @@ fail:
static int automount_start(Unit *u) {
Automount *a = AUTOMOUNT(u);
+ Unit *trigger;
+ int r;
assert(a);
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
@@ -752,8 +788,21 @@ static int automount_start(Unit *u) {
return -EEXIST;
}
- if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
+ trigger = UNIT_TRIGGER(u);
+ if (!trigger || trigger->load_state != UNIT_LOADED) {
+ log_unit_error(u, "Refusing to start, unit to trigger not loaded.");
return -ENOENT;
+ }
+
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
a->result = AUTOMOUNT_SUCCESS;
automount_enter_waiting(a);
@@ -772,8 +821,9 @@ static int automount_stop(Unit *u) {
static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
Automount *a = AUTOMOUNT(u);
- void *p;
Iterator i;
+ void *p;
+ int r;
assert(a);
assert(f);
@@ -788,15 +838,9 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
SET_FOREACH(p, a->expire_tokens, i)
unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p));
- if (a->pipe_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, a->pipe_fd);
- if (copy < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
- }
+ r = unit_serialize_item_fd(u, f, fds, "pipe-fd", a->pipe_fd);
+ if (r < 0)
+ return r;
return 0;
}
@@ -901,9 +945,11 @@ static bool automount_check_gc(Unit *u) {
}
static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
union autofs_v5_packet_union packet;
Automount *a = AUTOMOUNT(userdata);
+ struct stat st;
+ Unit *trigger;
int r;
assert(a);
@@ -950,7 +996,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
case autofs_ptype_expire_direct:
log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where);
- (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+ automount_stop_expire(a);
r = set_ensure_allocated(&a->expire_tokens, NULL);
if (r < 0) {
@@ -963,7 +1009,26 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m");
goto fail;
}
- r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL);
+
+ /* Before we do anything, let's see if somebody is playing games with us? */
+ if (lstat(a->where, &st) < 0) {
+ log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m");
+ goto fail;
+ }
+
+ if (!S_ISDIR(st.st_mode) || st.st_dev == a->dev_id) {
+ log_unit_info(UNIT(a), "Automount point already unmounted?");
+ automount_send_ready(a, a->expire_tokens, 0);
+ break;
+ }
+
+ trigger = UNIT_TRIGGER(UNIT(a));
+ if (!trigger) {
+ log_unit_error(UNIT(a), "Unit to trigger vanished.");
+ goto fail;
+ }
+
+ r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, JOB_REPLACE, &error, NULL);
if (r < 0) {
log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r));
goto fail;
@@ -1008,18 +1073,11 @@ static bool automount_supported(void) {
return supported;
}
-static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
- [AUTOMOUNT_DEAD] = "dead",
- [AUTOMOUNT_WAITING] = "waiting",
- [AUTOMOUNT_RUNNING] = "running",
- [AUTOMOUNT_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
-
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[AUTOMOUNT_SUCCESS] = "success",
- [AUTOMOUNT_FAILURE_RESOURCES] = "resources"
+ [AUTOMOUNT_FAILURE_RESOURCES] = "resources",
+ [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
+ [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);
@@ -1032,9 +1090,6 @@ const UnitVTable automount_vtable = {
"Automount\0"
"Install\0",
- .no_alias = true,
- .no_instances = true,
-
.init = automount_init,
.load = automount_load,
.done = automount_done,
@@ -1054,10 +1109,14 @@ const UnitVTable automount_vtable = {
.check_gc = automount_check_gc,
+ .trigger_notify = automount_trigger_notify,
+
.reset_failed = automount_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Automount",
.bus_vtable = bus_automount_vtable,
+ .bus_set_property = bus_automount_set_property,
+
+ .can_transient = true,
.shutdown = automount_shutdown,
.supported = automount_supported,
@@ -1066,7 +1125,6 @@ const UnitVTable automount_vtable = {
.finished_start_job = {
[JOB_DONE] = "Set up automount %s.",
[JOB_FAILED] = "Failed to set up automount %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Unset automount %s.",
diff --git a/src/core/automount.h b/src/core/automount.h
index 2a50fef68d..76a201178e 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -25,18 +23,11 @@ typedef struct Automount Automount;
#include "unit.h"
-typedef enum AutomountState {
- AUTOMOUNT_DEAD,
- AUTOMOUNT_WAITING,
- AUTOMOUNT_RUNNING,
- AUTOMOUNT_FAILED,
- _AUTOMOUNT_STATE_MAX,
- _AUTOMOUNT_STATE_INVALID = -1
-} AutomountState;
-
typedef enum AutomountResult {
AUTOMOUNT_SUCCESS,
AUTOMOUNT_FAILURE_RESOURCES,
+ AUTOMOUNT_FAILURE_START_LIMIT_HIT,
+ AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT,
_AUTOMOUNT_RESULT_MAX,
_AUTOMOUNT_RESULT_INVALID = -1
} AutomountResult;
@@ -64,10 +55,5 @@ struct Automount {
extern const UnitVTable automount_vtable;
-int automount_update_mount(Automount *a, MountState old_state, MountState state);
-
-const char* automount_state_to_string(AutomountState i) _const_;
-AutomountState automount_state_from_string(const char *s) _pure_;
-
const char* automount_result_to_string(AutomountResult i) _const_;
AutomountResult automount_result_from_string(const char *s) _pure_;
diff --git a/src/core/bus-endpoint.c b/src/core/bus-endpoint.c
deleted file mode 100644
index 0c4b3e7c8b..0000000000
--- a/src/core/bus-endpoint.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/***
- This file is part of systemd.
-
- Copyright 2014 Daniel Mack
-
- 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 <stdlib.h>
-
-#include "kdbus.h"
-#include "bus-kernel.h"
-#include "bus-policy.h"
-#include "bus-endpoint.h"
-
-int bus_kernel_set_endpoint_policy(int fd, uid_t uid, BusEndpoint *ep) {
-
- struct kdbus_cmd *update;
- struct kdbus_item *n;
- BusEndpointPolicy *po;
- Iterator i;
- size_t size;
- int r;
-
- size = ALIGN8(offsetof(struct kdbus_cmd, items));
-
- HASHMAP_FOREACH(po, ep->policy_hash, i) {
- size += ALIGN8(offsetof(struct kdbus_item, str) + strlen(po->name) + 1);
- size += ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access));
- }
-
- update = alloca0_align(size, 8);
- update->size = size;
-
- n = update->items;
-
- HASHMAP_FOREACH(po, ep->policy_hash, i) {
- n->type = KDBUS_ITEM_NAME;
- n->size = offsetof(struct kdbus_item, str) + strlen(po->name) + 1;
- strcpy(n->str, po->name);
- n = KDBUS_ITEM_NEXT(n);
-
- n->type = KDBUS_ITEM_POLICY_ACCESS;
- n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access);
-
- n->policy_access.type = KDBUS_POLICY_ACCESS_USER;
- n->policy_access.access = bus_kernel_translate_access(po->access);
- n->policy_access.id = uid;
-
- n = KDBUS_ITEM_NEXT(n);
- }
-
- r = ioctl(fd, KDBUS_CMD_ENDPOINT_UPDATE, update);
- if (r < 0)
- return -errno;
-
- return 0;
-}
-
-int bus_endpoint_new(BusEndpoint **ep) {
- assert(ep);
-
- *ep = new0(BusEndpoint, 1);
- if (!*ep)
- return -ENOMEM;
-
- return 0;
-}
-
-int bus_endpoint_add_policy(BusEndpoint *ep, const char *name, BusPolicyAccess access) {
- _cleanup_free_ BusEndpointPolicy *po = NULL;
- _cleanup_free_ char *key = NULL;
- int r;
-
- assert(ep);
- assert(name);
- assert(access > _BUS_POLICY_ACCESS_INVALID && access < _BUS_POLICY_ACCESS_MAX);
-
- /* check if we already have this name in the policy list. If we do, see if the new access level
- * is higher than the exising one, and upgrade the entry in that case. Otherwise, do nothing.
- */
-
- if (ep->policy_hash) {
- po = hashmap_get(ep->policy_hash, name);
- if (po) {
- if (po->access < access)
- po->access = access;
-
- return 0;
- }
- } else {
- ep->policy_hash = hashmap_new(&string_hash_ops);
- if (!ep->policy_hash)
- return -ENOMEM;
- }
-
- po = new0(BusEndpointPolicy, 1);
- if (!po)
- return -ENOMEM;
-
- key = strdup(name);
- if (!key)
- return -ENOMEM;
-
- po->name = key;
- po->access = access;
-
- r = hashmap_put(ep->policy_hash, key, po);
- if (r < 0)
- return r;
-
- po = NULL;
- key = NULL;
- return 0;
-}
-
-void bus_endpoint_free(BusEndpoint *endpoint) {
- if (!endpoint)
- return;
-
- hashmap_free_free_free(endpoint->policy_hash);
- free(endpoint);
-}
diff --git a/src/core/bus-endpoint.h b/src/core/bus-endpoint.h
deleted file mode 100644
index 4a31f4c4be..0000000000
--- a/src/core/bus-endpoint.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2014 Daniel Mack
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct BusEndpoint BusEndpoint;
-typedef struct BusEndpointPolicy BusEndpointPolicy;
-
-#include "hashmap.h"
-#include "bus-policy.h"
-
-struct BusEndpointPolicy {
- char *name;
- BusPolicyAccess access;
-};
-
-struct BusEndpoint {
- Hashmap *policy_hash;
-};
-
-int bus_endpoint_new(BusEndpoint **ep);
-void bus_endpoint_free(BusEndpoint *endpoint);
-
-int bus_endpoint_add_policy(BusEndpoint *ep, const char *name, BusPolicyAccess access);
-
-int bus_kernel_set_endpoint_policy(int fd, uid_t uid, BusEndpoint *ep);
diff --git a/src/core/bus-policy.c b/src/core/bus-policy.c
index a6a8fcd4d3..4907c268e8 100644
--- a/src/core/bus-policy.c
+++ b/src/core/bus-policy.c
@@ -19,10 +19,13 @@
#include <stdlib.h>
-#include "kdbus.h"
-#include "util.h"
+#include "alloc-util.h"
#include "bus-kernel.h"
#include "bus-policy.h"
+#include "kdbus.h"
+#include "string-table.h"
+#include "user-util.h"
+#include "util.h"
int bus_kernel_translate_access(BusPolicyAccess access) {
assert(access >= 0);
diff --git a/src/core/bus-policy.h b/src/core/bus-policy.h
index 3b04f5457a..5b2c4d5953 100644
--- a/src/core/bus-policy.h
+++ b/src/core/bus-policy.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -21,9 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "kdbus.h"
#include "list.h"
#include "macro.h"
-#include "kdbus.h"
typedef struct BusNamePolicy BusNamePolicy;
diff --git a/src/core/busname.c b/src/core/busname.c
index 2085721546..b96ec09e67 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,17 +19,23 @@
#include <sys/mman.h>
-#include "special.h"
-#include "formats-util.h"
-#include "signal-util.h"
-#include "bus-kernel.h"
+#include "alloc-util.h"
#include "bus-internal.h"
+#include "bus-kernel.h"
+#include "bus-policy.h"
#include "bus-util.h"
+#include "busname.h"
+#include "dbus-busname.h"
+#include "fd-util.h"
+#include "formats-util.h"
#include "kdbus.h"
-#include "bus-policy.h"
+#include "parse-util.h"
+#include "process-util.h"
#include "service.h"
-#include "dbus-busname.h"
-#include "busname.h"
+#include "signal-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = {
[BUSNAME_DEAD] = UNIT_INACTIVE,
@@ -95,8 +99,7 @@ static void busname_done(Unit *u) {
assert(n);
- free(n->name);
- n->name = NULL;
+ n->name = mfree(n->name);
busname_free_policy(n);
busname_unwatch_control_pid(n);
@@ -107,29 +110,27 @@ static void busname_done(Unit *u) {
n->timer_event_source = sd_event_source_unref(n->timer_event_source);
}
-static int busname_arm_timer(BusName *n) {
+static int busname_arm_timer(BusName *n, usec_t usec) {
int r;
assert(n);
- if (n->timeout_usec <= 0) {
- n->timer_event_source = sd_event_source_unref(n->timer_event_source);
- return 0;
- }
-
if (n->timer_event_source) {
- r = sd_event_source_set_time(n->timer_event_source, now(CLOCK_MONOTONIC) + n->timeout_usec);
+ r = sd_event_source_set_time(n->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(n->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(n)->manager->event,
&n->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + n->timeout_usec, 0,
+ usec, 0,
busname_dispatch_timer, n);
if (r < 0)
return r;
@@ -148,7 +149,7 @@ static int busname_add_default_default_dependencies(BusName *n) {
if (r < 0)
return r;
- if (UNIT(n)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(n)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -317,7 +318,7 @@ static int busname_open_fd(BusName *n) {
if (n->starter_fd >= 0)
return 0;
- mode = UNIT(n)->manager->running_as == MANAGER_SYSTEM ? "system" : "user";
+ mode = MANAGER_IS_SYSTEM(UNIT(n)->manager) ? "system" : "user";
n->starter_fd = bus_kernel_open_bus_fd(mode, &path);
if (n->starter_fd < 0)
return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus");
@@ -359,16 +360,15 @@ static int busname_coldplug(Unit *u) {
if (n->deserialized_state == n->state)
return 0;
- if (IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) {
-
- if (n->control_pid <= 0)
- return -EBADMSG;
+ if (n->control_pid > 0 &&
+ pid_is_unwaited(n->control_pid) &&
+ IN_SET(n->deserialized_state, BUSNAME_MAKING, BUSNAME_SIGTERM, BUSNAME_SIGKILL)) {
r = unit_watch_pid(UNIT(n), n->control_pid);
if (r < 0)
return r;
- r = busname_arm_timer(n);
+ r = busname_arm_timer(n, usec_add(u->state_change_timestamp.monotonic, n->timeout_usec));
if (r < 0)
return r;
}
@@ -393,7 +393,7 @@ static int busname_make_starter(BusName *n, pid_t *_pid) {
pid_t pid;
int r;
- r = busname_arm_timer(n);
+ r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec));
if (r < 0)
goto fail;
@@ -442,7 +442,7 @@ fail:
static void busname_enter_dead(BusName *n, BusNameResult f) {
assert(n);
- if (f != BUSNAME_SUCCESS)
+ if (n->result == BUSNAME_SUCCESS)
n->result = f;
busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD);
@@ -454,7 +454,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f
assert(n);
- if (f != BUSNAME_SUCCESS)
+ if (n->result == BUSNAME_SUCCESS)
n->result = f;
kill_context_init(&kill_context);
@@ -471,7 +471,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f
}
if (r > 0) {
- r = busname_arm_timer(n);
+ r = busname_arm_timer(n, usec_add(now(CLOCK_MONOTONIC), n->timeout_usec));
if (r < 0) {
log_unit_warning_errno(UNIT(n), r, "Failed to arm timer: %m");
goto fail;
@@ -553,7 +553,7 @@ fail:
}
static void busname_enter_running(BusName *n) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool pending = false;
Unit *other;
Iterator i;
@@ -586,7 +586,13 @@ static void busname_enter_running(BusName *n) {
}
if (!pending) {
- r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, true, &error, NULL);
+ if (!UNIT_ISSET(n->service)) {
+ log_unit_error(UNIT(n), "Service to activate vanished, refusing activation.");
+ r = -ENOENT;
+ goto fail;
+ }
+
+ r = manager_add_job(UNIT(n)->manager, JOB_START, UNIT_DEREF(n->service), JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
}
@@ -601,6 +607,7 @@ fail:
static int busname_start(Unit *u) {
BusName *n = BUSNAME(u);
+ int r;
assert(n);
@@ -626,6 +633,16 @@ static int busname_start(Unit *u) {
assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED));
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ busname_enter_dead(n, BUSNAME_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
n->result = BUSNAME_SUCCESS;
busname_enter_making(n);
@@ -657,6 +674,7 @@ static int busname_stop(Unit *u) {
static int busname_serialize(Unit *u, FILE *f, FDSet *fds) {
BusName *n = BUSNAME(u);
+ int r;
assert(n);
assert(f);
@@ -668,15 +686,9 @@ static int busname_serialize(Unit *u, FILE *f, FDSet *fds) {
if (n->control_pid > 0)
unit_serialize_item_format(u, f, "control-pid", PID_FMT, n->control_pid);
- if (n->starter_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, n->starter_fd);
- if (copy < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "starter-fd", "%i", copy);
- }
+ r = unit_serialize_item_fd(u, f, fds, "starter-fd", n->starter_fd);
+ if (r < 0)
+ return r;
return 0;
}
@@ -860,7 +872,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) {
n->control_pid = 0;
- if (is_clean_exit(code, status, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = BUSNAME_SUCCESS;
else if (code == CLD_EXITED)
f = BUSNAME_FAILURE_EXIT_CODE;
@@ -874,7 +886,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) {
log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
"Control process exited, code=%s status=%i", sigchld_code_to_string(code), status);
- if (f != BUSNAME_SUCCESS)
+ if (n->result == BUSNAME_SUCCESS)
n->result = f;
switch (n->state) {
@@ -942,7 +954,6 @@ static void busname_reset_failed(Unit *u) {
static void busname_trigger_notify(Unit *u, Unit *other) {
BusName *n = BUSNAME(u);
- Service *s;
assert(n);
assert(other);
@@ -950,60 +961,57 @@ static void busname_trigger_notify(Unit *u, Unit *other) {
if (!IN_SET(n->state, BUSNAME_RUNNING, BUSNAME_LISTENING))
return;
- if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
+ if (other->start_limit_hit) {
+ busname_enter_dead(n, BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT);
return;
+ }
- s = SERVICE(other);
+ if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
+ return;
- if (s->state == SERVICE_FAILED && s->result == SERVICE_FAILURE_START_LIMIT)
- busname_enter_dead(n, BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT);
- else if (IN_SET(s->state,
- SERVICE_DEAD, SERVICE_FAILED,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL,
- SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
- SERVICE_AUTO_RESTART))
+ if (IN_SET(SERVICE(other)->state,
+ SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+ SERVICE_AUTO_RESTART))
busname_enter_listening(n);
+
+ if (SERVICE(other)->state == SERVICE_RUNNING)
+ busname_set_state(n, BUSNAME_RUNNING);
}
static int busname_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, BUSNAME(u)->control_pid, error);
}
-static int busname_get_timeout(Unit *u, uint64_t *timeout) {
+static int busname_get_timeout(Unit *u, usec_t *timeout) {
BusName *n = BUSNAME(u);
+ usec_t t;
int r;
if (!n->timer_event_source)
return 0;
- r = sd_event_source_get_time(n->timer_event_source, timeout);
+ r = sd_event_source_get_time(n->timer_event_source, &t);
if (r < 0)
return r;
+ if (t == USEC_INFINITY)
+ return 0;
+ *timeout = t;
return 1;
}
static bool busname_supported(void) {
- static int supported = -1;
-
- if (supported < 0)
- supported = is_kdbus_available();
-
- return supported;
+ return false;
}
-static const char* const busname_state_table[_BUSNAME_STATE_MAX] = {
- [BUSNAME_DEAD] = "dead",
- [BUSNAME_MAKING] = "making",
- [BUSNAME_REGISTERED] = "registered",
- [BUSNAME_LISTENING] = "listening",
- [BUSNAME_RUNNING] = "running",
- [BUSNAME_SIGTERM] = "sigterm",
- [BUSNAME_SIGKILL] = "sigkill",
- [BUSNAME_FAILED] = "failed",
-};
+static int busname_control_pid(Unit *u) {
+ BusName *n = BUSNAME(u);
+
+ assert(n);
-DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState);
+ return n->control_pid;
+}
static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
[BUSNAME_SUCCESS] = "success",
@@ -1012,7 +1020,8 @@ static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
[BUSNAME_FAILURE_EXIT_CODE] = "exit-code",
[BUSNAME_FAILURE_SIGNAL] = "signal",
[BUSNAME_FAILURE_CORE_DUMP] = "core-dump",
- [BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent",
+ [BUSNAME_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
+ [BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(busname_result, BusNameResult);
@@ -1026,9 +1035,6 @@ const UnitVTable busname_vtable = {
"Install\0",
.private_section = "BusName",
- .no_alias = true,
- .no_instances = true,
-
.init = busname_init,
.done = busname_done,
.load = busname_load,
@@ -1058,20 +1064,18 @@ const UnitVTable busname_vtable = {
.supported = busname_supported,
- .bus_interface = "org.freedesktop.systemd1.BusName",
+ .control_pid = busname_control_pid,
+
.bus_vtable = bus_busname_vtable,
.status_message_formats = {
.finished_start_job = {
[JOB_DONE] = "Listening on %s.",
[JOB_FAILED] = "Failed to listen on %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
- [JOB_TIMEOUT] = "Timed out starting %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Closed %s.",
[JOB_FAILED] = "Failed stopping %s.",
- [JOB_TIMEOUT] = "Timed out stopping %s.",
},
},
};
diff --git a/src/core/busname.h b/src/core/busname.h
index 69528a2aef..a8562db458 100644
--- a/src/core/busname.h
+++ b/src/core/busname.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -24,19 +22,8 @@
typedef struct BusName BusName;
typedef struct BusNamePolicy BusNamePolicy;
-
-typedef enum BusNameState {
- BUSNAME_DEAD,
- BUSNAME_MAKING,
- BUSNAME_REGISTERED,
- BUSNAME_LISTENING,
- BUSNAME_RUNNING,
- BUSNAME_SIGTERM,
- BUSNAME_SIGKILL,
- BUSNAME_FAILED,
- _BUSNAME_STATE_MAX,
- _BUSNAME_STATE_INVALID = -1
-} BusNameState;
+#include "unit.h"
+#include "bus-policy.h"
typedef enum BusNameResult {
BUSNAME_SUCCESS,
@@ -45,7 +32,8 @@ typedef enum BusNameResult {
BUSNAME_FAILURE_EXIT_CODE,
BUSNAME_FAILURE_SIGNAL,
BUSNAME_FAILURE_CORE_DUMP,
- BUSNAME_FAILURE_SERVICE_FAILED_PERMANENT,
+ BUSNAME_FAILURE_START_LIMIT_HIT,
+ BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT,
_BUSNAME_RESULT_MAX,
_BUSNAME_RESULT_INVALID = -1
} BusNameResult;
@@ -77,8 +65,5 @@ struct BusName {
extern const UnitVTable busname_vtable;
-const char* busname_state_to_string(BusNameState i) _const_;
-BusNameState busname_state_from_string(const char *s) _pure_;
-
const char* busname_result_to_string(BusNameResult i) _const_;
BusNameResult busname_result_from_string(const char *s) _pure_;
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 6474e08bd2..20bdbc39d0 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -22,27 +20,63 @@
#include <fcntl.h>
#include <fnmatch.h>
-#include "process-util.h"
-#include "path-util.h"
-#include "special.h"
+#include "alloc-util.h"
#include "cgroup-util.h"
#include "cgroup.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "stdio-util.h"
#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC)
+static void cgroup_compat_warn(void) {
+ static bool cgroup_compat_warned = false;
+
+ if (cgroup_compat_warned)
+ return;
+
+ log_warning("cgroup compatibility translation between legacy and unified hierarchy settings activated. See cgroup-compat debug messages for details.");
+ cgroup_compat_warned = true;
+}
+
+#define log_cgroup_compat(unit, fmt, ...) do { \
+ cgroup_compat_warn(); \
+ log_unit_debug(unit, "cgroup-compat: " fmt, ##__VA_ARGS__); \
+ } while (false)
+
void cgroup_context_init(CGroupContext *c) {
assert(c);
/* Initialize everything to the kernel defaults, assuming the
* structure is preinitialized to 0 */
- c->cpu_shares = (unsigned long) -1;
- c->startup_cpu_shares = (unsigned long) -1;
- c->memory_limit = (uint64_t) -1;
- c->blockio_weight = (unsigned long) -1;
- c->startup_blockio_weight = (unsigned long) -1;
-
+ c->cpu_weight = CGROUP_WEIGHT_INVALID;
+ c->startup_cpu_weight = CGROUP_WEIGHT_INVALID;
c->cpu_quota_per_sec_usec = USEC_INFINITY;
+
+ c->cpu_shares = CGROUP_CPU_SHARES_INVALID;
+ c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID;
+
+ c->memory_high = CGROUP_LIMIT_MAX;
+ c->memory_max = CGROUP_LIMIT_MAX;
+ c->memory_swap_max = CGROUP_LIMIT_MAX;
+
+ c->memory_limit = CGROUP_LIMIT_MAX;
+
+ c->io_weight = CGROUP_WEIGHT_INVALID;
+ c->startup_io_weight = CGROUP_WEIGHT_INVALID;
+
+ c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
+ c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID;
+
+ c->tasks_max = (uint64_t) -1;
}
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
@@ -54,6 +88,24 @@ void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
free(a);
}
+void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w) {
+ assert(c);
+ assert(w);
+
+ LIST_REMOVE(device_weights, c->io_device_weights, w);
+ free(w->path);
+ free(w);
+}
+
+void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l) {
+ assert(c);
+ assert(l);
+
+ LIST_REMOVE(device_limits, c->io_device_limits, l);
+ free(l->path);
+ free(l);
+}
+
void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
assert(c);
assert(w);
@@ -75,6 +127,12 @@ void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockI
void cgroup_context_done(CGroupContext *c) {
assert(c);
+ while (c->io_device_weights)
+ cgroup_context_free_io_device_weight(c, c->io_device_weights);
+
+ while (c->io_device_limits)
+ cgroup_context_free_io_device_limit(c, c->io_device_limits);
+
while (c->blockio_device_weights)
cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
@@ -86,6 +144,8 @@ void cgroup_context_done(CGroupContext *c) {
}
void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
+ CGroupIODeviceLimit *il;
+ CGroupIODeviceWeight *iw;
CGroupBlockIODeviceBandwidth *b;
CGroupBlockIODeviceWeight *w;
CGroupDeviceAllow *a;
@@ -98,25 +158,47 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
fprintf(f,
"%sCPUAccounting=%s\n"
+ "%sIOAccounting=%s\n"
"%sBlockIOAccounting=%s\n"
"%sMemoryAccounting=%s\n"
- "%sCPUShares=%lu\n"
- "%sStartupCPUShares=%lu\n"
+ "%sTasksAccounting=%s\n"
+ "%sCPUWeight=%" PRIu64 "\n"
+ "%sStartupCPUWeight=%" PRIu64 "\n"
+ "%sCPUShares=%" PRIu64 "\n"
+ "%sStartupCPUShares=%" PRIu64 "\n"
"%sCPUQuotaPerSecSec=%s\n"
- "%sBlockIOWeight=%lu\n"
- "%sStartupBlockIOWeight=%lu\n"
+ "%sIOWeight=%" PRIu64 "\n"
+ "%sStartupIOWeight=%" PRIu64 "\n"
+ "%sBlockIOWeight=%" PRIu64 "\n"
+ "%sStartupBlockIOWeight=%" PRIu64 "\n"
+ "%sMemoryLow=%" PRIu64 "\n"
+ "%sMemoryHigh=%" PRIu64 "\n"
+ "%sMemoryMax=%" PRIu64 "\n"
+ "%sMemorySwapMax=%" PRIu64 "\n"
"%sMemoryLimit=%" PRIu64 "\n"
+ "%sTasksMax=%" PRIu64 "\n"
"%sDevicePolicy=%s\n"
"%sDelegate=%s\n",
prefix, yes_no(c->cpu_accounting),
+ prefix, yes_no(c->io_accounting),
prefix, yes_no(c->blockio_accounting),
prefix, yes_no(c->memory_accounting),
+ prefix, yes_no(c->tasks_accounting),
+ prefix, c->cpu_weight,
+ prefix, c->startup_cpu_weight,
prefix, c->cpu_shares,
prefix, c->startup_cpu_shares,
prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1),
+ prefix, c->io_weight,
+ prefix, c->startup_io_weight,
prefix, c->blockio_weight,
prefix, c->startup_blockio_weight,
+ prefix, c->memory_low,
+ prefix, c->memory_high,
+ prefix, c->memory_max,
+ prefix, c->memory_swap_max,
prefix, c->memory_limit,
+ prefix, c->tasks_max,
prefix, cgroup_device_policy_to_string(c->device_policy),
prefix, yes_no(c->delegate));
@@ -127,9 +209,30 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
a->path,
a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
+ LIST_FOREACH(device_weights, iw, c->io_device_weights)
+ fprintf(f,
+ "%sIODeviceWeight=%s %" PRIu64,
+ prefix,
+ iw->path,
+ iw->weight);
+
+ LIST_FOREACH(device_limits, il, c->io_device_limits) {
+ char buf[FORMAT_BYTES_MAX];
+ CGroupIOLimitType type;
+
+ for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
+ if (il->limits[type] != cgroup_io_limit_defaults[type])
+ fprintf(f,
+ "%s%s=%s %s\n",
+ prefix,
+ cgroup_io_limit_type_to_string(type),
+ il->path,
+ format_bytes(buf, sizeof(buf), il->limits[type]));
+ }
+
LIST_FOREACH(device_weights, w, c->blockio_device_weights)
fprintf(f,
- "%sBlockIODeviceWeight=%s %lu",
+ "%sBlockIODeviceWeight=%s %" PRIu64,
prefix,
w->path,
w->weight);
@@ -137,16 +240,22 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
char buf[FORMAT_BYTES_MAX];
- fprintf(f,
- "%s%s=%s %s\n",
- prefix,
- b->read ? "BlockIOReadBandwidth" : "BlockIOWriteBandwidth",
- b->path,
- format_bytes(buf, sizeof(buf), b->bandwidth));
+ if (b->rbps != CGROUP_LIMIT_MAX)
+ fprintf(f,
+ "%sBlockIOReadBandwidth=%s %s\n",
+ prefix,
+ b->path,
+ format_bytes(buf, sizeof(buf), b->rbps));
+ if (b->wbps != CGROUP_LIMIT_MAX)
+ fprintf(f,
+ "%sBlockIOWriteBandwidth=%s %s\n",
+ prefix,
+ b->path,
+ format_bytes(buf, sizeof(buf), b->wbps));
}
}
-static int lookup_blkio_device(const char *p, dev_t *dev) {
+static int lookup_block_device(const char *p, dev_t *dev) {
struct stat st;
int r;
@@ -201,7 +310,7 @@ static int whitelist_device(const char *path, const char *node, const char *acc)
r = cg_set_attribute("devices", path, "devices.allow", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set devices.allow on %s: %m", path);
return r;
@@ -272,7 +381,7 @@ static int whitelist_major(const char *path, const char *name, char type, const
r = cg_set_attribute("devices", path, "devices.allow", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r,
+ log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set devices.allow on %s: %m", path);
}
@@ -283,10 +392,261 @@ fail:
return -errno;
}
-void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path, ManagerState state) {
+static bool cgroup_context_has_cpu_weight(CGroupContext *c) {
+ return c->cpu_weight != CGROUP_WEIGHT_INVALID ||
+ c->startup_cpu_weight != CGROUP_WEIGHT_INVALID;
+}
+
+static bool cgroup_context_has_cpu_shares(CGroupContext *c) {
+ return c->cpu_shares != CGROUP_CPU_SHARES_INVALID ||
+ c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID;
+}
+
+static uint64_t cgroup_context_cpu_weight(CGroupContext *c, ManagerState state) {
+ if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) &&
+ c->startup_cpu_weight != CGROUP_WEIGHT_INVALID)
+ return c->startup_cpu_weight;
+ else if (c->cpu_weight != CGROUP_WEIGHT_INVALID)
+ return c->cpu_weight;
+ else
+ return CGROUP_WEIGHT_DEFAULT;
+}
+
+static uint64_t cgroup_context_cpu_shares(CGroupContext *c, ManagerState state) {
+ if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) &&
+ c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID)
+ return c->startup_cpu_shares;
+ else if (c->cpu_shares != CGROUP_CPU_SHARES_INVALID)
+ return c->cpu_shares;
+ else
+ return CGROUP_CPU_SHARES_DEFAULT;
+}
+
+static void cgroup_apply_unified_cpu_config(Unit *u, uint64_t weight, uint64_t quota) {
+ char buf[MAX(DECIMAL_STR_MAX(uint64_t) + 1, (DECIMAL_STR_MAX(usec_t) + 1) * 2)];
+ int r;
+
+ xsprintf(buf, "%" PRIu64 "\n", weight);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.weight", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.weight: %m");
+
+ if (quota != USEC_INFINITY)
+ xsprintf(buf, USEC_FMT " " USEC_FMT "\n",
+ quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC, CGROUP_CPU_QUOTA_PERIOD_USEC);
+ else
+ xsprintf(buf, "max " USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
+
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.max", buf);
+
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.max: %m");
+}
+
+static void cgroup_apply_legacy_cpu_config(Unit *u, uint64_t shares, uint64_t quota) {
+ char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1];
+ int r;
+
+ xsprintf(buf, "%" PRIu64 "\n", shares);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.shares", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.shares: %m");
+
+ xsprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_period_us", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.cfs_period_us: %m");
+
+ if (quota != USEC_INFINITY) {
+ xsprintf(buf, USEC_FMT "\n", quota * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC);
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", buf);
+ } else
+ r = cg_set_attribute("cpu", u->cgroup_path, "cpu.cfs_quota_us", "-1");
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set cpu.cfs_quota_us: %m");
+}
+
+static uint64_t cgroup_cpu_shares_to_weight(uint64_t shares) {
+ return CLAMP(shares * CGROUP_WEIGHT_DEFAULT / CGROUP_CPU_SHARES_DEFAULT,
+ CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX);
+}
+
+static uint64_t cgroup_cpu_weight_to_shares(uint64_t weight) {
+ return CLAMP(weight * CGROUP_CPU_SHARES_DEFAULT / CGROUP_WEIGHT_DEFAULT,
+ CGROUP_CPU_SHARES_MIN, CGROUP_CPU_SHARES_MAX);
+}
+
+static bool cgroup_context_has_io_config(CGroupContext *c) {
+ return c->io_accounting ||
+ c->io_weight != CGROUP_WEIGHT_INVALID ||
+ c->startup_io_weight != CGROUP_WEIGHT_INVALID ||
+ c->io_device_weights ||
+ c->io_device_limits;
+}
+
+static bool cgroup_context_has_blockio_config(CGroupContext *c) {
+ return c->blockio_accounting ||
+ c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ||
+ c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ||
+ c->blockio_device_weights ||
+ c->blockio_device_bandwidths;
+}
+
+static uint64_t cgroup_context_io_weight(CGroupContext *c, ManagerState state) {
+ if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) &&
+ c->startup_io_weight != CGROUP_WEIGHT_INVALID)
+ return c->startup_io_weight;
+ else if (c->io_weight != CGROUP_WEIGHT_INVALID)
+ return c->io_weight;
+ else
+ return CGROUP_WEIGHT_DEFAULT;
+}
+
+static uint64_t cgroup_context_blkio_weight(CGroupContext *c, ManagerState state) {
+ if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) &&
+ c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID)
+ return c->startup_blockio_weight;
+ else if (c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID)
+ return c->blockio_weight;
+ else
+ return CGROUP_BLKIO_WEIGHT_DEFAULT;
+}
+
+static uint64_t cgroup_weight_blkio_to_io(uint64_t blkio_weight) {
+ return CLAMP(blkio_weight * CGROUP_WEIGHT_DEFAULT / CGROUP_BLKIO_WEIGHT_DEFAULT,
+ CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX);
+}
+
+static uint64_t cgroup_weight_io_to_blkio(uint64_t io_weight) {
+ return CLAMP(io_weight * CGROUP_BLKIO_WEIGHT_DEFAULT / CGROUP_WEIGHT_DEFAULT,
+ CGROUP_BLKIO_WEIGHT_MIN, CGROUP_BLKIO_WEIGHT_MAX);
+}
+
+static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_t io_weight) {
+ char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1];
+ dev_t dev;
+ int r;
+
+ r = lookup_block_device(dev_path, &dev);
+ if (r < 0)
+ return;
+
+ xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight);
+ r = cg_set_attribute("io", u->cgroup_path, "io.weight", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set io.weight: %m");
+}
+
+static void cgroup_apply_blkio_device_weight(Unit *u, const char *dev_path, uint64_t blkio_weight) {
+ char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1];
+ dev_t dev;
+ int r;
+
+ r = lookup_block_device(dev_path, &dev);
+ if (r < 0)
+ return;
+
+ xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight);
+ r = cg_set_attribute("blkio", u->cgroup_path, "blkio.weight_device", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set blkio.weight_device: %m");
+}
+
+static unsigned cgroup_apply_io_device_limit(Unit *u, const char *dev_path, uint64_t *limits) {
+ char limit_bufs[_CGROUP_IO_LIMIT_TYPE_MAX][DECIMAL_STR_MAX(uint64_t)];
+ char buf[DECIMAL_STR_MAX(dev_t)*2+2+(6+DECIMAL_STR_MAX(uint64_t)+1)*4];
+ CGroupIOLimitType type;
+ dev_t dev;
+ unsigned n = 0;
+ int r;
+
+ r = lookup_block_device(dev_path, &dev);
+ if (r < 0)
+ return 0;
+
+ for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) {
+ if (limits[type] != cgroup_io_limit_defaults[type]) {
+ xsprintf(limit_bufs[type], "%" PRIu64, limits[type]);
+ n++;
+ } else {
+ xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0");
+ }
+ }
+
+ xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev),
+ limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX],
+ limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]);
+ r = cg_set_attribute("io", u->cgroup_path, "io.max", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set io.max: %m");
+ return n;
+}
+
+static unsigned cgroup_apply_blkio_device_limit(Unit *u, const char *dev_path, uint64_t rbps, uint64_t wbps) {
+ char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1];
+ dev_t dev;
+ unsigned n = 0;
+ int r;
+
+ r = lookup_block_device(dev_path, &dev);
+ if (r < 0)
+ return 0;
+
+ if (rbps != CGROUP_LIMIT_MAX)
+ n++;
+ sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps);
+ r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.read_bps_device", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set blkio.throttle.read_bps_device: %m");
+
+ if (wbps != CGROUP_LIMIT_MAX)
+ n++;
+ sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps);
+ r = cg_set_attribute("blkio", u->cgroup_path, "blkio.throttle.write_bps_device", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set blkio.throttle.write_bps_device: %m");
+
+ return n;
+}
+
+static bool cgroup_context_has_unified_memory_config(CGroupContext *c) {
+ return c->memory_low > 0 || c->memory_high != CGROUP_LIMIT_MAX || c->memory_max != CGROUP_LIMIT_MAX || c->memory_swap_max != CGROUP_LIMIT_MAX;
+}
+
+static void cgroup_apply_unified_memory_limit(Unit *u, const char *file, uint64_t v) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 1] = "max";
+ int r;
+
+ if (v != CGROUP_LIMIT_MAX)
+ xsprintf(buf, "%" PRIu64 "\n", v);
+
+ r = cg_set_attribute("memory", u->cgroup_path, file, buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set %s: %m", file);
+}
+
+static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) {
+ const char *path;
+ CGroupContext *c;
bool is_root;
int r;
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ path = u->cgroup_path;
+
assert(c);
assert(path);
@@ -304,98 +664,233 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
* cgroup trees (assuming we are running in a container then),
* and missing cgroups, i.e. EROFS and ENOENT. */
- if ((mask & CGROUP_CPU) && !is_root) {
- char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1];
+ if ((mask & CGROUP_MASK_CPU) && !is_root) {
+ bool has_weight = cgroup_context_has_cpu_weight(c);
+ bool has_shares = cgroup_context_has_cpu_shares(c);
- sprintf(buf, "%lu\n",
- IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares :
- c->cpu_shares != (unsigned long) -1 ? c->cpu_shares : 1024);
- r = cg_set_attribute("cpu", path, "cpu.shares", buf);
- if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.shares on %s: %m", path);
+ if (cg_all_unified() > 0) {
+ uint64_t weight;
- sprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
- r = cg_set_attribute("cpu", path, "cpu.cfs_period_us", buf);
- if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.cfs_period_us on %s: %m", path);
+ if (has_weight)
+ weight = cgroup_context_cpu_weight(c, state);
+ else if (has_shares) {
+ uint64_t shares = cgroup_context_cpu_shares(c, state);
- if (c->cpu_quota_per_sec_usec != USEC_INFINITY) {
- sprintf(buf, USEC_FMT "\n", c->cpu_quota_per_sec_usec * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC);
- r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", buf);
- } else
- r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1");
- if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set cpu.cfs_quota_us on %s: %m", path);
+ weight = cgroup_cpu_shares_to_weight(shares);
+
+ log_cgroup_compat(u, "Applying [Startup]CpuShares %" PRIu64 " as [Startup]CpuWeight %" PRIu64 " on %s",
+ shares, weight, path);
+ } else
+ weight = CGROUP_WEIGHT_DEFAULT;
+
+ cgroup_apply_unified_cpu_config(u, weight, c->cpu_quota_per_sec_usec);
+ } else {
+ uint64_t shares;
+
+ if (has_shares)
+ shares = cgroup_context_cpu_shares(c, state);
+ else if (has_weight) {
+ uint64_t weight = cgroup_context_cpu_weight(c, state);
+
+ shares = cgroup_cpu_weight_to_shares(weight);
+
+ log_cgroup_compat(u, "Applying [Startup]CpuWeight %" PRIu64 " as [Startup]CpuShares %" PRIu64 " on %s",
+ weight, shares, path);
+ } else
+ shares = CGROUP_CPU_SHARES_DEFAULT;
+
+ cgroup_apply_legacy_cpu_config(u, shares, c->cpu_quota_per_sec_usec);
+ }
}
- if (mask & CGROUP_BLKIO) {
- char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1,
- DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1,
- DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)];
- CGroupBlockIODeviceWeight *w;
- CGroupBlockIODeviceBandwidth *b;
+ if (mask & CGROUP_MASK_IO) {
+ bool has_io = cgroup_context_has_io_config(c);
+ bool has_blockio = cgroup_context_has_blockio_config(c);
if (!is_root) {
- sprintf(buf, "%lu\n", IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight :
- c->blockio_weight != (unsigned long) -1 ? c->blockio_weight : 1000);
- r = cg_set_attribute("blkio", path, "blkio.weight", buf);
+ char buf[8+DECIMAL_STR_MAX(uint64_t)+1];
+ uint64_t weight;
+
+ if (has_io)
+ weight = cgroup_context_io_weight(c, state);
+ else if (has_blockio) {
+ uint64_t blkio_weight = cgroup_context_blkio_weight(c, state);
+
+ weight = cgroup_weight_blkio_to_io(blkio_weight);
+
+ log_cgroup_compat(u, "Applying [Startup]BlockIOWeight %" PRIu64 " as [Startup]IOWeight %" PRIu64,
+ blkio_weight, weight);
+ } else
+ weight = CGROUP_WEIGHT_DEFAULT;
+
+ xsprintf(buf, "default %" PRIu64 "\n", weight);
+ r = cg_set_attribute("io", path, "io.weight", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set blkio.weight on %s: %m", path);
-
- /* FIXME: no way to reset this list */
- LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
- dev_t dev;
-
- r = lookup_blkio_device(w->path, &dev);
- if (r < 0)
- continue;
-
- sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight);
- r = cg_set_attribute("blkio", path, "blkio.weight_device", buf);
- if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set blkio.weight_device on %s: %m", path);
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set io.weight: %m");
+
+ if (has_io) {
+ CGroupIODeviceWeight *w;
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_weights, w, c->io_device_weights)
+ cgroup_apply_io_device_weight(u, w->path, w->weight);
+ } else if (has_blockio) {
+ CGroupBlockIODeviceWeight *w;
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
+ weight = cgroup_weight_blkio_to_io(w->weight);
+
+ log_cgroup_compat(u, "Applying BlockIODeviceWeight %" PRIu64 " as IODeviceWeight %" PRIu64 " for %s",
+ w->weight, weight, w->path);
+
+ cgroup_apply_io_device_weight(u, w->path, weight);
+ }
}
}
- /* FIXME: no way to reset this list */
- LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
- const char *a;
- dev_t dev;
+ /* Apply limits and free ones without config. */
+ if (has_io) {
+ CGroupIODeviceLimit *l, *next;
- r = lookup_blkio_device(b->path, &dev);
- if (r < 0)
- continue;
+ LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) {
+ if (!cgroup_apply_io_device_limit(u, l->path, l->limits))
+ cgroup_context_free_io_device_limit(c, l);
+ }
+ } else if (has_blockio) {
+ CGroupBlockIODeviceBandwidth *b, *next;
+
+ LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) {
+ uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX];
+ CGroupIOLimitType type;
+
+ for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
+ limits[type] = cgroup_io_limit_defaults[type];
- a = b->read ? "blkio.throttle.read_bps_device" : "blkio.throttle.write_bps_device";
+ limits[CGROUP_IO_RBPS_MAX] = b->rbps;
+ limits[CGROUP_IO_WBPS_MAX] = b->wbps;
- sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth);
- r = cg_set_attribute("blkio", path, a, buf);
+ log_cgroup_compat(u, "Applying BlockIO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as IO{Read|Write}BandwidthMax for %s",
+ b->rbps, b->wbps, b->path);
+
+ if (!cgroup_apply_io_device_limit(u, b->path, limits))
+ cgroup_context_free_blockio_device_bandwidth(c, b);
+ }
+ }
+ }
+
+ if (mask & CGROUP_MASK_BLKIO) {
+ bool has_io = cgroup_context_has_io_config(c);
+ bool has_blockio = cgroup_context_has_blockio_config(c);
+
+ if (!is_root) {
+ char buf[DECIMAL_STR_MAX(uint64_t)+1];
+ uint64_t weight;
+
+ if (has_blockio)
+ weight = cgroup_context_blkio_weight(c, state);
+ else if (has_io) {
+ uint64_t io_weight = cgroup_context_io_weight(c, state);
+
+ weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state));
+
+ log_cgroup_compat(u, "Applying [Startup]IOWeight %" PRIu64 " as [Startup]BlockIOWeight %" PRIu64,
+ io_weight, weight);
+ } else
+ weight = CGROUP_BLKIO_WEIGHT_DEFAULT;
+
+ xsprintf(buf, "%" PRIu64 "\n", weight);
+ r = cg_set_attribute("blkio", path, "blkio.weight", buf);
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set %s on %s: %m", a, path);
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set blkio.weight: %m");
+
+ if (has_blockio) {
+ CGroupBlockIODeviceWeight *w;
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights)
+ cgroup_apply_blkio_device_weight(u, w->path, w->weight);
+ } else if (has_io) {
+ CGroupIODeviceWeight *w;
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_weights, w, c->io_device_weights) {
+ weight = cgroup_weight_io_to_blkio(w->weight);
+
+ log_cgroup_compat(u, "Applying IODeviceWeight %" PRIu64 " as BlockIODeviceWeight %" PRIu64 " for %s",
+ w->weight, weight, w->path);
+
+ cgroup_apply_blkio_device_weight(u, w->path, weight);
+ }
+ }
+ }
+
+ /* Apply limits and free ones without config. */
+ if (has_blockio) {
+ CGroupBlockIODeviceBandwidth *b, *next;
+
+ LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) {
+ if (!cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps))
+ cgroup_context_free_blockio_device_bandwidth(c, b);
+ }
+ } else if (has_io) {
+ CGroupIODeviceLimit *l, *next;
+
+ LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) {
+ log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth %" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax for %s",
+ l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path);
+
+ if (!cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]))
+ cgroup_context_free_io_device_limit(c, l);
+ }
}
}
- if ((mask & CGROUP_MEMORY) && !is_root) {
- if (c->memory_limit != (uint64_t) -1) {
+ if ((mask & CGROUP_MASK_MEMORY) && !is_root) {
+ if (cg_all_unified() > 0) {
+ uint64_t max = c->memory_max;
+ uint64_t swap_max = c->memory_swap_max;
+
+ if (cgroup_context_has_unified_memory_config(c)) {
+ max = c->memory_max;
+ swap_max = c->memory_swap_max;
+ } else {
+ max = c->memory_limit;
+
+ if (max != CGROUP_LIMIT_MAX)
+ log_cgroup_compat(u, "Applying MemoryLimit %" PRIu64 " as MemoryMax", max);
+ }
+
+ cgroup_apply_unified_memory_limit(u, "memory.low", c->memory_low);
+ cgroup_apply_unified_memory_limit(u, "memory.high", c->memory_high);
+ cgroup_apply_unified_memory_limit(u, "memory.max", max);
+ cgroup_apply_unified_memory_limit(u, "memory.swap.max", swap_max);
+ } else {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
+ uint64_t val = c->memory_limit;
- sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
- r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
- } else
- r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1");
+ if (val == CGROUP_LIMIT_MAX) {
+ val = c->memory_max;
- if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to set memory.limit_in_bytes on %s: %m", path);
+ if (val != CGROUP_LIMIT_MAX)
+ log_cgroup_compat(u, "Applying MemoryMax %" PRIi64 " as MemoryLimit", c->memory_max);
+ }
+
+ if (val == CGROUP_LIMIT_MAX)
+ strncpy(buf, "-1\n", sizeof(buf));
+ else
+ xsprintf(buf, "%" PRIu64 "\n", val);
+
+ r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set memory.limit_in_bytes: %m");
+ }
}
- if ((mask & CGROUP_DEVICE) && !is_root) {
+ if ((mask & CGROUP_MASK_DEVICES) && !is_root) {
CGroupDeviceAllow *a;
/* Changing the devices list of a populated cgroup
@@ -407,8 +902,8 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
else
r = cg_set_attribute("devices", path, "devices.allow", "a");
if (r < 0)
- log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EINVAL) ? LOG_DEBUG : LOG_WARNING, r,
- "Failed to reset devices.list on %s: %m", path);
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EINVAL, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to reset devices.list: %m");
if (c->device_policy == CGROUP_CLOSED ||
(c->device_policy == CGROUP_AUTO && c->device_allow)) {
@@ -419,7 +914,10 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
"/dev/random\0" "rwm\0"
"/dev/urandom\0" "rwm\0"
"/dev/tty\0" "rwm\0"
- "/dev/pts/ptmx\0" "rw\0"; /* /dev/pts/ptmx may not be duplicated, but accessed */
+ "/dev/pts/ptmx\0" "rw\0" /* /dev/pts/ptmx may not be duplicated, but accessed */
+ /* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */
+ "/run/systemd/inaccessible/chr\0" "rwm\0"
+ "/run/systemd/inaccessible/blk\0" "rwm\0";
const char *x, *y;
@@ -454,66 +952,92 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha
else if (startswith(a->path, "char-"))
whitelist_major(path, a->path + 5, 'c', acc);
else
- log_debug("Ignoring device %s while writing cgroup attribute.", a->path);
+ log_unit_debug(u, "Ignoring device %s while writing cgroup attribute.", a->path);
}
}
+
+ if ((mask & CGROUP_MASK_PIDS) && !is_root) {
+
+ if (c->tasks_max != CGROUP_LIMIT_MAX) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 2];
+
+ sprintf(buf, "%" PRIu64 "\n", c->tasks_max);
+ r = cg_set_attribute("pids", path, "pids.max", buf);
+ } else
+ r = cg_set_attribute("pids", path, "pids.max", "max");
+
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set pids.max: %m");
+ }
}
-CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
- CGroupControllerMask mask = 0;
+CGroupMask cgroup_context_get_mask(CGroupContext *c) {
+ CGroupMask mask = 0;
/* Figure out which controllers we need */
if (c->cpu_accounting ||
- c->cpu_shares != (unsigned long) -1 ||
- c->startup_cpu_shares != (unsigned long) -1 ||
+ cgroup_context_has_cpu_weight(c) ||
+ cgroup_context_has_cpu_shares(c) ||
c->cpu_quota_per_sec_usec != USEC_INFINITY)
- mask |= CGROUP_CPUACCT | CGROUP_CPU;
+ mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU;
- if (c->blockio_accounting ||
- c->blockio_weight != (unsigned long) -1 ||
- c->startup_blockio_weight != (unsigned long) -1 ||
- c->blockio_device_weights ||
- c->blockio_device_bandwidths)
- mask |= CGROUP_BLKIO;
+ if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c))
+ mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO;
if (c->memory_accounting ||
- c->memory_limit != (uint64_t) -1)
- mask |= CGROUP_MEMORY;
+ c->memory_limit != CGROUP_LIMIT_MAX ||
+ cgroup_context_has_unified_memory_config(c))
+ mask |= CGROUP_MASK_MEMORY;
if (c->device_allow ||
c->device_policy != CGROUP_AUTO)
- mask |= CGROUP_DEVICE;
+ mask |= CGROUP_MASK_DEVICES;
+
+ if (c->tasks_accounting ||
+ c->tasks_max != (uint64_t) -1)
+ mask |= CGROUP_MASK_PIDS;
return mask;
}
-CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
+CGroupMask unit_get_own_mask(Unit *u) {
CGroupContext *c;
+ /* Returns the mask of controllers the unit needs for itself */
+
c = unit_get_cgroup_context(u);
if (!c)
return 0;
/* If delegation is turned on, then turn on all cgroups,
- * unless the process we fork into it is known to drop
- * privileges anyway, and shouldn't get access to the
- * controllers anyway. */
+ * unless we are on the legacy hierarchy and the process we
+ * fork into it is known to drop privileges, and hence
+ * shouldn't get access to the controllers.
+ *
+ * Note that on the unified hierarchy it is safe to delegate
+ * controllers to unprivileged services. */
if (c->delegate) {
ExecContext *e;
e = unit_get_exec_context(u);
- if (!e || exec_context_maintains_privileges(e))
- return _CGROUP_CONTROLLER_MASK_ALL;
+ if (!e ||
+ exec_context_maintains_privileges(e) ||
+ cg_all_unified() > 0)
+ return _CGROUP_MASK_ALL;
}
return cgroup_context_get_mask(c);
}
-CGroupControllerMask unit_get_members_mask(Unit *u) {
+CGroupMask unit_get_members_mask(Unit *u) {
assert(u);
+ /* Returns the mask of controllers all of the unit's children
+ * require, merged */
+
if (u->cgroup_members_mask_valid)
return u->cgroup_members_mask;
@@ -532,7 +1056,7 @@ CGroupControllerMask unit_get_members_mask(Unit *u) {
continue;
u->cgroup_members_mask |=
- unit_get_cgroup_mask(member) |
+ unit_get_own_mask(member) |
unit_get_members_mask(member);
}
}
@@ -541,19 +1065,52 @@ CGroupControllerMask unit_get_members_mask(Unit *u) {
return u->cgroup_members_mask;
}
-CGroupControllerMask unit_get_siblings_mask(Unit *u) {
+CGroupMask unit_get_siblings_mask(Unit *u) {
assert(u);
+ /* Returns the mask of controllers all of the unit's siblings
+ * require, i.e. the members mask of the unit's parent slice
+ * if there is one. */
+
if (UNIT_ISSET(u->slice))
return unit_get_members_mask(UNIT_DEREF(u->slice));
- return unit_get_cgroup_mask(u) | unit_get_members_mask(u);
+ return unit_get_own_mask(u) | unit_get_members_mask(u);
+}
+
+CGroupMask unit_get_subtree_mask(Unit *u) {
+
+ /* Returns the mask of this subtree, meaning of the group
+ * itself and its children. */
+
+ return unit_get_own_mask(u) | unit_get_members_mask(u);
+}
+
+CGroupMask unit_get_target_mask(Unit *u) {
+ CGroupMask mask;
+
+ /* This returns the cgroup mask of all controllers to enable
+ * for a specific cgroup, i.e. everything it needs itself,
+ * plus all that its children need, plus all that its siblings
+ * need. This is primarily useful on the legacy cgroup
+ * hierarchy, where we need to duplicate each cgroup in each
+ * hierarchy that shall be enabled for it. */
+
+ mask = unit_get_own_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
+ mask &= u->manager->cgroup_supported;
+
+ return mask;
}
-CGroupControllerMask unit_get_target_mask(Unit *u) {
- CGroupControllerMask mask;
+CGroupMask unit_get_enable_mask(Unit *u) {
+ CGroupMask mask;
- mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
+ /* This returns the cgroup mask of all controllers to enable
+ * for the children of a specific cgroup. This is primarily
+ * useful for the unified cgroup hierarchy, where each cgroup
+ * controls which controllers are enabled for its children. */
+
+ mask = unit_get_members_mask(u);
mask &= u->manager->cgroup_supported;
return mask;
@@ -562,13 +1119,13 @@ CGroupControllerMask unit_get_target_mask(Unit *u) {
/* Recurse from a unit up through its containing slices, propagating
* mask bits upward. A unit is also member of itself. */
void unit_update_cgroup_members_masks(Unit *u) {
- CGroupControllerMask m;
+ CGroupMask m;
bool more;
assert(u);
/* Calculate subtree mask */
- m = unit_get_cgroup_mask(u) | unit_get_members_mask(u);
+ m = unit_get_subtree_mask(u);
/* See if anything changed from the previous invocation. If
* not, we're done. */
@@ -608,7 +1165,7 @@ void unit_update_cgroup_members_masks(Unit *u) {
}
}
-static const char *migrate_callback(CGroupControllerMask mask, void *userdata) {
+static const char *migrate_callback(CGroupMask mask, void *userdata) {
Unit *u = userdata;
assert(mask != 0);
@@ -626,7 +1183,115 @@ static const char *migrate_callback(CGroupControllerMask mask, void *userdata) {
return NULL;
}
-static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
+char *unit_default_cgroup_path(Unit *u) {
+ _cleanup_free_ char *escaped = NULL, *slice = NULL;
+ int r;
+
+ assert(u);
+
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+ return strdup(u->manager->cgroup_root);
+
+ if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) {
+ r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
+ if (r < 0)
+ return NULL;
+ }
+
+ escaped = cg_escape(u->id);
+ if (!escaped)
+ return NULL;
+
+ if (slice)
+ return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
+ else
+ return strjoin(u->manager->cgroup_root, "/", escaped, NULL);
+}
+
+int unit_set_cgroup_path(Unit *u, const char *path) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(u);
+
+ if (path) {
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+ } else
+ p = NULL;
+
+ if (streq_ptr(u->cgroup_path, p))
+ return 0;
+
+ if (p) {
+ r = hashmap_put(u->manager->cgroup_unit, p, u);
+ if (r < 0)
+ return r;
+ }
+
+ unit_release_cgroup(u);
+
+ u->cgroup_path = p;
+ p = NULL;
+
+ return 1;
+}
+
+int unit_watch_cgroup(Unit *u) {
+ _cleanup_free_ char *events = NULL;
+ int r;
+
+ assert(u);
+
+ if (!u->cgroup_path)
+ return 0;
+
+ if (u->cgroup_inotify_wd >= 0)
+ return 0;
+
+ /* Only applies to the unified hierarchy */
+ r = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m");
+ if (r == 0)
+ return 0;
+
+ /* Don't watch the root slice, it's pointless. */
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+ return 0;
+
+ r = hashmap_ensure_allocated(&u->manager->cgroup_inotify_wd_unit, &trivial_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", &events);
+ if (r < 0)
+ return log_oom();
+
+ u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, events, IN_MODIFY);
+ if (u->cgroup_inotify_wd < 0) {
+
+ if (errno == ENOENT) /* If the directory is already
+ * gone we don't need to track
+ * it, so this is not an error */
+ return 0;
+
+ return log_unit_error_errno(u, errno, "Failed to add inotify watch descriptor for control group %s: %m", u->cgroup_path);
+ }
+
+ r = hashmap_put(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd), u);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to add inotify watch descriptor to hash map: %m");
+
+ return 0;
+}
+
+static int unit_create_cgroup(
+ Unit *u,
+ CGroupMask target_mask,
+ CGroupMask enable_mask) {
+
CGroupContext *c;
int r;
@@ -643,25 +1308,30 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
if (!path)
return log_oom();
- r = hashmap_put(u->manager->cgroup_unit, path, u);
- if (r < 0) {
- log_error(r == -EEXIST ? "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s", path, strerror(-r));
- return r;
- }
- if (r > 0) {
- u->cgroup_path = path;
- path = NULL;
- }
+ r = unit_set_cgroup_path(u, path);
+ if (r == -EEXIST)
+ return log_unit_error_errno(u, r, "Control group %s exists already.", path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", path);
}
/* First, create our own group */
- r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path);
+ r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path);
if (r < 0)
- return log_error_errno(r, "Failed to create cgroup %s: %m", u->cgroup_path);
+ return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path);
+
+ /* Start watching it */
+ (void) unit_watch_cgroup(u);
+
+ /* Enable all controllers we need */
+ r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m", u->cgroup_path);
/* Keep track that this is now realized */
u->cgroup_realized = true;
- u->cgroup_realized_mask = mask;
+ u->cgroup_realized_mask = target_mask;
+ u->cgroup_enabled_mask = enable_mask;
if (u->type != UNIT_SLICE && !c->delegate) {
@@ -670,7 +1340,7 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
* for slice and delegation units. */
r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u);
if (r < 0)
- log_warning_errno(r, "Failed to migrate cgroup from to %s: %m", u->cgroup_path);
+ log_unit_warning_errno(u, r, "Failed to migrate cgroup from to %s, ignoring: %m", u->cgroup_path);
}
return 0;
@@ -691,10 +1361,30 @@ int unit_attach_pids_to_cgroup(Unit *u) {
return 0;
}
-static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
+static void cgroup_xattr_apply(Unit *u) {
+ char ids[SD_ID128_STRING_MAX];
+ int r;
+
+ assert(u);
+
+ if (!MANAGER_IS_SYSTEM(u->manager))
+ return;
+
+ if (sd_id128_is_null(u->invocation_id))
+ return;
+
+ r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+ "trusted.invocation_id",
+ sd_id128_to_string(u->invocation_id, ids), 32,
+ 0);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
+}
+
+static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) {
assert(u);
- return u->cgroup_realized && u->cgroup_realized_mask == mask;
+ return u->cgroup_realized && u->cgroup_realized_mask == target_mask && u->cgroup_enabled_mask == enable_mask;
}
/* Check if necessary controllers and attributes for a unit are in place.
@@ -704,7 +1394,7 @@ static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
*
* Returns 0 on success and < 0 on failure. */
static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
- CGroupControllerMask mask;
+ CGroupMask target_mask, enable_mask;
int r;
assert(u);
@@ -714,9 +1404,10 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
u->in_cgroup_queue = false;
}
- mask = unit_get_target_mask(u);
+ target_mask = unit_get_target_mask(u);
+ enable_mask = unit_get_enable_mask(u);
- if (unit_has_mask_realized(u, mask))
+ if (unit_has_mask_realized(u, target_mask, enable_mask))
return 0;
/* First, realize parents */
@@ -727,12 +1418,13 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
}
/* And then do the real work */
- r = unit_create_cgroups(u, mask);
+ r = unit_create_cgroup(u, target_mask, enable_mask);
if (r < 0)
return r;
/* Finally, apply the necessary attributes. */
- cgroup_context_apply(unit_get_cgroup_context(u), mask, u->cgroup_path, state);
+ cgroup_context_apply(u, target_mask, state);
+ cgroup_xattr_apply(u);
return 0;
}
@@ -759,7 +1451,7 @@ unsigned manager_dispatch_cgroup_queue(Manager *m) {
r = unit_realize_cgroup_now(i, state);
if (r < 0)
- log_warning_errno(r, "Failed to realize cgroups for queued unit %s: %m", i->id);
+ log_warning_errno(r, "Failed to realize cgroups for queued unit %s, ignoring: %m", i->id);
n++;
}
@@ -795,7 +1487,7 @@ static void unit_queue_siblings(Unit *u) {
/* If the unit doesn't need any new controllers
* and has current ones realized, it doesn't need
* any changes. */
- if (unit_has_mask_realized(m, unit_get_target_mask(m)))
+ if (unit_has_mask_realized(m, unit_get_target_mask(m), unit_get_enable_mask(m)))
continue;
unit_add_to_cgroup_queue(m);
@@ -806,12 +1498,9 @@ static void unit_queue_siblings(Unit *u) {
}
int unit_realize_cgroup(Unit *u) {
- CGroupContext *c;
-
assert(u);
- c = unit_get_cgroup_context(u);
- if (!c)
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
/* So, here's the deal: when realizing the cgroups for this
@@ -832,39 +1521,70 @@ int unit_realize_cgroup(Unit *u) {
return unit_realize_cgroup_now(u, manager_state(u->manager));
}
-void unit_destroy_cgroup_if_empty(Unit *u) {
+void unit_release_cgroup(Unit *u) {
+ assert(u);
+
+ /* Forgets all cgroup details for this cgroup */
+
+ if (u->cgroup_path) {
+ (void) hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
+ u->cgroup_path = mfree(u->cgroup_path);
+ }
+
+ if (u->cgroup_inotify_wd >= 0) {
+ if (inotify_rm_watch(u->manager->cgroup_inotify_fd, u->cgroup_inotify_wd) < 0)
+ log_unit_debug_errno(u, errno, "Failed to remove cgroup inotify watch %i for %s, ignoring", u->cgroup_inotify_wd, u->id);
+
+ (void) hashmap_remove(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd));
+ u->cgroup_inotify_wd = -1;
+ }
+}
+
+void unit_prune_cgroup(Unit *u) {
int r;
+ bool is_root_slice;
assert(u);
+ /* Removes the cgroup, if empty and possible, and stops watching it. */
+
if (!u->cgroup_path)
return;
- r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
+ (void) unit_get_cpu_usage(u, NULL); /* Cache the last CPU usage value before we destroy the cgroup */
+
+ is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
+
+ r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice);
if (r < 0) {
- log_debug_errno(r, "Failed to destroy cgroup %s: %m", u->cgroup_path);
+ log_unit_debug_errno(u, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
return;
}
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
+ if (is_root_slice)
+ return;
+
+ unit_release_cgroup(u);
- free(u->cgroup_path);
- u->cgroup_path = NULL;
u->cgroup_realized = false;
u->cgroup_realized_mask = 0;
+ u->cgroup_enabled_mask = 0;
}
-pid_t unit_search_main_pid(Unit *u) {
+int unit_search_main_pid(Unit *u, pid_t *ret) {
_cleanup_fclose_ FILE *f = NULL;
pid_t pid = 0, npid, mypid;
+ int r;
assert(u);
+ assert(ret);
if (!u->cgroup_path)
- return 0;
+ return -ENXIO;
- if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
- return 0;
+ r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f);
+ if (r < 0)
+ return r;
mypid = getpid();
while (cg_read_pid(f, &npid) > 0) {
@@ -874,93 +1594,285 @@ pid_t unit_search_main_pid(Unit *u) {
continue;
/* Ignore processes that aren't our kids */
- if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
+ if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid)
continue;
- if (pid != 0) {
+ if (pid != 0)
/* Dang, there's more than one daemonized PID
in this group, so we don't know what process
is the main process. */
- pid = 0;
- break;
- }
+
+ return -ENODATA;
pid = npid;
}
- return pid;
+ *ret = pid;
+ return 0;
+}
+
+static int unit_watch_pids_in_path(Unit *u, const char *path) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int ret = 0, r;
+
+ assert(u);
+ assert(path);
+
+ r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f);
+ if (r < 0)
+ ret = r;
+ else {
+ pid_t pid;
+
+ while ((r = cg_read_pid(f, &pid)) > 0) {
+ r = unit_watch_pid(u, pid);
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
+ if (r < 0) {
+ if (ret >= 0)
+ ret = r;
+ } else {
+ char *fn;
+
+ while ((r = cg_read_subgroup(d, &fn)) > 0) {
+ _cleanup_free_ char *p = NULL;
+
+ p = strjoin(path, "/", fn, NULL);
+ free(fn);
+
+ if (!p)
+ return -ENOMEM;
+
+ r = unit_watch_pids_in_path(u, p);
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ if (r < 0 && ret >= 0)
+ ret = r;
+ }
+
+ return ret;
+}
+
+int unit_watch_all_pids(Unit *u) {
+ assert(u);
+
+ /* Adds all PIDs from our cgroup to the set of PIDs we
+ * watch. This is a fallback logic for cases where we do not
+ * get reliable cgroup empty notifications: we try to use
+ * SIGCHLD as replacement. */
+
+ if (!u->cgroup_path)
+ return -ENOENT;
+
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* On unified we can use proper notifications */
+ return 0;
+
+ return unit_watch_pids_in_path(u, u->cgroup_path);
+}
+
+int unit_notify_cgroup_empty(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (!u->cgroup_path)
+ return 0;
+
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
+ if (r <= 0)
+ return r;
+
+ unit_add_to_gc_queue(u);
+
+ if (UNIT_VTABLE(u)->notify_cgroup_empty)
+ UNIT_VTABLE(u)->notify_cgroup_empty(u);
+
+ return 0;
+}
+
+static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ Manager *m = userdata;
+
+ assert(s);
+ assert(fd >= 0);
+ assert(m);
+
+ for (;;) {
+ union inotify_event_buffer buffer;
+ struct inotify_event *e;
+ ssize_t l;
+
+ l = read(fd, &buffer, sizeof(buffer));
+ if (l < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ return log_error_errno(errno, "Failed to read control group inotify events: %m");
+ }
+
+ FOREACH_INOTIFY_EVENT(e, buffer, l) {
+ Unit *u;
+
+ if (e->wd < 0)
+ /* Queue overflow has no watch descriptor */
+ continue;
+
+ if (e->mask & IN_IGNORED)
+ /* The watch was just removed */
+ continue;
+
+ u = hashmap_get(m->cgroup_inotify_wd_unit, INT_TO_PTR(e->wd));
+ if (!u) /* Not that inotify might deliver
+ * events for a watch even after it
+ * was removed, because it was queued
+ * before the removal. Let's ignore
+ * this here safely. */
+ continue;
+
+ (void) unit_notify_cgroup_empty(u);
+ }
+ }
}
int manager_setup_cgroup(Manager *m) {
_cleanup_free_ char *path = NULL;
- int r;
+ CGroupController c;
+ int r, all_unified, systemd_unified;
+ char *e;
assert(m);
/* 1. Determine hierarchy */
- free(m->cgroup_root);
- m->cgroup_root = NULL;
-
+ m->cgroup_root = mfree(m->cgroup_root);
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
if (r < 0)
return log_error_errno(r, "Cannot determine cgroup we are running in: %m");
- /* LEGACY: Already in /system.slice? If so, let's cut this
- * off. This is to support live upgrades from older systemd
- * versions where PID 1 was moved there. */
- if (m->running_as == MANAGER_SYSTEM) {
- char *e;
+ /* Chop off the init scope, if we are already located in it */
+ e = endswith(m->cgroup_root, "/" SPECIAL_INIT_SCOPE);
+ /* LEGACY: Also chop off the system slice if we are in
+ * it. This is to support live upgrades from older systemd
+ * versions where PID 1 was moved there. Also see
+ * cg_get_root_path(). */
+ if (!e && MANAGER_IS_SYSTEM(m)) {
e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
if (!e)
- e = endswith(m->cgroup_root, "/system");
- if (e)
- *e = 0;
+ e = endswith(m->cgroup_root, "/system"); /* even more legacy */
}
+ if (e)
+ *e = 0;
/* And make sure to store away the root value without trailing
* slash, even for the root dir, so that we can easily prepend
* it everywhere. */
- if (streq(m->cgroup_root, "/"))
- m->cgroup_root[0] = 0;
+ while ((e = endswith(m->cgroup_root, "/")))
+ *e = 0;
/* 2. Show data */
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
if (r < 0)
return log_error_errno(r, "Cannot find cgroup mount point: %m");
- log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
+ all_unified = cg_all_unified();
+ systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+
+ if (all_unified < 0 || systemd_unified < 0)
+ return log_error_errno(all_unified < 0 ? all_unified : systemd_unified,
+ "Couldn't determine if we are running in the unified hierarchy: %m");
+
+ if (all_unified > 0)
+ log_debug("Unified cgroup hierarchy is located at %s.", path);
+ else if (systemd_unified > 0)
+ log_debug("Unified cgroup hierarchy is located at %s. Controllers are on legacy hierarchies.", path);
+ else
+ log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
+
if (!m->test_run) {
+ const char *scope_path;
/* 3. Install agent */
- if (m->running_as == MANAGER_SYSTEM) {
+ if (systemd_unified) {
+
+ /* In the unified hierarchy we can get
+ * cgroup empty notifications via inotify. */
+
+ m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source);
+ safe_close(m->cgroup_inotify_fd);
+
+ m->cgroup_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
+ if (m->cgroup_inotify_fd < 0)
+ return log_error_errno(errno, "Failed to create control group inotify object: %m");
+
+ r = sd_event_add_io(m->event, &m->cgroup_inotify_event_source, m->cgroup_inotify_fd, EPOLLIN, on_cgroup_inotify_event, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch control group inotify object: %m");
+
+ /* Process cgroup empty notifications early, but after service notifications and SIGCHLD. Also
+ * see handling of cgroup agent notifications, for the classic cgroup hierarchy support. */
+ r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_NORMAL-5);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set priority of inotify event source: %m");
+
+ (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify");
+
+ } else if (MANAGER_IS_SYSTEM(m)) {
+
+ /* On the legacy hierarchy we only get
+ * notifications via cgroup agents. (Which
+ * isn't really reliable, since it does not
+ * generate events when control groups with
+ * children run empty. */
+
r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
if (r < 0)
log_warning_errno(r, "Failed to install release agent, ignoring: %m");
else if (r > 0)
log_debug("Installed release agent.");
- else
+ else if (r == 0)
log_debug("Release agent already installed.");
}
- /* 4. Make sure we are in the root cgroup */
- r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
+ /* 4. Make sure we are in the special "init.scope" unit in the root slice. */
+ scope_path = strjoina(m->cgroup_root, "/" SPECIAL_INIT_SCOPE);
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create %s control group: %m", scope_path);
+
+ /* also, move all other userspace processes remaining
+ * in the root cgroup into that scope. */
+ r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, 0);
if (r < 0)
- return log_error_errno(r, "Failed to create root cgroup hierarchy: %m");
+ log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m");
/* 5. And pin it, so that it cannot be unmounted */
safe_close(m->pin_cgroupfs_fd);
-
m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
if (m->pin_cgroupfs_fd < 0)
return log_error_errno(errno, "Failed to open pin file: %m");
/* 6. Always enable hierarchical support if it exists... */
- cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
+ if (!all_unified)
+ (void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
}
/* 7. Figure out which controllers are supported */
- m->cgroup_supported = cg_mask_supported();
+ r = cg_mask_supported(&m->cgroup_supported);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine supported controllers: %m");
+
+ for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++)
+ log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & CGROUP_CONTROLLER_TO_MASK(c)));
return 0;
}
@@ -971,12 +1883,16 @@ void manager_shutdown_cgroup(Manager *m, bool delete) {
/* We can't really delete the group, since we are in it. But
* let's trim it. */
if (delete && m->cgroup_root)
- cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
+ (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
+
+ m->cgroup_inotify_wd_unit = hashmap_free(m->cgroup_inotify_wd_unit);
+
+ m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source);
+ m->cgroup_inotify_fd = safe_close(m->cgroup_inotify_fd);
m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd);
- free(m->cgroup_root);
- m->cgroup_root = NULL;
+ m->cgroup_root = mfree(m->cgroup_root);
}
Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
@@ -995,8 +1911,8 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
char *e;
e = strrchr(p, '/');
- if (e == p || !e)
- return NULL;
+ if (!e || e == p)
+ return hashmap_get(m->cgroup_unit, SPECIAL_ROOT_SLICE);
*e = 0;
@@ -1006,13 +1922,13 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
}
}
-Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
+Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) {
_cleanup_free_ char *cgroup = NULL;
int r;
assert(m);
- if (pid <= 1)
+ if (pid <= 0)
return NULL;
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
@@ -1022,26 +1938,41 @@ Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
return manager_get_unit_by_cgroup(m, cgroup);
}
+Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
+ Unit *u;
+
+ assert(m);
+
+ if (pid <= 0)
+ return NULL;
+
+ if (pid == 1)
+ return hashmap_get(m->units, SPECIAL_INIT_SCOPE);
+
+ u = hashmap_get(m->watch_pids1, PID_TO_PTR(pid));
+ if (u)
+ return u;
+
+ u = hashmap_get(m->watch_pids2, PID_TO_PTR(pid));
+ if (u)
+ return u;
+
+ return manager_get_unit_by_pid_cgroup(m, pid);
+}
+
int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
Unit *u;
- int r;
assert(m);
assert(cgroup);
+ log_debug("Got cgroup empty notification for: %s", cgroup);
+
u = manager_get_unit_by_cgroup(m, cgroup);
if (!u)
return 0;
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
- if (r <= 0)
- return r;
-
- if (UNIT_VTABLE(u)->notify_cgroup_empty)
- UNIT_VTABLE(u)->notify_cgroup_empty(u);
-
- unit_add_to_gc_queue(u);
- return 0;
+ return unit_notify_cgroup_empty(u);
}
int unit_get_memory_current(Unit *u, uint64_t *ret) {
@@ -1054,10 +1985,13 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
if (!u->cgroup_path)
return -ENODATA;
- if ((u->cgroup_realized_mask & CGROUP_MEMORY) == 0)
+ if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0)
return -ENODATA;
- r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
+ if (cg_all_unified() <= 0)
+ r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
+ else
+ r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
@@ -1066,9 +2000,8 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
return safe_atou64(v, ret);
}
-static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
+int unit_get_tasks_current(Unit *u, uint64_t *ret) {
_cleanup_free_ char *v = NULL;
- uint64_t ns;
int r;
assert(u);
@@ -1077,18 +2010,60 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
if (!u->cgroup_path)
return -ENODATA;
- if ((u->cgroup_realized_mask & CGROUP_CPUACCT) == 0)
+ if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0)
return -ENODATA;
- r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
+ r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
return r;
- r = safe_atou64(v, &ns);
- if (r < 0)
- return r;
+ return safe_atou64(v, ret);
+}
+
+static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
+ _cleanup_free_ char *v = NULL;
+ uint64_t ns;
+ int r;
+
+ assert(u);
+ assert(ret);
+
+ if (!u->cgroup_path)
+ return -ENODATA;
+
+ if (cg_all_unified() > 0) {
+ const char *keys[] = { "usage_usec", NULL };
+ _cleanup_free_ char *val = NULL;
+ uint64_t us;
+
+ if ((u->cgroup_realized_mask & CGROUP_MASK_CPU) == 0)
+ return -ENODATA;
+
+ r = cg_get_keyed_attribute("cpu", u->cgroup_path, "cpu.stat", keys, &val);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(val, &us);
+ if (r < 0)
+ return r;
+
+ ns = us * NSEC_PER_USEC;
+ } else {
+ if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0)
+ return -ENODATA;
+
+ r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
+ if (r == -ENOENT)
+ return -ENODATA;
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(v, &ns);
+ if (r < 0)
+ return r;
+ }
*ret = ns;
return 0;
@@ -1098,16 +2073,33 @@ int unit_get_cpu_usage(Unit *u, nsec_t *ret) {
nsec_t ns;
int r;
+ assert(u);
+
+ /* Retrieve the current CPU usage counter. This will subtract the CPU counter taken when the unit was
+ * started. If the cgroup has been removed already, returns the last cached value. To cache the value, simply
+ * call this function with a NULL return value. */
+
r = unit_get_cpu_usage_raw(u, &ns);
+ if (r == -ENODATA && u->cpu_usage_last != NSEC_INFINITY) {
+ /* If we can't get the CPU usage anymore (because the cgroup was already removed, for example), use our
+ * cached value. */
+
+ if (ret)
+ *ret = u->cpu_usage_last;
+ return 0;
+ }
if (r < 0)
return r;
- if (ns > u->cpuacct_usage_base)
- ns -= u->cpuacct_usage_base;
+ if (ns > u->cpu_usage_base)
+ ns -= u->cpu_usage_base;
else
ns = 0;
- *ret = ns;
+ u->cpu_usage_last = ns;
+ if (ret)
+ *ret = ns;
+
return 0;
}
@@ -1117,16 +2109,60 @@ int unit_reset_cpu_usage(Unit *u) {
assert(u);
+ u->cpu_usage_last = NSEC_INFINITY;
+
r = unit_get_cpu_usage_raw(u, &ns);
if (r < 0) {
- u->cpuacct_usage_base = 0;
+ u->cpu_usage_base = 0;
return r;
}
- u->cpuacct_usage_base = ns;
+ u->cpu_usage_base = ns;
return 0;
}
+bool unit_cgroup_delegate(Unit *u) {
+ CGroupContext *c;
+
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return false;
+
+ return c->delegate;
+}
+
+void unit_invalidate_cgroup(Unit *u, CGroupMask m) {
+ assert(u);
+
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return;
+
+ if (m == 0)
+ return;
+
+ /* always invalidate compat pairs together */
+ if (m & (CGROUP_MASK_IO | CGROUP_MASK_BLKIO))
+ m |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO;
+
+ if ((u->cgroup_realized_mask & m) == 0)
+ return;
+
+ u->cgroup_realized_mask &= ~m;
+ unit_add_to_cgroup_queue(u);
+}
+
+void manager_invalidate_startup_units(Manager *m) {
+ Iterator i;
+ Unit *u;
+
+ assert(m);
+
+ SET_FOREACH(u, m->startup_units, i)
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_BLKIO);
+}
+
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
[CGROUP_AUTO] = "auto",
[CGROUP_CLOSED] = "closed",
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 869ddae8c4..4cd168f63e 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -25,9 +23,12 @@
#include "list.h"
#include "time-util.h"
+#include "cgroup-util.h"
typedef struct CGroupContext CGroupContext;
typedef struct CGroupDeviceAllow CGroupDeviceAllow;
+typedef struct CGroupIODeviceWeight CGroupIODeviceWeight;
+typedef struct CGroupIODeviceLimit CGroupIODeviceLimit;
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
@@ -55,30 +56,59 @@ struct CGroupDeviceAllow {
bool m:1;
};
+struct CGroupIODeviceWeight {
+ LIST_FIELDS(CGroupIODeviceWeight, device_weights);
+ char *path;
+ uint64_t weight;
+};
+
+struct CGroupIODeviceLimit {
+ LIST_FIELDS(CGroupIODeviceLimit, device_limits);
+ char *path;
+ uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX];
+};
+
struct CGroupBlockIODeviceWeight {
LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights);
char *path;
- unsigned long weight;
+ uint64_t weight;
};
struct CGroupBlockIODeviceBandwidth {
LIST_FIELDS(CGroupBlockIODeviceBandwidth, device_bandwidths);
char *path;
- uint64_t bandwidth;
- bool read;
+ uint64_t rbps;
+ uint64_t wbps;
};
struct CGroupContext {
bool cpu_accounting;
+ bool io_accounting;
bool blockio_accounting;
bool memory_accounting;
+ bool tasks_accounting;
- unsigned long cpu_shares;
- unsigned long startup_cpu_shares;
+ /* For unified hierarchy */
+ uint64_t cpu_weight;
+ uint64_t startup_cpu_weight;
usec_t cpu_quota_per_sec_usec;
- unsigned long blockio_weight;
- unsigned long startup_blockio_weight;
+ uint64_t io_weight;
+ uint64_t startup_io_weight;
+ LIST_HEAD(CGroupIODeviceWeight, io_device_weights);
+ LIST_HEAD(CGroupIODeviceLimit, io_device_limits);
+
+ uint64_t memory_low;
+ uint64_t memory_high;
+ uint64_t memory_max;
+ uint64_t memory_swap_max;
+
+ /* For legacy hierarchies */
+ uint64_t cpu_shares;
+ uint64_t startup_cpu_shares;
+
+ uint64_t blockio_weight;
+ uint64_t startup_blockio_weight;
LIST_HEAD(CGroupBlockIODeviceWeight, blockio_device_weights);
LIST_HEAD(CGroupBlockIODeviceBandwidth, blockio_device_bandwidths);
@@ -87,31 +117,44 @@ struct CGroupContext {
CGroupDevicePolicy device_policy;
LIST_HEAD(CGroupDeviceAllow, device_allow);
+ /* Common */
+ uint64_t tasks_max;
+
bool delegate;
};
#include "unit.h"
-#include "cgroup-util.h"
void cgroup_context_init(CGroupContext *c);
void cgroup_context_done(CGroupContext *c);
void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix);
-void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path, ManagerState state);
-CGroupControllerMask cgroup_context_get_mask(CGroupContext *c);
+CGroupMask cgroup_context_get_mask(CGroupContext *c);
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
+void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w);
+void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l);
void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
-CGroupControllerMask unit_get_cgroup_mask(Unit *u);
-CGroupControllerMask unit_get_siblings_mask(Unit *u);
-CGroupControllerMask unit_get_members_mask(Unit *u);
-CGroupControllerMask unit_get_target_mask(Unit *u);
+CGroupMask unit_get_own_mask(Unit *u);
+CGroupMask unit_get_siblings_mask(Unit *u);
+CGroupMask unit_get_members_mask(Unit *u);
+CGroupMask unit_get_subtree_mask(Unit *u);
+
+CGroupMask unit_get_target_mask(Unit *u);
+CGroupMask unit_get_enable_mask(Unit *u);
void unit_update_cgroup_members_masks(Unit *u);
+
+char *unit_default_cgroup_path(Unit *u);
+int unit_set_cgroup_path(Unit *u, const char *path);
+
int unit_realize_cgroup(Unit *u);
-void unit_destroy_cgroup_if_empty(Unit *u);
+void unit_release_cgroup(Unit *u);
+void unit_prune_cgroup(Unit *u);
+int unit_watch_cgroup(Unit *u);
+
int unit_attach_pids_to_cgroup(Unit *u);
int manager_setup_cgroup(Manager *m);
@@ -120,15 +163,25 @@ void manager_shutdown_cgroup(Manager *m, bool delete);
unsigned manager_dispatch_cgroup_queue(Manager *m);
Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup);
+Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid);
Unit* manager_get_unit_by_pid(Manager *m, pid_t pid);
-pid_t unit_search_main_pid(Unit *u);
-
-int manager_notify_cgroup_empty(Manager *m, const char *group);
+int unit_search_main_pid(Unit *u, pid_t *ret);
+int unit_watch_all_pids(Unit *u);
int unit_get_memory_current(Unit *u, uint64_t *ret);
+int unit_get_tasks_current(Unit *u, uint64_t *ret);
int unit_get_cpu_usage(Unit *u, nsec_t *ret);
int unit_reset_cpu_usage(Unit *u);
+bool unit_cgroup_delegate(Unit *u);
+
+int unit_notify_cgroup_empty(Unit *u);
+int manager_notify_cgroup_empty(Manager *m, const char *group);
+
+void unit_invalidate_cgroup(Unit *u, CGroupMask m);
+
+void manager_invalidate_startup_units(Manager *m);
+
const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_;
CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_;
diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c
index 5162ce34cb..26212b3a95 100644
--- a/src/core/dbus-automount.c
+++ b/src/core/dbus-automount.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,8 +18,9 @@
***/
#include "automount.h"
-#include "dbus-automount.h"
#include "bus-util.h"
+#include "dbus-automount.h"
+#include "string-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, automount_result, AutomountResult);
@@ -33,3 +32,57 @@ const sd_bus_vtable bus_automount_vtable[] = {
SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
+
+static int bus_automount_set_transient_property(
+ Automount *a,
+ const char *name,
+ sd_bus_message *message,
+ UnitSetPropertiesMode mode,
+ sd_bus_error *error) {
+
+ int r;
+
+ assert(a);
+ assert(name);
+ assert(message);
+
+ if (streq(name, "TimeoutIdleUSec")) {
+ usec_t timeout_idle_usec;
+ r = sd_bus_message_read(message, "t", &timeout_idle_usec);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ char time[FORMAT_TIMESPAN_MAX];
+
+ a->timeout_idle_usec = timeout_idle_usec;
+ unit_write_drop_in_format(UNIT(a), mode, name, "[Automount]\nTimeoutIdleSec=%s\n",
+ format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC));
+ }
+ } else
+ return 0;
+
+ return 1;
+}
+
+int bus_automount_set_property(
+ Unit *u,
+ const char *name,
+ sd_bus_message *message,
+ UnitSetPropertiesMode mode,
+ sd_bus_error *error) {
+
+ Automount *a = AUTOMOUNT(u);
+ int r = 0;
+
+ assert(a);
+ assert(name);
+ assert(message);
+
+ if (u->transient && u->load_state == UNIT_STUB)
+ /* This is a transient unit, let's load a little more */
+
+ r = bus_automount_set_transient_property(a, name, message, mode, error);
+
+ return r;
+}
diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h
index a2b124d756..f41adda2a6 100644
--- a/src/core/dbus-automount.h
+++ b/src/core/dbus-automount.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,3 +21,5 @@
extern const sd_bus_vtable bus_automount_vtable[];
+
+int bus_automount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error);
diff --git a/src/core/dbus-busname.c b/src/core/dbus-busname.c
index b1ceb05b1a..cf816ba15b 100644
--- a/src/core/dbus-busname.c
+++ b/src/core/dbus-busname.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,10 +17,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
+#include "bus-util.h"
#include "busname.h"
#include "dbus-busname.h"
-#include "bus-util.h"
+#include "string-util.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, busname_result, BusNameResult);
diff --git a/src/core/dbus-busname.h b/src/core/dbus-busname.h
index ea55b6c8c9..8643d1a404 100644
--- a/src/core/dbus-busname.h
+++ b/src/core/dbus-busname.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 4a9df06016..c4067a95bf 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,14 +17,83 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
#include "bus-util.h"
-#include "path-util.h"
#include "cgroup-util.h"
#include "cgroup.h"
#include "dbus-cgroup.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "path-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
+static int property_get_io_device_weight(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ CGroupContext *c = userdata;
+ CGroupIODeviceWeight *w;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ r = sd_bus_message_open_container(reply, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(device_weights, w, c->io_device_weights) {
+ r = sd_bus_message_append(reply, "(st)", w->path, w->weight);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static int property_get_io_device_limits(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ CGroupContext *c = userdata;
+ CGroupIODeviceLimit *l;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ r = sd_bus_message_open_container(reply, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(device_limits, l, c->io_device_limits) {
+ CGroupIOLimitType type;
+
+ type = cgroup_io_limit_type_from_string(property);
+ if (type < 0 || l->limits[type] == cgroup_io_limit_defaults[type])
+ continue;
+
+ r = sd_bus_message_append(reply, "(st)", l->path, l->limits[type]);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
static int property_get_blockio_device_weight(
sd_bus *bus,
const char *path,
@@ -79,11 +146,17 @@ static int property_get_blockio_device_bandwidths(
return r;
LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+ uint64_t v;
+
+ if (streq(property, "BlockIOReadBandwidth"))
+ v = b->rbps;
+ else
+ v = b->wbps;
- if (streq(property, "BlockIOReadBandwidth") != b->read)
+ if (v == CGROUP_LIMIT_MAX)
continue;
- r = sd_bus_message_append(reply, "(st)", b->path, b->bandwidth);
+ r = sd_bus_message_append(reply, "(st)", b->path, v);
if (r < 0)
return r;
}
@@ -133,41 +206,39 @@ static int property_get_device_allow(
return sd_bus_message_close_container(reply);
}
-static int property_get_ulong_as_u64(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- unsigned long *ul = userdata;
-
- assert(bus);
- assert(reply);
- assert(ul);
-
- return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul);
-}
-
const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0),
- SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0),
- SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0),
+ SD_BUS_PROPERTY("CPUWeight", "t", NULL, offsetof(CGroupContext, cpu_weight), 0),
+ SD_BUS_PROPERTY("StartupCPUWeight", "t", NULL, offsetof(CGroupContext, startup_cpu_weight), 0),
+ SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0),
+ SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0),
SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0),
+ SD_BUS_PROPERTY("IOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, io_accounting), 0),
+ SD_BUS_PROPERTY("IOWeight", "t", NULL, offsetof(CGroupContext, io_weight), 0),
+ SD_BUS_PROPERTY("StartupIOWeight", "t", NULL, offsetof(CGroupContext, startup_io_weight), 0),
+ SD_BUS_PROPERTY("IODeviceWeight", "a(st)", property_get_io_device_weight, 0, 0),
+ SD_BUS_PROPERTY("IOReadBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0),
+ SD_BUS_PROPERTY("IOWriteBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0),
+ SD_BUS_PROPERTY("IOReadIOPSMax", "a(st)", property_get_io_device_limits, 0, 0),
+ SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0),
SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0),
- SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0),
- SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0),
+ SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0),
+ SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0),
SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0),
SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0),
SD_BUS_PROPERTY("MemoryAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, memory_accounting), 0),
+ SD_BUS_PROPERTY("MemoryLow", "t", NULL, offsetof(CGroupContext, memory_low), 0),
+ SD_BUS_PROPERTY("MemoryHigh", "t", NULL, offsetof(CGroupContext, memory_high), 0),
+ SD_BUS_PROPERTY("MemoryMax", "t", NULL, offsetof(CGroupContext, memory_max), 0),
+ SD_BUS_PROPERTY("MemorySwapMax", "t", NULL, offsetof(CGroupContext, memory_swap_max), 0),
SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0),
SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0),
SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0),
+ SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0),
+ SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0),
SD_BUS_VTABLE_END
};
@@ -212,6 +283,7 @@ int bus_cgroup_set_property(
UnitSetPropertiesMode mode,
sd_bus_error *error) {
+ CGroupIOLimitType iol_type;
int r;
assert(u);
@@ -228,56 +300,96 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->cpu_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_CPUACCT;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU);
unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no");
}
return 1;
- } else if (streq(name, "CPUShares")) {
- uint64_t u64;
- unsigned long ul;
+ } else if (streq(name, "CPUWeight")) {
+ uint64_t weight;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &weight);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul <= 0 || (uint64_t) ul != u64)
- return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
+ if (!CGROUP_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "CPUWeight value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->cpu_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+
+ if (weight == CGROUP_WEIGHT_INVALID)
+ unit_write_drop_in_private(u, mode, name, "CPUWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "CPUWeight=%" PRIu64, weight);
}
+ return 1;
+
+ } else if (streq(name, "StartupCPUWeight")) {
+ uint64_t weight;
+
+ r = sd_bus_message_read(message, "t", &weight);
+ if (r < 0)
+ return r;
+
+ if (!CGROUP_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUWeight value out of range");
+
if (mode != UNIT_CHECK) {
- c->cpu_shares = ul;
- u->cgroup_realized_mask &= ~CGROUP_CPU;
- unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
+ c->startup_cpu_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+
+ if (weight == CGROUP_CPU_SHARES_INVALID)
+ unit_write_drop_in_private(u, mode, name, "StartupCPUWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "StartupCPUWeight=%" PRIu64, weight);
}
return 1;
- } else if (streq(name, "StartupCPUShares")) {
- uint64_t u64;
- unsigned long ul;
+ } else if (streq(name, "CPUShares")) {
+ uint64_t shares;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &shares);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul <= 0 || (uint64_t) ul != u64)
- return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
+ if (!CGROUP_CPU_SHARES_IS_OK(shares))
+ return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->cpu_shares = shares;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+
+ if (shares == CGROUP_CPU_SHARES_INVALID)
+ unit_write_drop_in_private(u, mode, name, "CPUShares=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "CPUShares=%" PRIu64, shares);
}
+ return 1;
+
+ } else if (streq(name, "StartupCPUShares")) {
+ uint64_t shares;
+
+ r = sd_bus_message_read(message, "t", &shares);
+ if (r < 0)
+ return r;
+
+ if (!CGROUP_CPU_SHARES_IS_OK(shares))
+ return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range");
+
if (mode != UNIT_CHECK) {
- c->startup_cpu_shares = ul;
- u->cgroup_realized_mask &= ~CGROUP_CPU;
- unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul);
+ c->startup_cpu_shares = shares;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
+
+ if (shares == CGROUP_CPU_SHARES_INVALID)
+ unit_write_drop_in_private(u, mode, name, "StartupCPUShares=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%" PRIu64, shares);
}
return 1;
@@ -294,12 +406,229 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->cpu_quota_per_sec_usec = u64;
- u->cgroup_realized_mask &= ~CGROUP_CPU;
+ unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000));
}
return 1;
+ } else if (streq(name, "IOAccounting")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->io_accounting = b;
+ unit_invalidate_cgroup(u, CGROUP_MASK_IO);
+ unit_write_drop_in_private(u, mode, name, b ? "IOAccounting=yes" : "IOAccounting=no");
+ }
+
+ return 1;
+
+ } else if (streq(name, "IOWeight")) {
+ uint64_t weight;
+
+ r = sd_bus_message_read(message, "t", &weight);
+ if (r < 0)
+ return r;
+
+ if (!CGROUP_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "IOWeight value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->io_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_IO);
+
+ if (weight == CGROUP_WEIGHT_INVALID)
+ unit_write_drop_in_private(u, mode, name, "IOWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "IOWeight=%" PRIu64, weight);
+ }
+
+ return 1;
+
+ } else if (streq(name, "StartupIOWeight")) {
+ uint64_t weight;
+
+ r = sd_bus_message_read(message, "t", &weight);
+ if (r < 0)
+ return r;
+
+ if (CGROUP_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "StartupIOWeight value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->startup_io_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_IO);
+
+ if (weight == CGROUP_WEIGHT_INVALID)
+ unit_write_drop_in_private(u, mode, name, "StartupIOWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "StartupIOWeight=%" PRIu64, weight);
+ }
+
+ return 1;
+
+ } else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) {
+ const char *path;
+ unsigned n = 0;
+ uint64_t u64;
+
+ r = sd_bus_message_enter_container(message, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
+
+ if (mode != UNIT_CHECK) {
+ CGroupIODeviceLimit *a = NULL, *b;
+
+ LIST_FOREACH(device_limits, b, c->io_device_limits) {
+ if (path_equal(path, b->path)) {
+ a = b;
+ break;
+ }
+ }
+
+ if (!a) {
+ CGroupIOLimitType type;
+
+ a = new0(CGroupIODeviceLimit, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->path = strdup(path);
+ if (!a->path) {
+ free(a);
+ return -ENOMEM;
+ }
+
+ for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++)
+ a->limits[type] = cgroup_io_limit_defaults[type];
+
+ LIST_PREPEND(device_limits, c->io_device_limits, a);
+ }
+
+ a->limits[iol_type] = u64;
+ }
+
+ n++;
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ CGroupIODeviceLimit *a;
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ size_t size = 0;
+
+ if (n == 0) {
+ LIST_FOREACH(device_limits, a, c->io_device_limits)
+ a->limits[iol_type] = cgroup_io_limit_defaults[iol_type];
+ }
+
+ unit_invalidate_cgroup(u, CGROUP_MASK_IO);
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fprintf(f, "%s=\n", name);
+ LIST_FOREACH(device_limits, a, c->io_device_limits)
+ if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type])
+ fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private(u, mode, name, buf);
+ }
+
+ return 1;
+
+ } else if (streq(name, "IODeviceWeight")) {
+ const char *path;
+ uint64_t weight;
+ unsigned n = 0;
+
+ r = sd_bus_message_enter_container(message, 'a', "(st)");
+ if (r < 0)
+ return r;
+
+ while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) {
+
+ if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID)
+ return sd_bus_error_set_errnof(error, EINVAL, "IODeviceWeight out of range");
+
+ if (mode != UNIT_CHECK) {
+ CGroupIODeviceWeight *a = NULL, *b;
+
+ LIST_FOREACH(device_weights, b, c->io_device_weights) {
+ if (path_equal(b->path, path)) {
+ a = b;
+ break;
+ }
+ }
+
+ if (!a) {
+ a = new0(CGroupIODeviceWeight, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->path = strdup(path);
+ if (!a->path) {
+ free(a);
+ return -ENOMEM;
+ }
+ LIST_PREPEND(device_weights,c->io_device_weights, a);
+ }
+
+ a->weight = weight;
+ }
+
+ n++;
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ CGroupIODeviceWeight *a;
+ size_t size = 0;
+
+ if (n == 0) {
+ while (c->io_device_weights)
+ cgroup_context_free_io_device_weight(c, c->io_device_weights);
+ }
+
+ unit_invalidate_cgroup(u, CGROUP_MASK_IO);
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs("IODeviceWeight=\n", f);
+ LIST_FOREACH(device_weights, a, c->io_device_weights)
+ fprintf(f, "IODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+ unit_write_drop_in_private(u, mode, name, buf);
+ }
+
+ return 1;
+
} else if (streq(name, "BlockIOAccounting")) {
int b;
@@ -309,61 +638,57 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->blockio_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no");
}
return 1;
} else if (streq(name, "BlockIOWeight")) {
- uint64_t u64;
- unsigned long ul;
+ uint64_t weight;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &weight);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul < 10 || ul > 1000)
- return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
- }
+ if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range");
if (mode != UNIT_CHECK) {
- c->blockio_weight = ul;
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
- unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
+ c->blockio_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
+
+ if (weight == CGROUP_BLKIO_WEIGHT_INVALID)
+ unit_write_drop_in_private(u, mode, name, "BlockIOWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%" PRIu64, weight);
}
return 1;
} else if (streq(name, "StartupBlockIOWeight")) {
- uint64_t u64;
- unsigned long ul;
+ uint64_t weight;
- r = sd_bus_message_read(message, "t", &u64);
+ r = sd_bus_message_read(message, "t", &weight);
if (r < 0)
return r;
- if (u64 == (uint64_t) -1)
- ul = (unsigned long) -1;
- else {
- ul = (unsigned long) u64;
- if (ul < 10 || ul > 1000)
- return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
- }
+ if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight))
+ return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range");
if (mode != UNIT_CHECK) {
- c->startup_blockio_weight = ul;
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
- unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul);
+ c->startup_blockio_weight = weight;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
+
+ if (weight == CGROUP_BLKIO_WEIGHT_INVALID)
+ unit_write_drop_in_private(u, mode, name, "StartupBlockIOWeight=");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%" PRIu64, weight);
}
return 1;
- } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
+ } else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
const char *path;
bool read = true;
unsigned n = 0;
@@ -382,7 +707,7 @@ int bus_cgroup_set_property(
CGroupBlockIODeviceBandwidth *a = NULL, *b;
LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
- if (path_equal(path, b->path) && read == b->read) {
+ if (path_equal(path, b->path)) {
a = b;
break;
}
@@ -393,7 +718,8 @@ int bus_cgroup_set_property(
if (!a)
return -ENOMEM;
- a->read = read;
+ a->rbps = CGROUP_LIMIT_MAX;
+ a->wbps = CGROUP_LIMIT_MAX;
a->path = strdup(path);
if (!a->path) {
free(a);
@@ -403,7 +729,10 @@ int bus_cgroup_set_property(
LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a);
}
- a->bandwidth = u64;
+ if (read)
+ a->rbps = u64;
+ else
+ a->wbps = u64;
}
n++;
@@ -416,36 +745,41 @@ int bus_cgroup_set_property(
return r;
if (mode != UNIT_CHECK) {
- CGroupBlockIODeviceBandwidth *a, *next;
+ CGroupBlockIODeviceBandwidth *a;
_cleanup_free_ char *buf = NULL;
_cleanup_fclose_ FILE *f = NULL;
size_t size = 0;
if (n == 0) {
- LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths)
- if (a->read == read)
- cgroup_context_free_blockio_device_bandwidth(c, a);
+ LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) {
+ if (read)
+ a->rbps = CGROUP_LIMIT_MAX;
+ else
+ a->wbps = CGROUP_LIMIT_MAX;
+ }
}
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
f = open_memstream(&buf, &size);
if (!f)
return -ENOMEM;
- if (read) {
+ if (read) {
fputs("BlockIOReadBandwidth=\n", f);
- LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
- if (a->read)
- fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
+ LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
+ if (a->rbps != CGROUP_LIMIT_MAX)
+ fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->rbps);
} else {
fputs("BlockIOWriteBandwidth=\n", f);
LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths)
- if (!a->read)
- fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth);
+ if (a->wbps != CGROUP_LIMIT_MAX)
+ fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->wbps);
}
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(u, mode, name, buf);
}
@@ -453,17 +787,16 @@ int bus_cgroup_set_property(
} else if (streq(name, "BlockIODeviceWeight")) {
const char *path;
- uint64_t u64;
+ uint64_t weight;
unsigned n = 0;
r = sd_bus_message_enter_container(message, 'a', "(st)");
if (r < 0)
return r;
- while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) {
- unsigned long ul = u64;
+ while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) {
- if (ul < 10 || ul > 1000)
+ if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID)
return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range");
if (mode != UNIT_CHECK) {
@@ -489,7 +822,7 @@ int bus_cgroup_set_property(
LIST_PREPEND(device_weights,c->blockio_device_weights, a);
}
- a->weight = ul;
+ a->weight = weight;
}
n++;
@@ -510,7 +843,7 @@ int bus_cgroup_set_property(
cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
}
- u->cgroup_realized_mask &= ~CGROUP_BLKIO;
+ unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO);
f = open_memstream(&buf, &size);
if (!f)
@@ -518,9 +851,11 @@ int bus_cgroup_set_property(
fputs("BlockIODeviceWeight=\n", f);
LIST_FOREACH(device_weights, a, c->blockio_device_weights)
- fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
+ fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight);
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(u, mode, name, buf);
}
@@ -535,23 +870,112 @@ int bus_cgroup_set_property(
if (mode != UNIT_CHECK) {
c->memory_accounting = b;
- u->cgroup_realized_mask &= ~CGROUP_MEMORY;
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
}
return 1;
+ } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax")) {
+ uint64_t v;
+
+ r = sd_bus_message_read(message, "t", &v);
+ if (r < 0)
+ return r;
+ if (v <= 0)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
+
+ if (mode != UNIT_CHECK) {
+ if (streq(name, "MemoryLow"))
+ c->memory_low = v;
+ else if (streq(name, "MemoryHigh"))
+ c->memory_high = v;
+ else if (streq(name, "MemorySwapMax"))
+ c->memory_swap_max = v;
+ else
+ c->memory_max = v;
+
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
+
+ if (v == CGROUP_LIMIT_MAX)
+ unit_write_drop_in_private_format(u, mode, name, "%s=infinity", name);
+ else
+ unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, v);
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name, "MemoryLowScale", "MemoryHighScale", "MemoryMaxScale")) {
+ uint32_t raw;
+ uint64_t v;
+
+ r = sd_bus_message_read(message, "u", &raw);
+ if (r < 0)
+ return r;
+
+ v = physical_memory_scale(raw, UINT32_MAX);
+ if (v <= 0 || v == UINT64_MAX)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
+
+ if (mode != UNIT_CHECK) {
+ const char *e;
+
+ /* Chop off suffix */
+ assert_se(e = endswith(name, "Scale"));
+ name = strndupa(name, e - name);
+
+ if (streq(name, "MemoryLow"))
+ c->memory_low = v;
+ else if (streq(name, "MemoryHigh"))
+ c->memory_high = v;
+ else
+ c->memory_max = v;
+
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
+ unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu32 "%%", name,
+ (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX)));
+ }
+
+ return 1;
+
} else if (streq(name, "MemoryLimit")) {
uint64_t limit;
r = sd_bus_message_read(message, "t", &limit);
if (r < 0)
return r;
+ if (limit <= 0)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
if (mode != UNIT_CHECK) {
c->memory_limit = limit;
- u->cgroup_realized_mask &= ~CGROUP_MEMORY;
- unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
+
+ if (limit == (uint64_t) -1)
+ unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit);
+ }
+
+ return 1;
+
+ } else if (streq(name, "MemoryLimitScale")) {
+ uint64_t limit;
+ uint32_t raw;
+
+ r = sd_bus_message_read(message, "u", &raw);
+ if (r < 0)
+ return r;
+
+ limit = physical_memory_scale(raw, UINT32_MAX);
+ if (limit <= 0 || limit == UINT64_MAX)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
+
+ if (mode != UNIT_CHECK) {
+ c->memory_limit = limit;
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
+ unit_write_drop_in_private_format(u, mode, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%",
+ (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX)));
}
return 1;
@@ -569,13 +993,9 @@ int bus_cgroup_set_property(
return -EINVAL;
if (mode != UNIT_CHECK) {
- char *buf;
-
c->device_policy = p;
- u->cgroup_realized_mask &= ~CGROUP_DEVICE;
-
- buf = strjoina("DevicePolicy=", policy);
- unit_write_drop_in_private(u, mode, name, buf);
+ unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES);
+ unit_write_drop_in_private_format(u, mode, name, "DevicePolicy=%s", policy);
}
return 1;
@@ -591,6 +1011,7 @@ int bus_cgroup_set_property(
while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) {
if ((!startswith(path, "/dev/") &&
+ !startswith(path, "/run/systemd/inaccessible/") &&
!startswith(path, "block-") &&
!startswith(path, "char-")) ||
strpbrk(path, WHITESPACE))
@@ -651,7 +1072,7 @@ int bus_cgroup_set_property(
cgroup_context_free_device_allow(c, c->device_allow);
}
- u->cgroup_realized_mask &= ~CGROUP_DEVICE;
+ unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES);
f = open_memstream(&buf, &size);
if (!f)
@@ -661,12 +1082,69 @@ int bus_cgroup_set_property(
LIST_FOREACH(device_allow, a, c->device_allow)
fprintf(f, "DeviceAllow=%s %s%s%s\n", a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(u, mode, name, buf);
}
return 1;
+ } else if (streq(name, "TasksAccounting")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->tasks_accounting = b;
+ unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
+ unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no");
+ }
+
+ return 1;
+
+ } else if (streq(name, "TasksMax")) {
+ uint64_t limit;
+
+ r = sd_bus_message_read(message, "t", &limit);
+ if (r < 0)
+ return r;
+ if (limit <= 0)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
+
+ if (mode != UNIT_CHECK) {
+ c->tasks_max = limit;
+ unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
+
+ if (limit == (uint64_t) -1)
+ unit_write_drop_in_private(u, mode, name, "TasksMax=infinity");
+ else
+ unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit);
+ }
+
+ return 1;
+ } else if (streq(name, "TasksMaxScale")) {
+ uint64_t limit;
+ uint32_t raw;
+
+ r = sd_bus_message_read(message, "u", &raw);
+ if (r < 0)
+ return r;
+
+ limit = system_tasks_max_scale(raw, UINT32_MAX);
+ if (limit <= 0 || limit >= UINT64_MAX)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
+
+ if (mode != UNIT_CHECK) {
+ c->tasks_max = limit;
+ unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
+ unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu32 "%%",
+ (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX)));
+ }
+
+ return 1;
}
if (u->transient && u->load_state == UNIT_STUB) {
diff --git a/src/core/dbus-cgroup.h b/src/core/dbus-cgroup.h
index c2a3910f3d..b2212fe44e 100644
--- a/src/core/dbus-cgroup.h
+++ b/src/core/dbus-cgroup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "cgroup.h"
extern const sd_bus_vtable bus_cgroup_vtable[];
diff --git a/src/core/dbus-device.c b/src/core/dbus-device.c
index cb156fd37c..e1a12224d3 100644
--- a/src/core/dbus-device.c
+++ b/src/core/dbus-device.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,9 +17,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "device.h"
#include "dbus-device.h"
+#include "device.h"
+#include "unit.h"
const sd_bus_vtable bus_device_vtable[] = {
SD_BUS_VTABLE_START(0),
diff --git a/src/core/dbus-device.h b/src/core/dbus-device.h
index 10e945e402..eb1d8c3278 100644
--- a/src/core/dbus-device.h
+++ b/src/core/dbus-device.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index a9f7971cde..eec4500c8c 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -25,27 +23,36 @@
#include <seccomp.h>
#endif
+#include "af-list.h"
+#include "alloc-util.h"
#include "bus-util.h"
-#include "missing.h"
-#include "ioprio.h"
-#include "strv.h"
-#include "fileio.h"
-#include "execute.h"
-#include "capability.h"
+#include "capability-util.h"
+#include "dbus-execute.h"
#include "env-util.h"
-#include "af-list.h"
+#include "execute.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "ioprio.h"
+#include "missing.h"
#include "namespace.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "dbus-execute.h"
-
+#include "process-util.h"
+#include "rlimit-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
+#include "strv.h"
+#include "syslog-util.h"
+#include "user-util.h"
+#include "utf8.h"
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
+
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem);
@@ -81,45 +88,6 @@ static int property_get_environment_files(
return sd_bus_message_close_container(reply);
}
-static int property_get_rlimit(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *error) {
-
- struct rlimit *rl;
- uint64_t u;
- rlim_t x;
-
- assert(bus);
- assert(reply);
- assert(userdata);
-
- rl = *(struct rlimit**) userdata;
- if (rl)
- x = rl->rlim_max;
- else {
- struct rlimit buf = {};
- int z;
-
- z = rlimit_from_string(property);
- assert(z >= 0);
-
- getrlimit(z, &buf);
- x = buf.rlim_max;
- }
-
- /* rlim_t might have different sizes, let's map
- * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on
- * all archs */
- u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
-
- return sd_bus_message_append(reply, "t", u);
-}
-
static int property_get_oom_score_adjust(
sd_bus *bus,
const char *path,
@@ -144,7 +112,7 @@ static int property_get_oom_score_adjust(
n = 0;
if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0)
- safe_atoi(t, &n);
+ safe_atoi32(t, &n);
}
return sd_bus_message_append(reply, "i", n);
@@ -172,7 +140,7 @@ static int property_get_nice(
else {
errno = 0;
n = getpriority(PRIO_PROCESS, 0);
- if (errno != 0)
+ if (errno > 0)
n = 0;
}
@@ -324,12 +292,10 @@ static int property_get_capability_bounding_set(
assert(reply);
assert(c);
- /* We store this negated internally, to match the kernel, but
- * we expose it normalized. */
- return sd_bus_message_append(reply, "t", ~c->capability_bounding_set_drop);
+ return sd_bus_message_append(reply, "t", c->capability_bounding_set);
}
-static int property_get_capabilities(
+static int property_get_ambient_capabilities(
sd_bus *bus,
const char *path,
const char *interface,
@@ -339,22 +305,27 @@ static int property_get_capabilities(
sd_bus_error *error) {
ExecContext *c = userdata;
- _cleanup_cap_free_charp_ char *t = NULL;
- const char *s;
assert(bus);
assert(reply);
assert(c);
- if (c->capabilities)
- s = t = cap_to_text(c->capabilities, NULL);
- else
- s = "";
+ return sd_bus_message_append(reply, "t", c->capability_ambient_set);
+}
+
+static int property_get_empty_string(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
- if (!s)
- return -ENOMEM;
+ assert(bus);
+ assert(reply);
- return sd_bus_message_append(reply, "s", s);
+ return sd_bus_message_append(reply, "s", "");
}
static int property_get_syscall_filter(
@@ -593,28 +564,108 @@ static int property_get_address_families(
return sd_bus_message_close_container(reply);
}
+static int property_get_working_directory(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ const char *wd;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ if (c->working_directory_home)
+ wd = "~";
+ else
+ wd = c->working_directory;
+
+ if (c->working_directory_missing_ok)
+ wd = strjoina("!", wd);
+
+ return sd_bus_message_append(reply, "s", wd);
+}
+
+static int property_get_syslog_level(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ return sd_bus_message_append(reply, "i", LOG_PRI(c->syslog_priority));
+}
+
+static int property_get_syslog_facility(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ return sd_bus_message_append(reply, "i", LOG_FAC(c->syslog_priority));
+}
+
const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("EnvironmentFiles", "a(sb)", property_get_environment_files, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("PassEnvironment", "as", NULL, offsetof(ExecContext, pass_environment), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UMask", "u", bus_property_get_mode, offsetof(ExecContext, umask), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitCPU", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitFSIZE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitDATA", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitSTACK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitCORE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitRSS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitNOFILE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitAS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitNPROC", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitMEMLOCK", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitLOCKS", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitSIGPENDING", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitMSGQUEUE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitNICE", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitRTPRIO", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("LimitRTTIME", "t", property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("WorkingDirectory", "s", NULL, offsetof(ExecContext, working_directory), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCPU", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCPUSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitFSIZE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitFSIZESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitDATA", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitDATASoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSTACK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSTACKSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCORE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitCORESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRSS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRSSSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNOFILE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitAS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitASSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNPROC", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNPROCSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMEMLOCKSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitLOCKS", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitLOCKSSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitSIGPENDINGSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitMSGQUEUESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNICE", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitNICESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTPRIO", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTPRIOSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTTIME", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -635,24 +686,36 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SyslogPriority", "i", bus_property_get_int, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogIdentifier", "s", NULL, offsetof(ExecContext, syslog_identifier), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("Capabilities", "s", property_get_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("User", "s", NULL, offsetof(ExecContext, user), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Group", "s", NULL, offsetof(ExecContext, group), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_dirs), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("ReadOnlyDirectories", "as", NULL, offsetof(ExecContext, read_only_dirs), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("InaccessibleDirectories", "as", NULL, offsetof(ExecContext, inaccessible_dirs), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("ReadOnlyDirectories", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("InaccessibleDirectories", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ReadOnlyPaths", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("InaccessiblePaths", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ProtectKernelTunables", "b", bus_property_get_bool, offsetof(ExecContext, protect_kernel_tunables), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ProtectControlGroups", "b", bus_property_get_bool, offsetof(ExecContext, protect_control_groups), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("PrivateUsers", "b", bus_property_get_bool, offsetof(ExecContext, private_users), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -665,6 +728,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, runtime_directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
@@ -766,7 +831,8 @@ int bus_exec_context_set_transient_property(
UnitSetPropertiesMode mode,
sd_bus_error *error) {
- int r;
+ const char *soft = NULL;
+ int r, ri;
assert(u);
assert(c);
@@ -780,23 +846,17 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
- if (mode != UNIT_CHECK) {
-
- if (isempty(uu)) {
- free(c->user);
- c->user = NULL;
- } else {
- char *t;
+ if (!isempty(uu) && !valid_user_group_name_or_id(uu))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user name: %s", uu);
- t = strdup(uu);
- if (!t)
- return -ENOMEM;
+ if (mode != UNIT_CHECK) {
- free(c->user);
- c->user = t;
- }
+ if (isempty(uu))
+ c->user = mfree(c->user);
+ else if (free_and_strdup(&c->user, uu) < 0)
+ return -ENOMEM;
- unit_write_drop_in_private_format(u, mode, name, "User=%s\n", uu);
+ unit_write_drop_in_private_format(u, mode, name, "User=%s", uu);
}
return 1;
@@ -808,27 +868,70 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
+ if (!isempty(gg) && !valid_user_group_name_or_id(gg))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group name: %s", gg);
+
if (mode != UNIT_CHECK) {
- if (isempty(gg)) {
- free(c->group);
- c->group = NULL;
- } else {
- char *t;
+ if (isempty(gg))
+ c->group = mfree(c->group);
+ else if (free_and_strdup(&c->group, gg) < 0)
+ return -ENOMEM;
- t = strdup(gg);
- if (!t)
- return -ENOMEM;
+ unit_write_drop_in_private_format(u, mode, name, "Group=%s", gg);
+ }
- free(c->group);
- c->group = t;
- }
+ return 1;
+ } else if (streq(name, "SyslogIdentifier")) {
+ const char *id;
+
+ r = sd_bus_message_read(message, "s", &id);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+
+ if (isempty(id))
+ c->syslog_identifier = mfree(c->syslog_identifier);
+ else if (free_and_strdup(&c->syslog_identifier, id) < 0)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "SyslogIdentifier=%s", id);
+ }
+
+ return 1;
+ } else if (streq(name, "SyslogLevel")) {
+ int level;
+
+ r = sd_bus_message_read(message, "i", &level);
+ if (r < 0)
+ return r;
+
+ if (!log_level_is_valid(level))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log level value out of range");
- unit_write_drop_in_private_format(u, mode, name, "Group=%s\n", gg);
+ if (mode != UNIT_CHECK) {
+ c->syslog_priority = (c->syslog_priority & LOG_FACMASK) | level;
+ unit_write_drop_in_private_format(u, mode, name, "SyslogLevel=%i", level);
}
return 1;
+ } else if (streq(name, "SyslogFacility")) {
+ int facility;
+
+ r = sd_bus_message_read(message, "i", &facility);
+ if (r < 0)
+ return r;
+ if (!log_facility_unshifted_is_valid(facility))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log facility value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->syslog_priority = (facility << 3) | LOG_PRI(c->syslog_priority);
+ unit_write_drop_in_private_format(u, mode, name, "SyslogFacility=%i", facility);
+ }
+
+ return 1;
} else if (streq(name, "Nice")) {
int n;
@@ -836,37 +939,72 @@ int bus_exec_context_set_transient_property(
if (r < 0)
return r;
- if (n < PRIO_MIN || n >= PRIO_MAX)
+ if (!nice_is_valid(n))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range");
if (mode != UNIT_CHECK) {
c->nice = n;
- unit_write_drop_in_private_format(u, mode, name, "Nice=%i\n", n);
+ unit_write_drop_in_private_format(u, mode, name, "Nice=%i", n);
}
return 1;
- } else if (streq(name, "TTYPath")) {
- const char *tty;
+ } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) {
+ const char *s;
- r = sd_bus_message_read(message, "s", &tty);
+ r = sd_bus_message_read(message, "s", &s);
if (r < 0)
return r;
- if (!path_is_absolute(tty))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY device not absolute path");
+ if (!path_is_absolute(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name);
if (mode != UNIT_CHECK) {
- char *t;
+ if (streq(name, "TTYPath"))
+ r = free_and_strdup(&c->tty_path, s);
+ else {
+ assert(streq(name, "RootDirectory"));
+ r = free_and_strdup(&c->root_directory, s);
+ }
+ if (r < 0)
+ return r;
- t = strdup(tty);
- if (!t)
- return -ENOMEM;
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, s);
+ }
+
+ return 1;
+
+ } else if (streq(name, "WorkingDirectory")) {
+ const char *s;
+ bool missing_ok;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
- free(c->tty_path);
- c->tty_path = t;
+ if (s[0] == '-') {
+ missing_ok = true;
+ s++;
+ } else
+ missing_ok = false;
- unit_write_drop_in_private_format(u, mode, name, "TTYPath=%s\n", tty);
+ if (!streq(s, "~") && !path_is_absolute(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'");
+
+ if (mode != UNIT_CHECK) {
+ if (streq(s, "~")) {
+ c->working_directory = mfree(c->working_directory);
+ c->working_directory_home = true;
+ } else {
+ r = free_and_strdup(&c->working_directory, s);
+ if (r < 0)
+ return r;
+
+ c->working_directory_home = false;
+ }
+
+ c->working_directory_missing_ok = missing_ok;
+ unit_write_drop_in_private_format(u, mode, name, "WorkingDirectory=%s%s", missing_ok ? "-" : "", s);
}
return 1;
@@ -886,7 +1024,7 @@ int bus_exec_context_set_transient_property(
if (mode != UNIT_CHECK) {
c->std_input = p;
- unit_write_drop_in_private_format(u, mode, name, "StandardInput=%s\n", exec_input_to_string(p));
+ unit_write_drop_in_private_format(u, mode, name, "StandardInput=%s", exec_input_to_string(p));
}
return 1;
@@ -907,7 +1045,7 @@ int bus_exec_context_set_transient_property(
if (mode != UNIT_CHECK) {
c->std_output = p;
- unit_write_drop_in_private_format(u, mode, name, "StandardOutput=%s\n", exec_output_to_string(p));
+ unit_write_drop_in_private_format(u, mode, name, "StandardOutput=%s", exec_output_to_string(p));
}
return 1;
@@ -927,7 +1065,112 @@ int bus_exec_context_set_transient_property(
if (mode != UNIT_CHECK) {
c->std_error = p;
- unit_write_drop_in_private_format(u, mode, name, "StandardError=%s\n", exec_output_to_string(p));
+ unit_write_drop_in_private_format(u, mode, name, "StandardError=%s", exec_output_to_string(p));
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name,
+ "IgnoreSIGPIPE", "TTYVHangup", "TTYReset",
+ "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers",
+ "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute",
+ "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables",
+ "ProtectControlGroups")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (streq(name, "IgnoreSIGPIPE"))
+ c->ignore_sigpipe = b;
+ else if (streq(name, "TTYVHangup"))
+ c->tty_vhangup = b;
+ else if (streq(name, "TTYReset"))
+ c->tty_reset = b;
+ else if (streq(name, "PrivateTmp"))
+ c->private_tmp = b;
+ else if (streq(name, "PrivateDevices"))
+ c->private_devices = b;
+ else if (streq(name, "PrivateNetwork"))
+ c->private_network = b;
+ else if (streq(name, "PrivateUsers"))
+ c->private_users = b;
+ else if (streq(name, "NoNewPrivileges"))
+ c->no_new_privileges = b;
+ else if (streq(name, "SyslogLevelPrefix"))
+ c->syslog_level_prefix = b;
+ else if (streq(name, "MemoryDenyWriteExecute"))
+ c->memory_deny_write_execute = b;
+ else if (streq(name, "RestrictRealtime"))
+ c->restrict_realtime = b;
+ else if (streq(name, "DynamicUser"))
+ c->dynamic_user = b;
+ else if (streq(name, "RemoveIPC"))
+ c->remove_ipc = b;
+ else if (streq(name, "ProtectKernelTunables"))
+ c->protect_kernel_tunables = b;
+ else if (streq(name, "ProtectControlGroups"))
+ c->protect_control_groups = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "UtmpIdentifier")) {
+ const char *id;
+
+ r = sd_bus_message_read(message, "s", &id);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (isempty(id))
+ c->utmp_id = mfree(c->utmp_id);
+ else if (free_and_strdup(&c->utmp_id, id) < 0)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s", strempty(id));
+ }
+
+ return 1;
+
+ } else if (streq(name, "UtmpMode")) {
+ const char *s;
+ ExecUtmpMode m;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ m = exec_utmp_mode_from_string(s);
+ if (m < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode");
+
+ if (mode != UNIT_CHECK) {
+ c->utmp_mode = m;
+
+ unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s", exec_utmp_mode_to_string(m));
+ }
+
+ return 1;
+
+ } else if (streq(name, "PAMName")) {
+ const char *n;
+
+ r = sd_bus_message_read(message, "s", &n);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (isempty(n))
+ c->pam_name = mfree(c->pam_name);
+ else if (free_and_strdup(&c->pam_name, n) < 0)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "PAMName=%s", strempty(n));
}
return 1;
@@ -947,23 +1190,338 @@ int bus_exec_context_set_transient_property(
_cleanup_free_ char *joined = NULL;
char **e;
- e = strv_env_merge(2, c->environment, l);
- if (!e)
- return -ENOMEM;
+ if (strv_length(l) == 0) {
+ c->environment = strv_free(c->environment);
+ unit_write_drop_in_private_format(u, mode, name, "Environment=");
+ } else {
+ e = strv_env_merge(2, c->environment, l);
+ if (!e)
+ return -ENOMEM;
+
+ strv_free(c->environment);
+ c->environment = e;
+
+ joined = strv_join_quoted(c->environment);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "Environment=%s", joined);
+ }
+ }
+
+ return 1;
+
+ } else if (streq(name, "TimerSlackNSec")) {
+
+ nsec_t n;
+
+ r = sd_bus_message_read(message, "t", &n);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ c->timer_slack_nsec = n;
+ unit_write_drop_in_private_format(u, mode, name, "TimerSlackNSec=" NSEC_FMT, n);
+ }
+
+ return 1;
+
+ } else if (streq(name, "OOMScoreAdjust")) {
+ int oa;
+
+ r = sd_bus_message_read(message, "i", &oa);
+ if (r < 0)
+ return r;
+
+ if (!oom_score_adjust_is_valid(oa))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "OOM score adjust value out of range");
+
+ if (mode != UNIT_CHECK) {
+ c->oom_score_adjust = oa;
+ c->oom_score_adjust_set = true;
+ unit_write_drop_in_private_format(u, mode, name, "OOMScoreAdjust=%i", oa);
+ }
+
+ return 1;
+
+ } else if (streq(name, "EnvironmentFiles")) {
+
+ _cleanup_free_ char *joined = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char **l = NULL;
+ size_t size = 0;
+ char **i;
- strv_free(c->environment);
- c->environment = e;
+ r = sd_bus_message_enter_container(message, 'a', "(sb)");
+ if (r < 0)
+ return r;
+
+ f = open_memstream(&joined, &size);
+ if (!f)
+ return -ENOMEM;
+
+ STRV_FOREACH(i, c->environment_files)
+ fprintf(f, "EnvironmentFile=%s", *i);
+
+ while ((r = sd_bus_message_enter_container(message, 'r', "sb")) > 0) {
+ const char *path;
+ int b;
+
+ r = sd_bus_message_read(message, "sb", &path, &b);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!isempty(path) && !path_is_absolute(path))
+ return sd_bus_error_set_errnof(error, EINVAL, "Path %s is not absolute.", path);
+
+ if (mode != UNIT_CHECK) {
+ char *buf = NULL;
+
+ buf = strjoin(b ? "-" : "", path, NULL);
+ if (!buf)
+ return -ENOMEM;
+
+ fprintf(f, "EnvironmentFile=%s", buf);
+
+ r = strv_consume(&l, buf);
+ if (r < 0)
+ return r;
+ }
+ }
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
- joined = strv_join_quoted(c->environment);
- if (!joined)
+ if (mode != UNIT_CHECK) {
+ if (strv_isempty(l)) {
+ c->environment_files = strv_free(c->environment_files);
+ unit_write_drop_in_private(u, mode, name, "EnvironmentFile=");
+ } else {
+ r = strv_extend_strv(&c->environment_files, l, true);
+ if (r < 0)
+ return r;
+
+ unit_write_drop_in_private(u, mode, name, joined);
+ }
+ }
+
+ return 1;
+
+ } else if (streq(name, "PassEnvironment")) {
+
+ _cleanup_strv_free_ char **l = NULL;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ if (!strv_env_name_is_valid(l))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PassEnvironment block.");
+
+ if (mode != UNIT_CHECK) {
+ if (strv_isempty(l)) {
+ c->pass_environment = strv_free(c->pass_environment);
+ unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=");
+ } else {
+ _cleanup_free_ char *joined = NULL;
+
+ r = strv_extend_strv(&c->pass_environment, l, true);
+ if (r < 0)
+ return r;
+
+ joined = strv_join_quoted(c->pass_environment);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "PassEnvironment=%s", joined);
+ }
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories",
+ "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) {
+ _cleanup_strv_free_ char **l = NULL;
+ char ***dirs;
+ char **p;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, l) {
+ int offset;
+ if (!utf8_is_valid(*p))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
+
+ offset = **p == '-';
+ if (!path_is_absolute(*p + offset))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *joined = NULL;
+
+ if (STR_IN_SET(name, "ReadWriteDirectories", "ReadWritePaths"))
+ dirs = &c->read_write_paths;
+ else if (STR_IN_SET(name, "ReadOnlyDirectories", "ReadOnlyPaths"))
+ dirs = &c->read_only_paths;
+ else /* "InaccessiblePaths" */
+ dirs = &c->inaccessible_paths;
+
+ if (strv_length(l) == 0) {
+ *dirs = strv_free(*dirs);
+ unit_write_drop_in_private_format(u, mode, name, "%s=", name);
+ } else {
+ r = strv_extend_strv(dirs, l, true);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ joined = strv_join_quoted(*dirs);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, joined);
+ }
+
+ }
+
+ return 1;
+
+ } else if (streq(name, "ProtectSystem")) {
+ const char *s;
+ ProtectSystem ps;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(s);
+ if (r > 0)
+ ps = PROTECT_SYSTEM_YES;
+ else if (r == 0)
+ ps = PROTECT_SYSTEM_NO;
+ else {
+ ps = protect_system_from_string(s);
+ if (ps < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect system value");
+ }
+
+ if (mode != UNIT_CHECK) {
+ c->protect_system = ps;
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, s);
+ }
+
+ return 1;
+
+ } else if (streq(name, "ProtectHome")) {
+ const char *s;
+ ProtectHome ph;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ r = parse_boolean(s);
+ if (r > 0)
+ ph = PROTECT_HOME_YES;
+ else if (r == 0)
+ ph = PROTECT_HOME_NO;
+ else {
+ ph = protect_home_from_string(s);
+ if (ph < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect home value");
+ }
+
+ if (mode != UNIT_CHECK) {
+ c->protect_home = ph;
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, s);
+ }
+
+ return 1;
+
+ } else if (streq(name, "RuntimeDirectory")) {
+ _cleanup_strv_free_ char **l = NULL;
+ char **p;
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(p, l) {
+ if (!filename_is_valid(*p))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Runtime directory is not valid %s", *p);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *joined = NULL;
+
+ if (strv_isempty(l)) {
+ c->runtime_directory = strv_free(c->runtime_directory);
+ unit_write_drop_in_private_format(u, mode, name, "%s=", name);
+ } else {
+ r = strv_extend_strv(&c->runtime_directory, l, true);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ joined = strv_join_quoted(c->runtime_directory);
+ if (!joined)
+ return -ENOMEM;
+
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, joined);
+ }
+ }
+
+ return 1;
+
+ } else if (streq(name, "SELinuxContext")) {
+ const char *s;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ if (isempty(s))
+ c->selinux_context = mfree(c->selinux_context);
+ else if (free_and_strdup(&c->selinux_context, s) < 0)
return -ENOMEM;
- unit_write_drop_in_private_format(u, mode, name, "Environment=%s\n", joined);
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, strempty(s));
}
return 1;
- } else if (rlimit_from_string(name) >= 0) {
+ }
+
+ ri = rlimit_from_string(name);
+ if (ri < 0) {
+ soft = endswith(name, "Soft");
+ if (soft) {
+ const char *n;
+
+ n = strndupa(name, soft - name);
+ ri = rlimit_from_string(n);
+ if (ri >= 0)
+ name = n;
+
+ }
+ }
+
+ if (ri >= 0) {
uint64_t rl;
rlim_t x;
@@ -981,22 +1539,36 @@ int bus_exec_context_set_transient_property(
}
if (mode != UNIT_CHECK) {
- int z;
-
- z = rlimit_from_string(name);
-
- if (!c->rlimit[z]) {
- c->rlimit[z] = new(struct rlimit, 1);
- if (!c->rlimit[z])
+ _cleanup_free_ char *f = NULL;
+ struct rlimit nl;
+
+ if (c->rlimit[ri]) {
+ nl = *c->rlimit[ri];
+
+ if (soft)
+ nl.rlim_cur = x;
+ else
+ nl.rlim_max = x;
+ } else
+ /* When the resource limit is not initialized yet, then assign the value to both fields */
+ nl = (struct rlimit) {
+ .rlim_cur = x,
+ .rlim_max = x,
+ };
+
+ r = rlimit_format(&nl, &f);
+ if (r < 0)
+ return r;
+
+ if (c->rlimit[ri])
+ *c->rlimit[ri] = nl;
+ else {
+ c->rlimit[ri] = newdup(struct rlimit, &nl, 1);
+ if (!c->rlimit[ri])
return -ENOMEM;
}
- c->rlimit[z]->rlim_cur = c->rlimit[z]->rlim_max = x;
-
- if (x == RLIM_INFINITY)
- unit_write_drop_in_private_format(u, mode, name, "%s=infinity\n", name);
- else
- unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64 "\n", name, rl);
+ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, f);
}
return 1;
diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h
index e4c2d5ddf6..d0aa8e1dd5 100644
--- a/src/core/dbus-execute.h
+++ b/src/core/dbus-execute.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "execute.h"
#define BUS_EXEC_STATUS_VTABLE(prefix, offset, flags) \
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index cd6b909426..ccf7453d47 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,12 +17,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "log.h"
#include "sd-bus.h"
-#include "selinux-access.h"
-#include "job.h"
+
+#include "alloc-util.h"
#include "dbus-job.h"
#include "dbus.h"
+#include "job.h"
+#include "log.h"
+#include "selinux-access.h"
+#include "string-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, job_type, JobType);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_state, job_state, JobState);
@@ -74,7 +75,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
}
- job_finish_and_invalidate(j, JOB_CANCELED, true);
+ job_finish_and_invalidate(j, JOB_CANCELED, true, false);
return sd_bus_reply_method_return(message, NULL);
}
@@ -90,7 +91,7 @@ const sd_bus_vtable bus_job_vtable[] = {
};
static int send_new_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Job *j = userdata;
int r;
@@ -150,7 +151,7 @@ void bus_job_send_change_signal(Job *j) {
}
static int send_removed_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Job *j = userdata;
int r;
diff --git a/src/core/dbus-job.h b/src/core/dbus-job.h
index fb5f1b513e..024d06719e 100644
--- a/src/core/dbus-job.h
+++ b/src/core/dbus-job.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "job.h"
extern const sd_bus_vtable bus_job_vtable[];
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index 3b8116281c..8c65be65fa 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,11 +17,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "signal-util.h"
#include "bus-util.h"
-
-#include "kill.h"
#include "dbus-kill.h"
+#include "kill.h"
+#include "signal-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode);
@@ -61,12 +58,12 @@ int bus_kill_context_set_transient_property(
k = kill_mode_from_string(m);
if (k < 0)
- return -EINVAL;
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m);
if (mode != UNIT_CHECK) {
c->kill_mode = k;
- unit_write_drop_in_private_format(u, mode, name, "KillMode=%s\n", kill_mode_to_string(k));
+ unit_write_drop_in_private_format(u, mode, name, "KillMode=%s", kill_mode_to_string(k));
}
return 1;
@@ -78,13 +75,13 @@ int bus_kill_context_set_transient_property(
if (r < 0)
return r;
- if (sig <= 0 || sig >= _NSIG)
+ if (!SIGNAL_VALID(sig))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig);
if (mode != UNIT_CHECK) {
c->kill_signal = sig;
- unit_write_drop_in_private_format(u, mode, name, "KillSignal=%s\n", signal_to_string(sig));
+ unit_write_drop_in_private_format(u, mode, name, "KillSignal=%s", signal_to_string(sig));
}
return 1;
@@ -99,7 +96,7 @@ int bus_kill_context_set_transient_property(
if (mode != UNIT_CHECK) {
c->send_sighup = b;
- unit_write_drop_in_private_format(u, mode, name, "SendSIGHUP=%s\n", yes_no(b));
+ unit_write_drop_in_private_format(u, mode, name, "SendSIGHUP=%s", yes_no(b));
}
return 1;
@@ -114,7 +111,7 @@ int bus_kill_context_set_transient_property(
if (mode != UNIT_CHECK) {
c->send_sigkill = b;
- unit_write_drop_in_private_format(u, mode, name, "SendSIGKILL=%s\n", yes_no(b));
+ unit_write_drop_in_private_format(u, mode, name, "SendSIGKILL=%s", yes_no(b));
}
return 1;
diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h
index 7c15f3a90b..b9b18811e3 100644
--- a/src/core/dbus-kill.h
+++ b/src/core/dbus-kill.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,8 +20,9 @@
***/
#include "sd-bus.h"
-#include "unit.h"
+
#include "kill.h"
+#include "unit.h"
extern const sd_bus_vtable bus_kill_vtable[];
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index d8b39bdf5f..12eb55cb7f 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,27 +18,34 @@
***/
#include <errno.h>
+#include <sys/prctl.h>
#include <unistd.h>
-#include "log.h"
-#include "strv.h"
+#include "alloc-util.h"
+#include "architecture.h"
#include "build.h"
-#include "install.h"
-#include "selinux-access.h"
-#include "watchdog.h"
+#include "bus-common-errors.h"
#include "clock-util.h"
-#include "path-util.h"
-#include "virt.h"
-#include "architecture.h"
-#include "env-util.h"
-#include "dbus.h"
+#include "dbus-execute.h"
#include "dbus-job.h"
#include "dbus-manager.h"
#include "dbus-unit.h"
-#include "dbus-snapshot.h"
-#include "dbus-execute.h"
-#include "bus-common-errors.h"
+#include "dbus.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "install.h"
+#include "log.h"
+#include "path-util.h"
+#include "selinux-access.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "syslog-util.h"
+#include "user-util.h"
+#include "virt.h"
+#include "watchdog.h"
static int property_get_version(
sd_bus *bus,
@@ -81,14 +86,21 @@ static int property_get_virtualization(
void *userdata,
sd_bus_error *error) {
- const char *id = NULL;
+ int v;
assert(bus);
assert(reply);
- detect_virtualization(&id);
+ v = detect_virtualization();
- return sd_bus_message_append(reply, "s", id);
+ /* Make sure to return the empty string when we detect no virtualization, as that is the API.
+ *
+ * https://github.com/systemd/systemd/issues/1423
+ */
+
+ return sd_bus_message_append(
+ reply, "s",
+ v == VIRTUALIZATION_NONE ? "" : virtualization_to_string(v));
}
static int property_get_architecture(
@@ -115,8 +127,7 @@ static int property_get_tainted(
void *userdata,
sd_bus_error *error) {
- char buf[sizeof("split-usr:mtab-not-symlink:cgroups-missing:local-hwclock:")] = "", *e = buf;
- _cleanup_free_ char *p = NULL;
+ char buf[sizeof("split-usr:cgroups-missing:local-hwclock:")] = "", *e = buf;
Manager *m = userdata;
assert(bus);
@@ -126,13 +137,10 @@ static int property_get_tainted(
if (m->taint_usr)
e = stpcpy(e, "split-usr:");
- if (readlink_malloc("/etc/mtab", &p) < 0)
- e = stpcpy(e, "mtab-not-symlink:");
-
if (access("/proc/cgroups", F_OK) < 0)
e = stpcpy(e, "cgroups-missing:");
- if (clock_is_localtime() > 0)
+ if (clock_is_localtime(NULL) > 0)
e = stpcpy(e, "local-hwclock:");
/* remove the last ':' */
@@ -220,7 +228,10 @@ static int property_set_log_level(
if (r < 0)
return r;
- return log_set_max_level_from_string(t);
+ r = log_set_max_level_from_string(t);
+ if (r == 0)
+ log_info("Setting log level to %s.", t);
+ return r;
}
static int property_get_n_names(
@@ -343,6 +354,21 @@ static int property_set_runtime_watchdog(
return watchdog_set_timeout(t);
}
+static int property_get_timer_slack_nsec(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ assert(bus);
+ assert(reply);
+
+ return sd_bus_message_append(reply, "t", (uint64_t) prctl(PR_GET_TIMERSLACK));
+}
+
static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
@@ -360,7 +386,7 @@ static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error
return r;
if (isempty(name)) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
@@ -412,7 +438,7 @@ static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid PID " PID_FMT, pid);
if (pid == 0) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
if (r < 0)
@@ -438,6 +464,64 @@ static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_reply_method_return(message, "o", path);
}
+static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *path = NULL;
+ Manager *m = userdata;
+ sd_id128_t id;
+ const void *a;
+ Unit *u;
+ size_t sz;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ /* Anyone can call this method */
+
+ r = sd_bus_message_read_array(message, 'y', &a, &sz);
+ if (r < 0)
+ return r;
+ if (sz == 0)
+ id = SD_ID128_NULL;
+ else if (sz == 16)
+ memcpy(&id, a, sz);
+ else
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid invocation ID");
+
+ if (sd_id128_is_null(id)) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ pid_t pid;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_pid(creds, &pid);
+ if (r < 0)
+ return r;
+
+ u = manager_get_unit_by_pid(m, pid);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client " PID_FMT " not member of any unit.", pid);
+ } else {
+ u = hashmap_get(m->units_by_invocation_id, &id);
+ if (!u)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", SD_ID128_FORMAT_VAL(id));
+ }
+
+ r = mac_selinux_unit_access_check(u, message, "status", error);
+ if (r < 0)
+ return r;
+
+ /* So here's a special trick: the bus path we return actually references the unit by its invocation ID instead
+ * of the unit name. This means it stays valid only as long as the invocation ID stays the same. */
+ path = unit_dbus_path_invocation_id(u);
+ if (!path)
+ return -ENOMEM;
+
+ return sd_bus_reply_method_return(message, "o", path);
+}
+
static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
Manager *m = userdata;
@@ -455,7 +539,7 @@ static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_erro
return r;
if (isempty(name)) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
@@ -606,13 +690,163 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- u = manager_get_unit(m, name);
- if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
return bus_unit_method_set_properties(message, u, error);
}
+static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_ref(message, u, error);
+}
+
+static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_unref(message, u, error);
+}
+
+static int reply_unit_info(sd_bus_message *reply, Unit *u) {
+ _cleanup_free_ char *unit_path = NULL, *job_path = NULL;
+ Unit *following;
+
+ following = unit_following(u);
+
+ unit_path = unit_dbus_path(u);
+ if (!unit_path)
+ return -ENOMEM;
+
+ if (u->job) {
+ job_path = job_dbus_path(u->job);
+ if (!job_path)
+ return -ENOMEM;
+ }
+
+ return sd_bus_message_append(
+ reply, "(ssssssouso)",
+ u->id,
+ unit_description(u),
+ unit_load_state_to_string(u->load_state),
+ unit_active_state_to_string(unit_active_state(u)),
+ unit_sub_state_to_string(u),
+ following ? following->id : "",
+ unit_path,
+ u->job ? u->job->id : 0,
+ u->job ? job_type_to_string(u->job->type) : "",
+ job_path ? job_path : "/");
+}
+
+static int method_list_units_by_names(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ Manager *m = userdata;
+ int r;
+ char **unit;
+ _cleanup_strv_free_ char **units = NULL;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read_strv(message, &units);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(ssssssouso)");
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(unit, units) {
+ Unit *u;
+
+ if (!unit_name_is_valid(*unit, UNIT_NAME_ANY))
+ continue;
+
+ r = manager_load_unit(m, *unit, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = reply_unit_info(reply, u);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
+static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ Unit *u;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read(message, "s", &name);
+ if (r < 0)
+ return r;
+
+ r = manager_load_unit(m, name, NULL, error, &u);
+ if (r < 0)
+ return r;
+
+ r = bus_unit_check_load_state(u, error);
+ if (r < 0)
+ return r;
+
+ return bus_unit_method_get_processes(message, u, error);
+}
+
static int transient_unit_from_message(
Manager *m,
sd_bus_message *message,
@@ -620,6 +854,7 @@ static int transient_unit_from_message(
Unit **unit,
sd_bus_error *error) {
+ UnitType t;
Unit *u;
int r;
@@ -627,12 +862,18 @@ static int transient_unit_from_message(
assert(message);
assert(name);
+ t = unit_name_to_type(name);
+ if (t < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name or type.");
+
+ if (!unit_vtable[t]->can_transient)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
+
r = manager_load_unit(m, name, NULL, error, &u);
if (r < 0)
return r;
- if (u->load_state != UNIT_NOT_FOUND ||
- set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0)
+ if (!unit_is_pristine(u))
return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name);
/* OK, the unit failed to load and is unreferenced, now let's
@@ -646,6 +887,17 @@ static int transient_unit_from_message(
if (r < 0)
return r;
+ /* If the client asked for it, automatically add a reference to this unit. */
+ if (u->bus_track_add) {
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch sender: %m");
+ }
+
+ /* Now load the missing bits of the unit we just created */
+ unit_add_to_load_queue(u);
+ manager_dispatch_load_queue(m);
+
*unit = u;
return 0;
@@ -656,8 +908,6 @@ static int transient_aux_units_from_message(
sd_bus_message *message,
sd_bus_error *error) {
- Unit *u;
- char *name = NULL;
int r;
assert(m);
@@ -668,20 +918,17 @@ static int transient_aux_units_from_message(
return r;
while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) {
+ const char *name = NULL;
+ Unit *u;
+
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
r = transient_unit_from_message(m, message, name, &u, error);
- if (r < 0 && r != -EEXIST)
+ if (r < 0)
return r;
- if (r != -EEXIST) {
- r = unit_load(u);
- if (r < 0)
- return r;
- }
-
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
@@ -700,7 +947,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
const char *name, *smode;
Manager *m = userdata;
JobMode mode;
- UnitType t;
Unit *u;
int r;
@@ -715,13 +961,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- t = unit_name_to_type(name);
- if (t < 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit type.");
-
- if (!unit_vtable[t]->can_transient)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
-
mode = job_mode_from_string(smode);
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s is invalid.", smode);
@@ -740,13 +979,6 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
- /* And load this stub fully */
- r = unit_load(u);
- if (r < 0)
- return r;
-
- manager_dispatch_load_queue(m);
-
/* Finally, start it */
return bus_unit_queue_job(message, u, JOB_START, mode, false, error);
}
@@ -846,8 +1078,8 @@ static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_e
return sd_bus_reply_method_return(message, NULL);
}
-static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
const char *k;
Iterator i;
@@ -872,42 +1104,20 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e
return r;
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
- _cleanup_free_ char *unit_path = NULL, *job_path = NULL;
- Unit *following;
-
if (k != u->id)
continue;
- following = unit_following(u);
-
if (!strv_isempty(states) &&
!strv_contains(states, unit_load_state_to_string(u->load_state)) &&
!strv_contains(states, unit_active_state_to_string(unit_active_state(u))) &&
!strv_contains(states, unit_sub_state_to_string(u)))
continue;
- unit_path = unit_dbus_path(u);
- if (!unit_path)
- return -ENOMEM;
-
- if (u->job) {
- job_path = job_dbus_path(u->job);
- if (!job_path)
- return -ENOMEM;
- }
+ if (!strv_isempty(patterns) &&
+ !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
+ continue;
- r = sd_bus_message_append(
- reply, "(ssssssouso)",
- u->id,
- unit_description(u),
- unit_load_state_to_string(u->load_state),
- unit_active_state_to_string(unit_active_state(u)),
- unit_sub_state_to_string(u),
- following ? following->id : "",
- unit_path,
- u->job ? u->job->id : 0,
- u->job ? job_type_to_string(u->job->type) : "",
- job_path ? job_path : "/");
+ r = reply_unit_info(reply, u);
if (r < 0)
return r;
}
@@ -920,7 +1130,7 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e
}
static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return list_units_filtered(message, userdata, error, NULL);
+ return list_units_filtered(message, userdata, error, NULL, NULL);
}
static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -931,11 +1141,27 @@ static int method_list_units_filtered(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- return list_units_filtered(message, userdata, error, states);
+ return list_units_filtered(message, userdata, error, states, NULL);
+}
+
+static int method_list_units_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **states = NULL;
+ _cleanup_strv_free_ char **patterns = NULL;
+ int r;
+
+ r = sd_bus_message_read_strv(message, &states);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &patterns);
+ if (r < 0)
+ return r;
+
+ return list_units_filtered(message, userdata, error, states, patterns);
}
static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
Iterator i;
Job *j;
@@ -1069,74 +1295,15 @@ static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *er
manager_dump_units(m, f, NULL);
manager_dump_jobs(m, f, NULL);
- fflush(f);
-
- if (ferror(f))
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "s", dump);
-}
-
-static int method_create_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *path = NULL;
- Manager *m = userdata;
- const char *name;
- int cleanup;
- Snapshot *s = NULL;
- int r;
-
- assert(message);
- assert(m);
-
- r = mac_selinux_access_check(message, "start", error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(message, "sb", &name, &cleanup);
+ r = fflush_and_check(f);
if (r < 0)
return r;
- if (isempty(name))
- name = NULL;
-
- r = bus_verify_manage_units_async(m, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
- r = snapshot_create(m, name, cleanup, error, &s);
- if (r < 0)
- return r;
-
- path = unit_dbus_path(UNIT(s));
- if (!path)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "o", path);
+ return sd_bus_reply_method_return(message, "s", dump);
}
-static int method_remove_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- const char *name;
- Unit *u;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
-
- u = manager_get_unit(m, name);
- if (!u)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s does not exist.", name);
-
- if (u->type != UNIT_SNAPSHOT)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not a snapshot", name);
-
- return bus_snapshot_method_remove(message, u, error);
+static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed.");
}
static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1206,8 +1373,10 @@ static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *er
if (r < 0)
return r;
- if (m->running_as == MANAGER_SYSTEM)
- return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers.");
+ /* Exit() (in contrast to SetExitCode()) is actually allowed even if
+ * we are running on the host. It will fall back on reboot() in
+ * systemd-shutdown if it cannot do the exit() because it isn't a
+ * container. */
m->exit_code = MANAGER_EXIT;
@@ -1225,7 +1394,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
m->exit_code = MANAGER_REBOOT;
@@ -1244,7 +1413,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
m->exit_code = MANAGER_POWEROFF;
@@ -1263,7 +1432,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers.");
m->exit_code = MANAGER_HALT;
@@ -1282,7 +1451,7 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers.");
m->exit_code = MANAGER_KEXEC;
@@ -1303,7 +1472,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
if (r < 0)
return r;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager.");
r = sd_bus_message_read(message, "ss", &root, &init);
@@ -1455,8 +1624,87 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd
return sd_bus_reply_method_return(message, NULL);
}
-static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ uint8_t code;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = mac_selinux_access_check(message, "exit", error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_basic(message, 'y', &code);
+ if (r < 0)
+ return r;
+
+ if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers.");
+
+ m->return_value = code;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_lookup_dynamic_user_by_name(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ const char *name;
+ uid_t uid;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read_basic(message, 's', &name);
+ if (r < 0)
+ return r;
+
+ if (!MANAGER_IS_SYSTEM(m))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance.");
+ if (!valid_user_group_name(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name invalid: %s", name);
+
+ r = dynamic_user_lookup_name(m, name, &uid);
+ if (r == -ESRCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER, "Dynamic user %s does not exist.", name);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, "u", (uint32_t) uid);
+}
+
+static int method_lookup_dynamic_user_by_uid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_free_ char *name = NULL;
+ Manager *m = userdata;
+ uid_t uid;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ assert_cc(sizeof(uid) == sizeof(uint32_t));
+ r = sd_bus_message_read_basic(message, 'u', &uid);
+ if (r < 0)
+ return r;
+
+ if (!MANAGER_IS_SYSTEM(m))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance.");
+ if (!uid_is_valid(uid))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User ID invalid: " UID_FMT, uid);
+
+ r = dynamic_user_lookup_uid(m, uid, &name);
+ if (r == -ESRCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER, "Dynamic user ID " UID_FMT " does not exist.", uid);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, "s", name);
+}
+
+static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
UnitFileList *item;
Hashmap *h;
@@ -1480,7 +1728,7 @@ static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bu
if (!h)
return -ENOMEM;
- r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h);
+ r = unit_file_get_list(m->unit_file_scope, NULL, h, states, patterns);
if (r < 0)
goto fail;
@@ -1508,11 +1756,30 @@ fail:
return r;
}
+static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return list_unit_files_by_patterns(message, userdata, error, NULL, NULL);
+}
+
+static int method_list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **states = NULL;
+ _cleanup_strv_free_ char **patterns = NULL;
+ int r;
+
+ r = sd_bus_message_read_strv(message, &states);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &patterns);
+ if (r < 0)
+ return r;
+
+ return list_unit_files_by_patterns(message, userdata, error, states, patterns);
+}
+
static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
UnitFileState state;
- UnitFileScope scope;
int r;
assert(message);
@@ -1528,11 +1795,9 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- state = unit_file_get_state(scope, NULL, name);
- if (state < 0)
- return state;
+ r = unit_file_get_state(m->unit_file_scope, NULL, name, &state);
+ if (r < 0)
+ return r;
return sd_bus_reply_method_return(message, "s", unit_file_state_to_string(state));
}
@@ -1540,7 +1805,6 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s
static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char *default_target = NULL;
Manager *m = userdata;
- UnitFileScope scope;
int r;
assert(message);
@@ -1552,9 +1816,7 @@ static int method_get_default_target(sd_bus_message *message, void *userdata, sd
if (r < 0)
return r;
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_get_default(scope, NULL, &default_target);
+ r = unit_file_get_default(m->unit_file_scope, NULL, &default_target);
if (r < 0)
return r;
@@ -1562,7 +1824,7 @@ static int method_get_default_target(sd_bus_message *message, void *userdata, sd
}
static int send_unit_files_changed(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *message = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
int r;
assert(bus);
@@ -1581,11 +1843,11 @@ static int reply_unit_file_changes_and_free(
UnitFileChange *changes,
unsigned n_changes) {
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
unsigned i;
int r;
- if (n_changes > 0) {
+ if (unit_file_changes_have_modification(changes, n_changes)) {
r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL);
if (r < 0)
log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m");
@@ -1605,20 +1867,25 @@ static int reply_unit_file_changes_and_free(
if (r < 0)
goto fail;
- for (i = 0; i < n_changes; i++) {
- r = sd_bus_message_append(
- reply, "(sss)",
- unit_file_change_type_to_string(changes[i].type),
- changes[i].path,
- changes[i].source);
- if (r < 0)
- goto fail;
- }
+ for (i = 0; i < n_changes; i++)
+ if (changes[i].type >= 0) {
+ const char *change = unit_file_change_type_to_string(changes[i].type);
+ assert(change != NULL);
+
+ r = sd_bus_message_append(
+ reply, "(sss)",
+ change,
+ changes[i].path,
+ changes[i].source);
+ if (r < 0)
+ goto fail;
+ }
r = sd_bus_message_close_container(reply);
if (r < 0)
goto fail;
+ unit_file_changes_free(changes, n_changes);
return sd_bus_send(NULL, reply, NULL);
fail:
@@ -1626,10 +1893,61 @@ fail:
return r;
}
+/* Create an error reply, using the error information from changes[]
+ * if possible, and fall back to generating an error from error code c.
+ * The error message only describes the first error.
+ *
+ * Coordinate with unit_file_dump_changes() in install.c.
+ */
+static int install_error(
+ sd_bus_error *error,
+ int c,
+ UnitFileChange *changes,
+ unsigned n_changes) {
+ int r;
+ unsigned i;
+ assert(c < 0);
+
+ for (i = 0; i < n_changes; i++)
+ switch(changes[i].type) {
+ case 0 ... INT_MAX:
+ continue;
+ case -EEXIST:
+ if (changes[i].source)
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
+ "File %s already exists and is a symlink to %s.",
+ changes[i].path, changes[i].source);
+ else
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
+ "File %s already exists.",
+ changes[i].path);
+ goto found;
+ case -ERFKILL:
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED,
+ "Unit file %s is masked.", changes[i].path);
+ goto found;
+ case -EADDRNOTAVAIL:
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED,
+ "Unit %s is transient or generated.", changes[i].path);
+ goto found;
+ case -ELOOP:
+ r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED,
+ "Refusing to operate on linked unit file %s", changes[i].path);
+ goto found;
+ default:
+ r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path);
+ goto found;
+ }
+
+ r = c;
+ found:
+ unit_file_changes_free(changes, n_changes);
+ return r;
+}
+
static int method_enable_unit_files_generic(
sd_bus_message *message,
Manager *m,
- const char *verb,
int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes),
bool carries_install_info,
sd_bus_error *error) {
@@ -1637,7 +1955,6 @@ static int method_enable_unit_files_generic(
_cleanup_strv_free_ char **l = NULL;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
- UnitFileScope scope;
int runtime, force, r;
assert(message);
@@ -1651,35 +1968,29 @@ static int method_enable_unit_files_generic(
if (r < 0)
return r;
- r = mac_selinux_unit_access_check_strv(l, message, m, verb, error);
- if (r < 0)
- return r;
-
r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = call(scope, runtime, NULL, l, force, &changes, &n_changes);
+ r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes);
}
static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_enable, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error);
}
static int method_reenable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_reenable, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_reenable, true, error);
}
static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_link, false, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error);
}
static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) {
@@ -1687,11 +1998,11 @@ static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, cons
}
static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "enable", unit_file_preset_without_mode, true, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_preset_without_mode, true, error);
}
static int method_mask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_enable_unit_files_generic(message, userdata, "disable", unit_file_mask, false, error);
+ return method_enable_unit_files_generic(message, userdata, unit_file_mask, false, error);
}
static int method_preset_unit_files_with_mode(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1701,7 +2012,6 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
unsigned n_changes = 0;
Manager *m = userdata;
UnitFilePresetMode mm;
- UnitFileScope scope;
int runtime, force, r;
const char *mode;
@@ -1724,36 +2034,28 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use
return -EINVAL;
}
- r = mac_selinux_unit_access_check_strv(l, message, m, "enable", error);
- if (r < 0)
- return r;
-
r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes);
+ r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, r, changes, n_changes);
}
static int method_disable_unit_files_generic(
sd_bus_message *message,
- Manager *m, const
- char *verb,
+ Manager *m,
int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes),
sd_bus_error *error) {
_cleanup_strv_free_ char **l = NULL;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
- UnitFileScope scope;
int r, runtime;
assert(message);
@@ -1767,38 +2069,58 @@ static int method_disable_unit_files_generic(
if (r < 0)
return r;
- r = mac_selinux_unit_access_check_strv(l, message, m, verb, error);
- if (r < 0)
- return r;
-
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
r = bus_verify_manage_unit_files_async(m, message, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- r = call(scope, runtime, NULL, l, &changes, &n_changes);
+ r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, "disable", unit_file_disable, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_disable, error);
}
static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error);
+ return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error);
+}
+
+static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **l = NULL;
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0;
+ Manager *m = userdata;
+ int r;
+
+ assert(message);
+ assert(m);
+
+ r = sd_bus_message_read_strv(message, &l);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_unit_files_async(m, message, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes);
+ if (r < 0)
+ return install_error(error, r, changes, n_changes);
+
+ return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) {
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
Manager *m = userdata;
- UnitFileScope scope;
const char *name;
int force, r;
@@ -1819,11 +2141,9 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_set_default(scope, NULL, name, force, &changes, &n_changes);
+ r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
@@ -1833,7 +2153,6 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
unsigned n_changes = 0;
Manager *m = userdata;
UnitFilePresetMode mm;
- UnitFileScope scope;
const char *mode;
int force, runtime, r;
@@ -1862,11 +2181,9 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata,
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes);
+ r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
@@ -1876,10 +2193,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
Manager *m = userdata;
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
- UnitFileScope scope;
int runtime, force, r;
- char *target;
- char *type;
+ char *target, *type;
UnitDependency dep;
assert(message);
@@ -1903,15 +2218,9 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd
if (dep < 0)
return -EINVAL;
- r = mac_selinux_unit_access_check_strv(l, message, m, "enable", error);
- if (r < 0)
- return r;
-
- scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
-
- r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
+ r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes);
if (r < 0)
- return r;
+ return install_error(error, r, changes, n_changes);
return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes);
}
@@ -1947,16 +2256,63 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0),
SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.unit_path), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0),
SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0),
SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0),
SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0),
+ SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0),
+ SD_BUS_PROPERTY("DefaultTimerAccuracyUSec", "t", bus_property_get_usec, offsetof(Manager, default_timer_accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
+ SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultMemoryAccounting", "b", bus_property_get_bool, offsetof(Manager, default_memory_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTasksAccounting", "b", bus_property_get_bool, offsetof(Manager, default_tasks_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCPU", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCPUSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CPU]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitFSIZE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitFSIZESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_FSIZE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitDATA", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitDATASoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_DATA]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSTACK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSTACKSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_STACK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCORE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitCORESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_CORE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRSS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRSSSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RSS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNOFILE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNOFILESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NOFILE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitAS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitASSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_AS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNPROC", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNPROCSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NPROC]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMEMLOCK", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMEMLOCKSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MEMLOCK]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitLOCKS", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitLOCKSSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_LOCKS]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSIGPENDING", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitSIGPENDINGSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_SIGPENDING]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMSGQUEUE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitMSGQUEUESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_MSGQUEUE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNICE", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitNICESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_NICE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTPRIO", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTPRIOSoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTTIME", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultLimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultTasksMax", "t", NULL, offsetof(Manager, default_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetUnitByInvocationID", "ay", "o", method_get_unit_by_invocation_id, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("LoadUnit", "s", "o", method_load_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartUnit", "ss", "o", method_start_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartUnitReplace", "sss", "o", method_start_unit_replace, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -1969,19 +2325,24 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListUnitsByNames", "as", "a(ssssssouso)", method_list_units_by_names, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_create_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_remove_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reload", NULL, NULL, method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reexecute", NULL, NULL, method_reexecute, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Exit", NULL, NULL, method_exit, 0),
@@ -1994,6 +2355,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2003,10 +2365,14 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("LookupDynamicUserByName", "s", "u", method_lookup_dynamic_user_by_name, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("LookupDynamicUserByUID", "u", "s", method_lookup_dynamic_user_by_uid, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("UnitNew", "so", 0),
SD_BUS_SIGNAL("UnitRemoved", "so", 0),
@@ -2020,7 +2386,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
};
static int send_finished(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *message = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
usec_t *times = userdata;
int r;
@@ -2068,7 +2434,7 @@ void bus_manager_send_finished(
}
static int send_reloading(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *message = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
int r;
assert(bus);
diff --git a/src/core/dbus-manager.h b/src/core/dbus-manager.h
index 5bdf6e17ab..36a2e9481b 100644
--- a/src/core/dbus-manager.h
+++ b/src/core/dbus-manager.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 24813c6d20..76a7a7ce97 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,13 +17,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "mount.h"
+#include "bus-util.h"
+#include "dbus-cgroup.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
-#include "dbus-cgroup.h"
#include "dbus-mount.h"
-#include "bus-util.h"
+#include "mount.h"
+#include "string-util.h"
+#include "unit.h"
static int property_get_what(
sd_bus *bus,
@@ -117,7 +116,11 @@ const sd_bus_vtable bus_mount_vtable[] = {
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Mount, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Mount, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_COMMAND_VTABLE("ExecMount", offsetof(Mount, exec_command[MOUNT_EXEC_MOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecUnmount", offsetof(Mount, exec_command[MOUNT_EXEC_UNMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecRemount", offsetof(Mount, exec_command[MOUNT_EXEC_REMOUNT]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
@@ -158,6 +161,9 @@ static int bus_mount_set_transient_property(
if (!p)
return -ENOMEM;
+ unit_write_drop_in_format(UNIT(m), mode, name, "[Mount]\n%s=%s\n",
+ name, new_property);
+
free(*property);
*property = p;
}
diff --git a/src/core/dbus-mount.h b/src/core/dbus-mount.h
index f7004d252f..ec16166d36 100644
--- a/src/core/dbus-mount.h
+++ b/src/core/dbus-mount.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_mount_vtable[];
diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c
index 683561999b..1e153e503f 100644
--- a/src/core/dbus-path.c
+++ b/src/core/dbus-path.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,10 +17,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "path.h"
-#include "dbus-path.h"
#include "bus-util.h"
+#include "dbus-path.h"
+#include "path.h"
+#include "string-util.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, path_result, PathResult);
diff --git a/src/core/dbus-path.h b/src/core/dbus-path.h
index 389b0d7f9b..d3c19e0c2b 100644
--- a/src/core/dbus-path.h
+++ b/src/core/dbus-path.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
index f8fb373bf0..1abaf9f658 100644
--- a/src/core/dbus-scope.c
+++ b/src/core/dbus-scope.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,17 +17,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "selinux-access.h"
-#include "unit.h"
-#include "scope.h"
-#include "dbus.h"
-#include "bus-util.h"
-#include "bus-internal.h"
+#include "alloc-util.h"
#include "bus-common-errors.h"
-#include "dbus-unit.h"
+#include "bus-internal.h"
+#include "bus-util.h"
#include "dbus-cgroup.h"
#include "dbus-kill.h"
#include "dbus-scope.h"
+#include "dbus-unit.h"
+#include "dbus.h"
+#include "scope.h"
+#include "selinux-access.h"
+#include "unit.h"
static int bus_scope_abandon(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Scope *s = userdata;
@@ -148,7 +147,7 @@ static int bus_scope_set_transient_property(
if (r < 0)
return r;
- unit_write_drop_in_format(UNIT(s), mode, name, "[Scope]\nTimeoutStopSec="USEC_FMT"us\n", s->timeout_stop_usec);
+ unit_write_drop_in_private_format(UNIT(s), mode, name, "TimeoutStopSec="USEC_FMT"us", s->timeout_stop_usec);
} else {
r = sd_bus_message_skip(message, "t");
if (r < 0)
@@ -204,7 +203,7 @@ int bus_scope_commit_properties(Unit *u) {
}
int bus_scope_send_request_stop(Scope *s) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
int r;
@@ -226,5 +225,5 @@ int bus_scope_send_request_stop(Scope *s) {
if (r < 0)
return r;
- return sd_bus_send_to(UNIT(s)->manager->api_bus, m, /* s->controller */ NULL, NULL);
+ return sd_bus_send_to(UNIT(s)->manager->api_bus, m, s->controller, NULL);
}
diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h
index 33beda47b7..270306f508 100644
--- a/src/core/dbus-scope.h
+++ b/src/core/dbus-scope.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_scope_vtable[];
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index e1f3d56495..3c55e0f7fe 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,15 +17,20 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "strv.h"
-#include "path-util.h"
-#include "unit.h"
-#include "service.h"
+#include "alloc-util.h"
+#include "async.h"
+#include "bus-util.h"
+#include "dbus-cgroup.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
-#include "dbus-cgroup.h"
#include "dbus-service.h"
-#include "bus-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "path-util.h"
+#include "service.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult);
@@ -44,12 +47,9 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
- SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Service, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Service, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Service, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Service, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -58,10 +58,16 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", NULL, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("NFileDescriptorStore", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store), 0),
SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("StatusErrno", "i", NULL, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
@@ -69,6 +75,12 @@ const sd_bus_vtable bus_service_vtable[] = {
BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+
+ /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */
+ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_VTABLE_END
};
@@ -94,7 +106,7 @@ static int bus_service_set_transient_property(
if (mode != UNIT_CHECK) {
s->remain_after_exit = b;
- unit_write_drop_in_private_format(UNIT(s), mode, name, "RemainAfterExit=%s\n", yes_no(b));
+ unit_write_drop_in_private_format(UNIT(s), mode, name, "RemainAfterExit=%s", yes_no(b));
}
return 1;
@@ -113,7 +125,53 @@ static int bus_service_set_transient_property(
if (mode != UNIT_CHECK) {
s->type = k;
- unit_write_drop_in_private_format(UNIT(s), mode, name, "Type=%s\n", service_type_to_string(s->type));
+ unit_write_drop_in_private_format(UNIT(s), mode, name, "Type=%s", service_type_to_string(s->type));
+ }
+
+ return 1;
+ } else if (streq(name, "RuntimeMaxUSec")) {
+ usec_t u;
+
+ r = sd_bus_message_read(message, "t", &u);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ s->runtime_max_usec = u;
+ unit_write_drop_in_private_format(UNIT(s), mode, name, "RuntimeMaxSec=" USEC_FMT "us", u);
+ }
+
+ return 1;
+
+ } else if (STR_IN_SET(name,
+ "StandardInputFileDescriptor",
+ "StandardOutputFileDescriptor",
+ "StandardErrorFileDescriptor")) {
+ int fd;
+
+ r = sd_bus_message_read(message, "h", &fd);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ int copy;
+
+ copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ if (streq(name, "StandardInputFileDescriptor")) {
+ asynchronous_close(s->stdin_fd);
+ s->stdin_fd = copy;
+ } else if (streq(name, "StandardOutputFileDescriptor")) {
+ asynchronous_close(s->stdout_fd);
+ s->stdout_fd = copy;
+ } else {
+ asynchronous_close(s->stderr_fd);
+ s->stderr_fd = copy;
+ }
+
+ s->exec_context.stdio_as_fds = true;
}
return 1;
@@ -209,7 +267,9 @@ static int bus_service_set_transient_property(
a);
}
- fflush(f);
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
unit_write_drop_in_private(UNIT(s), mode, name, buf);
}
diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h
index aab9f7aa26..769a53769e 100644
--- a/src/core/dbus-service.h
+++ b/src/core/dbus-service.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_service_vtable[];
diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c
index 09e78d1f33..e37f50b283 100644
--- a/src/core/dbus-slice.c
+++ b/src/core/dbus-slice.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,10 +17,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "slice.h"
#include "dbus-cgroup.h"
#include "dbus-slice.h"
+#include "slice.h"
+#include "unit.h"
const sd_bus_vtable bus_slice_vtable[] = {
SD_BUS_VTABLE_START(0),
diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h
index eadc3b1a9c..52ceebb135 100644
--- a/src/core/dbus-slice.h
+++ b/src/core/dbus-slice.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_slice_vtable[];
diff --git a/src/core/dbus-snapshot.c b/src/core/dbus-snapshot.c
deleted file mode 100644
index cfe44c9c15..0000000000
--- a/src/core/dbus-snapshot.c
+++ /dev/null
@@ -1,55 +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 "selinux-access.h"
-#include "unit.h"
-#include "dbus.h"
-#include "snapshot.h"
-#include "dbus-snapshot.h"
-
-int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Snapshot *s = userdata;
- int r;
-
- assert(message);
- assert(s);
-
- r = mac_selinux_unit_access_check(UNIT(s), message, "stop", error);
- if (r < 0)
- return r;
-
- r = bus_verify_manage_units_async(UNIT(s)->manager, message, error);
- if (r < 0)
- return r;
- if (r == 0)
- return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
-
- snapshot_remove(s);
-
- return sd_bus_reply_method_return(message, NULL);
-}
-
-const sd_bus_vtable bus_snapshot_vtable[] = {
- SD_BUS_VTABLE_START(0),
- SD_BUS_PROPERTY("Cleanup", "b", bus_property_get_bool, offsetof(Snapshot, cleanup), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_METHOD("Remove", NULL, NULL, bus_snapshot_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_VTABLE_END
-};
diff --git a/src/core/dbus-snapshot.h b/src/core/dbus-snapshot.h
deleted file mode 100644
index 9288f44e15..0000000000
--- a/src/core/dbus-snapshot.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- 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 "sd-bus.h"
-
-extern const sd_bus_vtable bus_snapshot_vtable[];
-
-int bus_snapshot_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 02599a9e55..21adb64e15 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,12 +17,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "socket.h"
-#include "dbus-execute.h"
+#include "alloc-util.h"
+#include "bus-util.h"
#include "dbus-cgroup.h"
+#include "dbus-execute.h"
#include "dbus-socket.h"
-#include "bus-util.h"
+#include "socket.h"
+#include "string-util.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, socket_result, SocketResult);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_bind_ipv6_only, socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
@@ -68,6 +68,7 @@ static int property_get_listen(
case SOCKET_SPECIAL:
case SOCKET_MQUEUE:
case SOCKET_FIFO:
+ case SOCKET_USB_FUNCTION:
a = p->path;
break;
@@ -83,6 +84,25 @@ static int property_get_listen(
return sd_bus_message_close_container(reply);
}
+
+static int property_get_fdname(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Socket *s = SOCKET(userdata);
+
+ assert(bus);
+ assert(reply);
+ assert(s);
+
+ return sd_bus_message_append(reply, "s", socket_fdname(s));
+}
+
const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("BindIPv6Only", "s", property_get_bind_ipv6_only, offsetof(Socket, bind_ipv6_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -94,6 +114,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("KeepAliveIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_interval), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -116,6 +137,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -126,6 +148,12 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Socket, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("NConnections", "u", bus_property_get_unsigned, offsetof(Socket, n_connections), 0),
SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0),
+ SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0),
+ SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h
index 17164d9871..7a792c7a89 100644
--- a/src/core/dbus-socket.h
+++ b/src/core/dbus-socket.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_socket_vtable[];
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index 0093371306..85a2c26b98 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,12 +18,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "swap.h"
-#include "dbus-execute.h"
+#include "bus-util.h"
#include "dbus-cgroup.h"
+#include "dbus-execute.h"
#include "dbus-swap.h"
-#include "bus-util.h"
+#include "string-util.h"
+#include "swap.h"
+#include "unit.h"
static int property_get_priority(
sd_bus *bus,
@@ -85,6 +84,8 @@ const sd_bus_vtable bus_swap_vtable[] = {
SD_BUS_PROPERTY("TimeoutUSec", "t", bus_property_get_usec, offsetof(Swap, timeout_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Swap, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Swap, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("GID", "u", NULL, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_COMMAND_VTABLE("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_VTABLE("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
SD_BUS_VTABLE_END
diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h
index 9469f68ab8..5238471f98 100644
--- a/src/core/dbus-swap.h
+++ b/src/core/dbus-swap.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,6 +21,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_swap_vtable[];
diff --git a/src/core/dbus-target.c b/src/core/dbus-target.c
index 350f5c3ed2..6858b1ce72 100644
--- a/src/core/dbus-target.c
+++ b/src/core/dbus-target.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,8 +17,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
#include "dbus-target.h"
+#include "unit.h"
const sd_bus_vtable bus_target_vtable[] = {
SD_BUS_VTABLE_START(0),
diff --git a/src/core/dbus-target.h b/src/core/dbus-target.h
index 4c4297bc9e..9be5ce06b7 100644
--- a/src/core/dbus-target.h
+++ b/src/core/dbus-target.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -21,5 +19,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "sd-bus.h"
extern const sd_bus_vtable bus_target_vtable[];
diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c
index 74a9914358..efbb0e8915 100644
--- a/src/core/dbus-timer.c
+++ b/src/core/dbus-timer.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,11 +17,12 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
-#include "timer.h"
-#include "dbus-timer.h"
+#include "alloc-util.h"
#include "bus-util.h"
+#include "dbus-timer.h"
#include "strv.h"
+#include "timer.h"
+#include "unit.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult);
@@ -157,7 +156,7 @@ static int property_get_next_elapse_monotonic(
usec_t a, b;
a = now(CLOCK_MONOTONIC);
- b = now(CLOCK_BOOTTIME);
+ b = now(clock_boottime_or_monotonic());
if (t->next_elapse_monotonic_or_boottime + a > b)
x = t->next_elapse_monotonic_or_boottime + a - b;
@@ -179,8 +178,10 @@ const sd_bus_vtable bus_timer_vtable[] = {
BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RandomizedDelayUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};
@@ -219,7 +220,7 @@ static int bus_timer_set_transient_property(
if (mode != UNIT_CHECK) {
char time[FORMAT_TIMESPAN_MAX];
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
v = new0(TimerValue, 1);
if (!v)
@@ -248,12 +249,11 @@ static int bus_timer_set_transient_property(
if (r < 0)
return r;
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, str);
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, str);
v = new0(TimerValue, 1);
if (!v) {
- if (c)
- calendar_spec_free(c);
+ calendar_spec_free(c);
return -ENOMEM;
}
@@ -265,25 +265,38 @@ static int bus_timer_set_transient_property(
return 1;
- } else if (streq(name, "AccuracySec")) {
-
+ } else if (STR_IN_SET(name, "AccuracyUSec", "AccuracySec")) {
usec_t u = 0;
+ if (streq(name, "AccuracySec"))
+ log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead.");
+
r = sd_bus_message_read(message, "t", &u);
if (r < 0)
return r;
if (mode != UNIT_CHECK) {
- char time[FORMAT_TIMESPAN_MAX];
-
t->accuracy_usec = u;
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "AccuracySec=" USEC_FMT "us", u);
}
return 1;
- } else if (streq(name, "WakeSystem")) {
+ } else if (streq(name, "RandomizedDelayUSec")) {
+ usec_t u = 0;
+
+ r = sd_bus_message_read(message, "t", &u);
+ if (r < 0)
+ return r;
+ if (mode != UNIT_CHECK) {
+ t->random_usec = u;
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomizedDelaySec=" USEC_FMT "us", u);
+ }
+
+ return 1;
+
+ } else if (streq(name, "WakeSystem")) {
int b;
r = sd_bus_message_read(message, "b", &b);
@@ -292,11 +305,24 @@ static int bus_timer_set_transient_property(
if (mode != UNIT_CHECK) {
t->wake_system = b;
- unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system));
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, yes_no(b));
}
return 1;
+ } else if (streq(name, "RemainAfterElapse")) {
+ int b;
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK) {
+ t->remain_after_elapse = b;
+ unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s", name, yes_no(b));
+ }
+
+ return 1;
}
return 0;
diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h
index 103172f055..39053dc4a2 100644
--- a/src/core/dbus-timer.h
+++ b/src/core/dbus-timer.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_timer_vtable[];
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 1892725f91..245912fc0f 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,13 +18,22 @@
***/
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "cgroup-util.h"
+#include "dbus-unit.h"
+#include "dbus.h"
+#include "fd-util.h"
+#include "locale-util.h"
#include "log.h"
+#include "process-util.h"
#include "selinux-access.h"
-#include "cgroup-util.h"
+#include "signal-util.h"
+#include "special.h"
+#include "string-util.h"
#include "strv.h"
-#include "bus-common-errors.h"
-#include "dbus.h"
-#include "dbus-unit.h"
+#include "user-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
@@ -112,6 +119,22 @@ static int property_get_dependencies(
return sd_bus_message_close_container(reply);
}
+static int property_get_obsolete_dependencies(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ assert(bus);
+ assert(reply);
+
+ /* For dependency types we don't support anymore always return an empty array */
+ return sd_bus_message_append(reply, "as", 0);
+}
+
static int property_get_description(
sd_bus *bus,
const char *path,
@@ -377,7 +400,7 @@ static int property_get_load_error(
void *userdata,
sd_bus_error *error) {
- _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
Unit *u = userdata;
assert(bus);
@@ -390,6 +413,38 @@ static int property_get_load_error(
return sd_bus_message_append(reply, "(ss)", e.name, e.message);
}
+static int bus_verify_manage_units_async_full(
+ Unit *u,
+ const char *verb,
+ int capability,
+ const char *polkit_message,
+ bool interactive,
+ sd_bus_message *call,
+ sd_bus_error *error) {
+
+ const char *details[9] = {
+ "unit", u->id,
+ "verb", verb,
+ };
+
+ if (polkit_message) {
+ details[4] = "polkit.message";
+ details[5] = polkit_message;
+ details[6] = "polkit.gettext_domain";
+ details[7] = GETTEXT_PACKAGE;
+ }
+
+ return bus_verify_polkit_async(
+ call,
+ capability,
+ "org.freedesktop.systemd1.manage-units",
+ details,
+ interactive,
+ UID_INVALID,
+ &u->manager->polkit_registry,
+ error);
+}
+
int bus_unit_method_start_generic(
sd_bus_message *message,
Unit *u,
@@ -399,13 +454,24 @@ int bus_unit_method_start_generic(
const char *smode;
JobMode mode;
+ _cleanup_free_ char *verb = NULL;
+ static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = {
+ [JOB_START] = N_("Authentication is required to start '$(unit)'."),
+ [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."),
+ [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."),
+ [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."),
+ [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."),
+ };
int r;
assert(message);
assert(u);
assert(job_type >= 0 && job_type < _JOB_TYPE_MAX);
- r = mac_selinux_unit_access_check(u, message, job_type == JOB_STOP ? "stop" : "start", error);
+ r = mac_selinux_unit_access_check(
+ u, message,
+ job_type_to_access_method(job_type),
+ error);
if (r < 0)
return r;
@@ -417,7 +483,21 @@ int bus_unit_method_start_generic(
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
- r = bus_verify_manage_units_async(u->manager, message, error);
+ if (reload_if_possible)
+ verb = strjoin("reload-or-", job_type_to_string(job_type), NULL);
+ else
+ verb = strdup(job_type_to_string(job_type));
+ if (!verb)
+ return -ENOMEM;
+
+ r = bus_verify_manage_units_async_full(
+ u,
+ verb,
+ CAP_SYS_ADMIN,
+ job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+ true,
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -480,10 +560,17 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho);
}
- if (signo <= 0 || signo >= _NSIG)
+ if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
- r = bus_verify_manage_units_async_for_kill(u->manager, message, error);
+ r = bus_verify_manage_units_async_full(
+ u,
+ "kill",
+ CAP_KILL,
+ N_("Authentication is required to kill '$(unit)'."),
+ true,
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -507,7 +594,14 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
if (r < 0)
return r;
- r = bus_verify_manage_units_async(u->manager, message, error);
+ r = bus_verify_manage_units_async_full(
+ u,
+ "reset-failed",
+ CAP_SYS_ADMIN,
+ N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
+ true,
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -533,7 +627,14 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
if (r < 0)
return r;
- r = bus_verify_manage_units_async(u->manager, message, error);
+ r = bus_verify_manage_units_async_full(
+ u,
+ "set-property",
+ CAP_SYS_ADMIN,
+ N_("Authentication is required to set properties on '$(unit)'."),
+ true,
+ message,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -546,6 +647,53 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
return sd_bus_reply_method_return(message, NULL);
}
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = mac_selinux_unit_access_check(u, message, "start", error);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async_full(
+ u,
+ "ref",
+ CAP_SYS_ADMIN,
+ NULL,
+ false,
+ message,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = bus_unit_track_add_sender(u, message);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = bus_unit_track_remove_sender(u, message);
+ if (r == -EUNATCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NOT_REFERENCED, "Unit has not been referenced yet.");
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -553,16 +701,12 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -587,6 +731,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0),
SD_BUS_PROPERTY("UnitFilePreset", "s", property_get_unit_file_preset, 0, 0),
+ BUS_PROPERTY_DUAL_TIMESTAMP("StateChangeTimestamp", offsetof(Unit, state_change_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -603,7 +748,6 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("IgnoreOnSnapshot", "b", bus_property_get_bool, offsetof(Unit, ignore_on_snapshot), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -616,6 +760,11 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0),
SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -627,7 +776,15 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
-
+ SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
+
+ /* Obsolete properties or obsolete alias names */
+ SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequisiteOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
SD_BUS_VTABLE_END
};
@@ -673,6 +830,30 @@ static int property_get_current_memory(
return sd_bus_message_append(reply, "t", sz);
}
+static int property_get_current_tasks(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ uint64_t cn = (uint64_t) -1;
+ Unit *u = userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ r = unit_get_tasks_current(u, &cn);
+ if (r < 0 && r != -ENODATA)
+ log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m");
+
+ return sd_bus_message_append(reply, "t", cn);
+}
+
static int property_get_cpu_usage(
sd_bus *bus,
const char *path,
@@ -697,17 +878,188 @@ static int property_get_cpu_usage(
return sd_bus_message_append(reply, "t", ns);
}
+static int property_get_cgroup(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Unit *u = userdata;
+ const char *t;
+
+ assert(bus);
+ assert(reply);
+ assert(u);
+
+ /* Three cases: a) u->cgroup_path is NULL, in which case the
+ * unit has no control group, which we report as the empty
+ * string. b) u->cgroup_path is the empty string, which
+ * indicates the root cgroup, which we report as "/". c) all
+ * other cases we report as-is. */
+
+ if (u->cgroup_path)
+ t = isempty(u->cgroup_path) ? "/" : u->cgroup_path;
+ else
+ t = "";
+
+ return sd_bus_message_append(reply, "s", t);
+}
+
+static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) {
+ _cleanup_free_ char *buf = NULL, *cmdline = NULL;
+ int r;
+
+ assert(reply);
+ assert(pid > 0);
+
+ r = set_put(pids, PID_TO_PTR(pid));
+ if (r == -EEXIST || r == 0)
+ return 0;
+ if (r < 0)
+ return r;
+
+ if (!p) {
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf);
+ if (r == -ESRCH)
+ return 0;
+ if (r < 0)
+ return r;
+
+ p = buf;
+ }
+
+ (void) get_process_cmdline(pid, 0, true, &cmdline);
+
+ return sd_bus_message_append(reply,
+ "(sus)",
+ p,
+ (uint32_t) pid,
+ cmdline);
+}
+
+static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(reply);
+ assert(p);
+
+ r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f);
+ if (r == ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ pid_t pid;
+
+ r = cg_read_pid(f, &pid);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (is_kernel_thread(pid) > 0)
+ continue;
+
+ r = append_process(reply, p, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ _cleanup_free_ char *g = NULL, *j = NULL;
+
+ r = cg_read_subgroup(d, &g);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ j = strjoin(p, "/", g, NULL);
+ if (!j)
+ return -ENOMEM;
+
+ r = append_cgroup(reply, j, pids);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_(set_freep) Set *pids = NULL;
+ Unit *u = userdata;
+ pid_t pid;
+ int r;
+
+ assert(message);
+
+ pids = set_new(NULL);
+ if (!pids)
+ return -ENOMEM;
+
+ r = sd_bus_message_new_method_return(message, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "(sus)");
+ if (r < 0)
+ return r;
+
+ if (u->cgroup_path) {
+ r = append_cgroup(reply, u->cgroup_path, pids);
+ if (r < 0)
+ return r;
+ }
+
+ /* The main and control pids might live outside of the cgroup, hence fetch them separately */
+ pid = unit_main_pid(u);
+ if (pid > 0) {
+ r = append_process(reply, NULL, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ pid = unit_control_pid(u);
+ if (pid > 0) {
+ r = append_process(reply, NULL, pid, pids);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(NULL, reply, NULL);
+}
+
const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
- SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0),
+ SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
+ SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
+ SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
static int send_new_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Unit *u = userdata;
int r;
@@ -753,7 +1105,7 @@ static int send_changed_signal(sd_bus *bus, void *userdata) {
r = sd_bus_emit_properties_changed_strv(
bus, p,
- UNIT_VTABLE(u)->bus_interface,
+ unit_dbus_interface_from_type(u->type),
NULL);
if (r < 0)
return r;
@@ -784,7 +1136,7 @@ void bus_unit_send_change_signal(Unit *u) {
}
static int send_removed_signal(sd_bus *bus, void *userdata) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *p = NULL;
Unit *u = userdata;
int r;
@@ -816,7 +1168,7 @@ void bus_unit_send_removed_signal(Unit *u) {
int r;
assert(u);
- if (!u->sent_dbus_new_signal)
+ if (!u->sent_dbus_new_signal || u->in_dbus_queue)
bus_unit_send_change_signal(u);
if (!u->id)
@@ -844,20 +1196,20 @@ int bus_unit_queue_job(
assert(type >= 0 && type < _JOB_TYPE_MAX);
assert(mode >= 0 && mode < _JOB_MODE_MAX);
+ r = mac_selinux_unit_access_check(
+ u, message,
+ job_type_to_access_method(type),
+ error);
+ if (r < 0)
+ return r;
+
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;
+ type = JOB_TRY_RELOAD;
}
- r = mac_selinux_unit_access_check(
- u, message,
- (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
- type == JOB_STOP ? "stop" : "reload", error);
- if (r < 0)
- return r;
-
if (type == JOB_STOP &&
(u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) &&
unit_active_state(u) == UNIT_INACTIVE)
@@ -865,10 +1217,11 @@ int bus_unit_queue_job(
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)))
+ ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) ||
+ (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id);
- r = manager_add_job(u->manager, type, u, mode, true, error, &j);
+ r = manager_add_job(u->manager, type, u, mode, error, &j);
if (r < 0)
return r;
@@ -916,7 +1269,7 @@ static int bus_unit_set_transient_property(
if (r < 0)
return r;
- unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", d);
+ unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s", d);
}
return 1;
@@ -930,43 +1283,49 @@ static int bus_unit_set_transient_property(
if (mode != UNIT_CHECK) {
u->default_dependencies = b;
- unit_write_drop_in_format(u, mode, name, "[Unit]\nDefaultDependencies=%s\n", yes_no(b));
+ unit_write_drop_in_format(u, mode, name, "[Unit]\nDefaultDependencies=%s", yes_no(b));
}
return 1;
- } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
+ } else if (streq(name, "Slice")) {
+ Unit *slice;
const char *s;
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
+ if (u->type == UNIT_SLICE)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
+
r = sd_bus_message_read(message, "s", &s);
if (r < 0)
return r;
- if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice"))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s);
+ if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s);
- if (isempty(s)) {
- if (mode != UNIT_CHECK) {
- unit_ref_unset(&u->slice);
- unit_remove_drop_in(u, mode, name);
- }
- } else {
- Unit *slice;
+ /* Note that we do not dispatch the load queue here yet, as we don't want our own transient unit to be
+ * loaded while we are still setting it up. Or in other words, we use manager_load_unit_prepare()
+ * instead of manager_load_unit() on purpose, here. */
+ r = manager_load_unit_prepare(u->manager, s, NULL, error, &slice);
+ if (r < 0)
+ return r;
- r = manager_load_unit(u->manager, s, NULL, error, &slice);
+ if (slice->type != UNIT_SLICE)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s);
+
+ if (mode != UNIT_CHECK) {
+ r = unit_set_slice(u, slice);
if (r < 0)
return r;
- if (slice->type != UNIT_SLICE)
- return -EINVAL;
-
- if (mode != UNIT_CHECK) {
- unit_ref_set(&u->slice, slice);
- unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
- }
+ unit_write_drop_in_private_format(u, mode, name, "Slice=%s", s);
}
return 1;
+
} else if (STR_IN_SET(name,
"Requires", "RequiresOverridable",
"Requisite", "RequisiteOverridable",
@@ -981,9 +1340,15 @@ static int bus_unit_set_transient_property(
UnitDependency d;
const char *other;
- d = unit_dependency_from_string(name);
- if (d < 0)
- return -EINVAL;
+ if (streq(name, "RequiresOverridable"))
+ d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */
+ else if (streq(name, "RequisiteOverridable"))
+ d = UNIT_REQUISITE; /* same here */
+ else {
+ d = unit_dependency_from_string(name);
+ if (d < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit dependency: %s", name);
+ }
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
@@ -1004,7 +1369,7 @@ static int bus_unit_set_transient_property(
if (!label)
return -ENOMEM;
- unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
+ unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s", name, other);
}
}
@@ -1016,6 +1381,29 @@ static int bus_unit_set_transient_property(
return r;
return 1;
+
+ } else if (streq(name, "AddRef")) {
+
+ int b;
+
+ /* Why is this called "AddRef" rather than just "Ref", or "Reference"? There's already a "Ref()" method
+ * on the Unit interface, and it's probably not a good idea to expose a property and a method on the
+ * same interface (well, strictly speaking AddRef isn't exposed as full property, we just read it for
+ * transient units, but still). And "References" and "ReferencedBy" is already used as unit reference
+ * dependency type, hence let's not confuse things with that.
+ *
+ * Note that we don't acually add the reference to the bus track. We do that only after the setup of
+ * the transient unit is complete, so that setting this property multiple times in the same transient
+ * unit creation call doesn't count as individual references. */
+
+ r = sd_bus_message_read(message, "b", &b);
+ if (r < 0)
+ return r;
+
+ if (mode != UNIT_CHECK)
+ u->bus_track_add = b;
+
+ return 1;
}
return 0;
@@ -1102,3 +1490,89 @@ int bus_unit_set_properties(
return n;
}
+
+int bus_unit_check_load_state(Unit *u, sd_bus_error *error) {
+ assert(u);
+
+ if (u->load_state == UNIT_LOADED)
+ return 0;
+
+ /* Give a better description of the unit error when
+ * possible. Note that in the case of UNIT_MASKED, load_error
+ * is not set. */
+ if (u->load_state == UNIT_MASKED)
+ return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit %s is masked.", u->id);
+
+ if (u->load_state == UNIT_NOT_FOUND)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not found.", u->id);
+
+ return sd_bus_error_set_errnof(error, u->load_error, "Unit %s is not loaded properly: %m.", u->id);
+}
+
+static int bus_track_handler(sd_bus_track *t, void *userdata) {
+ Unit *u = userdata;
+
+ assert(t);
+ assert(u);
+
+ u->bus_track = sd_bus_track_unref(u->bus_track); /* make sure we aren't called again */
+
+ unit_add_to_gc_queue(u);
+ return 0;
+}
+
+static int allocate_bus_track(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (u->bus_track)
+ return 0;
+
+ r = sd_bus_track_new(u->manager->api_bus, &u->bus_track, bus_track_handler, u);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_track_set_recursive(u->bus_track, true);
+ if (r < 0) {
+ u->bus_track = sd_bus_track_unref(u->bus_track);
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_unit_track_add_name(Unit *u, const char *name) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_name(u->bus_track, name);
+}
+
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
+ int r;
+
+ assert(u);
+
+ r = allocate_bus_track(u);
+ if (r < 0)
+ return r;
+
+ return sd_bus_track_add_sender(u->bus_track, m);
+}
+
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m) {
+ assert(u);
+
+ /* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet, return an
+ * error */
+ if (!u->bus_track)
+ return -EUNATCH;
+
+ return sd_bus_track_remove_sender(u->bus_track, m);
+}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index b622e0ae8d..b280de7a1d 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,6 +20,7 @@
***/
#include "sd-bus.h"
+
#include "unit.h"
extern const sd_bus_vtable bus_unit_vtable[];
@@ -34,6 +33,15 @@ int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_
int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error);
int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+
+int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error);
+int bus_unit_check_load_state(Unit *u, sd_bus_error *error);
+
+int bus_unit_track_add_name(Unit *u, const char *name);
+int bus_unit_track_add_sender(Unit *u, sd_bus_message *m);
+int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m);
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 057653a8b5..070974fe66 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,29 +17,34 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/epoll.h>
#include <errno.h>
+#include <sys/epoll.h>
#include <unistd.h>
#include "sd-bus.h"
-#include "log.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "missing.h"
-#include "dbus-unit.h"
-#include "dbus-job.h"
-#include "dbus-manager.h"
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-error.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "dbus-cgroup.h"
#include "dbus-execute.h"
+#include "dbus-job.h"
#include "dbus-kill.h"
-#include "dbus-cgroup.h"
-#include "special.h"
+#include "dbus-manager.h"
+#include "dbus-unit.h"
#include "dbus.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "bus-common-errors.h"
-#include "strxcpyx.h"
-#include "bus-internal.h"
+#include "fd-util.h"
+#include "log.h"
+#include "missing.h"
+#include "mkdir.h"
#include "selinux-access.h"
+#include "special.h"
+#include "string-util.h"
+#include "strv.h"
+#include "strxcpyx.h"
+#include "user-util.h"
#define CONNECTIONS_MAX 4096
@@ -68,28 +71,42 @@ int bus_send_queued_message(Manager *m) {
return 0;
}
+int bus_forward_agent_released(Manager *m, const char *path) {
+ int r;
+
+ assert(m);
+ assert(path);
+
+ if (!MANAGER_IS_SYSTEM(m))
+ return 0;
+
+ if (!m->system_bus)
+ return 0;
+
+ /* If we are running a system instance we forward the agent message on the system bus, so that the user
+ * instances get notified about this, too */
+
+ r = sd_bus_emit_signal(m->system_bus,
+ "/org/freedesktop/systemd1/agent",
+ "org.freedesktop.systemd1.Agent",
+ "Released",
+ "s", path);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to propagate agent release message: %m");
+
+ return 1;
+}
+
static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
- const char *cgroup, *me;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
Manager *m = userdata;
+ const char *cgroup;
uid_t sender_uid;
- sd_bus *bus;
int r;
assert(message);
assert(m);
- /* ignore recursive events sent by us on the system/user bus */
- bus = sd_bus_message_get_bus(message);
- if (!sd_bus_is_server(bus)) {
- r = sd_bus_get_unique_name(bus, &me);
- if (r < 0)
- return r;
-
- if (streq_ptr(sd_bus_message_get_sender(message), me))
- return 0;
- }
-
/* only accept org.freedesktop.systemd1.Agent from UID=0 */
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
@@ -107,16 +124,6 @@ static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus
}
manager_notify_cgroup_empty(m, cgroup);
-
- /* if running as system-instance, forward under our name */
- if (m->running_as == MANAGER_SYSTEM && m->system_bus) {
- r = sd_bus_message_rewind(message, 1);
- if (r >= 0)
- r = sd_bus_send(m->system_bus, message, NULL);
- if (r < 0)
- log_warning_errno(r, "Failed to forward Released message: %m");
- }
-
return 0;
}
@@ -140,31 +147,9 @@ static int signal_disconnected(sd_bus_message *message, void *userdata, sd_bus_e
return 0;
}
-static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- const char *name, *old_owner, *new_owner;
- Manager *m = userdata;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
- if (r < 0) {
- bus_log_parse_error(r);
- return 0;
- }
-
- manager_dispatch_bus_name_owner_changed(
- m, name,
- isempty(old_owner) ? NULL : old_owner,
- isempty(new_owner) ? NULL : new_owner);
-
- return 0;
-}
-
static int signal_activation_request(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
const char *name;
Unit *u;
@@ -194,7 +179,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd
goto failed;
}
- r = manager_add_job(m, JOB_START, u, JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL);
if (r < 0)
goto failed;
@@ -262,7 +247,7 @@ static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_er
}
if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
@@ -321,7 +306,7 @@ static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_
assert(path);
if (streq_ptr(path, "/org/freedesktop/systemd1/unit/self")) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
sd_bus_message *message;
pid_t pid;
@@ -378,7 +363,7 @@ static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *in
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
*found = u;
@@ -400,10 +385,10 @@ static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *inter
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
- if (!unit_get_cgroup_context(u))
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
*found = u;
@@ -426,7 +411,7 @@ static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *in
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
c = unit_get_cgroup_context(u);
@@ -453,7 +438,7 @@ static int bus_exec_context_find(sd_bus *bus, const char *path, const char *inte
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
c = unit_get_exec_context(u);
@@ -480,7 +465,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte
if (r <= 0)
return r;
- if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface))
+ if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type)))
return 0;
c = unit_get_kill_context(u);
@@ -577,30 +562,34 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) {
return log_error_errno(r, "Failed to add job enumerator: %m");
for (t = 0; t < _UNIT_TYPE_MAX; t++) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m);
+ const char *interface;
+
+ assert_se(interface = unit_dbus_interface_from_type(t));
+
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register type specific vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface);
if (unit_vtable[t]->cgroup_context_offset > 0) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface);
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_cgroup_vtable, bus_cgroup_context_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register control group vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface);
}
if (unit_vtable[t]->exec_context_offset > 0) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_exec_vtable, bus_exec_context_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register execute vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface);
}
if (unit_vtable[t]->kill_context_offset > 0) {
- r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_kill_vtable, bus_kill_context_find, m);
+ r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m);
if (r < 0)
- return log_error_errno(r, "Failed to register kill vtable for %s: %m", unit_vtable[t]->bus_interface);
+ return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface);
}
}
@@ -630,7 +619,7 @@ static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) {
}
static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
_cleanup_close_ int nfd = -1;
Manager *m = userdata;
sd_id128_t id;
@@ -705,25 +694,6 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
- if (m->running_as == MANAGER_SYSTEM) {
- /* When we run as system instance we get the Released
- * signal via a direct connection */
-
- r = sd_bus_add_match(
- bus,
- NULL,
- "type='signal',"
- "interface='org.freedesktop.systemd1.Agent',"
- "member='Released',"
- "path='/org/freedesktop/systemd1/agent'",
- signal_agent_released, m);
-
- if (r < 0) {
- log_warning_errno(r, "Failed to register Released match on new connection bus: %m");
- return 0;
- }
- }
-
r = bus_setup_disconnected_match(m, bus);
if (r < 0)
return 0;
@@ -747,9 +717,11 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
-static int bus_list_names(Manager *m, sd_bus *bus) {
+int manager_sync_bus_names(Manager *m, sd_bus *bus) {
_cleanup_strv_free_ char **names = NULL;
- char **i;
+ const char *name;
+ Iterator i;
+ Unit *u;
int r;
assert(m);
@@ -759,16 +731,64 @@ static int bus_list_names(Manager *m, sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to get initial list of names: %m");
- /* This is a bit hacky, we say the owner of the name is the
- * name itself, because we don't want the extra traffic to
- * figure out the real owner. */
- STRV_FOREACH(i, names)
- manager_dispatch_bus_name_owner_changed(m, *i, NULL, *i);
+ /* We have to synchronize the current bus names with the
+ * list of active services. To do this, walk the list of
+ * all units with bus names. */
+ HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ if (!streq_ptr(s->bus_name, name)) {
+ log_unit_warning(u, "Bus name has changed from %s → %s, ignoring.", s->bus_name, name);
+ continue;
+ }
+
+ /* Check if a service's bus name is in the list of currently
+ * active names */
+ if (strv_contains(names, name)) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ const char *unique;
+
+ /* If it is, determine its current owner */
+ r = sd_bus_get_name_creds(bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get bus name owner %s: %m", name);
+ continue;
+ }
+
+ r = sd_bus_creds_get_unique_name(creds, &unique);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get unique name for %s: %m", name);
+ continue;
+ }
+
+ /* Now, let's compare that to the previous bus owner, and
+ * if it's still the same, all is fine, so just don't
+ * bother the service. Otherwise, the name has apparently
+ * changed, so synthesize a name owner changed signal. */
+
+ if (!streq_ptr(unique, s->bus_name_owner))
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, s->bus_name_owner, unique);
+ } else {
+ /* So, the name we're watching is not on the bus.
+ * This either means it simply hasn't appeared yet,
+ * or it was lost during the daemon reload.
+ * Check if the service has a stored name owner,
+ * and synthesize a name loss signal in this case. */
+
+ if (s->bus_name_owner)
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, s->bus_name_owner, NULL);
+ }
+ }
return 0;
}
static int bus_setup_api(Manager *m, sd_bus *bus) {
+ Iterator i;
+ char *name;
+ Unit *u;
int r;
assert(m);
@@ -786,17 +806,11 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
if (r < 0)
return r;
- r = sd_bus_add_match(
- bus,
- NULL,
- "type='signal',"
- "sender='org.freedesktop.DBus',"
- "path='/org/freedesktop/DBus',"
- "interface='org.freedesktop.DBus',"
- "member='NameOwnerChanged'",
- signal_name_owner_changed, m);
- if (r < 0)
- log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m");
+ HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
+ r = unit_install_bus_match(u, bus, name);
+ if (r < 0)
+ log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name);
+ }
r = sd_bus_add_match(
bus,
@@ -819,24 +833,26 @@ static int bus_setup_api(Manager *m, sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to register name: %m");
- bus_list_names(m, bus);
+ r = manager_sync_bus_names(m, bus);
+ if (r < 0)
+ return r;
log_debug("Successfully connected to API bus.");
return 0;
}
static int bus_init_api(Manager *m) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
if (m->api_bus)
return 0;
/* The API and system bus is the same if we are running in system mode */
- if (m->running_as == MANAGER_SYSTEM && m->system_bus)
+ if (MANAGER_IS_SYSTEM(m) && m->system_bus)
bus = sd_bus_ref(m->system_bus);
else {
- if (m->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(m))
r = sd_bus_open_system(&bus);
else
r = sd_bus_open_user(&bus);
@@ -875,8 +891,8 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
assert(m);
assert(bus);
- /* On kdbus or if we are a user instance we get the Released message via the system bus */
- if (m->running_as == MANAGER_USER || m->kdbus_fd >= 0) {
+ /* if we are a user instance we get the Released message via the system bus */
+ if (MANAGER_IS_USER(m)) {
r = sd_bus_add_match(
bus,
NULL,
@@ -894,14 +910,14 @@ static int bus_setup_system(Manager *m, sd_bus *bus) {
}
static int bus_init_system(Manager *m) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
int r;
if (m->system_bus)
return 0;
/* The API and system bus is the same if we are running in system mode */
- if (m->running_as == MANAGER_SYSTEM && m->api_bus) {
+ if (MANAGER_IS_SYSTEM(m) && m->api_bus) {
m->system_bus = sd_bus_ref(m->api_bus);
return 0;
}
@@ -948,18 +964,14 @@ static int bus_init_private(Manager *m) {
if (m->private_listen_fd >= 0)
return 0;
- /* We don't need the private socket if we have kdbus */
- if (m->kdbus_fd >= 0)
- return 0;
-
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* We want the private bus only when running as init */
if (getpid() != 1)
return 0;
strcpy(sa.un.sun_path, "/run/systemd/private");
- salen = offsetof(union sockaddr_union, un.sun_path) + strlen("/run/systemd/private");
+ salen = SOCKADDR_UN_LEN(sa.un);
} else {
size_t left = sizeof(sa.un.sun_path);
char *p = sa.un.sun_path;
@@ -1051,7 +1063,7 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
/* Possibly flush unwritten data, but only if we are
* unprivileged, since we don't want to sync here */
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
sd_bus_flush(*bus);
/* And destroy the object */
@@ -1071,12 +1083,10 @@ void bus_done(Manager *m) {
while ((b = set_steal_first(m->private_buses)))
destroy_bus(m, &b);
- set_free(m->private_buses);
- m->private_buses = NULL;
+ m->private_buses = set_free(m->private_buses);
m->subscribed = sd_bus_track_unref(m->subscribed);
- strv_free(m->deserialized_subscribed);
- m->deserialized_subscribed = NULL;
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
if (m->private_listen_event_source)
m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source);
@@ -1154,82 +1164,73 @@ int bus_foreach_bus(
return ret;
}
-void bus_track_serialize(sd_bus_track *t, FILE *f) {
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix) {
const char *n;
assert(f);
+ assert(prefix);
- for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t))
- fprintf(f, "subscribed=%s\n", n);
-}
-
-int bus_track_deserialize_item(char ***l, const char *line) {
- const char *e;
- int r;
+ for (n = sd_bus_track_first(t); n; n = sd_bus_track_next(t)) {
+ int c, j;
- assert(l);
- assert(line);
+ c = sd_bus_track_count_name(t, n);
- e = startswith(line, "subscribed=");
- if (!e)
- return 0;
-
- r = strv_extend(l, e);
- if (r < 0)
- return r;
-
- return 1;
+ for (j = 0; j < c; j++) {
+ fputs(prefix, f);
+ fputc('=', f);
+ fputs(n, f);
+ fputc('\n', f);
+ }
+ }
}
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) {
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l) {
+ char **i;
int r = 0;
assert(m);
assert(t);
- assert(l);
-
- if (!strv_isempty(*l) && m->api_bus) {
- char **i;
- if (!*t) {
- r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
- if (r < 0)
- return r;
- }
+ if (strv_isempty(l))
+ return 0;
- r = 0;
- STRV_FOREACH(i, *l) {
- int k;
+ if (!m->api_bus)
+ return 0;
- k = sd_bus_track_add_name(*t, *i);
- if (k < 0)
- r = k;
- }
+ if (!*t) {
+ r = sd_bus_track_new(m->api_bus, t, NULL, NULL);
+ if (r < 0)
+ return r;
}
- strv_free(*l);
- *l = NULL;
+ r = sd_bus_track_set_recursive(*t, recursive);
+ if (r < 0)
+ return r;
+
+ r = 0;
+ STRV_FOREACH(i, l) {
+ int k;
+
+ k = sd_bus_track_add_name(*t, *i);
+ if (k < 0)
+ r = k;
+ }
return r;
}
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error);
-}
-
-/* Same as bus_verify_manage_unit_async(), but checks for CAP_KILL instead of CAP_SYS_ADMIN */
-int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_KILL, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) {
- return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", false, UID_INVALID, &m->polkit_registry, error);
+ return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error);
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index 4832722069..a092ed9d76 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -30,14 +28,16 @@ void bus_done(Manager *m);
int bus_fdset_add_all(Manager *m, FDSet *fds);
-void bus_track_serialize(sd_bus_track *t, FILE *f);
-int bus_track_deserialize_item(char ***l, const char *line);
-int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l);
+void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
+int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
+
+int manager_sync_bus_names(Manager *m, sd_bus *bus);
int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
-int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error);
+
+int bus_forward_agent_released(Manager *m, const char *path);
diff --git a/src/core/device.c b/src/core/device.c
index e7efcf0f0a..8a3e888e5e 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,16 +19,21 @@
#include <errno.h>
#include <sys/epoll.h>
-#include <libudev.h>
-#include "log.h"
-#include "unit-name.h"
+#include "libudev.h"
+
+#include "alloc-util.h"
#include "dbus-device.h"
+#include "device.h"
+#include "log.h"
+#include "parse-util.h"
#include "path-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "swap.h"
#include "udev-util.h"
+#include "unit-name.h"
#include "unit.h"
-#include "swap.h"
-#include "device.h"
static const UnitActiveState state_translation_table[_DEVICE_STATE_MAX] = {
[DEVICE_DEAD] = UNIT_INACTIVE,
@@ -60,8 +63,7 @@ static void device_unset_sysfs(Device *d) {
else
hashmap_remove(devices, d->sysfs);
- free(d->sysfs);
- d->sysfs = NULL;
+ d->sysfs = mfree(d->sysfs);
}
static int device_set_sysfs(Device *d, const char *sysfs) {
@@ -113,7 +115,6 @@ static void device_init(Unit *u) {
u->job_timeout = u->manager->default_timeout_start_usec;
u->ignore_on_isolate = true;
- u->ignore_on_snapshot = true;
}
static void device_done(Unit *u) {
@@ -264,7 +265,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
assert(u);
assert(dev);
- property = u->manager->running_as == MANAGER_USER ? "MANAGER_USER_WANTS" : "SYSTEMD_WANTS";
+ property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS";
wants = udev_device_get_property_value(dev, property);
if (!wants)
return 0;
@@ -312,12 +313,19 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
u = manager_get_unit(m, e);
- if (u &&
- sysfs &&
- DEVICE(u)->sysfs &&
- !path_equal(DEVICE(u)->sysfs, sysfs)) {
- log_unit_debug(u, "Device %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs);
- return -EEXIST;
+ /* The device unit can still be present even if the device was
+ * unplugged: a mount unit can reference it hence preventing
+ * the GC to have garbaged it. That's desired since the device
+ * unit may have a dependency on the mount unit which was
+ * added during the loading of the later. */
+ if (dev && u && DEVICE(u)->state == DEVICE_PLUGGED) {
+ /* This unit is in plugged state: we're sure it's
+ * attached to a device. */
+ if (!path_equal(DEVICE(u)->sysfs, sysfs)) {
+ log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s",
+ e, DEVICE(u)->sysfs, sysfs);
+ return -EEXIST;
+ }
}
if (!u) {
@@ -456,6 +464,10 @@ static void device_update_found_one(Device *d, bool add, DeviceFound found, bool
if (!now)
return;
+ /* Didn't exist before, but does now? if so, generate a new invocation ID for it */
+ if (previous == DEVICE_NOT_FOUND && d->found != DEVICE_NOT_FOUND)
+ (void) unit_acquire_invocation_id(UNIT(d));
+
if (d->found & DEVICE_FOUND_UDEV)
/* When the device is known to udev we consider it
* plugged. */
@@ -595,11 +607,10 @@ static void device_shutdown(Manager *m) {
m->udev_monitor = NULL;
}
- hashmap_free(m->devices_by_sysfs);
- m->devices_by_sysfs = NULL;
+ m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs);
}
-static int device_enumerate(Manager *m) {
+static void device_enumerate(Manager *m) {
_cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
struct udev_list_entry *item = NULL, *first = NULL;
int r;
@@ -609,7 +620,7 @@ static int device_enumerate(Manager *m) {
if (!m->udev_monitor) {
m->udev_monitor = udev_monitor_new_from_netlink(m->udev, "udev");
if (!m->udev_monitor) {
- r = -ENOMEM;
+ log_oom();
goto fail;
}
@@ -619,37 +630,49 @@ static int device_enumerate(Manager *m) {
(void) udev_monitor_set_receive_buffer_size(m->udev_monitor, 128*1024*1024);
r = udev_monitor_filter_add_match_tag(m->udev_monitor, "systemd");
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to add udev tag match: %m");
goto fail;
+ }
r = udev_monitor_enable_receiving(m->udev_monitor);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to enable udev event reception: %m");
goto fail;
+ }
r = sd_event_add_io(m->event, &m->udev_event_source, udev_monitor_get_fd(m->udev_monitor), EPOLLIN, device_dispatch_io, m);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to watch udev file descriptor: %m");
goto fail;
+ }
(void) sd_event_source_set_description(m->udev_event_source, "device");
}
e = udev_enumerate_new(m->udev);
if (!e) {
- r = -ENOMEM;
+ log_oom();
goto fail;
}
r = udev_enumerate_add_match_tag(e, "systemd");
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to create udev tag enumeration: %m");
goto fail;
+ }
r = udev_enumerate_add_match_is_initialized(e);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to install initialization match into enumeration: %m");
goto fail;
+ }
r = udev_enumerate_scan_devices(e);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to enumerate devices: %m");
goto fail;
+ }
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
@@ -672,13 +695,10 @@ static int device_enumerate(Manager *m) {
device_update_found_by_sysfs(m, sysfs, true, DEVICE_FOUND_UDEV, false);
}
- return 0;
+ return;
fail:
- log_error_errno(r, "Failed to enumerate devices: %m");
-
device_shutdown(m);
- return r;
}
static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
@@ -818,14 +838,6 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found,
return device_update_found_by_name(m, node, add, found, now);
}
-static const char* const device_state_table[_DEVICE_STATE_MAX] = {
- [DEVICE_DEAD] = "dead",
- [DEVICE_TENTATIVE] = "tentative",
- [DEVICE_PLUGGED] = "plugged",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
-
const UnitVTable device_vtable = {
.object_size = sizeof(Device),
.sections =
@@ -833,8 +845,6 @@ const UnitVTable device_vtable = {
"Device\0"
"Install\0",
- .no_instances = true,
-
.init = device_init,
.done = device_done,
.load = unit_load_fragment_and_dropin_optional,
@@ -849,7 +859,6 @@ const UnitVTable device_vtable = {
.active_state = device_active_state,
.sub_state_to_string = device_sub_state_to_string,
- .bus_interface = "org.freedesktop.systemd1.Device",
.bus_vtable = bus_device_vtable,
.following = device_following,
diff --git a/src/core/device.h b/src/core/device.h
index 10ab113176..184a1a349b 100644
--- a/src/core/device.h
+++ b/src/core/device.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,16 +21,6 @@
typedef struct Device Device;
-/* We simply watch devices, we cannot plug/unplug them. That
- * simplifies the state engine greatly */
-typedef enum DeviceState {
- DEVICE_DEAD,
- DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */
- DEVICE_PLUGGED, /* announced by udev */
- _DEVICE_STATE_MAX,
- _DEVICE_STATE_INVALID = -1
-} DeviceState;
-
typedef enum DeviceFound {
DEVICE_NOT_FOUND = 0,
DEVICE_FOUND_UDEV = 1,
@@ -56,7 +44,4 @@ struct Device {
extern const UnitVTable device_vtable;
-const char* device_state_to_string(DeviceState i) _const_;
-DeviceState device_state_from_string(const char *s) _pure_;
-
int device_found_node(Manager *m, const char *node, bool add, DeviceFound found, bool now);
diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c
new file mode 100644
index 0000000000..1043da3eb7
--- /dev/null
+++ b/src/core/dynamic-user.c
@@ -0,0 +1,796 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 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 <grp.h>
+#include <pwd.h>
+#include <sys/file.h>
+
+#include "dynamic-user.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "parse-util.h"
+#include "random-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "user-util.h"
+#include "fileio.h"
+
+/* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
+#define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN)
+
+static DynamicUser* dynamic_user_free(DynamicUser *d) {
+ if (!d)
+ return NULL;
+
+ if (d->manager)
+ (void) hashmap_remove(d->manager->dynamic_users, d->name);
+
+ safe_close_pair(d->storage_socket);
+ free(d);
+
+ return NULL;
+}
+
+static int dynamic_user_add(Manager *m, const char *name, int storage_socket[2], DynamicUser **ret) {
+ DynamicUser *d = NULL;
+ int r;
+
+ assert(m);
+ assert(name);
+ assert(storage_socket);
+
+ r = hashmap_ensure_allocated(&m->dynamic_users, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ d = malloc0(offsetof(DynamicUser, name) + strlen(name) + 1);
+ if (!d)
+ return -ENOMEM;
+
+ strcpy(d->name, name);
+
+ d->storage_socket[0] = storage_socket[0];
+ d->storage_socket[1] = storage_socket[1];
+
+ r = hashmap_put(m->dynamic_users, d->name, d);
+ if (r < 0) {
+ free(d);
+ return r;
+ }
+
+ d->manager = m;
+
+ if (ret)
+ *ret = d;
+
+ return 0;
+}
+
+int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) {
+ _cleanup_close_pair_ int storage_socket[2] = { -1, -1 };
+ DynamicUser *d;
+ int r;
+
+ assert(m);
+ assert(name);
+
+ /* Return the DynamicUser structure for a specific user name. Note that this won't actually allocate a UID for
+ * it, but just prepare the data structure for it. The UID is allocated only on demand, when it's really
+ * needed, and in the child process we fork off, since allocation involves NSS checks which are not OK to do
+ * from PID 1. To allow the children and PID 1 share information about allocated UIDs we use an anonymous
+ * AF_UNIX/SOCK_DGRAM socket (called the "storage socket") that contains at most one datagram with the
+ * allocated UID number, plus an fd referencing the lock file for the UID
+ * (i.e. /run/systemd/dynamic-uid/$UID). Why involve the socket pair? So that PID 1 and all its children can
+ * share the same storage for the UID and lock fd, simply by inheriting the storage socket fds. The socket pair
+ * may exist in three different states:
+ *
+ * a) no datagram stored. This is the initial state. In this case the dynamic user was never realized.
+ *
+ * b) a datagram containing a UID stored, but no lock fd attached to it. In this case there was already a
+ * statically assigned UID by the same name, which we are reusing.
+ *
+ * c) a datagram containing a UID stored, and a lock fd is attached to it. In this case we allocated a dynamic
+ * UID and locked it in the file system, using the lock fd.
+ *
+ * As PID 1 and various children might access the socket pair simultaneously, and pop the datagram or push it
+ * back in any time, we also maintain a lock on the socket pair. Note one peculiarity regarding locking here:
+ * the UID lock on disk is protected via a BSD file lock (i.e. an fd-bound lock), so that the lock is kept in
+ * place as long as there's a reference to the fd open. The lock on the storage socket pair however is a POSIX
+ * file lock (i.e. a process-bound lock), as all users share the same fd of this (after all it is anonymous,
+ * nobody else could get any access to it except via our own fd) and we want to synchronize access between all
+ * processes that have access to it. */
+
+ d = hashmap_get(m->dynamic_users, name);
+ if (d) {
+ /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */
+ d->n_ref++;
+ *ret = d;
+ return 0;
+ }
+
+ if (!valid_user_group_name_or_id(name))
+ return -EINVAL;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, storage_socket) < 0)
+ return -errno;
+
+ r = dynamic_user_add(m, name, storage_socket, &d);
+ if (r < 0)
+ return r;
+
+ storage_socket[0] = storage_socket[1] = -1;
+
+ if (ret) {
+ d->n_ref++;
+ *ret = d;
+ }
+
+ return 1;
+}
+
+static int make_uid_symlinks(uid_t uid, const char *name, bool b) {
+
+ char path1[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1];
+ const char *path2;
+ int r = 0, k;
+
+ /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
+ * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
+ * would be its own client then). We hence keep these world-readable symlinks in place, so that the
+ * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
+ * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
+ * on them and as those may be taken by any user with read access we can't make them world-readable. */
+
+ xsprintf(path1, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
+ if (unlink(path1) < 0 && errno != ENOENT)
+ r = -errno;
+
+ if (b && symlink(name, path1) < 0) {
+ k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path1);
+ if (r == 0)
+ r = k;
+ }
+
+ path2 = strjoina("/run/systemd/dynamic-uid/direct:", name);
+ if (unlink(path2) < 0 && errno != ENOENT) {
+ k = -errno;
+ if (r == 0)
+ r = k;
+ }
+
+ if (b && symlink(path1 + strlen("/run/systemd/dynamic-uid/direct:"), path2) < 0) {
+ k = log_warning_errno(errno, "Failed to symlink \"%s\": %m", path2);
+ if (r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
+static int pick_uid(const char *name, uid_t *ret_uid) {
+
+ static const uint8_t hash_key[] = {
+ 0x37, 0x53, 0x7e, 0x31, 0xcf, 0xce, 0x48, 0xf5,
+ 0x8a, 0xbb, 0x39, 0x57, 0x8d, 0xd9, 0xec, 0x59
+ };
+
+ unsigned n_tries = 100;
+ uid_t candidate;
+ int r;
+
+ /* A static user by this name does not exist yet. Let's find a free ID then, and use that. We start with a UID
+ * generated as hash from the user name. */
+ candidate = UID_CLAMP_INTO_RANGE(siphash24(name, strlen(name), hash_key));
+
+ (void) mkdir("/run/systemd/dynamic-uid", 0755);
+
+ for (;;) {
+ char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
+ _cleanup_close_ int lock_fd = -1;
+ ssize_t l;
+
+ if (--n_tries <= 0) /* Give up retrying eventually */
+ return -EBUSY;
+
+ if (!uid_is_dynamic(candidate))
+ goto next;
+
+ xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, candidate);
+
+ for (;;) {
+ struct stat st;
+
+ lock_fd = open(lock_path, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600);
+ if (lock_fd < 0)
+ return -errno;
+
+ r = flock(lock_fd, LOCK_EX|LOCK_NB); /* Try to get a BSD file lock on the UID lock file */
+ if (r < 0) {
+ if (errno == EBUSY || errno == EAGAIN)
+ goto next; /* already in use */
+
+ return -errno;
+ }
+
+ if (fstat(lock_fd, &st) < 0)
+ return -errno;
+ if (st.st_nlink > 0)
+ break;
+
+ /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
+ * got the lock. Close it, and try again. */
+ lock_fd = safe_close(lock_fd);
+ }
+
+ /* Some superficial check whether this UID/GID might already be taken by some static user */
+ if (getpwuid(candidate) || getgrgid((gid_t) candidate)) {
+ (void) unlink(lock_path);
+ goto next;
+ }
+
+ /* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */
+ l = pwritev(lock_fd,
+ (struct iovec[2]) {
+ { .iov_base = (char*) name, .iov_len = strlen(name) },
+ { .iov_base = (char[1]) { '\n' }, .iov_len = 1 }
+ }, 2, 0);
+ if (l < 0) {
+ (void) unlink(lock_path);
+ return -errno;
+ }
+
+ (void) ftruncate(lock_fd, l);
+ (void) make_uid_symlinks(candidate, name, true); /* also add direct lookup symlinks */
+
+ *ret_uid = candidate;
+ r = lock_fd;
+ lock_fd = -1;
+
+ return r;
+
+ next:
+ /* Pick another random UID, and see if that works for us. */
+ random_bytes(&candidate, sizeof(candidate));
+ candidate = UID_CLAMP_INTO_RANGE(candidate);
+ }
+}
+
+static int dynamic_user_pop(DynamicUser *d, uid_t *ret_uid, int *ret_lock_fd) {
+ uid_t uid = UID_INVALID;
+ struct iovec iov = {
+ .iov_base = &uid,
+ .iov_len = sizeof(uid),
+ };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ struct cmsghdr *cmsg;
+
+ ssize_t k;
+ int lock_fd = -1;
+
+ assert(d);
+ assert(ret_uid);
+ assert(ret_lock_fd);
+
+ /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock
+ * on the socket taken. */
+
+ k = recvmsg(d->storage_socket[0], &mh, MSG_DONTWAIT|MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
+ if (k < 0)
+ return -errno;
+
+ cmsg = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
+ if (cmsg)
+ lock_fd = *(int*) CMSG_DATA(cmsg);
+ else
+ cmsg_close_all(&mh); /* just in case... */
+
+ *ret_uid = uid;
+ *ret_lock_fd = lock_fd;
+
+ return 0;
+}
+
+static int dynamic_user_push(DynamicUser *d, uid_t uid, int lock_fd) {
+ struct iovec iov = {
+ .iov_base = &uid,
+ .iov_len = sizeof(uid),
+ };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ } control = {};
+ struct msghdr mh = {
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ ssize_t k;
+
+ assert(d);
+
+ /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */
+
+ if (lock_fd >= 0) {
+ struct cmsghdr *cmsg;
+
+ cmsg = CMSG_FIRSTHDR(&mh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &lock_fd, sizeof(int));
+
+ mh.msg_controllen = CMSG_SPACE(sizeof(int));
+ } else {
+ mh.msg_control = NULL;
+ mh.msg_controllen = 0;
+ }
+
+ k = sendmsg(d->storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
+ if (k < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void unlink_uid_lock(int lock_fd, uid_t uid, const char *name) {
+ char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
+
+ if (lock_fd < 0)
+ return;
+
+ xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
+ (void) unlink(lock_path);
+
+ (void) make_uid_symlinks(uid, name, false); /* remove direct lookup symlinks */
+}
+
+int dynamic_user_realize(DynamicUser *d, uid_t *ret) {
+
+ _cleanup_close_ int etc_passwd_lock_fd = -1, uid_lock_fd = -1;
+ uid_t uid = UID_INVALID;
+ int r;
+
+ assert(d);
+
+ /* Acquire a UID for the user name. This will allocate a UID for the user name if the user doesn't exist
+ * yet. If it already exists its existing UID/GID will be reused. */
+
+ if (lockf(d->storage_socket[0], F_LOCK, 0) < 0)
+ return -errno;
+
+ r = dynamic_user_pop(d, &uid, &uid_lock_fd);
+ if (r < 0) {
+ int new_uid_lock_fd;
+ uid_t new_uid;
+
+ if (r != -EAGAIN)
+ goto finish;
+
+ /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the
+ * lock however, so that nobody else blocks on our NSS lookups. */
+ (void) lockf(d->storage_socket[0], F_ULOCK, 0);
+
+ /* Let's see if a proper, static user or group by this name exists. Try to take the lock on
+ * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't
+ * take the lock, given that users can't be added there anyway in this case. */
+ etc_passwd_lock_fd = take_etc_passwd_lock(NULL);
+ if (etc_passwd_lock_fd < 0 && etc_passwd_lock_fd != -EROFS)
+ return etc_passwd_lock_fd;
+
+ /* First, let's parse this as numeric UID */
+ r = parse_uid(d->name, &uid);
+ if (r < 0) {
+ struct passwd *p;
+ struct group *g;
+
+ /* OK, this is not a numeric UID. Let's see if there's a user by this name */
+ p = getpwnam(d->name);
+ if (p)
+ uid = p->pw_uid;
+
+ /* Let's see if there's a group by this name */
+ g = getgrnam(d->name);
+ if (g) {
+ /* If the UID/GID of the user/group of the same don't match, refuse operation */
+ if (uid != UID_INVALID && uid != (uid_t) g->gr_gid)
+ return -EILSEQ;
+
+ uid = (uid_t) g->gr_gid;
+ }
+ }
+
+ if (uid == UID_INVALID) {
+ /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */
+
+ uid_lock_fd = pick_uid(d->name, &uid);
+ if (uid_lock_fd < 0)
+ return uid_lock_fd;
+ }
+
+ /* So, we found a working UID/lock combination. Let's see if we actually still need it. */
+ if (lockf(d->storage_socket[0], F_LOCK, 0) < 0) {
+ unlink_uid_lock(uid_lock_fd, uid, d->name);
+ return -errno;
+ }
+
+ r = dynamic_user_pop(d, &new_uid, &new_uid_lock_fd);
+ if (r < 0) {
+ if (r != -EAGAIN) {
+ /* OK, something bad happened, let's get rid of the bits we acquired. */
+ unlink_uid_lock(uid_lock_fd, uid, d->name);
+ goto finish;
+ }
+
+ /* Great! Nothing is stored here, still. Store our newly acquired data. */
+ } else {
+ /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
+ * acquired, and use what's stored now. */
+
+ unlink_uid_lock(uid_lock_fd, uid, d->name);
+ safe_close(uid_lock_fd);
+
+ uid = new_uid;
+ uid_lock_fd = new_uid_lock_fd;
+ }
+ }
+
+ /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
+ * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID
+ * dynamically right here, push that in along with the lock fd for it. */
+ r = dynamic_user_push(d, uid, uid_lock_fd);
+ if (r < 0)
+ goto finish;
+
+ *ret = uid;
+ r = 0;
+
+finish:
+ (void) lockf(d->storage_socket[0], F_ULOCK, 0);
+ return r;
+}
+
+int dynamic_user_current(DynamicUser *d, uid_t *ret) {
+ _cleanup_close_ int lock_fd = -1;
+ uid_t uid;
+ int r;
+
+ assert(d);
+ assert(ret);
+
+ /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the storage socket, and pushes it back in right-away. */
+
+ if (lockf(d->storage_socket[0], F_LOCK, 0) < 0)
+ return -errno;
+
+ r = dynamic_user_pop(d, &uid, &lock_fd);
+ if (r < 0)
+ goto finish;
+
+ r = dynamic_user_push(d, uid, lock_fd);
+ if (r < 0)
+ goto finish;
+
+ *ret = uid;
+ r = 0;
+
+finish:
+ (void) lockf(d->storage_socket[0], F_ULOCK, 0);
+ return r;
+}
+
+DynamicUser* dynamic_user_ref(DynamicUser *d) {
+ if (!d)
+ return NULL;
+
+ assert(d->n_ref > 0);
+ d->n_ref++;
+
+ return d;
+}
+
+DynamicUser* dynamic_user_unref(DynamicUser *d) {
+ if (!d)
+ return NULL;
+
+ /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully destroyed
+ * and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may contain entries
+ * with no references, which is commonly the case right before a daemon reload. */
+
+ assert(d->n_ref > 0);
+ d->n_ref--;
+
+ return NULL;
+}
+
+static int dynamic_user_close(DynamicUser *d) {
+ _cleanup_close_ int lock_fd = -1;
+ uid_t uid;
+ int r;
+
+ /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the user is
+ * unrealized again, much like it was after it the DynamicUser object was first allocated. */
+
+ if (lockf(d->storage_socket[0], F_LOCK, 0) < 0)
+ return -errno;
+
+ r = dynamic_user_pop(d, &uid, &lock_fd);
+ if (r == -EAGAIN) {
+ /* User wasn't realized yet, nothing to do. */
+ r = 0;
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+
+ /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
+ unlink_uid_lock(lock_fd, uid, d->name);
+ r = 1;
+
+finish:
+ (void) lockf(d->storage_socket[0], F_ULOCK, 0);
+ return r;
+}
+
+DynamicUser* dynamic_user_destroy(DynamicUser *d) {
+ if (!d)
+ return NULL;
+
+ /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last
+ * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that
+ * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload
+ * cycle, where the dynamic users should not be destroyed, but our datastructures should. */
+
+ dynamic_user_unref(d);
+
+ if (d->n_ref > 0)
+ return NULL;
+
+ (void) dynamic_user_close(d);
+ return dynamic_user_free(d);
+}
+
+int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds) {
+ DynamicUser *d;
+ Iterator i;
+
+ assert(m);
+ assert(f);
+ assert(fds);
+
+ /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */
+
+ HASHMAP_FOREACH(d, m->dynamic_users, i) {
+ int copy0, copy1;
+
+ copy0 = fdset_put_dup(fds, d->storage_socket[0]);
+ if (copy0 < 0)
+ return copy0;
+
+ copy1 = fdset_put_dup(fds, d->storage_socket[1]);
+ if (copy1 < 0)
+ return copy1;
+
+ fprintf(f, "dynamic-user=%s %i %i\n", d->name, copy0, copy1);
+ }
+
+ return 0;
+}
+
+void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds) {
+ _cleanup_free_ char *name = NULL, *s0 = NULL, *s1 = NULL;
+ int r, fd0, fd1;
+
+ assert(m);
+ assert(value);
+ assert(fds);
+
+ /* Parse the serialization again, after a daemon reload */
+
+ r = extract_many_words(&value, NULL, 0, &name, &s0, &s1, NULL);
+ if (r != 3 || !isempty(value)) {
+ log_debug("Unable to parse dynamic user line.");
+ return;
+ }
+
+ if (safe_atoi(s0, &fd0) < 0 || !fdset_contains(fds, fd0)) {
+ log_debug("Unable to process dynamic user fd specification.");
+ return;
+ }
+
+ if (safe_atoi(s1, &fd1) < 0 || !fdset_contains(fds, fd1)) {
+ log_debug("Unable to process dynamic user fd specification.");
+ return;
+ }
+
+ r = dynamic_user_add(m, name, (int[]) { fd0, fd1 }, NULL);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to add dynamic user: %m");
+ return;
+ }
+
+ (void) fdset_remove(fds, fd0);
+ (void) fdset_remove(fds, fd1);
+}
+
+void dynamic_user_vacuum(Manager *m, bool close_user) {
+ DynamicUser *d;
+ Iterator i;
+
+ assert(m);
+
+ /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users
+ * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which
+ * might not be referenced anymore. */
+
+ HASHMAP_FOREACH(d, m->dynamic_users, i) {
+ if (d->n_ref > 0)
+ continue;
+
+ if (close_user) {
+ log_debug("Removing orphaned dynamic user %s", d->name);
+ (void) dynamic_user_close(d);
+ }
+
+ dynamic_user_free(d);
+ }
+}
+
+int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) {
+ char lock_path[strlen("/run/systemd/dynamic-uid/") + DECIMAL_STR_MAX(uid_t) + 1];
+ _cleanup_free_ char *user = NULL;
+ uid_t check_uid;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ /* A friendly way to translate a dynamic user's UID into a name. */
+ if (!uid_is_dynamic(uid))
+ return -ESRCH;
+
+ xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid);
+ r = read_one_line_file(lock_path, &user);
+ if (r == -ENOENT)
+ return -ESRCH;
+ if (r < 0)
+ return r;
+
+ /* The lock file might be stale, hence let's verify the data before we return it */
+ r = dynamic_user_lookup_name(m, user, &check_uid);
+ if (r < 0)
+ return r;
+ if (check_uid != uid) /* lock file doesn't match our own idea */
+ return -ESRCH;
+
+ *ret = user;
+ user = NULL;
+
+ return 0;
+}
+
+int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret) {
+ DynamicUser *d;
+ int r;
+
+ assert(m);
+ assert(name);
+ assert(ret);
+
+ /* A friendly call for translating a dynamic user's name into its UID */
+
+ d = hashmap_get(m->dynamic_users, name);
+ if (!d)
+ return -ESRCH;
+
+ r = dynamic_user_current(d, ret);
+ if (r == -EAGAIN) /* not realized yet? */
+ return -ESRCH;
+
+ return r;
+}
+
+int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group) {
+ bool acquired = false;
+ int r;
+
+ assert(creds);
+ assert(m);
+
+ /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
+ * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
+ * and group. This call allocates a pair. */
+
+ if (!creds->user && user) {
+ r = dynamic_user_acquire(m, user, &creds->user);
+ if (r < 0)
+ return r;
+
+ acquired = true;
+ }
+
+ if (!creds->group) {
+
+ if (creds->user && (!group || streq_ptr(user, group)))
+ creds->group = dynamic_user_ref(creds->user);
+ else {
+ r = dynamic_user_acquire(m, group, &creds->group);
+ if (r < 0) {
+ if (acquired)
+ creds->user = dynamic_user_unref(creds->user);
+ return r;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int dynamic_creds_realize(DynamicCreds *creds, uid_t *uid, gid_t *gid) {
+ uid_t u = UID_INVALID;
+ gid_t g = GID_INVALID;
+ int r;
+
+ assert(creds);
+ assert(uid);
+ assert(gid);
+
+ /* Realize both the referenced user and group */
+
+ if (creds->user) {
+ r = dynamic_user_realize(creds->user, &u);
+ if (r < 0)
+ return r;
+ }
+
+ if (creds->group && creds->group != creds->user) {
+ r = dynamic_user_realize(creds->group, &g);
+ if (r < 0)
+ return r;
+ } else
+ g = u;
+
+ *uid = u;
+ *gid = g;
+
+ return 0;
+}
+
+void dynamic_creds_unref(DynamicCreds *creds) {
+ assert(creds);
+
+ creds->user = dynamic_user_unref(creds->user);
+ creds->group = dynamic_user_unref(creds->group);
+}
+
+void dynamic_creds_destroy(DynamicCreds *creds) {
+ assert(creds);
+
+ creds->user = dynamic_user_destroy(creds->user);
+ creds->group = dynamic_user_destroy(creds->group);
+}
diff --git a/src/core/dynamic-user.h b/src/core/dynamic-user.h
new file mode 100644
index 0000000000..0b8bce1a72
--- /dev/null
+++ b/src/core/dynamic-user.h
@@ -0,0 +1,66 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct DynamicUser DynamicUser;
+
+typedef struct DynamicCreds {
+ /* A combination of a dynamic user and group */
+ DynamicUser *user;
+ DynamicUser *group;
+} DynamicCreds;
+
+#include "manager.h"
+
+/* Note that this object always allocates a pair of user and group under the same name, even if one of them isn't
+ * used. This means, if you want to allocate a group and user pair, and they might have two different names, then you
+ * need to allocated two of these objects. DynamicCreds below makes that easy. */
+struct DynamicUser {
+ int n_ref;
+ Manager *manager;
+
+ /* An AF_UNIX socket pair that contains a datagram containing both the numeric ID assigned, as well as a lock
+ * file fd locking the user ID we picked. */
+ int storage_socket[2];
+
+ char name[];
+};
+
+int dynamic_user_acquire(Manager *m, const char *name, DynamicUser **ret);
+
+int dynamic_user_realize(DynamicUser *d, uid_t *ret);
+int dynamic_user_current(DynamicUser *d, uid_t *ret);
+
+DynamicUser* dynamic_user_ref(DynamicUser *d);
+DynamicUser* dynamic_user_unref(DynamicUser *d);
+DynamicUser* dynamic_user_destroy(DynamicUser *d);
+
+int dynamic_user_serialize(Manager *m, FILE *f, FDSet *fds);
+void dynamic_user_deserialize_one(Manager *m, const char *value, FDSet *fds);
+void dynamic_user_vacuum(Manager *m, bool close_user);
+
+int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret);
+int dynamic_user_lookup_name(Manager *m, const char *name, uid_t *ret);
+
+int dynamic_creds_acquire(DynamicCreds *creds, Manager *m, const char *user, const char *group);
+int dynamic_creds_realize(DynamicCreds *creds, uid_t *uid, gid_t *gid);
+
+void dynamic_creds_unref(DynamicCreds *creds);
+void dynamic_creds_destroy(DynamicCreds *creds);
diff --git a/src/core/execute.c b/src/core/execute.c
index 21721dc240..0c983f4953 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,17 +19,21 @@
#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <signal.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/prctl.h>
-#include <sys/stat.h>
+#include <glob.h>
#include <grp.h>
#include <poll.h>
-#include <glob.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/capability.h>
+#include <sys/eventfd.h>
+#include <sys/mman.h>
#include <sys/personality.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <utmpx.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
@@ -50,45 +52,55 @@
#endif
#include "sd-messages.h"
-#include "rm-rf.h"
-#include "strv.h"
-#include "macro.h"
-#include "capability.h"
-#include "util.h"
-#include "log.h"
-#include "ioprio.h"
-#include "securebits.h"
-#include "namespace.h"
-#include "exit-status.h"
-#include "missing.h"
-#include "utmp-wtmp.h"
+
+#include "af-list.h"
+#include "alloc-util.h"
+#ifdef HAVE_APPARMOR
+#include "apparmor-util.h"
+#endif
+#include "async.h"
+#include "barrier.h"
+#include "cap-list.h"
+#include "capability-util.h"
#include "def.h"
-#include "path-util.h"
#include "env-util.h"
-#include "fileio.h"
-#include "unit.h"
-#include "async.h"
-#include "selinux-util.h"
#include "errno-list.h"
-#include "af-list.h"
-#include "mkdir.h"
-#include "smack-util.h"
-#include "bus-endpoint.h"
-#include "cap-list.h"
+#include "execute.h"
+#include "exit-status.h"
+#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "glob-util.h"
+#include "io-util.h"
+#include "ioprio.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "namespace.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
-#include "terminal-util.h"
-#include "signal-util.h"
-
-#ifdef HAVE_APPARMOR
-#include "apparmor-util.h"
-#endif
-
+#include "rlimit-util.h"
+#include "rm-rf.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
-
-#include "execute.h"
+#include "securebits.h"
+#include "selinux-util.h"
+#include "signal-util.h"
+#include "smack-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "syslog-util.h"
+#include "terminal-util.h"
+#include "unit.h"
+#include "user-util.h"
+#include "util.h"
+#include "utmp-wtmp.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
@@ -121,7 +133,8 @@ static int shift_fds(int fds[], unsigned n_fds) {
if (fds[i] == i+3)
continue;
- if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
+ nfd = fcntl(fds[i], F_DUPFD, i + 3);
+ if (nfd < 0)
return -errno;
safe_close(fds[i]);
@@ -155,48 +168,89 @@ static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
for (i = 0; i < n_fds; i++) {
- if ((r = fd_nonblock(fds[i], nonblock)) < 0)
+ r = fd_nonblock(fds[i], nonblock);
+ if (r < 0)
return r;
/* We unconditionally drop FD_CLOEXEC from the fds,
* since after all we want to pass these fds to our
* children */
- if ((r = fd_cloexec(fds[i], false)) < 0)
+ r = fd_cloexec(fds[i], false);
+ if (r < 0)
return r;
}
return 0;
}
-_pure_ static const char *tty_path(const ExecContext *context) {
+static const char *exec_context_tty_path(const ExecContext *context) {
assert(context);
+ if (context->stdio_as_fds)
+ return NULL;
+
if (context->tty_path)
return context->tty_path;
return "/dev/console";
}
-static void exec_context_tty_reset(const ExecContext *context) {
+static void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) {
+ const char *path;
+
assert(context);
- if (context->tty_vhangup)
- terminal_vhangup(tty_path(context));
+ path = exec_context_tty_path(context);
+
+ if (context->tty_vhangup) {
+ if (p && p->stdin_fd >= 0)
+ (void) terminal_vhangup_fd(p->stdin_fd);
+ else if (path)
+ (void) terminal_vhangup(path);
+ }
- if (context->tty_reset)
- reset_terminal(tty_path(context));
+ if (context->tty_reset) {
+ if (p && p->stdin_fd >= 0)
+ (void) reset_terminal_fd(p->stdin_fd, true);
+ else if (path)
+ (void) reset_terminal(path);
+ }
- if (context->tty_vt_disallocate && context->tty_path)
- vt_disallocate(context->tty_path);
+ if (context->tty_vt_disallocate && path)
+ (void) vt_disallocate(path);
+}
+
+static bool is_terminal_input(ExecInput i) {
+ return IN_SET(i,
+ EXEC_INPUT_TTY,
+ EXEC_INPUT_TTY_FORCE,
+ EXEC_INPUT_TTY_FAIL);
}
static bool is_terminal_output(ExecOutput o) {
- return
- o == EXEC_OUTPUT_TTY ||
- o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
- o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
- o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+ return IN_SET(o,
+ EXEC_OUTPUT_TTY,
+ EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
+ EXEC_OUTPUT_KMSG_AND_CONSOLE,
+ EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
+}
+
+static bool exec_context_needs_term(const ExecContext *c) {
+ assert(c);
+
+ /* Return true if the execution context suggests we should set $TERM to something useful. */
+
+ if (is_terminal_input(c->std_input))
+ return true;
+
+ if (is_terminal_output(c->std_output))
+ return true;
+
+ if (is_terminal_output(c->std_error))
+ return true;
+
+ return !!c->tty_path;
}
static int open_null_as(int flags, int nfd) {
@@ -244,7 +298,7 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
}
}
- r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
+ r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
r = -errno;
@@ -261,7 +315,15 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
return r;
}
-static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
+static int connect_logger_as(
+ Unit *unit,
+ const ExecContext *context,
+ ExecOutput output,
+ const char *ident,
+ int nfd,
+ uid_t uid,
+ gid_t gid) {
+
int fd, r;
assert(context);
@@ -282,7 +344,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
return -errno;
}
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
dprintf(fd,
"%s\n"
@@ -293,18 +355,18 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
"%i\n"
"%i\n",
context->syslog_identifier ? context->syslog_identifier : ident,
- unit_id,
+ unit->id,
context->syslog_priority,
!!context->syslog_level_prefix,
output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
is_terminal_output(output));
- if (fd != nfd) {
- r = dup2(fd, nfd) < 0 ? -errno : nfd;
- safe_close(fd);
- } else
- r = nfd;
+ if (fd == nfd)
+ return nfd;
+
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ safe_close(fd);
return r;
}
@@ -314,7 +376,8 @@ static int open_terminal_as(const char *path, mode_t mode, int nfd) {
assert(path);
assert(nfd >= 0);
- if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0)
+ fd = open_terminal(path, mode | O_NOCTTY);
+ if (fd < 0)
return fd;
if (fd != nfd) {
@@ -326,13 +389,6 @@ static int open_terminal_as(const char *path, mode_t mode, int nfd) {
return r;
}
-static bool is_terminal_input(ExecInput i) {
- return
- i == EXEC_INPUT_TTY ||
- i == EXEC_INPUT_TTY_FORCE ||
- i == EXEC_INPUT_TTY_FAIL;
-}
-
static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
if (is_terminal_input(std_input) && !apply_tty_stdin)
@@ -352,12 +408,28 @@ static int fixup_output(ExecOutput std_output, int socket_fd) {
return std_output;
}
-static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) {
+static int setup_input(
+ const ExecContext *context,
+ const ExecParameters *params,
+ int socket_fd) {
+
ExecInput i;
assert(context);
+ assert(params);
+
+ if (params->stdin_fd >= 0) {
+ if (dup2(params->stdin_fd, STDIN_FILENO) < 0)
+ return -errno;
+
+ /* Try to make this the controlling tty, if it is a tty, and reset it */
+ (void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
+ (void) reset_terminal_fd(STDIN_FILENO, true);
+
+ return STDIN_FILENO;
+ }
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
+ i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
switch (i) {
@@ -369,7 +441,7 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
case EXEC_INPUT_TTY_FAIL: {
int fd, r;
- fd = acquire_terminal(tty_path(context),
+ fd = acquire_terminal(exec_context_tty_path(context),
i == EXEC_INPUT_TTY_FAIL,
i == EXEC_INPUT_TTY_FORCE,
false,
@@ -394,16 +466,45 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
}
}
-static int setup_output(Unit *unit, const ExecContext *context, int fileno, int socket_fd, const char *ident, bool apply_tty_stdin, uid_t uid, gid_t gid) {
+static int setup_output(
+ Unit *unit,
+ const ExecContext *context,
+ const ExecParameters *params,
+ int fileno,
+ int socket_fd,
+ const char *ident,
+ uid_t uid,
+ gid_t gid,
+ dev_t *journal_stream_dev,
+ ino_t *journal_stream_ino) {
+
ExecOutput o;
ExecInput i;
int r;
assert(unit);
assert(context);
+ assert(params);
assert(ident);
+ assert(journal_stream_dev);
+ assert(journal_stream_ino);
+
+ if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) {
+
+ if (dup2(params->stdout_fd, STDOUT_FILENO) < 0)
+ return -errno;
+
+ return STDOUT_FILENO;
+ }
+
+ if (fileno == STDERR_FILENO && params->stderr_fd >= 0) {
+ if (dup2(params->stderr_fd, STDERR_FILENO) < 0)
+ return -errno;
+
+ return STDERR_FILENO;
+ }
- i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
+ i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
o = fixup_output(context->std_output, socket_fd);
if (fileno == STDERR_FILENO) {
@@ -430,7 +531,7 @@ static int setup_output(Unit *unit, const ExecContext *context, int fileno, int
} else if (o == EXEC_OUTPUT_INHERIT) {
/* If input got downgraded, inherit the original value */
if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
- return open_terminal_as(tty_path(context), O_WRONLY, fileno);
+ return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
/* If the input is connected to anything that's not a /dev/null, inherit that... */
if (i != EXEC_INPUT_NULL)
@@ -454,7 +555,7 @@ static int setup_output(Unit *unit, const ExecContext *context, int fileno, int
return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
/* We don't reset the terminal if this is just about output */
- return open_terminal_as(tty_path(context), O_WRONLY, fileno);
+ return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
case EXEC_OUTPUT_SYSLOG:
case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
@@ -462,10 +563,21 @@ static int setup_output(Unit *unit, const ExecContext *context, int fileno, int
case EXEC_OUTPUT_KMSG_AND_CONSOLE:
case EXEC_OUTPUT_JOURNAL:
case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
- r = connect_logger_as(context, o, ident, unit->id, fileno, uid, gid);
+ r = connect_logger_as(unit, context, o, ident, fileno, uid, gid);
if (r < 0) {
log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr");
r = open_null_as(O_WRONLY, fileno);
+ } else {
+ struct stat st;
+
+ /* If we connected this fd to the journal via a stream, patch the device/inode into the passed
+ * parameters, but only then. This is useful so that we can set $JOURNAL_STREAM that permits
+ * services to detect whether they are connected to the journal or not. */
+
+ if (fstat(fileno, &st) >= 0) {
+ *journal_stream_dev = st.st_dev;
+ *journal_stream_ino = st.st_ino;
+ }
}
return r;
@@ -483,6 +595,10 @@ static int chown_terminal(int fd, uid_t uid) {
assert(fd >= 0);
+ /* Before we chown/chmod the TTY, let's ensure this is actually a tty */
+ if (isatty(fd) < 1)
+ return 0;
+
/* This might fail. What matters are the results. */
(void) fchown(fd, uid, -1);
(void) fchmod(fd, TTY_MODE);
@@ -496,9 +612,9 @@ static int chown_terminal(int fd, uid_t uid) {
return 0;
}
-static int setup_confirm_stdio(int *_saved_stdin,
- int *_saved_stdout) {
- int fd = -1, saved_stdin, saved_stdout = -1, r;
+static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
+ _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1;
+ int r;
assert(_saved_stdin);
assert(_saved_stdout);
@@ -508,10 +624,8 @@ static int setup_confirm_stdio(int *_saved_stdin,
return -errno;
saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
- if (saved_stdout < 0) {
- r = errno;
- goto fail;
- }
+ if (saved_stdout < 0)
+ return -errno;
fd = acquire_terminal(
"/dev/console",
@@ -519,39 +633,33 @@ static int setup_confirm_stdio(int *_saved_stdin,
false,
false,
DEFAULT_CONFIRM_USEC);
- if (fd < 0) {
- r = fd;
- goto fail;
- }
+ if (fd < 0)
+ return fd;
r = chown_terminal(fd, getuid());
if (r < 0)
- goto fail;
+ return r;
- if (dup2(fd, STDIN_FILENO) < 0) {
- r = -errno;
- goto fail;
- }
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ return r;
- if (dup2(fd, STDOUT_FILENO) < 0) {
- r = -errno;
- goto fail;
- }
+ if (dup2(fd, STDIN_FILENO) < 0)
+ return -errno;
+
+ if (dup2(fd, STDOUT_FILENO) < 0)
+ return -errno;
if (fd >= 2)
safe_close(fd);
+ fd = -1;
*_saved_stdin = saved_stdin;
*_saved_stdout = saved_stdout;
- return 0;
-
-fail:
- safe_close(saved_stdout);
- safe_close(saved_stdin);
- safe_close(fd);
+ saved_stdin = saved_stdout = -1;
- return r;
+ return 0;
}
_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
@@ -571,9 +679,7 @@ _printf_(1, 2) static int write_confirm_message(const char *format, ...) {
return 0;
}
-static int restore_confirm_stdio(int *saved_stdin,
- int *saved_stdout) {
-
+static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
int r = 0;
assert(saved_stdin);
@@ -589,8 +695,8 @@ static int restore_confirm_stdio(int *saved_stdin,
if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
r = -errno;
- safe_close(*saved_stdin);
- safe_close(*saved_stdout);
+ *saved_stdin = safe_close(*saved_stdin);
+ *saved_stdout = safe_close(*saved_stdout);
return r;
}
@@ -624,14 +730,6 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_
* we avoid NSS lookups for gid=0. */
if (context->group || username) {
-
- if (context->group) {
- const char *g = context->group;
-
- if ((r = get_group_creds(&g, &gid)) < 0)
- return r;
- }
-
/* First step, initialize groups from /etc/groups */
if (username && gid != 0) {
if (initgroups(username, gid) < 0)
@@ -657,7 +755,8 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_
return -ENOMEM;
if (keep_groups) {
- if ((k = getgroups(ngroups_max, gids)) < 0) {
+ k = getgroups(ngroups_max, gids);
+ if (k < 0) {
free(gids);
return -errno;
}
@@ -682,9 +781,10 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_
k++;
}
- if (setgroups(k, gids) < 0) {
+ r = maybe_setgroups(k, gids);
+ if (r < 0) {
free(gids);
- return -errno;
+ return r;
}
free(gids);
@@ -696,15 +796,10 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_
static int enforce_user(const ExecContext *context, uid_t uid) {
assert(context);
- /* Sets (but doesn't lookup) the uid and make sure we keep the
+ /* Sets (but doesn't look up) the uid and make sure we keep the
* capabilities while doing so. */
- if (context->capabilities) {
- _cleanup_cap_free_ cap_t d = NULL;
- static const cap_value_t bits[] = {
- CAP_SETUID, /* Necessary so that we can run setresuid() below */
- CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
- };
+ if (context->capability_ambient_set != 0) {
/* First step: If we need to keep capabilities but
* drop privileges we need to make sure we keep our
@@ -716,23 +811,9 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
if (prctl(PR_SET_SECUREBITS, sb) < 0)
return -errno;
}
-
- /* Second step: set the capabilities. This will reduce
- * the capabilities to the minimum we need. */
-
- d = cap_dup(context->capabilities);
- if (!d)
- return -errno;
-
- if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
- cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
- return -errno;
-
- if (cap_set_proc(d) < 0)
- return -errno;
}
- /* Third step: actually set the uids */
+ /* Second step: actually set the uids */
if (setresuid(uid, uid, uid) < 0)
return -errno;
@@ -757,31 +838,36 @@ static int null_conv(
return PAM_CONV_ERR;
}
+#endif
+
static int setup_pam(
const char *name,
const char *user,
uid_t uid,
+ gid_t gid,
const char *tty,
- char ***pam_env,
+ char ***env,
int fds[], unsigned n_fds) {
+#ifdef HAVE_PAM
+
static const struct pam_conv conv = {
.conv = null_conv,
.appdata_ptr = NULL
};
+ _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
pam_handle_t *handle = NULL;
sigset_t old_ss;
- int pam_code = PAM_SUCCESS;
- int err;
- char **e = NULL;
+ int pam_code = PAM_SUCCESS, r;
+ char **nv, **e = NULL;
bool close_session = false;
pid_t pam_pid = 0, parent_pid;
int flags = 0;
assert(name);
assert(user);
- assert(pam_env);
+ assert(env);
/* We set up PAM in the parent process, then fork. The child
* will then stay around until killed via PR_GET_PDEATHSIG or
@@ -790,6 +876,10 @@ static int setup_pam(
* daemon. We do things this way to ensure that the main PID
* of the daemon is the one we initially fork()ed. */
+ r = barrier_create(&barrier);
+ if (r < 0)
+ goto fail;
+
if (log_get_max_level() < LOG_DEBUG)
flags |= PAM_SILENT;
@@ -805,6 +895,12 @@ static int setup_pam(
goto fail;
}
+ STRV_FOREACH(nv, *env) {
+ pam_code = pam_putenv(handle, *nv);
+ if (pam_code != PAM_SUCCESS)
+ goto fail;
+ }
+
pam_code = pam_acct_mgmt(handle, flags);
if (pam_code != PAM_SUCCESS)
goto fail;
@@ -829,15 +925,17 @@ static int setup_pam(
parent_pid = getpid();
pam_pid = fork();
- if (pam_pid < 0)
+ if (pam_pid < 0) {
+ r = -errno;
goto fail;
+ }
if (pam_pid == 0) {
- int sig;
- int r = EXIT_PAM;
+ int sig, ret = EXIT_PAM;
/* The child's job is to reset the PAM session on
* termination */
+ barrier_set_role(&barrier, BARRIER_CHILD);
/* This string must fit in 10 chars (i.e. the length
* of "/sbin/init"), to look pretty in /bin/ps */
@@ -852,8 +950,14 @@ static int setup_pam(
* and this will make PR_SET_PDEATHSIG work in most cases.
* If this fails, ignore the error - but expect sd-pam threads
* to fail to exit normally */
+
+ r = maybe_setgroups(0, NULL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to setgroups() in sd-pam: %m");
+ if (setresgid(gid, gid, gid) < 0)
+ log_warning_errno(errno, "Failed to setresgid() in sd-pam: %m");
if (setresuid(uid, uid, uid) < 0)
- log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
+ log_warning_errno(errno, "Failed to setresuid() in sd-pam: %m");
(void) ignore_signals(SIGPIPE, -1);
@@ -865,6 +969,11 @@ static int setup_pam(
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
goto child_finish;
+ /* Tell the parent that our setup is done. This is especially
+ * important regarding dropping privileges. Otherwise, unit
+ * setup might race against our setresuid(2) call. */
+ barrier_place(&barrier);
+
/* Check if our parent process might already have
* died? */
if (getppid() == parent_pid) {
@@ -893,13 +1002,15 @@ static int setup_pam(
goto child_finish;
}
- r = 0;
+ ret = 0;
child_finish:
pam_end(handle, pam_code | flags);
- _exit(r);
+ _exit(ret);
}
+ barrier_set_role(&barrier, BARRIER_PARENT);
+
/* If the child was forked off successfully it will do all the
* cleanups, so forget about the handle here. */
handle = NULL;
@@ -911,19 +1022,22 @@ static int setup_pam(
* might have opened it, but we don't want this fd around. */
closelog();
- *pam_env = e;
- e = NULL;
+ /* Synchronously wait for the child to initialize. We don't care for
+ * errors as we cannot recover. However, warn loudly if it happens. */
+ if (!barrier_place_and_sync(&barrier))
+ log_error("PAM initialization failed");
+
+ strv_free(*env);
+ *env = e;
return 0;
fail:
if (pam_code != PAM_SUCCESS) {
log_error("PAM failed: %s", pam_strerror(handle, pam_code));
- err = -EPERM; /* PAM errors do not map to errno */
- } else {
- log_error_errno(errno, "PAM failed: %m");
- err = -errno;
- }
+ r = -EPERM; /* PAM errors do not map to errno */
+ } else
+ log_error_errno(r, "PAM failed: %m");
if (handle) {
if (close_session)
@@ -933,17 +1047,13 @@ fail:
}
strv_free(e);
-
closelog();
- if (pam_pid > 1) {
- kill(pam_pid, SIGTERM);
- kill(pam_pid, SIGCONT);
- }
-
- return err;
-}
+ return r;
+#else
+ return 0;
#endif
+}
static void rename_process_from_path(const char *path) {
char process_name[11];
@@ -978,7 +1088,17 @@ static void rename_process_from_path(const char *path) {
#ifdef HAVE_SECCOMP
-static int apply_seccomp(const ExecContext *c) {
+static bool skip_seccomp_unavailable(const Unit* u, const char* msg) {
+ if (!is_seccomp_available()) {
+ log_open();
+ log_unit_debug(u, "SECCOMP features not detected in the kernel, skipping %s", msg);
+ log_close();
+ return true;
+ }
+ return false;
+}
+
+static int apply_seccomp(const Unit* u, const ExecContext *c) {
uint32_t negative_action, action;
scmp_filter_ctx *seccomp;
Iterator i;
@@ -987,6 +1107,9 @@ static int apply_seccomp(const ExecContext *c) {
assert(c);
+ if (skip_seccomp_unavailable(u, "syscall filtering"))
+ return 0;
+
negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW);
@@ -1027,13 +1150,16 @@ finish:
return r;
}
-static int apply_address_families(const ExecContext *c) {
+static int apply_address_families(const Unit* u, const ExecContext *c) {
scmp_filter_ctx *seccomp;
Iterator i;
int r;
assert(c);
+ if (skip_seccomp_unavailable(u, "RestrictAddressFamilies="))
+ return 0;
+
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return -ENOMEM;
@@ -1148,14 +1274,246 @@ finish:
return r;
}
+static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) {
+ scmp_filter_ctx *seccomp;
+ int r;
+
+ assert(c);
+
+ if (skip_seccomp_unavailable(u, "MemoryDenyWriteExecute="))
+ return 0;
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return -ENOMEM;
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(mmap),
+ 1,
+ SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE));
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(mprotect),
+ 1,
+ SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC));
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_load(seccomp);
+
+finish:
+ seccomp_release(seccomp);
+ return r;
+}
+
+static int apply_restrict_realtime(const Unit* u, const ExecContext *c) {
+ static const int permitted_policies[] = {
+ SCHED_OTHER,
+ SCHED_BATCH,
+ SCHED_IDLE,
+ };
+
+ scmp_filter_ctx *seccomp;
+ unsigned i;
+ int r, p, max_policy = 0;
+
+ assert(c);
+
+ if (skip_seccomp_unavailable(u, "RestrictRealtime="))
+ return 0;
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return -ENOMEM;
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
+ /* Determine the highest policy constant we want to allow */
+ for (i = 0; i < ELEMENTSOF(permitted_policies); i++)
+ if (permitted_policies[i] > max_policy)
+ max_policy = permitted_policies[i];
+
+ /* Go through all policies with lower values than that, and block them -- unless they appear in the
+ * whitelist. */
+ for (p = 0; p < max_policy; p++) {
+ bool good = false;
+
+ /* Check if this is in the whitelist. */
+ for (i = 0; i < ELEMENTSOF(permitted_policies); i++)
+ if (permitted_policies[i] == p) {
+ good = true;
+ break;
+ }
+
+ if (good)
+ continue;
+
+ /* Deny this policy */
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(sched_setscheduler),
+ 1,
+ SCMP_A1(SCMP_CMP_EQ, p));
+ if (r < 0)
+ goto finish;
+ }
+
+ /* Blacklist all other policies, i.e. the ones with higher values. Note that all comparisons are unsigned here,
+ * hence no need no check for < 0 values. */
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(sched_setscheduler),
+ 1,
+ SCMP_A1(SCMP_CMP_GT, max_policy));
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_load(seccomp);
+
+finish:
+ seccomp_release(seccomp);
+ return r;
+}
+
+static int apply_protect_sysctl(Unit *u, const ExecContext *c) {
+ scmp_filter_ctx *seccomp;
+ int r;
+
+ assert(c);
+
+ /* Turn off the legacy sysctl() system call. Many distributions turn this off while building the kernel, but
+ * let's protect even those systems where this is left on in the kernel. */
+
+ if (skip_seccomp_unavailable(u, "ProtectKernelTunables="))
+ return 0;
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return -ENOMEM;
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ SCMP_SYS(_sysctl),
+ 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_load(seccomp);
+
+finish:
+ seccomp_release(seccomp);
+ return r;
+}
+
+static int apply_private_devices(Unit *u, const ExecContext *c) {
+ const SystemCallFilterSet *set;
+ scmp_filter_ctx *seccomp;
+ const char *sys;
+ bool syscalls_found = false;
+ int r;
+
+ assert(c);
+
+ /* If PrivateDevices= is set, also turn off iopl and all @raw-io syscalls. */
+
+ if (skip_seccomp_unavailable(u, "PrivateDevices="))
+ return 0;
+
+ seccomp = seccomp_init(SCMP_ACT_ALLOW);
+ if (!seccomp)
+ return -ENOMEM;
+
+ r = seccomp_add_secondary_archs(seccomp);
+ if (r < 0)
+ goto finish;
+
+ for (set = syscall_filter_sets; set->set_name; set++)
+ if (streq(set->set_name, "@raw-io")) {
+ syscalls_found = true;
+ break;
+ }
+
+ /* We should never fail here */
+ if (!syscalls_found) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ NULSTR_FOREACH(sys, set->value) {
+ int id;
+ bool add = true;
+
+#ifndef __NR_s390_pci_mmio_read
+ if (streq(sys, "s390_pci_mmio_read"))
+ add = false;
+#endif
+#ifndef __NR_s390_pci_mmio_write
+ if (streq(sys, "s390_pci_mmio_write"))
+ add = false;
+#endif
+
+ if (!add)
+ continue;
+
+ id = seccomp_syscall_resolve_name(sys);
+
+ r = seccomp_rule_add(
+ seccomp,
+ SCMP_ACT_ERRNO(EPERM),
+ id, 0);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
+ if (r < 0)
+ goto finish;
+
+ r = seccomp_load(seccomp);
+
+finish:
+ seccomp_release(seccomp);
+ return r;
+}
+
#endif
static void do_idle_pipe_dance(int idle_pipe[4]) {
assert(idle_pipe);
-
- safe_close(idle_pipe[1]);
- safe_close(idle_pipe[2]);
+ idle_pipe[1] = safe_close(idle_pipe[1]);
+ idle_pipe[2] = safe_close(idle_pipe[2]);
if (idle_pipe[0] >= 0) {
int r;
@@ -1163,41 +1521,49 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
+ ssize_t n;
+
/* Signal systemd that we are bored and want to continue. */
- r = write(idle_pipe[3], "x", 1);
- if (r > 0)
+ n = write(idle_pipe[3], "x", 1);
+ if (n > 0)
/* Wait for systemd to react to the signal above. */
fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
}
- safe_close(idle_pipe[0]);
+ idle_pipe[0] = safe_close(idle_pipe[0]);
}
- safe_close(idle_pipe[3]);
+ idle_pipe[3] = safe_close(idle_pipe[3]);
}
static int build_environment(
+ Unit *u,
const ExecContext *c,
+ const ExecParameters *p,
unsigned n_fds,
- usec_t watchdog_usec,
const char *home,
const char *username,
const char *shell,
+ dev_t journal_stream_dev,
+ ino_t journal_stream_ino,
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
unsigned n_env = 0;
char *x;
+ assert(u);
assert(c);
assert(ret);
- our_env = new0(char*, 10);
+ our_env = new0(char*, 14);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
+ _cleanup_free_ char *joined = NULL;
+
if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
@@ -1205,14 +1571,33 @@ static int build_environment(
if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)
return -ENOMEM;
our_env[n_env++] = x;
+
+ joined = strv_join(p->fd_names, ":");
+ if (!joined)
+ return -ENOMEM;
+
+ x = strjoin("LISTEN_FDNAMES=", joined, NULL);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
}
- if (watchdog_usec > 0) {
+ if ((p->flags & EXEC_SET_WATCHDOG) && p->watchdog_usec > 0) {
if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
- if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, watchdog_usec) < 0)
+ if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, p->watchdog_usec) < 0)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
+ /* If this is D-Bus, tell the nss-systemd module, since it relies on being able to use D-Bus look up dynamic
+ * users via PID 1, possibly dead-locking the dbus daemon. This way it will not use D-Bus to resolve names, but
+ * check the database directly. */
+ if (unit_has_name(u, SPECIAL_DBUS_SERVICE)) {
+ x = strdup("SYSTEMD_NSS_BYPASS_BUS=1");
+ if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
@@ -1243,19 +1628,42 @@ static int build_environment(
our_env[n_env++] = x;
}
- if (is_terminal_input(c->std_input) ||
- c->std_output == EXEC_OUTPUT_TTY ||
- c->std_error == EXEC_OUTPUT_TTY ||
- c->tty_path) {
+ if (!sd_id128_is_null(u->invocation_id)) {
+ if (asprintf(&x, "INVOCATION_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id)) < 0)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
+ if (exec_context_needs_term(c)) {
+ const char *tty_path, *term = NULL;
+
+ tty_path = exec_context_tty_path(c);
- x = strdup(default_term_for_tty(tty_path(c)));
+ /* If we are forked off PID 1 and we are supposed to operate on /dev/console, then let's try to inherit
+ * the $TERM set for PID 1. This is useful for containers so that the $TERM the container manager
+ * passes to PID 1 ends up all the way in the console login shown. */
+
+ if (path_equal(tty_path, "/dev/console") && getppid() == 1)
+ term = getenv("TERM");
+ if (!term)
+ term = default_term_for_tty(tty_path);
+
+ x = strappend("TERM=", term);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
+ if (journal_stream_dev != 0 && journal_stream_ino != 0) {
+ if (asprintf(&x, "JOURNAL_STREAM=" DEV_FMT ":" INO_FMT, journal_stream_dev, journal_stream_ino) < 0)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
our_env[n_env++] = NULL;
- assert(n_env <= 10);
+ assert(n_env <= 12);
*ret = our_env;
our_env = NULL;
@@ -1263,6 +1671,34 @@ static int build_environment(
return 0;
}
+static int build_pass_environment(const ExecContext *c, char ***ret) {
+ _cleanup_strv_free_ char **pass_env = NULL;
+ size_t n_env = 0, n_bufsize = 0;
+ char **i;
+
+ STRV_FOREACH(i, c->pass_environment) {
+ _cleanup_free_ char *x = NULL;
+ char *v;
+
+ v = getenv(*i);
+ if (!v)
+ continue;
+ x = strjoin(*i, "=", v, NULL);
+ if (!x)
+ return -ENOMEM;
+ if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2))
+ return -ENOMEM;
+ pass_env[n_env++] = x;
+ pass_env[n_env] = NULL;
+ x = NULL;
+ }
+
+ *ret = pass_env;
+ pass_env = NULL;
+
+ return 0;
+}
+
static bool exec_needs_mount_namespace(
const ExecContext *context,
const ExecParameters *params,
@@ -1271,9 +1707,9 @@ static bool exec_needs_mount_namespace(
assert(context);
assert(params);
- if (!strv_isempty(context->read_write_dirs) ||
- !strv_isempty(context->read_only_dirs) ||
- !strv_isempty(context->inaccessible_dirs))
+ if (!strv_isempty(context->read_write_paths) ||
+ !strv_isempty(context->read_only_paths) ||
+ !strv_isempty(context->inaccessible_paths))
return true;
if (context->mount_flags != 0)
@@ -1282,38 +1718,412 @@ static bool exec_needs_mount_namespace(
if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir))
return true;
- if (params->bus_endpoint_path)
- return true;
-
if (context->private_devices ||
context->protect_system != PROTECT_SYSTEM_NO ||
- context->protect_home != PROTECT_HOME_NO)
+ context->protect_home != PROTECT_HOME_NO ||
+ context->protect_kernel_tunables ||
+ context->protect_control_groups)
return true;
return false;
}
+static int setup_private_users(uid_t uid, gid_t gid) {
+ _cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
+ _cleanup_close_pair_ int errno_pipe[2] = { -1, -1 };
+ _cleanup_close_ int unshare_ready_fd = -1;
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
+ uint64_t c = 1;
+ siginfo_t si;
+ ssize_t n;
+ int r;
+
+ /* Set up a user namespace and map root to root, the selected UID/GID to itself, and everything else to
+ * nobody. In order to be able to write this mapping we need CAP_SETUID in the original user namespace, which
+ * we however lack after opening the user namespace. To work around this we fork() a temporary child process,
+ * which waits for the parent to create the new user namespace while staying in the original namespace. The
+ * child then writes the UID mapping, under full privileges. The parent waits for the child to finish and
+ * continues execution normally. */
+
+ if (uid != 0 && uid_is_valid(uid))
+ asprintf(&uid_map,
+ "0 0 1\n" /* Map root → root */
+ UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */
+ uid, uid); /* The case where the above is the same */
+ else
+ uid_map = strdup("0 0 1\n");
+ if (!uid_map)
+ return -ENOMEM;
+
+ if (gid != 0 && gid_is_valid(gid))
+ asprintf(&gid_map,
+ "0 0 1\n" /* Map root → root */
+ GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */
+ gid, gid);
+ else
+ gid_map = strdup("0 0 1\n"); /* The case where the above is the same */
+ if (!gid_map)
+ return -ENOMEM;
+
+ /* Create a communication channel so that the parent can tell the child when it finished creating the user
+ * namespace. */
+ unshare_ready_fd = eventfd(0, EFD_CLOEXEC);
+ if (unshare_ready_fd < 0)
+ return -errno;
+
+ /* Create a communication channel so that the child can tell the parent a proper error code in case it
+ * failed. */
+ if (pipe2(errno_pipe, O_CLOEXEC) < 0)
+ return -errno;
+
+ pid = fork();
+ if (pid < 0)
+ return -errno;
+
+ if (pid == 0) {
+ _cleanup_close_ int fd = -1;
+ const char *a;
+ pid_t ppid;
+
+ /* Child process, running in the original user namespace. Let's update the parent's UID/GID map from
+ * here, after the parent opened its own user namespace. */
+
+ ppid = getppid();
+ errno_pipe[0] = safe_close(errno_pipe[0]);
+
+ /* Wait until the parent unshared the user namespace */
+ if (read(unshare_ready_fd, &c, sizeof(c)) < 0) {
+ r = -errno;
+ goto child_fail;
+ }
+
+ /* Disable the setgroups() system call in the child user namespace, for good. */
+ a = procfs_file_alloca(ppid, "setgroups");
+ fd = open(a, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ if (errno != ENOENT) {
+ r = -errno;
+ goto child_fail;
+ }
+
+ /* If the file is missing the kernel is too old, let's continue anyway. */
+ } else {
+ if (write(fd, "deny\n", 5) < 0) {
+ r = -errno;
+ goto child_fail;
+ }
+
+ fd = safe_close(fd);
+ }
+
+ /* First write the GID map */
+ a = procfs_file_alloca(ppid, "gid_map");
+ fd = open(a, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ r = -errno;
+ goto child_fail;
+ }
+ if (write(fd, gid_map, strlen(gid_map)) < 0) {
+ r = -errno;
+ goto child_fail;
+ }
+ fd = safe_close(fd);
+
+ /* The write the UID map */
+ a = procfs_file_alloca(ppid, "uid_map");
+ fd = open(a, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ r = -errno;
+ goto child_fail;
+ }
+ if (write(fd, uid_map, strlen(uid_map)) < 0) {
+ r = -errno;
+ goto child_fail;
+ }
+
+ _exit(EXIT_SUCCESS);
+
+ child_fail:
+ (void) write(errno_pipe[1], &r, sizeof(r));
+ _exit(EXIT_FAILURE);
+ }
+
+ errno_pipe[1] = safe_close(errno_pipe[1]);
+
+ if (unshare(CLONE_NEWUSER) < 0)
+ return -errno;
+
+ /* Let the child know that the namespace is ready now */
+ if (write(unshare_ready_fd, &c, sizeof(c)) < 0)
+ return -errno;
+
+ /* Try to read an error code from the child */
+ n = read(errno_pipe[0], &r, sizeof(r));
+ if (n < 0)
+ return -errno;
+ if (n == sizeof(r)) { /* an error code was sent to us */
+ if (r < 0)
+ return r;
+ return -EIO;
+ }
+ if (n != 0) /* on success we should have read 0 bytes */
+ return -EIO;
+
+ r = wait_for_terminate(pid, &si);
+ if (r < 0)
+ return r;
+ pid = 0;
+
+ /* If something strange happened with the child, let's consider this fatal, too */
+ if (si.si_code != CLD_EXITED || si.si_status != 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int setup_runtime_directory(
+ const ExecContext *context,
+ const ExecParameters *params,
+ uid_t uid,
+ gid_t gid) {
+
+ char **rt;
+ int r;
+
+ assert(context);
+ assert(params);
+
+ STRV_FOREACH(rt, context->runtime_directory) {
+ _cleanup_free_ char *p;
+
+ p = strjoin(params->runtime_prefix, "/", *rt, NULL);
+ if (!p)
+ return -ENOMEM;
+
+ r = mkdir_p_label(p, context->runtime_directory_mode);
+ if (r < 0)
+ return r;
+
+ r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int setup_smack(
+ const ExecContext *context,
+ const ExecCommand *command) {
+
+#ifdef HAVE_SMACK
+ int r;
+
+ assert(context);
+ assert(command);
+
+ if (!mac_smack_use())
+ return 0;
+
+ if (context->smack_process_label) {
+ r = mac_smack_apply_pid(0, context->smack_process_label);
+ if (r < 0)
+ return r;
+ }
+#ifdef SMACK_DEFAULT_PROCESS_LABEL
+ else {
+ _cleanup_free_ char *exec_label = NULL;
+
+ r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label);
+ if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP)
+ return r;
+
+ r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL);
+ if (r < 0)
+ return r;
+ }
+#endif
+#endif
+
+ return 0;
+}
+
+static int compile_read_write_paths(
+ const ExecContext *context,
+ const ExecParameters *params,
+ char ***ret) {
+
+ _cleanup_strv_free_ char **l = NULL;
+ char **rt;
+
+ /* Compile the list of writable paths. This is the combination of the explicitly configured paths, plus all
+ * runtime directories. */
+
+ if (strv_isempty(context->read_write_paths) &&
+ strv_isempty(context->runtime_directory)) {
+ *ret = NULL; /* NOP if neither is set */
+ return 0;
+ }
+
+ l = strv_copy(context->read_write_paths);
+ if (!l)
+ return -ENOMEM;
+
+ STRV_FOREACH(rt, context->runtime_directory) {
+ char *s;
+
+ s = strjoin(params->runtime_prefix, "/", *rt, NULL);
+ if (!s)
+ return -ENOMEM;
+
+ if (strv_consume(&l, s) < 0)
+ return -ENOMEM;
+ }
+
+ *ret = l;
+ l = NULL;
+
+ return 0;
+}
+
+static void append_socket_pair(int *array, unsigned *n, int pair[2]) {
+ assert(array);
+ assert(n);
+
+ if (!pair)
+ return;
+
+ if (pair[0] >= 0)
+ array[(*n)++] = pair[0];
+ if (pair[1] >= 0)
+ array[(*n)++] = pair[1];
+}
+
+static int close_remaining_fds(
+ const ExecParameters *params,
+ ExecRuntime *runtime,
+ DynamicCreds *dcreds,
+ int user_lookup_fd,
+ int socket_fd,
+ int *fds, unsigned n_fds) {
+
+ unsigned n_dont_close = 0;
+ int dont_close[n_fds + 12];
+
+ assert(params);
+
+ if (params->stdin_fd >= 0)
+ dont_close[n_dont_close++] = params->stdin_fd;
+ if (params->stdout_fd >= 0)
+ dont_close[n_dont_close++] = params->stdout_fd;
+ if (params->stderr_fd >= 0)
+ dont_close[n_dont_close++] = params->stderr_fd;
+
+ if (socket_fd >= 0)
+ dont_close[n_dont_close++] = socket_fd;
+ if (n_fds > 0) {
+ memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
+ n_dont_close += n_fds;
+ }
+
+ if (runtime)
+ append_socket_pair(dont_close, &n_dont_close, runtime->netns_storage_socket);
+
+ if (dcreds) {
+ if (dcreds->user)
+ append_socket_pair(dont_close, &n_dont_close, dcreds->user->storage_socket);
+ if (dcreds->group)
+ append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket);
+ }
+
+ if (user_lookup_fd >= 0)
+ dont_close[n_dont_close++] = user_lookup_fd;
+
+ return close_all_fds(dont_close, n_dont_close);
+}
+
+static bool context_has_address_families(const ExecContext *c) {
+ assert(c);
+
+ return c->address_families_whitelist ||
+ !set_isempty(c->address_families);
+}
+
+static bool context_has_syscall_filters(const ExecContext *c) {
+ assert(c);
+
+ return c->syscall_whitelist ||
+ !set_isempty(c->syscall_filter) ||
+ !set_isempty(c->syscall_archs);
+}
+
+static bool context_has_no_new_privileges(const ExecContext *c) {
+ assert(c);
+
+ if (c->no_new_privileges)
+ return true;
+
+ if (have_effective_cap(CAP_SYS_ADMIN)) /* if we are privileged, we don't need NNP */
+ return false;
+
+ return context_has_address_families(c) || /* we need NNP if we have any form of seccomp and are unprivileged */
+ c->memory_deny_write_execute ||
+ c->restrict_realtime ||
+ c->protect_kernel_tunables ||
+ context_has_syscall_filters(c);
+}
+
+static int send_user_lookup(
+ Unit *unit,
+ int user_lookup_fd,
+ uid_t uid,
+ gid_t gid) {
+
+ assert(unit);
+
+ /* Send the resolved UID/GID to PID 1 after we learnt it. We send a single datagram, containing the UID/GID
+ * data as well as the unit name. Note that we suppress sending this if no user/group to resolve was
+ * specified. */
+
+ if (user_lookup_fd < 0)
+ return 0;
+
+ if (!uid_is_valid(uid) && !gid_is_valid(gid))
+ return 0;
+
+ if (writev(user_lookup_fd,
+ (struct iovec[]) {
+ { .iov_base = &uid, .iov_len = sizeof(uid) },
+ { .iov_base = &gid, .iov_len = sizeof(gid) },
+ { .iov_base = unit->id, .iov_len = strlen(unit->id) }}, 3) < 0)
+ return -errno;
+
+ return 0;
+}
+
static int exec_child(
Unit *unit,
ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
+ DynamicCreds *dcreds,
char **argv,
int socket_fd,
int *fds, unsigned n_fds,
char **files_env,
+ int user_lookup_fd,
int *exit_status) {
- _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
+ _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
_cleanup_free_ char *mac_selinux_context_net = NULL;
- const char *username = NULL, *home = NULL, *shell = NULL;
- unsigned n_dont_close = 0;
- int dont_close[n_fds + 4];
+ const char *username = NULL, *home = NULL, *shell = NULL, *wd;
+ dev_t journal_stream_dev = 0;
+ ino_t journal_stream_ino = 0;
+ bool needs_mount_namespace;
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
- bool needs_mount_namespace;
assert(unit);
assert(command);
@@ -1349,22 +2159,7 @@ static int exec_child(
log_forget_fds();
- if (socket_fd >= 0)
- dont_close[n_dont_close++] = socket_fd;
- if (n_fds > 0) {
- memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
- n_dont_close += n_fds;
- }
- if (params->bus_endpoint_fd >= 0)
- dont_close[n_dont_close++] = params->bus_endpoint_fd;
- if (runtime) {
- if (runtime->netns_storage_socket[0] >= 0)
- dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
- if (runtime->netns_storage_socket[1] >= 0)
- dont_close[n_dont_close++] = runtime->netns_storage_socket[1];
- }
-
- r = close_all_fds(dont_close, n_dont_close);
+ r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
@@ -1376,9 +2171,9 @@ static int exec_child(
return -errno;
}
- exec_context_tty_reset(context);
+ exec_context_tty_reset(context, params);
- if (params->confirm_spawn) {
+ if (params->flags & EXEC_CONFIRM_SPAWN) {
char response;
r = ask_for_confirmation(&response, argv);
@@ -1397,33 +2192,78 @@ static int exec_child(
}
}
- if (context->user) {
- username = context->user;
- r = get_user_creds(&username, &uid, &gid, &home, &shell);
+ if (context->dynamic_user && dcreds) {
+
+ /* Make sure we bypass our own NSS module for any NSS checks */
+ if (putenv((char*) "SYSTEMD_NSS_DYNAMIC_BYPASS=1") != 0) {
+ *exit_status = EXIT_USER;
+ return -errno;
+ }
+
+ r = dynamic_creds_realize(dcreds, &uid, &gid);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
+
+ if (!uid_is_valid(uid) || !gid_is_valid(gid)) {
+ *exit_status = EXIT_USER;
+ return -ESRCH;
+ }
+
+ if (dcreds->user)
+ username = dcreds->user->name;
+
+ } else {
+ if (context->user) {
+ username = context->user;
+ r = get_user_creds_clean(&username, &uid, &gid, &home, &shell);
+ if (r < 0) {
+ *exit_status = EXIT_USER;
+ return r;
+ }
+
+ /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway
+ * (i.e. are "/" or "/bin/nologin"). */
+ }
+
+ if (context->group) {
+ const char *g = context->group;
+
+ r = get_group_creds(&g, &gid);
+ if (r < 0) {
+ *exit_status = EXIT_GROUP;
+ return r;
+ }
+ }
+ }
+
+ r = send_user_lookup(unit, user_lookup_fd, uid, gid);
+ if (r < 0) {
+ *exit_status = EXIT_USER;
+ return r;
}
+ user_lookup_fd = safe_close(user_lookup_fd);
+
/* If a socket is connected to STDIN/STDOUT/STDERR, we
* must sure to drop O_NONBLOCK */
if (socket_fd >= 0)
- fd_nonblock(socket_fd, false);
+ (void) fd_nonblock(socket_fd, false);
- r = setup_input(context, socket_fd, params->apply_tty_stdin);
+ r = setup_input(context, params, socket_fd);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
- r = setup_output(unit, context, STDOUT_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
+ r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
- r = setup_output(unit, context, STDERR_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
+ r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
@@ -1504,9 +2344,13 @@ static int exec_child(
}
if (context->utmp_id)
- utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path);
+ utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path,
+ context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS :
+ context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS :
+ USER_PROCESS,
+ username ? "root" : context->user);
- if (context->user && is_terminal_input(context->std_input)) {
+ if (context->user) {
r = chown_terminal(STDIN_FILENO, uid);
if (r < 0) {
*exit_status = EXIT_STDIN;
@@ -1514,16 +2358,6 @@ static int exec_child(
}
}
- if (params->bus_endpoint_fd >= 0 && context->bus_endpoint) {
- uid_t ep_uid = (uid == UID_INVALID) ? 0 : uid;
-
- r = bus_kernel_set_endpoint_policy(params->bus_endpoint_fd, ep_uid, context->bus_endpoint);
- if (r < 0) {
- *exit_status = EXIT_BUS_ENDPOINT;
- return r;
- }
- }
-
/* If delegation is enabled we'll pass ownership of the cgroup
* (but only in systemd's own controller hierarchy!) to the
* user of the new process. */
@@ -1543,44 +2377,65 @@ static int exec_child(
}
if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) {
- char **rt;
-
- STRV_FOREACH(rt, context->runtime_directory) {
- _cleanup_free_ char *p;
+ r = setup_runtime_directory(context, params, uid, gid);
+ if (r < 0) {
+ *exit_status = EXIT_RUNTIME_DIRECTORY;
+ return r;
+ }
+ }
- p = strjoin(params->runtime_prefix, "/", *rt, NULL);
- if (!p) {
- *exit_status = EXIT_RUNTIME_DIRECTORY;
- return -ENOMEM;
- }
+ r = build_environment(
+ unit,
+ context,
+ params,
+ n_fds,
+ home,
+ username,
+ shell,
+ journal_stream_dev,
+ journal_stream_ino,
+ &our_env);
+ if (r < 0) {
+ *exit_status = EXIT_MEMORY;
+ return r;
+ }
- r = mkdir_safe_label(p, context->runtime_directory_mode, uid, gid);
- if (r < 0) {
- *exit_status = EXIT_RUNTIME_DIRECTORY;
- return r;
- }
- }
+ r = build_pass_environment(context, &pass_env);
+ if (r < 0) {
+ *exit_status = EXIT_MEMORY;
+ return r;
}
- if (params->apply_permissions) {
- r = enforce_groups(context, username, gid);
- if (r < 0) {
- *exit_status = EXIT_GROUP;
- return r;
- }
+ accum_env = strv_env_merge(5,
+ params->environment,
+ our_env,
+ pass_env,
+ context->environment,
+ files_env,
+ NULL);
+ if (!accum_env) {
+ *exit_status = EXIT_MEMORY;
+ return -ENOMEM;
}
+ accum_env = strv_env_clean(accum_env);
- umask(context->umask);
+ (void) umask(context->umask);
-#ifdef HAVE_PAM
- if (params->apply_permissions && context->pam_name && username) {
- r = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds);
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
+ r = setup_smack(context, command);
if (r < 0) {
- *exit_status = EXIT_PAM;
+ *exit_status = EXIT_SMACK_PROCESS_LABEL;
return r;
}
+
+ if (context->pam_name && username) {
+ r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds);
+ if (r < 0) {
+ *exit_status = EXIT_PAM;
+ return r;
+ }
+ }
}
-#endif
if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) {
r = setup_netns(runtime->netns_storage_socket);
@@ -1591,8 +2446,8 @@ static int exec_child(
}
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
-
if (needs_mount_namespace) {
+ _cleanup_free_ char **rw = NULL;
char *tmp = NULL, *var = NULL;
/* The runtime struct only contains the parent
@@ -1608,15 +2463,22 @@ static int exec_child(
var = strjoina(runtime->var_tmp_dir, "/tmp");
}
+ r = compile_read_write_paths(context, params, &rw);
+ if (r < 0) {
+ *exit_status = EXIT_NAMESPACE;
+ return r;
+ }
+
r = setup_namespace(
- params->apply_chroot ? context->root_directory : NULL,
- context->read_write_dirs,
- context->read_only_dirs,
- context->inaccessible_dirs,
+ (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL,
+ rw,
+ context->read_only_paths,
+ context->inaccessible_paths,
tmp,
var,
- params->bus_endpoint_path,
context->private_devices,
+ context->protect_kernel_tunables,
+ context->protect_control_groups,
context->protect_home,
context->protect_system,
context->mount_flags);
@@ -1634,28 +2496,37 @@ static int exec_child(
}
}
- if (params->apply_chroot) {
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
+ r = enforce_groups(context, username, gid);
+ if (r < 0) {
+ *exit_status = EXIT_GROUP;
+ return r;
+ }
+ }
+
+ if (context->working_directory_home)
+ wd = home;
+ else if (context->working_directory)
+ wd = context->working_directory;
+ else
+ wd = "/";
+
+ if (params->flags & EXEC_APPLY_CHROOT) {
if (!needs_mount_namespace && context->root_directory)
if (chroot(context->root_directory) < 0) {
*exit_status = EXIT_CHROOT;
return -errno;
}
- if (chdir(context->working_directory ?: "/") < 0 &&
+ if (chdir(wd) < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
return -errno;
}
} else {
- _cleanup_free_ char *d = NULL;
-
- if (asprintf(&d, "%s/%s",
- context->root_directory ?: "",
- context->working_directory ?: "") < 0) {
- *exit_status = EXIT_MEMORY;
- return -ENOMEM;
- }
+ const char *d;
+ d = strjoina(strempty(context->root_directory), "/", strempty(wd));
if (chdir(d) < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
@@ -1664,7 +2535,12 @@ static int exec_child(
}
#ifdef HAVE_SELINUX
- if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0) {
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) &&
+ mac_selinux_use() &&
+ params->selinux_context_net &&
+ socket_fd >= 0 &&
+ !command->privileged) {
+
r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
@@ -1673,6 +2549,14 @@ static int exec_child(
}
#endif
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && context->private_users) {
+ r = setup_private_users(uid, gid);
+ if (r < 0) {
+ *exit_status = EXIT_USER;
+ return r;
+ }
+ }
+
/* We repeat the fd closing here, to make sure that
* nothing is leaked from the PAM modules. Note that
* we are more aggressive this time since socket_fd
@@ -1689,44 +2573,47 @@ static int exec_child(
return r;
}
- if (params->apply_permissions) {
+ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
+
+ int secure_bits = context->secure_bits;
for (i = 0; i < _RLIMIT_MAX; i++) {
+
if (!context->rlimit[i])
continue;
- if (setrlimit_closest(i, context->rlimit[i]) < 0) {
+ r = setrlimit_closest(i, context->rlimit[i]);
+ if (r < 0) {
*exit_status = EXIT_LIMITS;
- return -errno;
+ return r;
}
}
- if (context->capability_bounding_set_drop) {
- r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
- if (r < 0) {
- *exit_status = EXIT_CAPABILITIES;
- return r;
+ /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly requested. */
+ if (context->restrict_realtime && !context->rlimit[RLIMIT_RTPRIO]) {
+ if (setrlimit(RLIMIT_RTPRIO, &RLIMIT_MAKE_CONST(0)) < 0) {
+ *exit_status = EXIT_LIMITS;
+ return -errno;
}
}
-#ifdef HAVE_SMACK
- if (context->smack_process_label) {
- r = mac_smack_apply_pid(0, context->smack_process_label);
+ if (!cap_test_all(context->capability_bounding_set)) {
+ r = capability_bounding_set_drop(context->capability_bounding_set, false);
if (r < 0) {
- *exit_status = EXIT_SMACK_PROCESS_LABEL;
+ *exit_status = EXIT_CAPABILITIES;
return r;
}
}
-#ifdef SMACK_DEFAULT_PROCESS_LABEL
- else {
- r = mac_smack_apply_pid(0, SMACK_DEFAULT_PROCESS_LABEL);
+
+ /* This is done before enforce_user, but ambient set
+ * does not survive over setresuid() if keep_caps is not set. */
+ if (context->capability_ambient_set != 0) {
+ r = capability_ambient_set_apply(context->capability_ambient_set, true);
if (r < 0) {
- *exit_status = EXIT_SMACK_PROCESS_LABEL;
+ *exit_status = EXIT_CAPABILITIES;
return r;
}
}
-#endif
-#endif
if (context->user) {
r = enforce_user(context, uid);
@@ -1734,44 +2621,85 @@ static int exec_child(
*exit_status = EXIT_USER;
return r;
}
+ if (context->capability_ambient_set != 0) {
+
+ /* Fix the ambient capabilities after user change. */
+ r = capability_ambient_set_apply(context->capability_ambient_set, false);
+ if (r < 0) {
+ *exit_status = EXIT_CAPABILITIES;
+ return r;
+ }
+
+ /* If we were asked to change user and ambient capabilities
+ * were requested, we had to add keep-caps to the securebits
+ * so that we would maintain the inherited capability set
+ * through the setresuid(). Make sure that the bit is added
+ * also to the context secure_bits so that we don't try to
+ * drop the bit away next. */
+
+ secure_bits |= 1<<SECURE_KEEP_CAPS;
+ }
}
/* PR_GET_SECUREBITS is not privileged, while
* PR_SET_SECUREBITS is. So to suppress
* potential EPERMs we'll try not to call
* PR_SET_SECUREBITS unless necessary. */
- if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
- if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
+ if (prctl(PR_GET_SECUREBITS) != secure_bits)
+ if (prctl(PR_SET_SECUREBITS, secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS;
return -errno;
}
- if (context->capabilities)
- if (cap_set_proc(context->capabilities) < 0) {
- *exit_status = EXIT_CAPABILITIES;
- return -errno;
- }
-
- if (context->no_new_privileges)
+ if (context_has_no_new_privileges(context))
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
*exit_status = EXIT_NO_NEW_PRIVILEGES;
return -errno;
}
#ifdef HAVE_SECCOMP
- if (context->address_families_whitelist ||
- !set_isempty(context->address_families)) {
- r = apply_address_families(context);
+ if (context_has_address_families(context)) {
+ r = apply_address_families(unit, context);
if (r < 0) {
*exit_status = EXIT_ADDRESS_FAMILIES;
return r;
}
}
- if (context->syscall_whitelist ||
- !set_isempty(context->syscall_filter) ||
- !set_isempty(context->syscall_archs)) {
- r = apply_seccomp(context);
+ if (context->memory_deny_write_execute) {
+ r = apply_memory_deny_write_execute(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return r;
+ }
+ }
+
+ if (context->restrict_realtime) {
+ r = apply_restrict_realtime(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return r;
+ }
+ }
+
+ if (context->protect_kernel_tunables) {
+ r = apply_protect_sysctl(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return r;
+ }
+ }
+
+ if (context->private_devices) {
+ r = apply_private_devices(unit, context);
+ if (r < 0) {
+ *exit_status = EXIT_SECCOMP;
+ return r;
+ }
+ }
+
+ if (context_has_syscall_filters(context)) {
+ r = apply_seccomp(unit, context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return r;
@@ -1804,32 +2732,12 @@ static int exec_child(
#endif
}
- r = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env);
- if (r < 0) {
- *exit_status = EXIT_MEMORY;
- return r;
- }
-
- final_env = strv_env_merge(5,
- params->environment,
- our_env,
- context->environment,
- files_env,
- pam_env,
- NULL);
- if (!final_env) {
- *exit_status = EXIT_MEMORY;
- return -ENOMEM;
- }
-
- final_argv = replace_env_argv(argv, final_env);
+ final_argv = replace_env_argv(argv, accum_env);
if (!final_argv) {
*exit_status = EXIT_MEMORY;
return -ENOMEM;
}
- final_env = strv_env_clean(final_env);
-
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
_cleanup_free_ char *line;
@@ -1845,7 +2753,7 @@ static int exec_child(
}
}
- execve(command->path, final_argv, final_env);
+ execve(command->path, final_argv, accum_env);
*exit_status = EXIT_EXEC;
return -errno;
}
@@ -1855,6 +2763,7 @@ int exec_spawn(Unit *unit,
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
+ DynamicCreds *dcreds,
pid_t *ret) {
_cleanup_strv_free_ char **files_env = NULL;
@@ -1903,7 +2812,7 @@ int exec_spawn(Unit *unit,
NULL);
pid = fork();
if (pid < 0)
- return log_unit_error_errno(unit, r, "Failed to fork: %m");
+ return log_unit_error_errno(unit, errno, "Failed to fork: %m");
if (pid == 0) {
int exit_status;
@@ -1913,10 +2822,12 @@ int exec_spawn(Unit *unit,
context,
params,
runtime,
+ dcreds,
argv,
socket_fd,
fds, n_fds,
files_env,
+ unit->manager->user_lookup_fds[1],
&exit_status);
if (r < 0) {
log_open();
@@ -1961,6 +2872,7 @@ void exec_context_init(ExecContext *c) {
c->timer_slack_nsec = NSEC_INFINITY;
c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
+ c->capability_bounding_set = CAP_ALL;
}
void exec_context_done(ExecContext *c) {
@@ -1968,80 +2880,40 @@ void exec_context_done(ExecContext *c) {
assert(c);
- strv_free(c->environment);
- c->environment = NULL;
-
- strv_free(c->environment_files);
- c->environment_files = NULL;
-
- for (l = 0; l < ELEMENTSOF(c->rlimit); l++) {
- free(c->rlimit[l]);
- c->rlimit[l] = NULL;
- }
-
- free(c->working_directory);
- c->working_directory = NULL;
- free(c->root_directory);
- c->root_directory = NULL;
-
- free(c->tty_path);
- c->tty_path = NULL;
+ c->environment = strv_free(c->environment);
+ c->environment_files = strv_free(c->environment_files);
+ c->pass_environment = strv_free(c->pass_environment);
- free(c->syslog_identifier);
- c->syslog_identifier = NULL;
+ for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
+ c->rlimit[l] = mfree(c->rlimit[l]);
- free(c->user);
- c->user = NULL;
+ c->working_directory = mfree(c->working_directory);
+ c->root_directory = mfree(c->root_directory);
+ c->tty_path = mfree(c->tty_path);
+ c->syslog_identifier = mfree(c->syslog_identifier);
+ c->user = mfree(c->user);
+ c->group = mfree(c->group);
- free(c->group);
- c->group = NULL;
+ c->supplementary_groups = strv_free(c->supplementary_groups);
- strv_free(c->supplementary_groups);
- c->supplementary_groups = NULL;
+ c->pam_name = mfree(c->pam_name);
- free(c->pam_name);
- c->pam_name = NULL;
-
- if (c->capabilities) {
- cap_free(c->capabilities);
- c->capabilities = NULL;
- }
-
- strv_free(c->read_only_dirs);
- c->read_only_dirs = NULL;
-
- strv_free(c->read_write_dirs);
- c->read_write_dirs = NULL;
-
- strv_free(c->inaccessible_dirs);
- c->inaccessible_dirs = NULL;
+ c->read_only_paths = strv_free(c->read_only_paths);
+ c->read_write_paths = strv_free(c->read_write_paths);
+ c->inaccessible_paths = strv_free(c->inaccessible_paths);
if (c->cpuset)
CPU_FREE(c->cpuset);
- free(c->utmp_id);
- c->utmp_id = NULL;
-
- free(c->selinux_context);
- c->selinux_context = NULL;
-
- free(c->apparmor_profile);
- c->apparmor_profile = NULL;
+ c->utmp_id = mfree(c->utmp_id);
+ c->selinux_context = mfree(c->selinux_context);
+ c->apparmor_profile = mfree(c->apparmor_profile);
- set_free(c->syscall_filter);
- c->syscall_filter = NULL;
+ c->syscall_filter = set_free(c->syscall_filter);
+ c->syscall_archs = set_free(c->syscall_archs);
+ c->address_families = set_free(c->address_families);
- set_free(c->syscall_archs);
- c->syscall_archs = NULL;
-
- set_free(c->address_families);
- c->address_families = NULL;
-
- strv_free(c->runtime_directory);
- c->runtime_directory = NULL;
-
- bus_endpoint_free(c->bus_endpoint);
- c->bus_endpoint = NULL;
+ c->runtime_directory = strv_free(c->runtime_directory);
}
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) {
@@ -2071,11 +2943,9 @@ int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_p
void exec_command_done(ExecCommand *c) {
assert(c);
- free(c->path);
- c->path = NULL;
+ c->path = mfree(c->path);
- strv_free(c->argv);
- c->argv = NULL;
+ c->argv = strv_free(c->argv);
}
void exec_command_done_array(ExecCommand *c, unsigned n) {
@@ -2133,7 +3003,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
if (fn[0] == '-') {
ignore = true;
- fn ++;
+ fn++;
}
if (!path_is_absolute(fn)) {
@@ -2151,7 +3021,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
continue;
strv_free(r);
- return errno ? -errno : -EINVAL;
+ return errno > 0 ? -errno : -EINVAL;
}
count = pglob.gl_pathc;
if (count == 0) {
@@ -2203,7 +3073,10 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
static bool tty_may_match_dev_console(const char *tty) {
_cleanup_free_ char *active = NULL;
- char *console;
+ char *console;
+
+ if (!tty)
+ return true;
if (startswith(tty, "/dev/"))
tty += 5;
@@ -2222,11 +3095,14 @@ static bool tty_may_match_dev_console(const char *tty) {
}
bool exec_context_may_touch_console(ExecContext *ec) {
- return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate ||
+
+ return (ec->tty_reset ||
+ ec->tty_vhangup ||
+ ec->tty_vt_disallocate ||
is_terminal_input(ec->std_input) ||
is_terminal_output(ec->std_output) ||
is_terminal_output(ec->std_error)) &&
- tty_may_match_dev_console(tty_path(ec));
+ tty_may_match_dev_console(exec_context_tty_path(ec));
}
static void strv_fprintf(FILE *f, char **l) {
@@ -2239,7 +3115,7 @@ static void strv_fprintf(FILE *f, char **l) {
}
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
- char **e;
+ char **e, **d;
unsigned i;
assert(c);
@@ -2253,21 +3129,31 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sRootDirectory: %s\n"
"%sNonBlocking: %s\n"
"%sPrivateTmp: %s\n"
- "%sPrivateNetwork: %s\n"
"%sPrivateDevices: %s\n"
+ "%sProtectKernelTunables: %s\n"
+ "%sProtectControlGroups: %s\n"
+ "%sPrivateNetwork: %s\n"
+ "%sPrivateUsers: %s\n"
"%sProtectHome: %s\n"
"%sProtectSystem: %s\n"
- "%sIgnoreSIGPIPE: %s\n",
+ "%sIgnoreSIGPIPE: %s\n"
+ "%sMemoryDenyWriteExecute: %s\n"
+ "%sRestrictRealtime: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
prefix, c->root_directory ? c->root_directory : "/",
prefix, yes_no(c->non_blocking),
prefix, yes_no(c->private_tmp),
- prefix, yes_no(c->private_network),
prefix, yes_no(c->private_devices),
+ prefix, yes_no(c->protect_kernel_tunables),
+ prefix, yes_no(c->protect_control_groups),
+ prefix, yes_no(c->private_network),
+ prefix, yes_no(c->private_users),
prefix, protect_home_to_string(c->protect_home),
prefix, protect_system_to_string(c->protect_system),
- prefix, yes_no(c->ignore_sigpipe));
+ prefix, yes_no(c->ignore_sigpipe),
+ prefix, yes_no(c->memory_deny_write_execute),
+ prefix, yes_no(c->restrict_realtime));
STRV_FOREACH(e, c->environment)
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
@@ -2275,6 +3161,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
STRV_FOREACH(e, c->environment_files)
fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
+ STRV_FOREACH(e, c->pass_environment)
+ fprintf(f, "%sPassEnvironment: %s\n", prefix, *e);
+
+ fprintf(f, "%sRuntimeDirectoryMode: %04o\n", prefix, c->runtime_directory_mode);
+
+ STRV_FOREACH(d, c->runtime_directory)
+ fprintf(f, "%sRuntimeDirectory: %s\n", prefix, *d);
+
if (c->nice_set)
fprintf(f,
"%sNice: %i\n",
@@ -2286,9 +3180,12 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, c->oom_score_adjust);
for (i = 0; i < RLIM_NLIMITS; i++)
- if (c->rlimit[i])
- fprintf(f, "%s%s: "RLIM_FMT"\n",
+ if (c->rlimit[i]) {
+ fprintf(f, "%s%s: " RLIM_FMT "\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
+ fprintf(f, "%s%sSoft: " RLIM_FMT "\n",
+ prefix, rlimit_to_string(i), c->rlimit[i]->rlim_cur);
+ }
if (c->ioprio_set) {
_cleanup_free_ char *class_str = NULL;
@@ -2369,14 +3266,6 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, strna(lvl_str));
}
- if (c->capabilities) {
- _cleanup_cap_free_charp_ char *t;
-
- t = cap_to_text(c->capabilities, NULL);
- if (t)
- fprintf(f, "%sCapabilities: %s\n", prefix, t);
- }
-
if (c->secure_bits)
fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
prefix,
@@ -2387,12 +3276,23 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
- if (c->capability_bounding_set_drop) {
+ if (c->capability_bounding_set != CAP_ALL) {
unsigned long l;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
- if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
+ if (c->capability_bounding_set & (UINT64_C(1) << l))
+ fprintf(f, " %s", strna(capability_to_name(l)));
+
+ fputs("\n", f);
+ }
+
+ if (c->capability_ambient_set != 0) {
+ unsigned long l;
+ fprintf(f, "%sAmbientCapabilities:", prefix);
+
+ for (l = 0; l <= cap_last_cap(); l++)
+ if (c->capability_ambient_set & (UINT64_C(1) << l))
fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);
@@ -2403,6 +3303,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->group)
fprintf(f, "%sGroup: %s\n", prefix, c->group);
+ fprintf(f, "%sDynamicUser: %s\n", prefix, yes_no(c->dynamic_user));
+
if (strv_length(c->supplementary_groups) > 0) {
fprintf(f, "%sSupplementaryGroups:", prefix);
strv_fprintf(f, c->supplementary_groups);
@@ -2412,21 +3314,21 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
if (c->pam_name)
fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
- if (strv_length(c->read_write_dirs) > 0) {
- fprintf(f, "%sReadWriteDirs:", prefix);
- strv_fprintf(f, c->read_write_dirs);
+ if (strv_length(c->read_write_paths) > 0) {
+ fprintf(f, "%sReadWritePaths:", prefix);
+ strv_fprintf(f, c->read_write_paths);
fputs("\n", f);
}
- if (strv_length(c->read_only_dirs) > 0) {
- fprintf(f, "%sReadOnlyDirs:", prefix);
- strv_fprintf(f, c->read_only_dirs);
+ if (strv_length(c->read_only_paths) > 0) {
+ fprintf(f, "%sReadOnlyPaths:", prefix);
+ strv_fprintf(f, c->read_only_paths);
fputs("\n", f);
}
- if (strv_length(c->inaccessible_dirs) > 0) {
- fprintf(f, "%sInaccessibleDirs:", prefix);
- strv_fprintf(f, c->inaccessible_dirs);
+ if (strv_length(c->inaccessible_paths) > 0) {
+ fprintf(f, "%sInaccessiblePaths:", prefix);
+ strv_fprintf(f, c->inaccessible_paths);
fputs("\n", f);
}
@@ -2493,7 +3395,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
fputc('\n', f);
}
- if (c->syscall_errno != 0)
+ if (c->syscall_errno > 0)
fprintf(f,
"%sSystemCallErrorNumber: %s\n",
prefix, strna(errno_to_name(c->syscall_errno)));
@@ -2507,7 +3409,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
bool exec_context_maintains_privileges(ExecContext *c) {
assert(c);
- /* Returns true if the process forked off would run run under
+ /* Returns true if the process forked off would run under
* an unchanged UID or as root. */
if (!c->user)
@@ -2543,7 +3445,7 @@ void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code,
if (context->utmp_id)
utmp_put_dead_process(context->utmp_id, pid, code, status);
- exec_context_tty_reset(context);
+ exec_context_tty_reset(context, NULL);
}
}
@@ -2562,12 +3464,12 @@ void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
"%sPID: "PID_FMT"\n",
prefix, s->pid);
- if (s->start_timestamp.realtime > 0)
+ if (dual_timestamp_is_set(&s->start_timestamp))
fprintf(f,
"%sStart Timestamp: %s\n",
prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
- if (s->exit_timestamp.realtime > 0)
+ if (dual_timestamp_is_set(&s->exit_timestamp))
fprintf(f,
"%sExit Timestamp: %s\n"
"%sExit Code: %s\n"
@@ -2701,7 +3603,7 @@ int exec_command_append(ExecCommand *c, const char *path, ...) {
if (!l)
return -ENOMEM;
- r = strv_extend_strv(&c->argv, l);
+ r = strv_extend_strv(&c->argv, l, false);
if (r < 0)
return r;
@@ -2742,7 +3644,7 @@ int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) {
return r;
if (c->private_network && (*rt)->netns_storage_socket[0] < 0) {
- if (socketpair(AF_UNIX, SOCK_DGRAM, 0, (*rt)->netns_storage_socket) < 0)
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, (*rt)->netns_storage_socket) < 0)
return -errno;
}
@@ -2954,3 +3856,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
+
+static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
+ [EXEC_UTMP_INIT] = "init",
+ [EXEC_UTMP_LOGIN] = "login",
+ [EXEC_UTMP_USER] = "user",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
diff --git a/src/core/execute.h b/src/core/execute.h
index f5d5c1dee7..449180c903 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -27,16 +25,24 @@ typedef struct ExecContext ExecContext;
typedef struct ExecRuntime ExecRuntime;
typedef struct ExecParameters ExecParameters;
-#include <sys/capability.h>
+#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
-#include <sched.h>
+#include <sys/capability.h>
-#include "list.h"
+#include "cgroup-util.h"
#include "fdset.h"
+#include "list.h"
#include "missing.h"
#include "namespace.h"
-#include "bus-endpoint.h"
+
+typedef enum ExecUtmpMode {
+ EXEC_UTMP_INIT,
+ EXEC_UTMP_LOGIN,
+ EXEC_UTMP_USER,
+ _EXEC_UTMP_MODE_MAX,
+ _EXEC_UTMP_MODE_INVALID = -1
+} ExecUtmpMode;
typedef enum ExecInput {
EXEC_INPUT_NULL,
@@ -76,7 +82,8 @@ struct ExecCommand {
char **argv;
ExecStatus exec_status;
LIST_FIELDS(ExecCommand, command); /* useful for chaining commands */
- bool ignore;
+ bool ignore:1;
+ bool privileged:1;
};
struct ExecRuntime {
@@ -85,16 +92,20 @@ struct ExecRuntime {
char *tmp_dir;
char *var_tmp_dir;
+ /* An AF_UNIX socket pair, that contains a datagram containing a file descriptor referring to the network
+ * namespace. */
int netns_storage_socket[2];
};
struct ExecContext {
char **environment;
char **environment_files;
+ char **pass_environment;
struct rlimit *rlimit[_RLIMIT_MAX];
char *working_directory, *root_directory;
bool working_directory_missing_ok;
+ bool working_directory_home;
mode_t umask;
int oom_score_adjust;
@@ -112,6 +123,8 @@ struct ExecContext {
nsec_t timer_slack_nsec;
+ bool stdio_as_fds;
+
char *tty_path;
bool tty_reset;
@@ -120,7 +133,7 @@ struct ExecContext {
bool ignore_sigpipe;
- /* Since resolving these names might might involve socket
+ /* Since resolving these names might involve socket
* connections and we don't want to deadlock ourselves these
* names are resolved on execution only and in the child
* process. */
@@ -131,6 +144,7 @@ struct ExecContext {
char *pam_name;
char *utmp_id;
+ ExecUtmpMode utmp_mode;
bool selinux_context_ignore;
char *selinux_context;
@@ -141,12 +155,11 @@ struct ExecContext {
bool smack_process_label_ignore;
char *smack_process_label;
- char **read_write_dirs, **read_only_dirs, **inaccessible_dirs;
+ char **read_write_paths, **read_only_paths, **inaccessible_paths;
unsigned long mount_flags;
- uint64_t capability_bounding_set_drop;
-
- cap_t capabilities;
+ uint64_t capability_bounding_set;
+ uint64_t capability_ambient_set;
int secure_bits;
int syslog_priority;
@@ -158,11 +171,17 @@ struct ExecContext {
bool private_tmp;
bool private_network;
bool private_devices;
+ bool private_users;
ProtectSystem protect_system;
ProtectHome protect_home;
+ bool protect_kernel_tunables;
+ bool protect_control_groups;
bool no_new_privileges;
+ bool dynamic_user;
+ bool remove_ipc;
+
/* This is not exposed to the user but available
* internally. We need it to make sure that whenever we spawn
* /usr/bin/mount it is run in the same process group as us so
@@ -183,43 +202,64 @@ struct ExecContext {
char **runtime_directory;
mode_t runtime_directory_mode;
+ bool memory_deny_write_execute;
+ bool restrict_realtime;
+
bool oom_score_adjust_set:1;
bool nice_set:1;
bool ioprio_set:1;
bool cpu_sched_set:1;
bool no_new_privileges_set:1;
-
- /* custom dbus enpoint */
- BusEndpoint *bus_endpoint;
};
-#include "cgroup.h"
-#include "cgroup-util.h"
+typedef enum ExecFlags {
+ EXEC_CONFIRM_SPAWN = 1U << 0,
+ EXEC_APPLY_PERMISSIONS = 1U << 1,
+ EXEC_APPLY_CHROOT = 1U << 2,
+ EXEC_APPLY_TTY_STDIN = 1U << 3,
+
+ /* The following are not used by execute.c, but by consumers internally */
+ EXEC_PASS_FDS = 1U << 4,
+ EXEC_IS_CONTROL = 1U << 5,
+ EXEC_SETENV_RESULT = 1U << 6,
+ EXEC_SET_WATCHDOG = 1U << 7,
+} ExecFlags;
struct ExecParameters {
char **argv;
- int *fds; unsigned n_fds;
char **environment;
- bool apply_permissions;
- bool apply_chroot;
- bool apply_tty_stdin;
- bool confirm_spawn;
- bool selinux_context_net;
- CGroupControllerMask cgroup_supported;
+
+ int *fds;
+ char **fd_names;
+ unsigned n_fds;
+
+ ExecFlags flags;
+ bool selinux_context_net:1;
+
+ bool cgroup_delegate:1;
+ CGroupMask cgroup_supported;
const char *cgroup_path;
- bool cgroup_delegate;
+
const char *runtime_prefix;
+
usec_t watchdog_usec;
+
int *idle_pipe;
- char *bus_endpoint_path;
- int bus_endpoint_fd;
+
+ int stdin_fd;
+ int stdout_fd;
+ int stderr_fd;
};
+#include "unit.h"
+#include "dynamic-user.h"
+
int exec_spawn(Unit *unit,
ExecCommand *command,
const ExecContext *context,
const ExecParameters *exec_params,
ExecRuntime *runtime,
+ DynamicCreds *dynamic_creds,
pid_t *ret);
void exec_command_done(ExecCommand *c);
@@ -265,3 +305,6 @@ ExecOutput exec_output_from_string(const char *s) _pure_;
const char* exec_input_to_string(ExecInput i) _const_;
ExecInput exec_input_from_string(const char *s) _pure_;
+
+const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_;
+ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_;
diff --git a/src/core/failure-action.c b/src/core/failure-action.c
index b06a7d2ae5..ddae46190f 100644
--- a/src/core/failure-action.c
+++ b/src/core/failure-action.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -23,16 +21,17 @@
#include <sys/reboot.h>
#include <linux/reboot.h>
-#include "bus-util.h"
#include "bus-error.h"
-#include "special.h"
+#include "bus-util.h"
#include "failure-action.h"
+#include "special.h"
+#include "string-table.h"
#include "terminal-util.h"
static void log_and_status(Manager *m, const char *message) {
log_warning("%s", message);
manager_status_printf(m, STATUS_TYPE_EMERGENCY,
- ANSI_HIGHLIGHT_RED_ON " !! " ANSI_HIGHLIGHT_OFF,
+ ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL,
"%s", message);
}
@@ -41,8 +40,6 @@ int failure_action(
FailureAction action,
const char *reboot_arg) {
- int r;
-
assert(m);
assert(action >= 0);
assert(action < _FAILURE_ACTION_MAX);
@@ -50,7 +47,7 @@ int failure_action(
if (action == FAILURE_ACTION_NONE)
return -ECANCELED;
- if (m->running_as == MANAGER_USER) {
+ if (!MANAGER_IS_SYSTEM(m)) {
/* Downgrade all options to simply exiting if we run
* in user mode */
@@ -61,24 +58,20 @@ int failure_action(
switch (action) {
- case FAILURE_ACTION_REBOOT: {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
+ case FAILURE_ACTION_REBOOT:
log_and_status(m, "Rebooting as result of failure.");
- update_reboot_param_file(reboot_arg);
- r = manager_add_job_by_name(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, true, &error, NULL);
- if (r < 0)
- log_error("Failed to reboot: %s.", bus_error_message(&error, r));
+ (void) update_reboot_parameter_and_warn(reboot_arg);
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break;
- }
case FAILURE_ACTION_REBOOT_FORCE:
log_and_status(m, "Forcibly rebooting as result of failure.");
- update_reboot_param_file(reboot_arg);
+ (void) update_reboot_parameter_and_warn(reboot_arg);
m->exit_code = MANAGER_REBOOT;
+
break;
case FAILURE_ACTION_REBOOT_IMMEDIATE:
@@ -86,26 +79,20 @@ int failure_action(
sync();
- if (reboot_arg) {
+ if (!isempty(reboot_arg)) {
log_info("Rebooting with argument '%s'.", reboot_arg);
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg);
+ log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
}
log_info("Rebooting.");
reboot(RB_AUTOBOOT);
break;
- case FAILURE_ACTION_POWEROFF: {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
+ case FAILURE_ACTION_POWEROFF:
log_and_status(m, "Powering off as result of failure.");
-
- r = manager_add_job_by_name(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, true, &error, NULL);
- if (r < 0)
- log_error("Failed to poweroff: %s.", bus_error_message(&error, r));
-
+ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break;
- }
case FAILURE_ACTION_POWEROFF_FORCE:
log_and_status(m, "Forcibly powering off as result of failure.");
diff --git a/src/core/failure-action.h b/src/core/failure-action.h
index 1af4dd987b..1adac4ad5c 100644
--- a/src/core/failure-action.h
+++ b/src/core/failure-action.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 932ddbf95a..68be52856b 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,16 +17,18 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
-#include "macro.h"
-#include "util.h"
-#include "log.h"
+#include "alloc-util.h"
#include "fileio.h"
-#include "hostname-util.h"
#include "hostname-setup.h"
+#include "hostname-util.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "util.h"
int hostname_setup(void) {
int r;
@@ -59,8 +59,9 @@ int hostname_setup(void) {
hn = "localhost";
}
- if (sethostname_idempotent(hn) < 0)
- return log_warning_errno(errno, "Failed to set hostname to <%s>: %m", hn);
+ r = sethostname_idempotent(hn);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
log_info("Set hostname to <%s>.", hn);
return 0;
diff --git a/src/core/hostname-setup.h b/src/core/hostname-setup.h
index 8dc3a9e1d8..73e8c75c71 100644
--- a/src/core/hostname-setup.h
+++ b/src/core/hostname-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c
index 42a3e97459..d1b0ce76ef 100644
--- a/src/core/ima-setup.c
+++ b/src/core/ima-setup.c
@@ -1,11 +1,9 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
+ TORSEC group — http://security.polito.it
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
@@ -21,12 +19,14 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <errno.h>
+#include <unistd.h>
+#include "fd-util.h"
+#include "fileio.h"
#include "ima-setup.h"
-#include "util.h"
#include "log.h"
+#include "util.h"
#define IMA_SECFS_DIR "/sys/kernel/security/ima"
#define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy"
diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h
index 14b56d1fc2..472b58cb00 100644
--- a/src/core/ima-setup.h
+++ b/src/core/ima-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -7,7 +5,7 @@
Copyright 2010 Lennart Poettering
Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy
- TORSEC group -- http://security.polito.it
+ TORSEC group — http://security.polito.it
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
diff --git a/src/core/job.c b/src/core/job.c
index 1448e5b69a..7faf2ef686 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -23,17 +21,25 @@
#include "sd-id128.h"
#include "sd-messages.h"
-#include "set.h"
-#include "unit.h"
-#include "macro.h"
-#include "strv.h"
-#include "log.h"
-#include "dbus-job.h"
-#include "special.h"
+
+#include "alloc-util.h"
#include "async.h"
-#include "virt.h"
+#include "dbus-job.h"
#include "dbus.h"
+#include "escape.h"
+#include "job.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "set.h"
+#include "special.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
#include "terminal-util.h"
+#include "unit.h"
+#include "virt.h"
Job* job_new_raw(Unit *unit) {
Job *j;
@@ -131,7 +137,7 @@ void job_uninstall(Job *j) {
/* Detach from next 'bigger' objects */
/* daemon-reload should be transparent to job observers */
- if (j->manager->n_reloading <= 0)
+ if (!MANAGER_IS_RELOADING(j->manager))
bus_job_send_removed_signal(j);
*pj = NULL;
@@ -168,7 +174,6 @@ static void job_merge_into_installed(Job *j, Job *other) {
else
assert(other->type == JOB_NOP);
- j->override = j->override || other->override;
j->irreversible = j->irreversible || other->irreversible;
j->ignore_order = j->ignore_order || other->ignore_order;
}
@@ -186,7 +191,7 @@ Job* job_install(Job *j) {
if (uj) {
if (job_type_is_conflicting(uj->type, j->type))
- job_finish_and_invalidate(uj, JOB_CANCELED, false);
+ job_finish_and_invalidate(uj, JOB_CANCELED, false, false);
else {
/* not conflicting, i.e. mergeable */
@@ -217,7 +222,7 @@ Job* job_install(Job *j) {
*pj = j;
j->installed = true;
- j->manager->n_installed_jobs ++;
+ j->manager->n_installed_jobs++;
log_unit_debug(j->unit,
"Installed new job %s/%s as %u",
j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
@@ -300,12 +305,10 @@ void job_dump(Job *j, FILE*f, const char *prefix) {
"%s-> Job %u:\n"
"%s\tAction: %s -> %s\n"
"%s\tState: %s\n"
- "%s\tForced: %s\n"
"%s\tIrreversible: %s\n",
prefix, j->id,
prefix, j->unit->id, job_type_to_string(j->type),
prefix, job_state_to_string(j->state),
- prefix, yes_no(j->override),
prefix, yes_no(j->irreversible));
}
@@ -400,6 +403,13 @@ JobType job_type_collapse(JobType t, Unit *u) {
return JOB_RESTART;
+ case JOB_TRY_RELOAD:
+ s = unit_active_state(u);
+ if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
+ return JOB_NOP;
+
+ return JOB_RELOAD;
+
case JOB_RELOAD_OR_START:
s = unit_active_state(u);
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
@@ -495,10 +505,58 @@ static void job_change_type(Job *j, JobType newtype) {
j->type = newtype;
}
+static int job_perform_on_unit(Job **j) {
+ uint32_t id;
+ Manager *m;
+ JobType t;
+ Unit *u;
+ int r;
+
+ /* While we execute this operation the job might go away (for
+ * example: because it finishes immediately or is replaced by
+ * a new, conflicting job.) To make sure we don't access a
+ * freed job later on we store the id here, so that we can
+ * verify the job is still valid. */
+
+ assert(j);
+ assert(*j);
+
+ m = (*j)->manager;
+ u = (*j)->unit;
+ t = (*j)->type;
+ id = (*j)->id;
+
+ switch (t) {
+ case JOB_START:
+ r = unit_start(u);
+ break;
+
+ case JOB_RESTART:
+ t = JOB_STOP;
+ /* fall through */
+ case JOB_STOP:
+ r = unit_stop(u);
+ break;
+
+ case JOB_RELOAD:
+ r = unit_reload(u);
+ break;
+
+ default:
+ assert_not_reached("Invalid job type");
+ }
+
+ /* Log if the job still exists and the start/stop/reload function
+ * actually did something. */
+ *j = manager_get_job(m, id);
+ if (*j && r > 0)
+ unit_status_emit_starting_stopping_reloading(u, t);
+
+ return r;
+}
+
int job_run_and_invalidate(Job *j) {
int r;
- uint32_t id;
- Manager *m = j->manager;
assert(j);
assert(j->installed);
@@ -517,23 +575,9 @@ int job_run_and_invalidate(Job *j) {
job_set_state(j, JOB_RUNNING);
job_add_to_dbus_queue(j);
- /* While we execute this operation the job might go away (for
- * example: because it is replaced by a new, conflicting
- * job.) To make sure we don't access a freed job later on we
- * store the id here, so that we can verify the job is still
- * valid. */
- id = j->id;
switch (j->type) {
- case JOB_START:
- r = unit_start(j->unit);
-
- /* If this unit cannot be started, then simply wait */
- if (r == -EBADR)
- r = 0;
- break;
-
case JOB_VERIFY_ACTIVE: {
UnitActiveState t = unit_active_state(j->unit);
if (UNIT_IS_ACTIVE_OR_RELOADING(t))
@@ -545,17 +589,19 @@ int job_run_and_invalidate(Job *j) {
break;
}
+ case JOB_START:
case JOB_STOP:
case JOB_RESTART:
- r = unit_stop(j->unit);
+ r = job_perform_on_unit(&j);
- /* If this unit cannot stopped, then simply wait. */
+ /* If the unit type does not support starting/stopping,
+ * then simply wait. */
if (r == -EBADR)
r = 0;
break;
case JOB_RELOAD:
- r = unit_reload(j->unit);
+ r = job_perform_on_unit(&j);
break;
case JOB_NOP:
@@ -566,183 +612,146 @@ int job_run_and_invalidate(Job *j) {
assert_not_reached("Unknown job type");
}
- j = manager_get_job(m, id);
if (j) {
if (r == -EALREADY)
- r = job_finish_and_invalidate(j, JOB_DONE, true);
+ r = job_finish_and_invalidate(j, JOB_DONE, true, true);
else if (r == -EBADR)
- r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
+ r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false);
else if (r == -ENOEXEC)
- r = job_finish_and_invalidate(j, JOB_INVALID, true);
+ r = job_finish_and_invalidate(j, JOB_INVALID, true, false);
else if (r == -EPROTO)
- r = job_finish_and_invalidate(j, JOB_ASSERT, true);
+ r = job_finish_and_invalidate(j, JOB_ASSERT, true, false);
else if (r == -EOPNOTSUPP)
- r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true);
+ r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false);
else if (r == -EAGAIN)
job_set_state(j, JOB_WAITING);
else if (r < 0)
- r = job_finish_and_invalidate(j, JOB_FAILED, true);
+ r = job_finish_and_invalidate(j, JOB_FAILED, true, false);
}
return r;
}
_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
- const UnitStatusMessageFormats *format_table;
-
- assert(u);
- assert(t >= 0);
- assert(t < _JOB_TYPE_MAX);
-
- format_table = &UNIT_VTABLE(u)->status_message_formats;
- if (!format_table)
- 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];
+ static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
+ [JOB_DONE] = "Started %s.",
+ [JOB_TIMEOUT] = "Timed out starting %s.",
+ [JOB_FAILED] = "Failed to start %s.",
+ [JOB_DEPENDENCY] = "Dependency failed for %s.",
+ [JOB_ASSERT] = "Assertion failed for %s.",
+ [JOB_UNSUPPORTED] = "Starting of %s not supported.",
+ };
+ static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
+ [JOB_DONE] = "Stopped %s.",
+ [JOB_FAILED] = "Stopped (with error) %s.",
+ [JOB_TIMEOUT] = "Timed out stopping %s.",
+ };
+ static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = {
+ [JOB_DONE] = "Reloaded %s.",
+ [JOB_FAILED] = "Reload failed for %s.",
+ [JOB_TIMEOUT] = "Timed out reloading %s.",
+ };
+ /* When verify-active detects the unit is inactive, report it.
+ * Most likely a DEPEND warning from a requisiting unit will
+ * occur next and it's nice to see what was requisited. */
+ static const char *const generic_finished_verify_active_job[_JOB_RESULT_MAX] = {
+ [JOB_SKIPPED] = "%s is not active.",
+ };
- return NULL;
-}
-
-_pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
+ const UnitStatusMessageFormats *format_table;
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;
+ if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
+ format_table = &UNIT_VTABLE(u)->status_message_formats;
+ if (format_table) {
+ format = t == JOB_START ? format_table->finished_start_job[result] :
+ format_table->finished_stop_job[result];
+ if (format)
+ return format;
+ }
+ }
/* Return generic strings */
- if (t == JOB_START) {
- if (result == JOB_DONE)
- return "Started %s.";
- else if (result == JOB_TIMEOUT)
- return "Timed out starting %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_ASSERT)
- return "Assertion failed for %s.";
- else if (result == JOB_UNSUPPORTED)
- return "Starting of %s not supported.";
- } 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.";
- }
+ if (t == JOB_START)
+ return generic_finished_start_job[result];
+ else if (t == JOB_STOP || t == JOB_RESTART)
+ return generic_finished_stop_job[result];
+ else if (t == JOB_RELOAD)
+ return generic_finished_reload_job[result];
+ else if (t == JOB_VERIFY_ACTIVE)
+ return generic_finished_verify_active_job[result];
return NULL;
}
static void job_print_status_message(Unit *u, JobType t, JobResult result) {
+ static struct {
+ const char *color, *word;
+ } const statuses[_JOB_RESULT_MAX] = {
+ [JOB_DONE] = {ANSI_GREEN, " OK "},
+ [JOB_TIMEOUT] = {ANSI_HIGHLIGHT_RED, " TIME "},
+ [JOB_FAILED] = {ANSI_HIGHLIGHT_RED, "FAILED"},
+ [JOB_DEPENDENCY] = {ANSI_HIGHLIGHT_YELLOW, "DEPEND"},
+ [JOB_SKIPPED] = {ANSI_HIGHLIGHT, " INFO "},
+ [JOB_ASSERT] = {ANSI_HIGHLIGHT_YELLOW, "ASSERT"},
+ [JOB_UNSUPPORTED] = {ANSI_HIGHLIGHT_YELLOW, "UNSUPP"},
+ };
+
const char *format;
+ const char *status;
assert(u);
assert(t >= 0);
assert(t < _JOB_TYPE_MAX);
- DISABLE_WARNING_FORMAT_NONLITERAL;
-
- if (t == JOB_START) {
- format = job_get_status_message_format(u, t, result);
- if (!format)
- return;
-
- switch (result) {
-
- case JOB_DONE:
- if (u->condition_result)
- unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
- break;
-
- case JOB_TIMEOUT:
- manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
- break;
-
- case JOB_FAILED: {
- _cleanup_free_ char *quoted = NULL;
-
- quoted = shell_maybe_quote(u->id);
-
- manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
- break;
- }
-
- case JOB_DEPENDENCY:
- manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
- break;
-
- case JOB_ASSERT:
- manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format);
- break;
-
- case JOB_UNSUPPORTED:
- manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF, format);
- break;
-
- default:
- ;
- }
-
- } else if (t == JOB_STOP || t == JOB_RESTART) {
-
- format = job_get_status_message_format(u, t, result);
- if (!format)
- return;
+ /* Reload status messages have traditionally not been printed to console. */
+ if (t == JOB_RELOAD)
+ return;
- switch (result) {
+ format = job_get_status_message_format(u, t, result);
+ if (!format)
+ return;
- case JOB_TIMEOUT:
- manager_flip_auto_status(u->manager, true);
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
- break;
+ if (log_get_show_color())
+ status = strjoina(statuses[result].color, statuses[result].word, ANSI_NORMAL);
+ else
+ status = statuses[result].word;
- case JOB_DONE:
- case JOB_FAILED:
- unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
- break;
+ if (result != JOB_DONE)
+ manager_flip_auto_status(u->manager, true);
- default:
- ;
- }
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ unit_status_printf(u, status, format);
+ REENABLE_WARNING;
- } else if (t == JOB_VERIFY_ACTIVE) {
+ if (t == JOB_START && result == JOB_FAILED) {
+ _cleanup_free_ char *quoted;
- /* When verify-active detects the unit is inactive, report it.
- * Most likely a DEPEND warning from a requisiting unit will
- * occur next and it's nice to see what was requisited. */
- if (result == JOB_SKIPPED)
- unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.");
+ quoted = shell_maybe_quote(u->id);
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
}
-
- REENABLE_WARNING;
}
static void job_log_status_message(Unit *u, JobType t, JobResult result) {
const char *format;
char buf[LINE_MAX];
+ sd_id128_t mid;
+ static const int job_result_log_level[_JOB_RESULT_MAX] = {
+ [JOB_DONE] = LOG_INFO,
+ [JOB_CANCELED] = LOG_INFO,
+ [JOB_TIMEOUT] = LOG_ERR,
+ [JOB_FAILED] = LOG_ERR,
+ [JOB_DEPENDENCY] = LOG_WARNING,
+ [JOB_SKIPPED] = LOG_NOTICE,
+ [JOB_INVALID] = LOG_INFO,
+ [JOB_ASSERT] = LOG_WARNING,
+ [JOB_UNSUPPORTED] = LOG_WARNING,
+ };
assert(u);
assert(t >= 0);
@@ -754,40 +763,54 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
if (log_on_console())
return;
- format = job_get_status_message_format_try_harder(u, t, result);
+ format = job_get_status_message_format(u, t, result);
if (!format)
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buf, sizeof(buf), format, unit_description(u));
+ xsprintf(buf, format, unit_description(u));
REENABLE_WARNING;
- if (t == JOB_START) {
- sd_id128_t mid;
+ switch (t) {
+ case JOB_START:
mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
- LOG_MESSAGE_ID(mid),
- LOG_UNIT_ID(u),
- LOG_MESSAGE("%s", buf),
- "RESULT=%s", job_result_to_string(result),
- NULL);
+ break;
- } else if (t == JOB_STOP)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
- LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
- LOG_UNIT_ID(u),
- LOG_MESSAGE("%s", buf),
- "RESULT=%s", job_result_to_string(result),
- NULL);
+ case JOB_RELOAD:
+ mid = SD_MESSAGE_UNIT_RELOADED;
+ break;
- else if (t == JOB_RELOAD)
- log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
- LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
+ case JOB_STOP:
+ case JOB_RESTART:
+ mid = SD_MESSAGE_UNIT_STOPPED;
+ break;
+
+ default:
+ log_struct(job_result_log_level[result],
LOG_UNIT_ID(u),
LOG_MESSAGE("%s", buf),
"RESULT=%s", job_result_to_string(result),
NULL);
+ return;
+ }
+
+ log_struct(job_result_log_level[result],
+ LOG_MESSAGE_ID(mid),
+ LOG_UNIT_ID(u),
+ LOG_MESSAGE("%s", buf),
+ "RESULT=%s", job_result_to_string(result),
+ NULL);
+}
+
+static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
+
+ /* No message if the job did not actually do anything due to failed condition. */
+ if (t == JOB_START && result == JOB_DONE && !u->condition_result)
+ return;
+
+ job_log_status_message(u, t, result);
+ job_print_status_message(u, t, result);
}
static void job_fail_dependencies(Unit *u, UnitDependency d) {
@@ -804,11 +827,11 @@ static void job_fail_dependencies(Unit *u, UnitDependency d) {
if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
continue;
- job_finish_and_invalidate(j, JOB_DEPENDENCY, true);
+ job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
}
}
-int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
+int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
Unit *u;
Unit *other;
JobType t;
@@ -825,8 +848,9 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
log_unit_debug(u, "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);
+ /* If this job did nothing to respective unit we don't log the status message */
+ if (!already)
+ job_emit_status_message(u, t, result);
job_add_to_dbus_queue(j);
@@ -842,7 +866,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
}
if (result == JOB_FAILED || result == JOB_INVALID)
- j->manager->n_failed_jobs ++;
+ j->manager->n_failed_jobs++;
job_uninstall(j);
job_free(j);
@@ -853,8 +877,6 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
job_fail_dependencies(u, UNIT_REQUIRED_BY);
job_fail_dependencies(u, UNIT_REQUISITE_OF);
job_fail_dependencies(u, UNIT_BOUND_BY);
- job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE);
- job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE);
} else if (t == JOB_STOP)
job_fail_dependencies(u, UNIT_CONFLICTED_BY);
}
@@ -903,7 +925,7 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user
log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
u = j->unit;
- job_finish_and_invalidate(j, JOB_TIMEOUT, true);
+ job_finish_and_invalidate(j, JOB_TIMEOUT, true, false);
failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg);
@@ -918,14 +940,14 @@ int job_start_timer(Job *j) {
j->begin_usec = now(CLOCK_MONOTONIC);
- if (j->unit->job_timeout <= 0)
+ if (j->unit->job_timeout == USEC_INFINITY)
return 0;
r = sd_event_add_time(
j->manager->event,
&j->timer_event_source,
CLOCK_MONOTONIC,
- j->begin_usec + j->unit->job_timeout, 0,
+ usec_add(j->begin_usec, j->unit->job_timeout), 0,
job_dispatch_timer, j);
if (r < 0)
return r;
@@ -975,11 +997,13 @@ char *job_dbus_path(Job *j) {
return p;
}
-int job_serialize(Job *j, FILE *f, FDSet *fds) {
+int job_serialize(Job *j, FILE *f) {
+ assert(j);
+ assert(f);
+
fprintf(f, "job-id=%u\n", j->id);
fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
- fprintf(f, "job-override=%s\n", yes_no(j->override));
fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
@@ -987,15 +1011,16 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) {
if (j->begin_usec > 0)
fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
- bus_track_serialize(j->clients, f);
+ bus_track_serialize(j->clients, f, "subscribed");
/* End marker */
fputc('\n', f);
return 0;
}
-int job_deserialize(Job *j, FILE *f, FDSet *fds) {
+int job_deserialize(Job *j, FILE *f) {
assert(j);
+ assert(f);
for (;;) {
char line[LINE_MAX], *l, *v;
@@ -1047,15 +1072,6 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
else
job_set_state(j, s);
- } else if (streq(l, "job-override")) {
- int b;
-
- b = parse_boolean(v);
- if (b < 0)
- log_debug("Failed to parse job override flag %s", v);
- else
- j->override = j->override || b;
-
} else if (streq(l, "job-irreversible")) {
int b;
@@ -1094,7 +1110,7 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
} else if (streq(l, "subscribed")) {
if (strv_extend(&j->deserialized_clients, v) < 0)
- return log_oom();
+ log_oom();
}
}
}
@@ -1106,24 +1122,22 @@ int job_coldplug(Job *j) {
/* After deserialization is complete and the bus connection
* set up again, let's start watching our subscribers again */
- r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
- if (r < 0)
- return r;
+ (void) bus_track_coldplug(j->manager, &j->clients, false, j->deserialized_clients);
+ j->deserialized_clients = strv_free(j->deserialized_clients);
if (j->state == JOB_WAITING)
job_add_to_run_queue(j);
- if (j->begin_usec == 0 || j->unit->job_timeout == 0)
+ if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY)
return 0;
- if (j->timer_event_source)
- j->timer_event_source = sd_event_source_unref(j->timer_event_source);
+ j->timer_event_source = sd_event_source_unref(j->timer_event_source);
r = sd_event_add_time(
j->manager->event,
&j->timer_event_source,
CLOCK_MONOTONIC,
- j->begin_usec + j->unit->job_timeout, 0,
+ usec_add(j->begin_usec, j->unit->job_timeout), 0,
job_dispatch_timer, j);
if (r < 0)
log_debug_errno(r, "Failed to restart timeout for job: %m");
@@ -1147,7 +1161,7 @@ void job_shutdown_magic(Job *j) {
if (j->type != JOB_START)
return;
- if (j->unit->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(j->unit->manager))
return;
if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
@@ -1156,16 +1170,16 @@ void job_shutdown_magic(Job *j) {
/* In case messages on console has been disabled on boot */
j->unit->manager->no_console_output = false;
- if (detect_container(NULL) > 0)
+ if (detect_container() > 0)
return;
asynchronous_sync();
}
-int job_get_timeout(Job *j, uint64_t *timeout) {
+int job_get_timeout(Job *j, usec_t *timeout) {
+ usec_t x = USEC_INFINITY, y = USEC_INFINITY;
Unit *u = j->unit;
- uint64_t x = -1, y = -1;
- int r = 0, q = 0;
+ int r;
assert(u);
@@ -1173,20 +1187,18 @@ int job_get_timeout(Job *j, uint64_t *timeout) {
r = sd_event_source_get_time(j->timer_event_source, &x);
if (r < 0)
return r;
- r = 1;
}
if (UNIT_VTABLE(u)->get_timeout) {
- q = UNIT_VTABLE(u)->get_timeout(u, &y);
- if (q < 0)
- return q;
+ r = UNIT_VTABLE(u)->get_timeout(u, &y);
+ if (r < 0)
+ return r;
}
- if (r == 0 && q == 0)
+ if (x == USEC_INFINITY && y == USEC_INFINITY)
return 0;
*timeout = MIN(x, y);
-
return 1;
}
@@ -1205,6 +1217,7 @@ static const char* const job_type_table[_JOB_TYPE_MAX] = {
[JOB_RELOAD_OR_START] = "reload-or-start",
[JOB_RESTART] = "restart",
[JOB_TRY_RESTART] = "try-restart",
+ [JOB_TRY_RELOAD] = "try-reload",
[JOB_NOP] = "nop",
};
@@ -1235,3 +1248,15 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
+
+const char* job_type_to_access_method(JobType t) {
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ if (IN_SET(t, JOB_START, JOB_RESTART, JOB_TRY_RESTART))
+ return "start";
+ else if (t == JOB_STOP)
+ return "stop";
+ else
+ return "reload";
+}
diff --git a/src/core/job.h b/src/core/job.h
index 1d1b10f1d3..85368f0d30 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,6 +21,11 @@
#include <stdbool.h>
+#include "sd-event.h"
+
+#include "list.h"
+#include "unit-name.h"
+
typedef struct Job Job;
typedef struct JobDependency JobDependency;
typedef enum JobType JobType;
@@ -61,6 +64,9 @@ enum JobType {
* Thus we never need to merge it with anything. */
JOB_TRY_RESTART = _JOB_TYPE_MAX_IN_TRANSACTION, /* if running, stop and then start */
+ /* Similar to JOB_TRY_RESTART but collapses to JOB_RELOAD or JOB_NOP */
+ JOB_TRY_RELOAD,
+
/* JOB_RELOAD_OR_START won't enter into a transaction and cannot result
* from transaction merging (there's no way for JOB_RELOAD and
* JOB_START to meet in one transaction). It can result from a merge
@@ -105,9 +111,7 @@ enum JobResult {
_JOB_RESULT_INVALID = -1
};
-#include "sd-event.h"
#include "unit.h"
-#include "list.h"
struct JobDependency {
/* Encodes that the 'subject' job needs the 'object' job in
@@ -160,7 +164,6 @@ struct Job {
bool installed:1;
bool in_run_queue:1;
bool matters_to_anchor:1;
- bool override:1;
bool in_dbus_queue:1;
bool sent_dbus_new_signal:1;
bool ignore_order:1;
@@ -174,8 +177,8 @@ Job* job_install(Job *j);
int job_install_deserialized(Job *j);
void job_uninstall(Job *j);
void job_dump(Job *j, FILE*f, const char *prefix);
-int job_serialize(Job *j, FILE *f, FDSet *fds);
-int job_deserialize(Job *j, FILE *f, FDSet *fds);
+int job_serialize(Job *j, FILE *f);
+int job_deserialize(Job *j, FILE *f);
int job_coldplug(Job *j);
JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts);
@@ -216,12 +219,14 @@ void job_add_to_dbus_queue(Job *j);
int job_start_timer(Job *j);
int job_run_and_invalidate(Job *j);
-int job_finish_and_invalidate(Job *j, JobResult result, bool recursive);
+int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already);
char *job_dbus_path(Job *j);
void job_shutdown_magic(Job *j);
+int job_get_timeout(Job *j, usec_t *timeout) _pure_;
+
const char* job_type_to_string(JobType t) _const_;
JobType job_type_from_string(const char *s) _pure_;
@@ -234,4 +239,4 @@ JobMode job_mode_from_string(const char *s) _pure_;
const char* job_result_to_string(JobResult t) _const_;
JobResult job_result_from_string(const char *s) _pure_;
-int job_get_timeout(Job *j, uint64_t *timeout) _pure_;
+const char* job_type_to_access_method(JobType t);
diff --git a/src/core/kill.c b/src/core/kill.c
index 2de71c6bf9..6854587d54 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,9 +17,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "util.h"
-#include "signal-util.h"
#include "kill.h"
+#include "signal-util.h"
+#include "string-table.h"
+#include "util.h"
void kill_context_init(KillContext *c) {
assert(c);
@@ -60,7 +59,10 @@ DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
static const char* const kill_who_table[_KILL_WHO_MAX] = {
[KILL_MAIN] = "main",
[KILL_CONTROL] = "control",
- [KILL_ALL] = "all"
+ [KILL_ALL] = "all",
+ [KILL_MAIN_FAIL] = "main-fail",
+ [KILL_CONTROL_FAIL] = "control-fail",
+ [KILL_ALL_FAIL] = "all-fail"
};
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
diff --git a/src/core/kill.h b/src/core/kill.h
index d5f125fa41..b3d2056cb0 100644
--- a/src/core/kill.h
+++ b/src/core/kill.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -50,6 +48,9 @@ typedef enum KillWho {
KILL_MAIN,
KILL_CONTROL,
KILL_ALL,
+ KILL_MAIN_FAIL,
+ KILL_CONTROL_FAIL,
+ KILL_ALL_FAIL,
_KILL_WHO_MAX,
_KILL_WHO_INVALID = -1
} KillWho;
diff --git a/src/core/killall.c b/src/core/killall.c
index 2a9d72c901..a8b814e868 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,21 +17,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/wait.h>
-#include <signal.h>
#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
#include <unistd.h>
-#include "util.h"
-#include "killall.h"
-#include "set.h"
+#include "alloc-util.h"
+#include "def.h"
+#include "fd-util.h"
#include "formats-util.h"
+#include "killall.h"
+#include "parse-util.h"
#include "process-util.h"
+#include "set.h"
+#include "string-util.h"
#include "terminal-util.h"
+#include "util.h"
-#define TIMEOUT_USEC (10 * USEC_PER_SEC)
-
-static bool ignore_proc(pid_t pid) {
+static bool ignore_proc(pid_t pid, bool warn_rootfs) {
_cleanup_fclose_ FILE *f = NULL;
char c;
const char *p;
@@ -68,7 +69,22 @@ static bool ignore_proc(pid_t pid) {
* spree.
*
* http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
- if (count == 1 && c == '@')
+ if (c == '@' && warn_rootfs) {
+ _cleanup_free_ char *comm = NULL;
+
+ r = pid_from_same_root_fs(pid);
+ if (r < 0)
+ return true;
+
+ get_process_comm(pid, &comm);
+
+ if (r)
+ log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is "
+ "running from the root file system, and thus likely to block re-mounting of the "
+ "root file system to read-only. Please consider moving it into an initrd file "
+ "system instead.", pid, strna(comm));
+ return true;
+ } else if (c == '@')
return true;
return false;
@@ -82,7 +98,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) {
if (set_isempty(pids))
return;
- until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+ until = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC;
for (;;) {
struct timespec ts;
int k;
@@ -108,7 +124,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) {
return;
}
- set_remove(pids, ULONG_TO_PTR(pid));
+ (void) set_remove(pids, PID_TO_PTR(pid));
}
/* Now explicitly check who might be remaining, who
@@ -117,7 +133,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) {
/* We misuse getpgid as a check whether a
* process still exists. */
- if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0)
+ if (getpgid(PTR_TO_PID(p)) >= 0)
continue;
if (errno != ESRCH)
@@ -167,7 +183,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
if (parse_pid(d->d_name, &pid) < 0)
continue;
- if (ignore_proc(pid))
+ if (ignore_proc(pid, sig == SIGKILL && !in_initrd()))
continue;
if (sig == SIGKILL) {
@@ -179,7 +195,7 @@ static int killall(int sig, Set *pids, bool send_sighup) {
if (kill(pid, sig) >= 0) {
if (pids) {
- r = set_put(pids, ULONG_TO_PTR(pid));
+ r = set_put(pids, PID_TO_PTR(pid));
if (r < 0)
log_oom();
}
diff --git a/src/core/killall.h b/src/core/killall.h
index 986cf432cb..acc2439f00 100644
--- a/src/core/killall.h
+++ b/src/core/killall.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c
index fc6d2f4acb..fd1021f706 100644
--- a/src/core/kmod-setup.c
+++ b/src/core/kmod-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,17 +17,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <string.h>
+#include <unistd.h>
#ifdef HAVE_KMOD
#include <libkmod.h>
#endif
-#include "macro.h"
-#include "capability.h"
#include "bus-util.h"
+#include "capability-util.h"
#include "kmod-setup.h"
+#include "macro.h"
#ifdef HAVE_KMOD
static void systemd_kmod_log(
@@ -66,9 +64,6 @@ int kmod_setup(void) {
/* this should never be a module */
{ "unix", "/proc/net/unix", true, true, NULL },
- /* IPC is needed before we bring up any other services */
- { "kdbus", "/sys/fs/kdbus", false, false, is_kdbus_wanted },
-
#ifdef HAVE_LIBIPTC
/* netfilter is needed by networkd, nspawn among others, and cannot be autoloaded */
{ "ip_tables", "/proc/net/ip_tables_names", false, false, NULL },
@@ -112,7 +107,7 @@ int kmod_setup(void) {
r = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL);
if (r == 0)
- log_info("Inserted module '%s'", kmod_module_get_name(mod));
+ log_debug("Inserted module '%s'", kmod_module_get_name(mod));
else if (r == KMOD_PROBE_APPLY_BLACKLIST)
log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
else {
diff --git a/src/core/kmod-setup.h b/src/core/kmod-setup.h
index 24dcdddfa4..685f4df301 100644
--- a/src/core/kmod-setup.h
+++ b/src/core/kmod-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c
index 11566af51b..f83fa09301 100644
--- a/src/core/load-dropin.c
+++ b/src/core/load-dropin.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,13 +18,13 @@
***/
-#include "unit.h"
+#include "conf-parser.h"
#include "load-dropin.h"
+#include "load-fragment.h"
#include "log.h"
#include "strv.h"
#include "unit-name.h"
-#include "conf-parser.h"
-#include "load-fragment.h"
+#include "unit.h"
static int add_dependency_consumer(
UnitDependency dependency,
@@ -46,6 +44,7 @@ static int add_dependency_consumer(
}
int unit_load_dropin(Unit *u) {
+ _cleanup_strv_free_ char **l = NULL;
Iterator i;
char *t, **f;
int r;
@@ -57,7 +56,7 @@ int unit_load_dropin(Unit *u) {
SET_FOREACH(t, u->names, i) {
char **p;
- STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+ STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS,
add_dependency_consumer, u, NULL);
unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES,
@@ -65,10 +64,19 @@ int unit_load_dropin(Unit *u) {
}
}
- r = unit_find_dropin_paths(u, &u->dropin_paths);
+ r = unit_find_dropin_paths(u, &l);
if (r <= 0)
return 0;
+ if (!u->dropin_paths) {
+ u->dropin_paths = l;
+ l = NULL;
+ } else {
+ r = strv_extend_strv(&u->dropin_paths, l, true);
+ if (r < 0)
+ return log_oom();
+ }
+
STRV_FOREACH(f, u->dropin_paths) {
config_parse(u->id, *f, NULL,
UNIT_VTABLE(u)->sections,
diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h
index 1e018c4525..942d26724e 100644
--- a/src/core/load-dropin.h
+++ b/src/core/load-dropin.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -21,13 +19,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
#include "dropin.h"
+#include "unit.h"
/* Read service data supplementary drop-in directories */
static inline int unit_find_dropin_paths(Unit *u, char ***paths) {
- return unit_file_find_dropin_paths(u->manager->lookup_paths.unit_path,
+ return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path,
u->manager->unit_path_cache,
u->names,
paths);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index aae81c80cb..c49c1d6732 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -17,11 +17,11 @@ struct ConfigPerfItem;
%%
m4_dnl Define the context options only once
m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
-`$1.WorkingDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.working_directory)
+`$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context)
$1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory)
-$1.User, config_parse_unit_string_printf, 0, offsetof($1, exec_context.user)
-$1.Group, config_parse_unit_string_printf, 0, offsetof($1, exec_context.group)
-$1.SupplementaryGroups, config_parse_strv, 0, offsetof($1, exec_context.supplementary_groups)
+$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user)
+$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group)
+$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups)
$1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context)
$1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context)
$1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context)
@@ -33,6 +33,8 @@ $1.CPUAffinity, config_parse_exec_cpu_affinity, 0,
$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask)
$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
+$1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment)
+$1.DynamicUser, config_parse_bool, 0, offsetof($1, exec_context.dynamic_user)
$1.StandardInput, config_parse_input, 0, offsetof($1, exec_context.std_input)
$1.StandardOutput, config_parse_output, 0, offsetof($1, exec_context.std_output)
$1.StandardError, config_parse_output, 0, offsetof($1, exec_context.std_error)
@@ -44,19 +46,24 @@ $1.SyslogIdentifier, config_parse_unit_string_printf, 0,
$1.SyslogFacility, config_parse_log_facility, 0, offsetof($1, exec_context.syslog_priority)
$1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority)
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
-$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context)
+$1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context)
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context)
-$1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop)
+$1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set)
+$1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set)
$1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec)
$1.NoNewPrivileges, config_parse_no_new_privileges, 0, offsetof($1, exec_context)
m4_ifdef(`HAVE_SECCOMP',
`$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context)
$1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs)
$1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context)
+$1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute)
+$1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime)
$1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)',
`$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+$1.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
+$1.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0
$1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)
@@ -74,12 +81,18 @@ $1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQ
$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit)
$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit)
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
-$1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
-$1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
-$1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
+$1.ReadWriteDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths)
+$1.ReadOnlyDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths)
+$1.InaccessibleDirectories, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths)
+$1.ReadWritePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_write_paths)
+$1.ReadOnlyPaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.read_only_paths)
+$1.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof($1, exec_context.inaccessible_paths)
$1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp)
-$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
$1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices)
+$1.ProtectKernelTunables, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_tunables)
+$1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups)
+$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
+$1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users)
$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context)
$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context)
$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context)
@@ -91,6 +104,7 @@ m4_ifdef(`HAVE_PAM',
`$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
$1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe)
$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id)
+$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode)
m4_ifdef(`HAVE_SELINUX',
`$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)',
`$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')
@@ -105,33 +119,48 @@ m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
`$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill)
$1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup)
$1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode)
-$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, kill_context.kill_signal)'
+$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)'
)m4_dnl
m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS',
`$1.Slice, config_parse_unit_slice, 0, 0
$1.CPUAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.cpu_accounting)
+$1.CPUWeight, config_parse_cpu_weight, 0, offsetof($1, cgroup_context.cpu_weight)
+$1.StartupCPUWeight, config_parse_cpu_weight, 0, offsetof($1, cgroup_context.startup_cpu_weight)
$1.CPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.cpu_shares)
$1.StartupCPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.startup_cpu_shares)
$1.CPUQuota, config_parse_cpu_quota, 0, offsetof($1, cgroup_context)
$1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting)
+$1.MemoryLow, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
+$1.MemoryHigh, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
+$1.MemoryMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
+$1.MemorySwapMax, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.MemoryLimit, config_parse_memory_limit, 0, offsetof($1, cgroup_context)
$1.DeviceAllow, config_parse_device_allow, 0, offsetof($1, cgroup_context)
$1.DevicePolicy, config_parse_device_policy, 0, offsetof($1, cgroup_context.device_policy)
+$1.IOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.io_accounting)
+$1.IOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.io_weight)
+$1.StartupIOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.startup_io_weight)
+$1.IODeviceWeight, config_parse_io_device_weight, 0, offsetof($1, cgroup_context)
+$1.IOReadBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context)
+$1.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context)
+$1.IOReadIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context)
+$1.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context)
$1.BlockIOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.blockio_accounting)
$1.BlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.blockio_weight)
$1.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.startup_blockio_weight)
$1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context)
$1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)
$1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)
-$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)'
+$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting)
+$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context.tasks_max)
+$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)
+$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0'
)m4_dnl
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
Unit.SourcePath, config_parse_path, 0, offsetof(Unit, source_path)
Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0
-Unit.RequiresOverridable, config_parse_unit_deps, UNIT_REQUIRES_OVERRIDABLE, 0
Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0
-Unit.RequisiteOverridable, config_parse_unit_deps, UNIT_REQUISITE_OVERRIDABLE, 0
Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0
Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
@@ -145,6 +174,8 @@ Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD
Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
+Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0
+Unit.RequisiteOverridable, config_parse_obsolete_unit_deps, UNIT_REQUISITE, 0
Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, 0
Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded)
Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start)
@@ -154,10 +185,16 @@ Unit.DefaultDependencies, config_parse_bool, 0,
Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
-Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot)
-Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout)
+Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0
+Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout)
Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action)
Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg)
+Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
+m4_dnl The following is a legacy alias name for compatibility
+Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
+Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
+Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
+Unit.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg)
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
@@ -205,14 +242,16 @@ 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_sec, 0, offsetof(Service, restart_usec)
-Service.TimeoutSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
-Service.TimeoutStartSec, config_parse_service_timeout, 0, offsetof(Service, timeout_start_usec)
-Service.TimeoutStopSec, config_parse_service_timeout, 0, offsetof(Service, timeout_stop_usec)
+Service.TimeoutSec, config_parse_service_timeout, 0, 0
+Service.TimeoutStartSec, config_parse_service_timeout, 0, 0
+Service.TimeoutStopSec, config_parse_service_timeout, 0, 0
+Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
-Service.StartLimitInterval, config_parse_sec, 0, offsetof(Service, start_limit.interval)
-Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Service, start_limit.burst)
-Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Service, start_limit_action)
-Service.RebootArgument, config_parse_string, 0, offsetof(Service, reboot_arg)
+m4_dnl The following three only exist for compatibility, they moved into Unit, see above
+Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
+Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
+Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
+Service.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg)
Service.FailureAction, config_parse_failure_action, 0, offsetof(Service, failure_action)
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
@@ -229,7 +268,9 @@ Service.BusName, config_parse_bus_name, 0,
Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max)
Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access)
Service.Sockets, config_parse_service_sockets, 0, 0
-Service.BusPolicy, config_parse_bus_endpoint_policy, 0, offsetof(Service, exec_context)
+Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0
+Service.USBFunctionDescriptors, config_parse_path, 0, offsetof(Service, usb_function_descriptors)
+Service.USBFunctionStrings, config_parse_path, 0, offsetof(Service, usb_function_strings)
EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
@@ -241,6 +282,8 @@ Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO
Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0
Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
+Socket.ListenUSBFunction, config_parse_socket_listen, SOCKET_USB_FUNCTION, 0
+Socket.SocketProtocol, config_parse_socket_protocol, 0, 0
Socket.BindIPv6Only, config_parse_socket_bind, 0, 0,
Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
@@ -249,12 +292,14 @@ Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC
Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command)
Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command)
Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec)
-Socket.SocketUser, config_parse_unit_string_printf, 0, offsetof(Socket, user)
-Socket.SocketGroup, config_parse_unit_string_printf, 0, offsetof(Socket, group)
+Socket.SocketUser, config_parse_user_group, 0, offsetof(Socket, user)
+Socket.SocketGroup, config_parse_user_group, 0, offsetof(Socket, group)
Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode)
Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode)
Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept)
+Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable)
Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections)
+Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source)
Socket.KeepAlive, config_parse_bool, 0, offsetof(Socket, keep_alive)
Socket.KeepAliveTimeSec, config_parse_sec, 0, offsetof(Socket, keep_alive_time)
Socket.KeepAliveIntervalSec, config_parse_sec, 0, offsetof(Socket, keep_alive_interval)
@@ -279,7 +324,10 @@ Socket.MessageQueueMaxMessages, config_parse_long, 0,
Socket.MessageQueueMessageSize, config_parse_long, 0, offsetof(Socket, mq_msgsize)
Socket.RemoveOnStop, config_parse_bool, 0, offsetof(Socket, remove_on_stop)
Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks)
+Socket.FileDescriptorName, config_parse_fdname, 0, 0
Socket.Service, config_parse_socket_service, 0, 0
+Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval)
+Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst)
m4_ifdef(`HAVE_SMACK',
`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack)
Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in)
@@ -310,6 +358,8 @@ Mount.Type, config_parse_string, 0,
Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec)
Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options)
+Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount)
+Mount.ForceUnmount, config_parse_bool, 0, offsetof(Mount, force_unmount)
EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
@@ -334,7 +384,9 @@ Timer.OnUnitActiveSec, config_parse_timer, 0,
Timer.OnUnitInactiveSec, config_parse_timer, 0, 0
Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent)
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
+Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
+Timer.RandomizedDelaySec, config_parse_sec, 0, offsetof(Timer, random_usec)
Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index a48cb4029a..06c156a623 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,44 +18,55 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <linux/oom.h>
#include <errno.h>
-#include <string.h>
#include <fcntl.h>
-#include <sched.h>
#include <linux/fs.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-
+#include <linux/oom.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
#endif
+#include <sched.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
-#include "unit.h"
-#include "strv.h"
+#include "af-list.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-internal.h"
+#include "bus-util.h"
+#include "cap-list.h"
+#include "capability-util.h"
+#include "cgroup.h"
#include "conf-parser.h"
+#include "cpu-set-util.h"
+#include "env-util.h"
+#include "errno-list.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "ioprio.h"
#include "load-fragment.h"
#include "log.h"
-#include "ioprio.h"
-#include "securebits.h"
#include "missing.h"
-#include "unit-name.h"
-#include "unit-printf.h"
-#include "utf8.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "env-util.h"
-#include "cgroup.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "errno-list.h"
-#include "af-list.h"
-#include "cap-list.h"
-#include "signal-util.h"
-#include "bus-internal.h"
-
+#include "process-util.h"
+#include "rlimit-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
+#include "securebits.h"
+#include "signal-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "user-util.h"
+#include "utf8.h"
+#include "web-util.h"
int config_parse_warn_compat(
const char *unit,
@@ -74,15 +83,15 @@ int config_parse_warn_compat(
switch(reason) {
case DISABLED_CONFIGURATION:
- log_syntax(unit, LOG_DEBUG, filename, line, EINVAL,
+ log_syntax(unit, LOG_DEBUG, filename, line, 0,
"Support for option %s= has been disabled at compile time and it is ignored", lvalue);
break;
case DISABLED_LEGACY:
- log_syntax(unit, LOG_INFO, filename, line, EINVAL,
+ log_syntax(unit, LOG_INFO, filename, line, 0,
"Support for option %s= has been removed and it is ignored", lvalue);
break;
case DISABLED_EXPERIMENTAL:
- log_syntax(unit, LOG_INFO, filename, line, EINVAL,
+ log_syntax(unit, LOG_INFO, filename, line, 0,
"Support for option %s= has not yet been enabled and it is ignored", lvalue);
break;
};
@@ -90,52 +99,73 @@ int config_parse_warn_compat(
return 0;
}
-int config_parse_unit_deps(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_deps(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
UnitDependency d = ltype;
Unit *u = userdata;
- const char *word, *state;
- size_t l;
+ const char *p;
assert(filename);
assert(lvalue);
assert(rvalue);
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL, *k = NULL;
+ p = rvalue;
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
int r;
- t = strndup(word, l);
- if (!t)
+ r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ break;
+ }
- r = unit_name_printf(u, t, &k);
+ r = unit_name_printf(u, word, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
continue;
}
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add dependency on %s, ignoring: %s", k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
return 0;
}
+int config_parse_obsolete_unit_deps(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Unit dependency type %s= is obsolete, replacing by %s=, please update your unit file", lvalue, unit_dependency_to_string(ltype));
+
+ return config_parse_unit_deps(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
+}
+
int config_parse_unit_string_printf(
const char *unit,
const char *filename,
@@ -166,16 +196,17 @@ int config_parse_unit_string_printf(
return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
}
-int config_parse_unit_strv_printf(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_strv_printf(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Unit *u = userdata;
_cleanup_free_ char *k = NULL;
@@ -187,24 +218,25 @@ int config_parse_unit_strv_printf(const char *unit,
assert(u);
r = unit_full_printf(u, rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
+ }
- return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype,
- k ? k : rvalue, data, userdata);
+ return config_parse_strv(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
}
-int config_parse_unit_path_printf(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_path_printf(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
_cleanup_free_ char *k = NULL;
Unit *u = userdata;
@@ -217,7 +249,7 @@ int config_parse_unit_path_printf(const char *unit,
r = unit_full_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
return 0;
}
@@ -256,17 +288,17 @@ int config_parse_unit_path_strv_printf(
r = unit_full_printf(u, t, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", t, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", t);
return 0;
}
if (!utf8_is_valid(k)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
return 0;
}
if (!path_is_absolute(k)) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Symlink path %s is not absolute, ignoring: %s", k, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Symlink path %s is not absolute, ignoring: %m", k);
return 0;
}
@@ -279,7 +311,7 @@ int config_parse_unit_path_strv_printf(
k = NULL;
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid syntax, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, ignoring.");
return 0;
}
@@ -322,12 +354,8 @@ int config_parse_socket_listen(const char *unit,
p->type = ltype;
r = unit_full_printf(UNIT(s), rvalue, &p->path);
if (r < 0) {
- p->path = strdup(rvalue);
- if (!p->path)
- return log_oom();
- else
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
}
path_kill_slashes(p->path);
@@ -337,14 +365,14 @@ int config_parse_socket_listen(const char *unit,
p->type = SOCKET_SOCKET;
r = unit_full_printf(UNIT(s), rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
+ }
- r = socket_address_parse_netlink(&p->address, k ?: rvalue);
+ r = socket_address_parse_netlink(&p->address, k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse address value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
return 0;
}
@@ -353,14 +381,14 @@ int config_parse_socket_listen(const char *unit,
p->type = SOCKET_SOCKET;
r = unit_full_printf(UNIT(s), rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,"Failed to resolve unit specifiers on %s, ignoring: %m", rvalue);
+ return 0;
+ }
- r = socket_address_parse_and_warn(&p->address, k ? k : rvalue);
+ r = socket_address_parse_and_warn(&p->address, k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse address value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue);
return 0;
}
@@ -374,13 +402,14 @@ int config_parse_socket_listen(const char *unit,
}
if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) {
- log_syntax(unit, LOG_ERR, filename, line, EOPNOTSUPP,
- "Address family not supported, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Address family not supported, ignoring: %s", rvalue);
return 0;
}
}
p->fd = -1;
+ p->auxiliary_fds = NULL;
+ p->n_auxiliary_fds = 0;
p->socket = s;
if (s->ports) {
@@ -393,6 +422,37 @@ int config_parse_socket_listen(const char *unit,
return 0;
}
+int config_parse_socket_protocol(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Socket *s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ s = SOCKET(data);
+
+ if (streq(rvalue, "udplite"))
+ s->socket_protocol = IPPROTO_UDPLITE;
+ else if (streq(rvalue, "sctp"))
+ s->socket_protocol = IPPROTO_SCTP;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
int config_parse_socket_bind(const char *unit,
const char *filename,
unsigned line,
@@ -420,8 +480,7 @@ int config_parse_socket_bind(const char *unit,
r = parse_boolean(rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue);
return 0;
}
@@ -432,16 +491,17 @@ int config_parse_socket_bind(const char *unit,
return 0;
}
-int config_parse_exec_nice(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_nice(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
ExecContext *c = data;
int priority, r;
@@ -451,16 +511,13 @@ int config_parse_exec_nice(const char *unit,
assert(rvalue);
assert(data);
- r = safe_atoi(rvalue, &priority);
+ r = parse_nice(rvalue, &priority);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse nice priority, ignoring: %s. ", rvalue);
- return 0;
- }
+ if (r == -ERANGE)
+ log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue);
+ else
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue);
- if (priority < PRIO_MIN || priority >= PRIO_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Nice priority out of range, ignoring: %s", rvalue);
return 0;
}
@@ -491,14 +548,12 @@ int config_parse_exec_oom_score_adjust(const char* unit,
r = safe_atoi(rvalue, &oa);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse the OOM score adjust value, ignoring: %s", rvalue);
return 0;
}
if (oa < OOM_SCORE_ADJ_MIN || oa > OOM_SCORE_ADJ_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "OOM score adjust value out of range, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "OOM score adjust value out of range, ignoring: %s", rvalue);
return 0;
}
@@ -531,9 +586,7 @@ int config_parse_exec(
assert(e);
e += ltype;
-
rvalue += strspn(rvalue, WHITESPACE);
- p = rvalue;
if (isempty(rvalue)) {
/* An empty assignment resets the list */
@@ -541,55 +594,55 @@ int config_parse_exec(
return 0;
}
+ p = rvalue;
do {
- int i;
+ _cleanup_free_ char *path = NULL, *firstword = NULL;
+ bool separate_argv0 = false, ignore = false, privileged = false;
+ _cleanup_free_ ExecCommand *nce = NULL;
_cleanup_strv_free_ char **n = NULL;
size_t nlen = 0, nbufsize = 0;
- _cleanup_free_ ExecCommand *nce = NULL;
- _cleanup_free_ char *path = NULL, *firstword = NULL;
char *f;
- bool separate_argv0 = false, ignore = false;
+ int i;
semicolon = false;
- r = unquote_first_word_and_warn(&p, &firstword, UNQUOTE_CUNESCAPE, unit, filename, line, rvalue);
+ r = extract_first_word_and_warn(&p, &firstword, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
if (r <= 0)
return 0;
f = firstword;
- for (i = 0; i < 2; i++) {
- /* We accept an absolute path as first argument, or
- * alternatively an absolute prefixed with @ to allow
- * overriding of argv[0]. */
+ for (i = 0; i < 3; i++) {
+ /* We accept an absolute path as first argument.
+ * If it's prefixed with - and the path doesn't exist,
+ * we ignore it instead of erroring out;
+ * if it's prefixed with @, we allow overriding of argv[0];
+ * and if it's prefixed with !, it will be run with full privileges */
if (*f == '-' && !ignore)
ignore = true;
else if (*f == '@' && !separate_argv0)
separate_argv0 = true;
+ else if (*f == '+' && !privileged)
+ privileged = true;
else
break;
- f ++;
+ f++;
}
if (isempty(f)) {
/* First word is either "-" or "@" with no command. */
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Empty path in command line, ignoring: \"%s\"", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue);
return 0;
}
-
if (!string_is_safe(f)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Executable path contains special characters, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue);
return 0;
}
if (!path_is_absolute(f)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Executable path is not absolute, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue);
return 0;
}
if (endswith(f, "/")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Executable path specifies a directory, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue);
return 0;
}
@@ -614,20 +667,20 @@ int config_parse_exec(
path_kill_slashes(path);
- for (;;) {
+ while (!isempty(p)) {
_cleanup_free_ char *word = NULL;
/* Check explicitly for an unquoted semicolon as
* command separator token. */
if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) {
- p ++;
+ p++;
p += strspn(p, WHITESPACE);
semicolon = true;
break;
}
/* Check for \; explicitly, to not confuse it with \\;
- * or "\;" or "\\;" etc. unquote_first_word would
+ * or "\;" or "\\;" etc. extract_first_word would
* return the same for all of those. */
if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
p += 2;
@@ -642,7 +695,7 @@ int config_parse_exec(
continue;
}
- r = unquote_first_word_and_warn(&p, &word, UNQUOTE_CUNESCAPE, unit, filename, line, rvalue);
+ r = extract_first_word_and_warn(&p, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
if (r == 0)
break;
else if (r < 0)
@@ -656,8 +709,7 @@ int config_parse_exec(
}
if (!n || !n[0]) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue);
return 0;
}
@@ -668,6 +720,7 @@ int config_parse_exec(
nce->argv = n;
nce->path = path;
nce->ignore = ignore;
+ nce->privileged = privileged;
exec_command_append_list(e, nce);
@@ -685,16 +738,17 @@ int config_parse_exec(
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
-int config_parse_socket_bindtodevice(const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_socket_bindtodevice(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Socket *s = data;
char *n;
@@ -705,6 +759,11 @@ int config_parse_socket_bindtodevice(const char* unit,
assert(data);
if (rvalue[0] && !streq(rvalue, "*")) {
+ if (!ifname_valid(rvalue)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue);
+ return 0;
+ }
+
n = strdup(rvalue);
if (!n)
return log_oom();
@@ -741,8 +800,7 @@ int config_parse_exec_io_class(const char *unit,
x = ioprio_class_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse IO scheduling class, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IO scheduling class, ignoring: %s", rvalue);
return 0;
}
@@ -773,8 +831,7 @@ int config_parse_exec_io_priority(const char *unit,
r = safe_atoi(rvalue, &i);
if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse IO priority, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue);
return 0;
}
@@ -806,8 +863,7 @@ int config_parse_exec_cpu_sched_policy(const char *unit,
x = sched_policy_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -x,
- "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
@@ -840,8 +896,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
r = safe_atoi(rvalue, &i);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU scheduling policy, ignoring: %s", rvalue);
return 0;
}
@@ -850,8 +905,7 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
max = sched_get_priority_max(c->cpu_sched_policy);
if (i < min || i > max) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "CPU scheduling priority is out of range, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "CPU scheduling priority is out of range, ignoring: %s", rvalue);
return 0;
}
@@ -873,83 +927,29 @@ int config_parse_exec_cpu_affinity(const char *unit,
void *userdata) {
ExecContext *c = data;
- const char *word, *state;
- size_t l;
+ _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
+ int ncpus;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (isempty(rvalue)) {
- /* An empty assignment resets the CPU list */
- if (c->cpuset)
- CPU_FREE(c->cpuset);
- c->cpuset = NULL;
- return 0;
- }
-
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL;
- int r;
- unsigned cpu;
-
- t = strndup(word, l);
- if (!t)
- return log_oom();
-
- r = safe_atou(t, &cpu);
-
- if (!c->cpuset) {
- c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
- if (!c->cpuset)
- return log_oom();
- }
-
- if (r < 0 || cpu >= c->cpuset_ncpus) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Failed to parse CPU affinity '%s', ignoring: %s", t, rvalue);
- return 0;
- }
-
- CPU_SET_S(cpu, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset);
- }
- if (!isempty(state))
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
-
- return 0;
-}
+ ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
+ if (ncpus < 0)
+ return ncpus;
-int config_parse_exec_capabilities(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+ if (c->cpuset)
+ CPU_FREE(c->cpuset);
- ExecContext *c = data;
- cap_t cap;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- cap = cap_from_text(rvalue);
- if (!cap) {
- log_syntax(unit, LOG_ERR, filename, line, errno,
- "Failed to parse capabilities, ignoring: %s", rvalue);
- return 0;
+ if (ncpus == 0)
+ /* An empty assignment resets the CPU list */
+ c->cpuset = NULL;
+ else {
+ c->cpuset = cpuset;
+ cpuset = NULL;
}
-
- if (c->capabilities)
- cap_free(c->capabilities);
- c->capabilities = cap;
+ c->cpuset_ncpus = ncpus;
return 0;
}
@@ -994,34 +994,32 @@ int config_parse_exec_secure_bits(const char *unit,
else if (first_word(word, "noroot-locked"))
c->secure_bits |= 1<<SECURE_NOROOT_LOCKED;
else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse secure bits, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse secure bits, ignoring: %s", rvalue);
return 0;
}
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid syntax, garbage at the end, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid syntax, garbage at the end, ignoring.");
return 0;
}
-int config_parse_bounding_set(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_capability_set(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- uint64_t *capability_bounding_set_drop = data;
- const char *word, *state;
- size_t l;
+ uint64_t *capability_set = data;
+ uint64_t sum = 0, initial = 0;
bool invert = false;
- uint64_t sum = 0;
+ const char *p;
assert(filename);
assert(lvalue);
@@ -1033,80 +1031,84 @@ int config_parse_bounding_set(const char *unit,
rvalue++;
}
- /* Note that we store this inverted internally, since the
- * kernel wants it like this. But we actually expose it
- * non-inverted everywhere to have a fully normalized
- * interface. */
+ if (strcmp(lvalue, "CapabilityBoundingSet") == 0)
+ initial = CAP_ALL; /* initialized to all bits on */
+ /* else "AmbientCapabilities" initialized to all bits off */
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL;
- int cap;
+ p = rvalue;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ int cap, r;
- t = strndup(word, l);
- if (!t)
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word, ignoring: %s", rvalue);
+ break;
+ }
- cap = capability_from_name(t);
+ cap = capability_from_name(word);
if (cap < 0) {
- log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capability in bounding set, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word);
continue;
}
- sum |= ((uint64_t) 1ULL) << (uint64_t) cap;
+ sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap;
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
- if (invert)
- *capability_bounding_set_drop |= sum;
+ sum = invert ? ~sum : sum;
+
+ if (sum == 0 || *capability_set == initial)
+ /* "" or uninitialized data -> replace */
+ *capability_set = sum;
else
- *capability_bounding_set_drop |= ~sum;
+ /* previous data -> merge */
+ *capability_set |= sum;
return 0;
}
-int config_parse_limit(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_limit(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- struct rlimit **rl = data;
- unsigned long long u;
+ struct rlimit **rl = data, d = {};
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- rl += ltype;
-
- if (streq(rvalue, "infinity"))
- u = (unsigned long long) RLIM_INFINITY;
- else {
- int r;
-
- r = safe_atollu(rvalue, &u);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse resource value, ignoring: %s", rvalue);
- return 0;
- }
+ r = rlimit_parse(ltype, rvalue, &d);
+ if (r == -EILSEQ) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
+ return 0;
}
- if (!*rl) {
- *rl = new(struct rlimit, 1);
- if (!*rl)
+ if (rl[ltype])
+ *rl[ltype] = d;
+ else {
+ rl[ltype] = newdup(struct rlimit, &d, 1);
+ if (!rl[ltype])
return log_oom();
}
- (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
return 0;
}
@@ -1132,8 +1134,7 @@ int config_parse_sysv_priority(const char *unit,
r = safe_atoi(rvalue, &i);
if (r < 0 || i < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse SysV start priority, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SysV start priority, ignoring: %s", rvalue);
return 0;
}
@@ -1142,38 +1143,9 @@ int config_parse_sysv_priority(const char *unit,
}
#endif
+DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
-int config_parse_kill_signal(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int *sig = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(sig);
-
- r = signal_from_string_try_harder(rvalue);
- if (r <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse kill signal, ignoring: %s", rvalue);
- return 0;
- }
-
- *sig = r;
- return 0;
-}
-
int config_parse_exec_mount_flags(const char *unit,
const char *filename,
unsigned line,
@@ -1185,38 +1157,28 @@ int config_parse_exec_mount_flags(const char *unit,
void *data,
void *userdata) {
- ExecContext *c = data;
- const char *word, *state;
- size_t l;
+
unsigned long flags = 0;
+ ExecContext *c = data;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- FOREACH_WORD_SEPARATOR(word, l, rvalue, ", ", state) {
- _cleanup_free_ char *t;
-
- t = strndup(word, l);
- if (!t)
- return log_oom();
-
- if (streq(t, "shared"))
- flags = MS_SHARED;
- else if (streq(t, "slave"))
- flags = MS_SLAVE;
- else if (streq(t, "private"))
- flags = MS_PRIVATE;
- else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse mount flag %s, ignoring: %s", t, rvalue);
- return 0;
- }
+ if (streq(rvalue, "shared"))
+ flags = MS_SHARED;
+ else if (streq(rvalue, "slave"))
+ flags = MS_SLAVE;
+ else if (streq(rvalue, "private"))
+ flags = MS_PRIVATE;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse mount flag %s, ignoring.", rvalue);
+ return 0;
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
c->mount_flags = flags;
+
return 0;
}
@@ -1244,8 +1206,7 @@ int config_parse_exec_selinux_context(
assert(data);
if (isempty(rvalue)) {
- free(c->selinux_context);
- c->selinux_context = NULL;
+ c->selinux_context = mfree(c->selinux_context);
c->selinux_context_ignore = false;
return 0;
}
@@ -1258,8 +1219,7 @@ int config_parse_exec_selinux_context(
r = unit_name_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
}
@@ -1294,8 +1254,7 @@ int config_parse_exec_apparmor_profile(
assert(data);
if (isempty(rvalue)) {
- free(c->apparmor_profile);
- c->apparmor_profile = NULL;
+ c->apparmor_profile = mfree(c->apparmor_profile);
c->apparmor_profile_ignore = false;
return 0;
}
@@ -1308,8 +1267,7 @@ int config_parse_exec_apparmor_profile(
r = unit_name_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
}
@@ -1344,8 +1302,7 @@ int config_parse_exec_smack_process_label(
assert(data);
if (isempty(rvalue)) {
- free(c->smack_process_label);
- c->smack_process_label = NULL;
+ c->smack_process_label = mfree(c->smack_process_label);
c->smack_process_label_ignore = false;
return 0;
}
@@ -1358,8 +1315,7 @@ int config_parse_exec_smack_process_label(
r = unit_name_printf(u, rvalue, &k);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
return 0;
}
@@ -1382,10 +1338,13 @@ int config_parse_timer(const char *unit,
void *userdata) {
Timer *t = data;
- usec_t u = 0;
+ usec_t usec = 0;
TimerValue *v;
TimerBase b;
CalendarSpec *c = NULL;
+ Unit *u = userdata;
+ _cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1400,23 +1359,24 @@ int config_parse_timer(const char *unit,
b = timer_base_from_string(lvalue);
if (b < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -b,
- "Failed to parse timer base, ignoring: %s", lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue);
+ return 0;
+ }
+
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
return 0;
}
if (b == TIMER_CALENDAR) {
- if (calendar_spec_from_string(rvalue, &c) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse calendar specification, ignoring: %s",
- rvalue);
+ if (calendar_spec_from_string(k, &c) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k);
return 0;
}
} else {
- if (parse_sec(rvalue, &u) < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse timer value, ignoring: %s",
- rvalue);
+ if (parse_sec(k, &usec) < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k);
return 0;
}
}
@@ -1428,7 +1388,7 @@ int config_parse_timer(const char *unit,
}
v->base = b;
- v->value = u;
+ v->value = usec;
v->calendar_spec = c;
LIST_PREPEND(value, t->values, v);
@@ -1459,33 +1419,30 @@ int config_parse_trigger_unit(
assert(data);
if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Multiple units to trigger specified, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
return 0;
}
r = unit_name_printf(u, rvalue, &p);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
+ return 0;
+ }
- type = unit_name_to_type(p ?: rvalue);
+ type = unit_name_to_type(p);
if (type < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unit type not valid, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit type not valid, ignoring: %s", rvalue);
return 0;
}
if (type == u->type) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trigger cannot be of same type, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trigger cannot be of same type, ignoring: %s", rvalue);
return 0;
}
- r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
return 0;
}
@@ -1522,25 +1479,18 @@ int config_parse_path_spec(const char *unit,
b = path_type_from_string(lvalue);
if (b < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse path type, ignoring: %s", lvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse path type, ignoring: %s", lvalue);
return 0;
}
r = unit_full_printf(UNIT(p), rvalue, &k);
if (r < 0) {
- k = strdup(rvalue);
- if (!k)
- return log_oom();
- else
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s. Ignoring.",
- rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ return 0;
}
if (!path_is_absolute(k)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Path is not absolute, ignoring: %s", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Path is not absolute, ignoring: %s", k);
return 0;
}
@@ -1571,11 +1521,11 @@ int config_parse_socket_service(
void *data,
void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *p = NULL;
Socket *s = data;
- int r;
Unit *x;
- _cleanup_free_ char *p = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1589,13 +1539,13 @@ int config_parse_socket_service(
}
if (!endswith(p, ".service")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
return 0;
}
@@ -1604,6 +1554,50 @@ int config_parse_socket_service(
return 0;
}
+int config_parse_fdname(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *p = NULL;
+ Socket *s = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ s->fdname = mfree(s->fdname);
+ return 0;
+ }
+
+ r = unit_name_printf(UNIT(s), rvalue, &p);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (!fdname_is_valid(p)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", p);
+ return 0;
+ }
+
+ free(s->fdname);
+ s->fdname = p;
+ p = NULL;
+
+ return 0;
+}
+
int config_parse_service_sockets(
const char *unit,
const char *filename,
@@ -1617,8 +1611,7 @@ int config_parse_service_sockets(
void *userdata) {
Service *s = data;
- const char *word, *state;
- size_t l;
+ const char *p;
int r;
assert(filename);
@@ -1626,21 +1619,28 @@ int config_parse_service_sockets(
assert(rvalue);
assert(data);
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *t = NULL, *k = NULL;
+ p = rvalue;
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
- t = strndup(word, l);
- if (!t)
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in sockets, ignoring: %s", rvalue);
+ break;
+ }
- r = unit_name_printf(UNIT(s), t, &k);
+ r = unit_name_printf(UNIT(s), word, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
continue;
}
if (!endswith(k, ".socket")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type socket, ignoring: %s", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type socket, ignoring: %s", k);
continue;
}
@@ -1652,8 +1652,6 @@ int config_parse_service_sockets(
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring.");
return 0;
}
@@ -1686,25 +1684,27 @@ int config_parse_bus_name(
}
if (!service_name_is_valid(k)) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bus name %s, ignoring.", k);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name %s, ignoring.", k);
return 0;
}
return config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
}
-int config_parse_service_timeout(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_service_timeout(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
Service *s = userdata;
+ usec_t usec;
int r;
assert(filename);
@@ -1712,16 +1712,180 @@ int config_parse_service_timeout(const char *unit,
assert(rvalue);
assert(s);
- r = config_parse_sec(unit, filename, line, section, section_line, lvalue, ltype,
- rvalue, data, userdata);
- if (r < 0)
- return r;
+ /* This is called for three cases: TimeoutSec=, TimeoutStopSec= and TimeoutStartSec=. */
- if (streq(lvalue, "TimeoutSec")) {
- s->start_timeout_defined = true;
- s->timeout_stop_usec = s->timeout_start_usec;
- } else if (streq(lvalue, "TimeoutStartSec"))
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ /* Traditionally, these options accepted 0 to disable the timeouts. However, a timeout of 0 suggests it happens
+ * immediately, hence fix this to become USEC_INFINITY instead. This is in-line with how we internally handle
+ * all other timeouts. */
+ if (usec <= 0)
+ usec = USEC_INFINITY;
+
+ if (!streq(lvalue, "TimeoutStopSec")) {
s->start_timeout_defined = true;
+ s->timeout_start_usec = usec;
+ }
+
+ if (!streq(lvalue, "TimeoutStartSec"))
+ s->timeout_stop_usec = usec;
+
+ return 0;
+}
+
+int config_parse_sec_fix_0(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t *usec = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(usec);
+
+ /* This is pretty much like config_parse_sec(), except that this treats a time of 0 as infinity, for
+ * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a
+ * timeout. */
+
+ r = parse_sec(rvalue, usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (*usec <= 0)
+ *usec = USEC_INFINITY;
+
+ return 0;
+}
+
+int config_parse_user_group(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char **user = data, *n;
+ Unit *u = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ if (isempty(rvalue))
+ n = NULL;
+ else {
+ _cleanup_free_ char *k = NULL;
+
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (!valid_user_group_name_or_id(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k);
+ return 0;
+ }
+
+ n = k;
+ k = NULL;
+ }
+
+ free(*user);
+ *user = n;
+
+ return 0;
+}
+
+int config_parse_user_group_strv(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char ***users = data;
+ Unit *u = userdata;
+ const char *p;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(u);
+
+ if (isempty(rvalue)) {
+ char **empty;
+
+ empty = new0(char*, 1);
+ if (!empty)
+ return log_oom();
+
+ strv_free(*users);
+ *users = empty;
+
+ return 0;
+ }
+
+ p = rvalue;
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
+
+ r = extract_first_word(&p, &word, WHITESPACE, 0);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+ break;
+ }
+
+ r = unit_full_printf(u, word, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word);
+ continue;
+ }
+
+ if (!valid_user_group_name_or_id(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k);
+ continue;
+ }
+
+ r = strv_push(users, k);
+ if (r < 0)
+ return log_oom();
+
+ k = NULL;
+ }
return 0;
}
@@ -1738,7 +1902,7 @@ int config_parse_busname_service(
void *data,
void *userdata) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
BusName *n = data;
int r;
Unit *x;
@@ -1751,21 +1915,18 @@ int config_parse_busname_service(
r = unit_name_printf(UNIT(n), rvalue, &p);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
return 0;
}
if (!endswith(p, ".service")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unit must be of type service, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
r = manager_load_unit(UNIT(n)->manager, p, NULL, &error, &x);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
return 0;
}
@@ -1815,8 +1976,7 @@ int config_parse_bus_policy(
access_str = strpbrk(id_str, WHITESPACE);
if (!access_str) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid busname policy value '%s'", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy value '%s'", rvalue);
return 0;
}
@@ -1826,8 +1986,7 @@ int config_parse_bus_policy(
p->access = bus_policy_access_from_string(access_str);
if (p->access < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid busname policy access type '%s'", access_str);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid busname policy access type '%s'", access_str);
return 0;
}
@@ -1840,7 +1999,7 @@ int config_parse_bus_policy(
return 0;
}
-int config_parse_bus_endpoint_policy(
+int config_parse_working_directory(
const char *unit,
const char *filename,
unsigned line,
@@ -1852,48 +2011,56 @@ int config_parse_bus_endpoint_policy(
void *data,
void *userdata) {
- _cleanup_free_ char *name = NULL;
- BusPolicyAccess access;
ExecContext *c = data;
- char *access_str;
+ Unit *u = userdata;
+ bool missing_ok;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(data);
+ assert(c);
+ assert(u);
- name = strdup(rvalue);
- if (!name)
- return log_oom();
+ if (rvalue[0] == '-') {
+ missing_ok = true;
+ rvalue++;
+ } else
+ missing_ok = false;
- access_str = strpbrk(name, WHITESPACE);
- if (!access_str) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid endpoint policy value '%s'", rvalue);
- return 0;
- }
+ if (streq(rvalue, "~")) {
+ c->working_directory_home = true;
+ c->working_directory = mfree(c->working_directory);
+ } else {
+ _cleanup_free_ char *k = NULL;
- *access_str = '\0';
- access_str++;
- access_str += strspn(access_str, WHITESPACE);
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue);
+ return 0;
+ }
- access = bus_policy_access_from_string(access_str);
- if (access <= _BUS_POLICY_ACCESS_INVALID ||
- access >= _BUS_POLICY_ACCESS_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid endpoint policy access type '%s'", access_str);
- return 0;
- }
+ path_kill_slashes(k);
- if (!c->bus_endpoint) {
- r = bus_endpoint_new(&c->bus_endpoint);
+ if (!utf8_is_valid(k)) {
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
+ return 0;
+ }
- if (r < 0)
- return r;
+ if (!path_is_absolute(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue);
+ return 0;
+ }
+
+ free(c->working_directory);
+ c->working_directory = k;
+ k = NULL;
+
+ c->working_directory_home = false;
}
- return bus_endpoint_add_policy(c->bus_endpoint, name, access);
+ c->working_directory_missing_ok = missing_ok;
+ return 0;
}
int config_parse_unit_env_file(const char *unit,
@@ -1910,7 +2077,6 @@ int config_parse_unit_env_file(const char *unit,
char ***env = data;
Unit *u = userdata;
_cleanup_free_ char *n = NULL;
- const char *s;
int r;
assert(filename);
@@ -1920,24 +2086,22 @@ int config_parse_unit_env_file(const char *unit,
if (isempty(rvalue)) {
/* Empty assignment frees the list */
- strv_free(*env);
- *env = NULL;
+ *env = strv_free(*env);
return 0;
}
r = unit_full_printf(u, rvalue, &n);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve specifiers, ignoring: %s", rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ return 0;
+ }
- s = n ?: rvalue;
- if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Path '%s' is not absolute, ignoring.", s);
+ if (!path_is_absolute(n[0] == '-' ? n + 1 : n)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Path '%s' is not absolute, ignoring.", n);
return 0;
}
- r = strv_extend(env, s);
+ r = strv_extend(env, n);
if (r < 0)
return log_oom();
@@ -1969,24 +2133,26 @@ int config_parse_environ(const char *unit,
if (isempty(rvalue)) {
/* Empty assignment resets the list */
- strv_free(*env);
- *env = NULL;
+ *env = strv_free(*env);
return 0;
}
if (u) {
r = unit_full_printf(u, rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ return 0;
+ }
}
- if (!k)
+ if (!k) {
k = strdup(rvalue);
- if (!k)
- return log_oom();
+ if (!k)
+ return log_oom();
+ }
FOREACH_WORD_QUOTED(word, l, k, state) {
- _cleanup_free_ char *n;
+ _cleanup_free_ char *n = NULL;
char **x;
r = cunescape_length(word, l, 0, &n);
@@ -1996,7 +2162,7 @@ int config_parse_environ(const char *unit,
}
if (!env_assignment_is_valid(n)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid environment assignment, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid environment assignment, ignoring: %s", rvalue);
continue;
}
@@ -2008,8 +2174,71 @@ int config_parse_environ(const char *unit,
*env = x;
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
+
+ return 0;
+}
+
+int config_parse_pass_environ(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ const char *whole_rvalue = rvalue;
+ char*** passenv = data;
+ _cleanup_strv_free_ char **n = NULL;
+ size_t nlen = 0, nbufsize = 0;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ *passenv = strv_free(*passenv);
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Trailing garbage in %s, ignoring: %s", lvalue, whole_rvalue);
+ break;
+ }
+
+ if (!env_name_is_valid(word)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid environment name for %s, ignoring: %s", lvalue, word);
+ continue;
+ }
+
+ if (!GREEDY_REALLOC(n, nbufsize, nlen + 2))
+ return log_oom();
+ n[nlen++] = word;
+ n[nlen] = NULL;
+ word = NULL;
+ }
+
+ if (n) {
+ r = strv_extend_strv(passenv, n, true);
+ if (r < 0)
+ return r;
+ }
return 0;
}
@@ -2034,8 +2263,7 @@ int config_parse_ip_tos(const char *unit,
x = ip_tos_from_string(rvalue);
if (x < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse IP TOS value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IP TOS value, ignoring: %s", rvalue);
return 0;
}
@@ -2083,12 +2311,12 @@ int config_parse_unit_condition_path(
r = unit_full_printf(u, rvalue, &p);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
return 0;
}
if (!path_is_absolute(p)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Path in condition not absolute, ignoring: %s", p);
return 0;
}
@@ -2140,7 +2368,7 @@ int config_parse_unit_condition_string(
r = unit_full_printf(u, rvalue, &s);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue);
return 0;
}
@@ -2189,7 +2417,7 @@ int config_parse_unit_condition_null(
b = parse_boolean(rvalue);
if (b < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, b, "Failed to parse boolean value in condition, ignoring: %s", rvalue);
return 0;
}
@@ -2237,20 +2465,18 @@ int config_parse_unit_requires_mounts_for(
return log_oom();
if (!utf8_is_valid(n)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
continue;
}
r = unit_require_mounts_for(u, n);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add required mount for, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount for, ignoring: %s", rvalue);
continue;
}
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -2277,8 +2503,7 @@ int config_parse_documentation(const char *unit,
if (isempty(rvalue)) {
/* Empty assignment resets the list */
- strv_free(u->documentation);
- u->documentation = NULL;
+ u->documentation = strv_free(u->documentation);
return 0;
}
@@ -2292,8 +2517,7 @@ int config_parse_documentation(const char *unit,
if (documentation_url_is_valid(*a))
*(b++) = *a;
else {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid URL, ignoring: %s", *a);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid URL, ignoring: %s", *a);
free(*a);
}
}
@@ -2304,6 +2528,55 @@ int config_parse_documentation(const char *unit,
}
#ifdef HAVE_SECCOMP
+static int syscall_filter_parse_one(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ ExecContext *c,
+ bool invert,
+ const char *t,
+ bool warn) {
+ int r;
+
+ if (*t == '@') {
+ const SystemCallFilterSet *set;
+
+ for (set = syscall_filter_sets; set->set_name; set++)
+ if (streq(set->set_name, t)) {
+ const char *sys;
+
+ NULSTR_FOREACH(sys, set->value) {
+ r = syscall_filter_parse_one(unit, filename, line, c, invert, sys, false);
+ if (r < 0)
+ return r;
+ }
+ break;
+ }
+ } else {
+ int id;
+
+ id = seccomp_syscall_resolve_name(t);
+ if (id == __NR_SCMP_ERROR) {
+ if (warn)
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t);
+ return 0;
+ }
+
+ /* If we previously wanted to forbid a syscall and now
+ * we want to allow it, then remove it from the list
+ */
+ if (!invert == c->syscall_whitelist) {
+ r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
+ if (r == 0)
+ return 0;
+ if (r < 0)
+ return log_oom();
+ } else
+ set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
+ }
+ return 0;
+}
+
int config_parse_syscall_filter(
const char *unit,
const char *filename,
@@ -2316,13 +2589,6 @@ int config_parse_syscall_filter(
void *data,
void *userdata) {
- static const char default_syscalls[] =
- "execve\0"
- "exit\0"
- "exit_group\0"
- "rt_sigreturn\0"
- "sigreturn\0";
-
ExecContext *c = data;
Unit *u = userdata;
bool invert = false;
@@ -2337,8 +2603,7 @@ int config_parse_syscall_filter(
if (isempty(rvalue)) {
/* Empty assignment resets the list */
- set_free(c->syscall_filter);
- c->syscall_filter = NULL;
+ c->syscall_filter = set_free(c->syscall_filter);
c->syscall_whitelist = false;
return 0;
}
@@ -2357,62 +2622,33 @@ int config_parse_syscall_filter(
/* Allow everything but the ones listed */
c->syscall_whitelist = false;
else {
- const char *i;
-
/* Allow nothing but the ones listed */
c->syscall_whitelist = true;
/* Accept default syscalls if we are on a whitelist */
- NULSTR_FOREACH(i, default_syscalls) {
- int id;
-
- id = seccomp_syscall_resolve_name(i);
- if (id < 0)
- continue;
-
- r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
- if (r == 0)
- continue;
- if (r < 0)
- return log_oom();
- }
+ r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false);
+ if (r < 0)
+ return r;
}
}
FOREACH_WORD_QUOTED(word, l, rvalue, state) {
_cleanup_free_ char *t = NULL;
- int id;
t = strndup(word, l);
if (!t)
return log_oom();
- id = seccomp_syscall_resolve_name(t);
- if (id < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse system call, ignoring: %s", t);
- continue;
- }
-
- /* If we previously wanted to forbid a syscall and now
- * we want to allow it, then remove it from the list
- */
- if (!invert == c->syscall_whitelist) {
- r = set_put(c->syscall_filter, INT_TO_PTR(id + 1));
- if (r == 0)
- continue;
- if (r < 0)
- return log_oom();
- } else
- set_remove(c->syscall_filter, INT_TO_PTR(id + 1));
+ r = syscall_filter_parse_one(unit, filename, line, c, invert, t, true);
+ if (r < 0)
+ return r;
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
/* Turn on NNP, but only if it wasn't configured explicitly
* before, and only if we are in user mode. */
- if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER)
+ if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager))
c->no_new_privileges = true;
return 0;
@@ -2436,8 +2672,7 @@ int config_parse_syscall_archs(
int r;
if (isempty(rvalue)) {
- set_free(*archs);
- *archs = NULL;
+ *archs = set_free(*archs);
return 0;
}
@@ -2455,8 +2690,7 @@ int config_parse_syscall_archs(
r = seccomp_arch_from_string(t, &a);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse system call architecture, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call architecture, ignoring: %s", t);
continue;
}
@@ -2467,8 +2701,7 @@ int config_parse_syscall_archs(
return log_oom();
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -2500,8 +2733,7 @@ int config_parse_syscall_errno(
e = errno_from_name(rvalue);
if (e < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse error number, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue);
return 0;
}
@@ -2533,8 +2765,7 @@ int config_parse_address_families(
if (isempty(rvalue)) {
/* Empty assignment resets the list */
- set_free(c->address_families);
- c->address_families = NULL;
+ c->address_families = set_free(c->address_families);
c->address_families_whitelist = false;
return 0;
}
@@ -2562,8 +2793,7 @@ int config_parse_address_families(
af = af_from_name(t);
if (af <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse address family, ignoring: %s", t);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse address family, ignoring: %s", t);
continue;
}
@@ -2580,8 +2810,7 @@ int config_parse_address_families(
set_remove(c->address_families, INT_TO_PTR(af));
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -2600,7 +2829,7 @@ int config_parse_unit_slice(
void *userdata) {
_cleanup_free_ char *k = NULL;
- Unit *u = userdata, *slice;
+ Unit *u = userdata, *slice = NULL;
int r;
assert(filename);
@@ -2609,35 +2838,29 @@ int config_parse_unit_slice(
assert(u);
r = unit_name_printf(u, rvalue, &k);
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
- if (!k) {
- k = strdup(rvalue);
- if (!k)
- return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ return 0;
}
r = manager_load_unit(u->manager, k, NULL, NULL, &slice);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to load slice unit %s. Ignoring.", k);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k);
return 0;
}
- if (slice->type != UNIT_SLICE) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Slice unit %s is not a slice. Ignoring.", k);
+ r = unit_set_slice(u, slice);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id);
return 0;
}
- unit_ref_set(&u->slice, slice);
return 0;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
-int config_parse_cpu_shares(
+int config_parse_cpu_weight(
const char *unit,
const char *filename,
unsigned line,
@@ -2649,26 +2872,47 @@ int config_parse_cpu_shares(
void *data,
void *userdata) {
- unsigned long *shares = data, lu;
+ uint64_t *weight = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- if (isempty(rvalue)) {
- *shares = (unsigned long) -1;
+ r = cg_weight_parse(rvalue, weight);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "CPU weight '%s' invalid. Ignoring.", rvalue);
return 0;
}
- r = safe_atolu(rvalue, &lu);
- if (r < 0 || lu <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "CPU shares '%s' invalid. Ignoring.", rvalue);
+ return 0;
+}
+
+int config_parse_cpu_shares(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ uint64_t *shares = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = cg_cpu_shares_parse(rvalue, shares);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue);
return 0;
}
- *shares = lu;
return 0;
}
@@ -2685,7 +2929,7 @@ int config_parse_cpu_quota(
void *userdata) {
CGroupContext *c = data;
- double percent;
+ int r;
assert(filename);
assert(lvalue);
@@ -2696,25 +2940,67 @@ int config_parse_cpu_quota(
return 0;
}
- if (!endswith(rvalue, "%")) {
-
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
+ r = parse_percent_unbounded(rvalue);
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
return 0;
}
- if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "CPU quota '%s' invalid. Ignoring.", rvalue);
- return 0;
+ c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
+ return 0;
+}
+
+int config_parse_memory_limit(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ CGroupContext *c = data;
+ uint64_t bytes = CGROUP_LIMIT_MAX;
+ int r;
+
+ if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
+
+ r = parse_percent(rvalue);
+ if (r < 0) {
+ r = parse_size(rvalue, 1024, &bytes);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+ } else
+ bytes = physical_memory_scale(r, 100U);
+
+ if (bytes <= 0 || bytes >= UINT64_MAX) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' out of range. Ignoring.", rvalue);
+ return 0;
+ }
}
- c->cpu_quota_per_sec_usec = (usec_t) (percent * USEC_PER_SEC / 100);
+ if (streq(lvalue, "MemoryLow"))
+ c->memory_low = bytes;
+ else if (streq(lvalue, "MemoryHigh"))
+ c->memory_high = bytes;
+ else if (streq(lvalue, "MemoryMax"))
+ c->memory_max = bytes;
+ else if (streq(lvalue, "MemorySwapMax"))
+ c->memory_swap_max = bytes;
+ else if (streq(lvalue, "MemoryLimit"))
+ c->memory_limit = bytes;
+ else
+ return -EINVAL;
return 0;
}
-int config_parse_memory_limit(
+int config_parse_tasks_max(
const char *unit,
const char *filename,
unsigned line,
@@ -2726,25 +3012,36 @@ int config_parse_memory_limit(
void *data,
void *userdata) {
- CGroupContext *c = data;
- off_t bytes;
+ uint64_t *tasks_max = data, v;
+ Unit *u = userdata;
int r;
if (isempty(rvalue)) {
- c->memory_limit = (uint64_t) -1;
+ *tasks_max = u->manager->default_tasks_max;
return 0;
}
- assert_cc(sizeof(uint64_t) == sizeof(off_t));
+ if (streq(rvalue, "infinity")) {
+ *tasks_max = CGROUP_LIMIT_MAX;
+ return 0;
+ }
- r = parse_size(rvalue, 1024, &bytes);
+ r = parse_percent(rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Memory limit '%s' invalid. Ignoring.", rvalue);
+ r = safe_atou64(rvalue, &v);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+ } else
+ v = system_tasks_max_scale(r, 100U);
+
+ if (v <= 0 || v >= UINT64_MAX) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
return 0;
}
- c->memory_limit = (uint64_t) bytes;
+ *tasks_max = v;
return 0;
}
@@ -2760,11 +3057,12 @@ int config_parse_device_allow(
void *data,
void *userdata) {
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *path = NULL, *t = NULL;
CGroupContext *c = data;
CGroupDeviceAllow *a;
- const char *m;
+ const char *m = NULL;
size_t n;
+ int r;
if (isempty(rvalue)) {
while (c->device_allow)
@@ -2773,26 +3071,30 @@ int config_parse_device_allow(
return 0;
}
- n = strcspn(rvalue, WHITESPACE);
- path = strndup(rvalue, n);
+ r = unit_full_printf(userdata, rvalue, &t);
+ if(r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to resolve specifiers in %s, ignoring: %m",
+ rvalue);
+ }
+
+ n = strcspn(t, WHITESPACE);
+
+ path = strndup(t, n);
if (!path)
return log_oom();
- if (!startswith(path, "/dev/") &&
- !startswith(path, "block-") &&
- !startswith(path, "char-")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device node path '%s'. Ignoring.", path);
+ if (!is_deviceallow_pattern(path)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
- m = rvalue + n + strspn(rvalue + n, WHITESPACE);
+ m = t + n + strspn(t + n, WHITESPACE);
if (isempty(m))
m = "rwm";
if (!in_charset(m, "rwm")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device rights '%s'. Ignoring.", m);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device rights '%s'. Ignoring.", m);
return 0;
}
@@ -2810,7 +3112,7 @@ int config_parse_device_allow(
return 0;
}
-int config_parse_blockio_weight(
+int config_parse_io_weight(
const char *unit,
const char *filename,
unsigned line,
@@ -2822,30 +3124,23 @@ int config_parse_blockio_weight(
void *data,
void *userdata) {
- unsigned long *weight = data, lu;
+ uint64_t *weight = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- if (isempty(rvalue)) {
- *weight = (unsigned long) -1;
- return 0;
- }
-
- r = safe_atolu(rvalue, &lu);
- if (r < 0 || lu < 10 || lu > 1000) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Block IO weight '%s' invalid. Ignoring.", rvalue);
+ r = cg_weight_parse(rvalue, weight);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue);
return 0;
}
- *weight = lu;
return 0;
}
-int config_parse_blockio_device_weight(
+int config_parse_io_device_weight(
const char *unit,
const char *filename,
unsigned line,
@@ -2858,10 +3153,10 @@ int config_parse_blockio_device_weight(
void *userdata) {
_cleanup_free_ char *path = NULL;
- CGroupBlockIODeviceWeight *w;
+ CGroupIODeviceWeight *w;
CGroupContext *c = data;
- unsigned long lu;
const char *weight;
+ uint64_t u;
size_t n;
int r;
@@ -2870,17 +3165,18 @@ int config_parse_blockio_device_weight(
assert(rvalue);
if (isempty(rvalue)) {
- while (c->blockio_device_weights)
- cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
+ while (c->io_device_weights)
+ cgroup_context_free_io_device_weight(c, c->io_device_weights);
return 0;
}
n = strcspn(rvalue, WHITESPACE);
weight = rvalue + n;
- if (!*weight) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Expected block device and device weight. Ignoring.");
+ weight += strspn(weight, WHITESPACE);
+
+ if (isempty(weight)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
return 0;
}
@@ -2889,33 +3185,32 @@ int config_parse_blockio_device_weight(
return log_oom();
if (!path_startswith(path, "/dev")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device node path '%s'. Ignoring.", path);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
- weight += strspn(weight, WHITESPACE);
- r = safe_atolu(weight, &lu);
- if (r < 0 || lu < 10 || lu > 1000) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Block IO weight '%s' invalid. Ignoring.", rvalue);
+ r = cg_weight_parse(weight, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight);
return 0;
}
- w = new0(CGroupBlockIODeviceWeight, 1);
+ assert(u != CGROUP_WEIGHT_INVALID);
+
+ w = new0(CGroupIODeviceWeight, 1);
if (!w)
return log_oom();
w->path = path;
path = NULL;
- w->weight = lu;
+ w->weight = u;
- LIST_PREPEND(device_weights, c->blockio_device_weights, w);
+ LIST_PREPEND(device_weights, c->io_device_weights, w);
return 0;
}
-int config_parse_blockio_bandwidth(
+int config_parse_io_limit(
const char *unit,
const char *filename,
unsigned line,
@@ -2928,11 +3223,11 @@ int config_parse_blockio_bandwidth(
void *userdata) {
_cleanup_free_ char *path = NULL;
- CGroupBlockIODeviceBandwidth *b;
+ CGroupIODeviceLimit *l = NULL, *t;
CGroupContext *c = data;
- const char *bandwidth;
- off_t bytes;
- bool read;
+ CGroupIOLimitType type;
+ const char *limit;
+ uint64_t num;
size_t n;
int r;
@@ -2940,25 +3235,135 @@ int config_parse_blockio_bandwidth(
assert(lvalue);
assert(rvalue);
- read = streq("BlockIOReadBandwidth", lvalue);
+ type = cgroup_io_limit_type_from_string(lvalue);
+ assert(type >= 0);
if (isempty(rvalue)) {
- CGroupBlockIODeviceBandwidth *next;
+ LIST_FOREACH(device_limits, l, c->io_device_limits)
+ l->limits[type] = cgroup_io_limit_defaults[type];
+ return 0;
+ }
+
+ n = strcspn(rvalue, WHITESPACE);
+ limit = rvalue + n;
+ limit += strspn(limit, WHITESPACE);
+
+ if (!*limit) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
+ return 0;
+ }
+
+ path = strndup(rvalue, n);
+ if (!path)
+ return log_oom();
+
+ if (!path_startswith(path, "/dev")) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
+ return 0;
+ }
+
+ if (streq("infinity", limit)) {
+ num = CGROUP_LIMIT_MAX;
+ } else {
+ r = parse_size(limit, 1000, &num);
+ if (r < 0 || num <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+ }
+
+ LIST_FOREACH(device_limits, t, c->io_device_limits) {
+ if (path_equal(path, t->path)) {
+ l = t;
+ break;
+ }
+ }
+
+ if (!l) {
+ CGroupIOLimitType ttype;
+
+ l = new0(CGroupIODeviceLimit, 1);
+ if (!l)
+ return log_oom();
+
+ l->path = path;
+ path = NULL;
+ for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++)
+ l->limits[ttype] = cgroup_io_limit_defaults[ttype];
+
+ LIST_PREPEND(device_limits, c->io_device_limits, l);
+ }
+
+ l->limits[type] = num;
+
+ return 0;
+}
+
+int config_parse_blockio_weight(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ uint64_t *weight = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = cg_blkio_weight_parse(rvalue, weight);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_blockio_device_weight(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *path = NULL;
+ CGroupBlockIODeviceWeight *w;
+ CGroupContext *c = data;
+ const char *weight;
+ uint64_t u;
+ size_t n;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
- LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
- if (b->read == read)
- cgroup_context_free_blockio_device_bandwidth(c, b);
+ if (isempty(rvalue)) {
+ while (c->blockio_device_weights)
+ cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
return 0;
}
n = strcspn(rvalue, WHITESPACE);
- bandwidth = rvalue + n;
- bandwidth += strspn(bandwidth, WHITESPACE);
+ weight = rvalue + n;
+ weight += strspn(weight, WHITESPACE);
- if (!*bandwidth) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Expected space separated pair of device node and bandwidth. Ignoring.");
+ if (isempty(weight)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring.");
return 0;
}
@@ -2967,35 +3372,32 @@ int config_parse_blockio_bandwidth(
return log_oom();
if (!path_startswith(path, "/dev")) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Invalid device node path '%s'. Ignoring.", path);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
return 0;
}
- r = parse_size(bandwidth, 1000, &bytes);
- if (r < 0 || bytes <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
+ r = cg_blkio_weight_parse(weight, &u);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight);
return 0;
}
- b = new0(CGroupBlockIODeviceBandwidth, 1);
- if (!b)
+ assert(u != CGROUP_BLKIO_WEIGHT_INVALID);
+
+ w = new0(CGroupBlockIODeviceWeight, 1);
+ if (!w)
return log_oom();
- b->path = path;
+ w->path = path;
path = NULL;
- b->bandwidth = (uint64_t) bytes;
- b->read = read;
- LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
+ w->weight = u;
+ LIST_PREPEND(device_weights, c->blockio_device_weights, w);
return 0;
}
-DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
-
-int config_parse_job_mode_isolate(
+int config_parse_blockio_bandwidth(
const char *unit,
const char *filename,
unsigned line,
@@ -3007,25 +3409,84 @@ int config_parse_job_mode_isolate(
void *data,
void *userdata) {
- JobMode *m = data;
+ _cleanup_free_ char *path = NULL;
+ CGroupBlockIODeviceBandwidth *b = NULL, *t;
+ CGroupContext *c = data;
+ const char *bandwidth;
+ uint64_t bytes;
+ bool read;
+ size_t n;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse boolean, ignoring: %s", rvalue);
+ read = streq("BlockIOReadBandwidth", lvalue);
+
+ if (isempty(rvalue)) {
+ LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+ b->rbps = CGROUP_LIMIT_MAX;
+ b->wbps = CGROUP_LIMIT_MAX;
+ }
return 0;
}
- *m = r ? JOB_ISOLATE : JOB_REPLACE;
+ n = strcspn(rvalue, WHITESPACE);
+ bandwidth = rvalue + n;
+ bandwidth += strspn(bandwidth, WHITESPACE);
+
+ if (!*bandwidth) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring.");
+ return 0;
+ }
+
+ path = strndup(rvalue, n);
+ if (!path)
+ return log_oom();
+
+ if (!path_startswith(path, "/dev")) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path);
+ return 0;
+ }
+
+ r = parse_size(bandwidth, 1000, &bytes);
+ if (r < 0 || bytes <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+
+ LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) {
+ if (path_equal(path, t->path)) {
+ b = t;
+ break;
+ }
+ }
+
+ if (!t) {
+ b = new0(CGroupBlockIODeviceBandwidth, 1);
+ if (!b)
+ return log_oom();
+
+ b->path = path;
+ path = NULL;
+ b->rbps = CGROUP_LIMIT_MAX;
+ b->wbps = CGROUP_LIMIT_MAX;
+
+ LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b);
+ }
+
+ if (read)
+ b->rbps = bytes;
+ else
+ b->wbps = bytes;
+
return 0;
}
-int config_parse_personality(
+DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
+
+int config_parse_job_mode_isolate(
const char *unit,
const char *filename,
unsigned line,
@@ -3037,21 +3498,20 @@ int config_parse_personality(
void *data,
void *userdata) {
- unsigned long *personality = data, p;
+ JobMode *m = data;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(personality);
- p = personality_from_string(rvalue);
- if (p == PERSONALITY_INVALID) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse personality, ignoring: %s", rvalue);
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse boolean, ignoring: %s", rvalue);
return 0;
}
- *personality = p;
+ *m = r ? JOB_ISOLATE : JOB_REPLACE;
return 0;
}
@@ -3068,6 +3528,7 @@ int config_parse_runtime_directory(
void *userdata) {
char***rt = data;
+ Unit *u = userdata;
const char *word, *state;
size_t l;
int r;
@@ -3079,21 +3540,25 @@ int config_parse_runtime_directory(
if (isempty(rvalue)) {
/* Empty assignment resets the list */
- strv_free(*rt);
- *rt = NULL;
+ *rt = strv_free(*rt);
return 0;
}
FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *n;
+ _cleanup_free_ char *t = NULL, *n = NULL;
- n = strndup(word, l);
- if (!n)
+ t = strndup(word, l);
+ if (!t)
return log_oom();
+ r = unit_name_printf(u, t, &n);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m");
+ continue;
+ }
+
if (!filename_is_valid(n)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Runtime directory is not valid, ignoring assignment: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
continue;
}
@@ -3104,8 +3569,7 @@ int config_parse_runtime_directory(
n = NULL;
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -3152,15 +3616,13 @@ int config_parse_set_status(
val = signal_from_string_try_harder(temp);
if (val <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, -val,
- "Failed to parse value, ignoring: %s", word);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word);
continue;
}
set = &status_set->signal;
} else {
if (val < 0 || val > 255) {
- log_syntax(unit, LOG_ERR, filename, line, ERANGE,
- "Value %d is outside range 0-255, ignoring", val);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Value %d is outside range 0-255, ignoring", val);
continue;
}
set = &status_set->status;
@@ -3172,14 +3634,12 @@ int config_parse_set_status(
r = set_put(*set, INT_TO_PTR(val));
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Unable to store: %s", word);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Unable to store: %s", word);
return r;
}
}
if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
@@ -3197,8 +3657,8 @@ int config_parse_namespace_path_strv(
void *userdata) {
char*** sv = data;
- const char *word, *state;
- size_t l;
+ const char *prev;
+ const char *cur;
int r;
assert(filename);
@@ -3208,42 +3668,47 @@ int config_parse_namespace_path_strv(
if (isempty(rvalue)) {
/* Empty assignment resets the list */
- strv_free(*sv);
- *sv = NULL;
+ *sv = strv_free(*sv);
return 0;
}
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- _cleanup_free_ char *n;
+ prev = cur = rvalue;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
int offset;
- n = strndup(word, l);
- if (!n)
+ r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES);
+ if (r == 0)
+ break;
+ if (r == -ENOMEM)
return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage, ignoring: %s", prev);
+ return 0;
+ }
- if (!utf8_is_valid(n)) {
- log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+ if (!utf8_is_valid(word)) {
+ log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
+ prev = cur;
continue;
}
- offset = n[0] == '-';
- if (!path_is_absolute(n + offset)) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Not an absolute path, ignoring: %s", rvalue);
+ offset = word[0] == '-';
+ if (!path_is_absolute(word + offset)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", word);
+ prev = cur;
continue;
}
- path_kill_slashes(n);
+ path_kill_slashes(word + offset);
- r = strv_push(sv, n);
+ r = strv_push(sv, word);
if (r < 0)
return log_oom();
- n = NULL;
+ prev = cur;
+ word = NULL;
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
return 0;
}
@@ -3270,8 +3735,7 @@ int config_parse_no_new_privileges(
k = parse_boolean(rvalue);
if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -k,
- "Failed to parse boolean value, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
return 0;
}
@@ -3302,7 +3766,7 @@ int config_parse_protect_home(
assert(data);
/* Our enum shall be a superset of booleans, hence first try
- * to parse as as boolean, and then as enum */
+ * to parse as boolean, and then as enum */
k = parse_boolean(rvalue);
if (k > 0)
@@ -3313,9 +3777,8 @@ int config_parse_protect_home(
ProtectHome h;
h = protect_home_from_string(rvalue);
- if (h < 0){
- log_syntax(unit, LOG_ERR, filename, line, -h,
- "Failed to parse protect home value, ignoring: %s", rvalue);
+ if (h < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue);
return 0;
}
@@ -3346,7 +3809,7 @@ int config_parse_protect_system(
assert(data);
/* Our enum shall be a superset of booleans, hence first try
- * to parse as as boolean, and then as enum */
+ * to parse as boolean, and then as enum */
k = parse_boolean(rvalue);
if (k > 0)
@@ -3357,9 +3820,8 @@ int config_parse_protect_system(
ProtectSystem s;
s = protect_system_from_string(rvalue);
- if (s < 0){
- log_syntax(unit, LOG_ERR, filename, line, -s,
- "Failed to parse protect system value, ignoring: %s", rvalue);
+ if (s < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue);
return 0;
}
@@ -3372,10 +3834,10 @@ int config_parse_protect_system(
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
+ char *id = NULL;
unsigned c = 0;
int fd, r;
FILE *f;
- char *id = NULL;
assert(filename);
assert(*filename);
@@ -3397,7 +3859,6 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
* the names of this unit, but only if it is a valid
* unit name. */
name = basename(*filename);
-
if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
id = set_get(names, name);
@@ -3437,6 +3898,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
*_f = f;
*_final = id;
+
return 0;
}
@@ -3461,7 +3923,19 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
* ours? Then let's try it the other way
* round */
- other = manager_get_unit((*u)->manager, k);
+ /* If the symlink name we are looking at is unit template, then
+ we must search for instance of this template */
+ if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE) && (*u)->instance) {
+ _cleanup_free_ char *instance = NULL;
+
+ r = unit_name_replace_instance(k, (*u)->instance, &instance);
+ if (r < 0)
+ return r;
+
+ other = manager_get_unit((*u)->manager, instance);
+ } else
+ other = manager_get_unit((*u)->manager, k);
+
free(k);
if (other) {
@@ -3485,13 +3959,13 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
}
static int load_from_path(Unit *u, const char *path) {
- int r;
_cleanup_set_free_free_ Set *symlink_names = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *filename = NULL;
char *id = NULL;
Unit *merged;
struct stat st;
+ int r;
assert(u);
assert(path);
@@ -3508,9 +3982,7 @@ static int load_from_path(Unit *u, const char *path) {
r = open_follow(&filename, &f, symlink_names, &id);
if (r < 0) {
- free(filename);
- filename = NULL;
-
+ filename = mfree(filename);
if (r != -ENOENT)
return r;
}
@@ -3518,7 +3990,7 @@ static int load_from_path(Unit *u, const char *path) {
} else {
char **p;
- STRV_FOREACH(p, u->manager->lookup_paths.unit_path) {
+ STRV_FOREACH(p, u->manager->lookup_paths.search_path) {
/* Instead of opening the path right away, we manually
* follow all symlinks and add their name to our unit
@@ -3532,20 +4004,22 @@ static int load_from_path(Unit *u, const char *path) {
r = -ENOENT;
else
r = open_follow(&filename, &f, symlink_names, &id);
+ if (r >= 0)
+ break;
+ filename = mfree(filename);
+
+ /* ENOENT means that the file is missing or is a dangling symlink.
+ * ENOTDIR means that one of paths we expect to be is a directory
+ * is not a directory, we should just ignore that.
+ * EACCES means that the directory or file permissions are wrong.
+ */
+ if (r == -EACCES)
+ log_debug_errno(r, "Cannot access \"%s\": %m", filename);
+ else if (!IN_SET(r, -ENOENT, -ENOTDIR))
+ return r;
- if (r < 0) {
- free(filename);
- filename = NULL;
-
- if (r != -ENOENT)
- return r;
-
- /* Empty the symlink names for the next run */
- set_clear_free(symlink_names);
- continue;
- }
-
- break;
+ /* Empty the symlink names for the next run */
+ set_clear_free(symlink_names);
}
}
@@ -3553,6 +4027,11 @@ static int load_from_path(Unit *u, const char *path) {
/* Hmm, no suitable file found? */
return 0;
+ if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
+ log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
+ return -ELOOP;
+ }
+
merged = u;
r = merge_by_names(&merged, symlink_names, id);
if (r < 0)
@@ -3566,10 +4045,12 @@ static int load_from_path(Unit *u, const char *path) {
if (fstat(fileno(f), &st) < 0)
return -errno;
- if (null_or_empty(&st))
+ if (null_or_empty(&st)) {
u->load_state = UNIT_MASKED;
- else {
+ u->fragment_mtime = 0;
+ } else {
u->load_state = UNIT_LOADED;
+ u->fragment_mtime = timespec_load(&st.st_mtim);
/* Now, parse the file contents */
r = config_parse(u->id, filename, f,
@@ -3584,8 +4065,6 @@ static int load_from_path(Unit *u, const char *path) {
u->fragment_path = filename;
filename = NULL;
- u->fragment_mtime = timespec_load(&st.st_mtim);
-
if (u->source_path) {
if (stat(u->source_path, &st) >= 0)
u->source_mtime = timespec_load(&st.st_mtim);
@@ -3605,6 +4084,11 @@ int unit_load_fragment(Unit *u) {
assert(u->load_state == UNIT_STUB);
assert(u->id);
+ if (u->transient) {
+ u->load_state = UNIT_LOADED;
+ return 0;
+ }
+
/* First, try to find the unit under its id. We always look
* for unit files in the default directories, to make it easy
* to override things by placing things in /etc/systemd/system */
@@ -3635,13 +4119,11 @@ int unit_load_fragment(Unit *u) {
if (r < 0)
return r;
- if (u->load_state == UNIT_STUB) {
+ if (u->load_state == UNIT_STUB)
/* Hmm, this didn't work? Then let's get rid
* of the fragment path stored for us, so that
* we don't point to an invalid location. */
- free(u->fragment_path);
- u->fragment_path = NULL;
- }
+ u->fragment_path = mfree(u->fragment_path);
}
/* Look for a template */
@@ -3691,7 +4173,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_int, "INTEGER" },
{ config_parse_unsigned, "UNSIGNED" },
{ config_parse_iec_size, "SIZE" },
- { config_parse_iec_off, "SIZE" },
+ { config_parse_iec_uint64, "SIZE" },
{ config_parse_si_size, "SIZE" },
{ config_parse_bool, "BOOLEAN" },
{ config_parse_string, "STRING" },
@@ -3711,9 +4193,8 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_input, "INPUT" },
{ config_parse_log_facility, "FACILITY" },
{ config_parse_log_level, "LEVEL" },
- { config_parse_exec_capabilities, "CAPABILITIES" },
{ config_parse_exec_secure_bits, "SECUREBITS" },
- { config_parse_bounding_set, "BOUNDINGSET" },
+ { config_parse_capability_set, "BOUNDINGSET" },
{ config_parse_limit, "LIMIT" },
{ config_parse_unit_deps, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
@@ -3723,7 +4204,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_sysv_priority, "SYSVPRIORITY" },
#endif
{ config_parse_kill_mode, "KILLMODE" },
- { config_parse_kill_signal, "SIGNAL" },
+ { config_parse_signal, "SIGNAL" },
{ config_parse_socket_listen, "SOCKET [...]" },
{ config_parse_socket_bind, "SOCKETBIND" },
{ config_parse_socket_bindtodevice, "NETWORKINTERFACE" },
@@ -3755,9 +4236,13 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_address_families, "FAMILIES" },
#endif
{ config_parse_cpu_shares, "SHARES" },
+ { config_parse_cpu_weight, "WEIGHT" },
{ config_parse_memory_limit, "LIMIT" },
{ config_parse_device_allow, "DEVICE" },
{ config_parse_device_policy, "POLICY" },
+ { config_parse_io_limit, "LIMIT" },
+ { config_parse_io_weight, "WEIGHT" },
+ { config_parse_io_device_weight, "DEVICEWEIGHT" },
{ config_parse_blockio_bandwidth, "BANDWIDTH" },
{ config_parse_blockio_weight, "WEIGHT" },
{ config_parse_blockio_device_weight, "DEVICEWEIGHT" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index ce10d03c3f..8b688740cf 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -31,12 +29,14 @@ void unit_dump_config_items(FILE *f);
int config_parse_warn_compat(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_obsolete_unit_deps(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_string_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_path_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_path_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_documentation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_nice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_oom_score_adjust(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -52,9 +52,8 @@ int config_parse_exec_io_priority(const char *unit, const char *filename, unsign
int config_parse_exec_cpu_sched_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_cpu_sched_prio(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_cpu_affinity(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_exec_capabilities(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_capability_set(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_sysv_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_kill_signal(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -67,7 +66,6 @@ int config_parse_service_sockets(const char *unit, const char *filename, unsigne
int config_parse_busname_service(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bus_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bus_policy_world(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bus_endpoint_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_env_file(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_ip_tos(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_condition_path(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -81,18 +79,24 @@ int config_parse_syscall_filter(const char *unit, const char *filename, unsigned
int config_parse_syscall_archs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_syscall_errno(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_pass_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_cpu_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_device_allow(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_io_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_io_device_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_io_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_blockio_device_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_netclass(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_job_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_apparmor_profile(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_smack_process_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_address_families(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -104,6 +108,12 @@ int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line
int config_parse_protect_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_working_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_fdname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_sec_fix_0(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_user_group(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_user_group_strv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index 108072ca9f..ccf61d29fb 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,23 +17,24 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdlib.h>
#include <errno.h>
+#include <stdlib.h>
+#include "env-util.h"
+#include "fileio.h"
#include "locale-setup.h"
+#include "locale-util.h"
+#include "string-util.h"
+#include "strv.h"
#include "util.h"
#include "virt.h"
-#include "fileio.h"
-#include "strv.h"
-#include "env-util.h"
-#include "locale-util.h"
int locale_setup(char ***environment) {
char **add;
char *variables[_VARIABLE_LC_MAX] = {};
int r = 0, i;
- if (detect_container(NULL) <= 0) {
+ if (detect_container() <= 0) {
r = parse_env_file("/proc/cmdline", WHITESPACE,
"locale.LANG", &variables[VARIABLE_LANG],
"locale.LANGUAGE", &variables[VARIABLE_LANGUAGE],
diff --git a/src/core/locale-setup.h b/src/core/locale-setup.h
index 62c654c37c..3b97497afe 100644
--- a/src/core/locale-setup.h
+++ b/src/core/locale-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c
index 4503fc9dcc..04062a7910 100644
--- a/src/core/loopback-setup.c
+++ b/src/core/loopback-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -23,12 +21,13 @@
#include <stdlib.h>
#include "sd-netlink.h"
-#include "netlink-util.h"
-#include "missing.h"
+
#include "loopback-setup.h"
+#include "missing.h"
+#include "netlink-util.h"
static int start_loopback(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX);
@@ -47,7 +46,7 @@ static int start_loopback(sd_netlink *rtnl) {
}
static bool check_loopback(sd_netlink *rtnl) {
- _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
unsigned flags;
int r;
@@ -67,7 +66,7 @@ static bool check_loopback(sd_netlink *rtnl) {
}
int loopback_setup(void) {
- _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r;
r = sd_netlink_open(&rtnl);
diff --git a/src/core/loopback-setup.h b/src/core/loopback-setup.h
index dd83cf13a7..e7547b8a26 100644
--- a/src/core/loopback-setup.h
+++ b/src/core/loopback-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index 8e26362546..76dfcfa6d7 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,196 +17,92 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
#include <fcntl.h>
+#include <sched.h>
#include <sys/mount.h>
+#include <unistd.h>
-#include "systemd/sd-id128.h"
+#include "sd-id128.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "id128-util.h"
+#include "log.h"
#include "machine-id-setup.h"
#include "macro.h"
-#include "util.h"
#include "mkdir.h"
-#include "log.h"
-#include "virt.h"
-#include "fileio.h"
+#include "mount-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "umask-util.h"
+#include "util.h"
+#include "virt.h"
-static int shorten_uuid(char destination[34], const char source[36]) {
- unsigned i, j;
-
- assert(destination);
- assert(source);
-
- /* Converts a UUID into a machine ID, by lowercasing it and
- * removing dashes. Validates everything. */
-
- for (i = 0, j = 0; i < 36 && j < 32; i++) {
- int t;
-
- t = unhexchar(source[i]);
- if (t < 0)
- continue;
-
- destination[j++] = hexchar(t);
- }
-
- if (i != 36 || j != 32)
- return -EINVAL;
-
- destination[32] = '\n';
- destination[33] = 0;
- return 0;
-}
-
-static int read_machine_id(int fd, char id[34]) {
- char id_to_validate[34];
+static int generate_machine_id(const char *root, sd_id128_t *ret) {
+ const char *dbus_machine_id;
+ _cleanup_close_ int fd = -1;
int r;
- assert(fd >= 0);
- assert(id);
-
- /* Reads a machine ID from a file, validates it, and returns
- * it. The returned ID ends in a newline. */
-
- r = loop_read_exact(fd, id_to_validate, 33, false);
- if (r < 0)
- return r;
-
- if (id_to_validate[32] != '\n')
- return -EINVAL;
-
- id_to_validate[32] = 0;
-
- if (!id128_is_valid(id_to_validate))
- return -EINVAL;
-
- memcpy(id, id_to_validate, 32);
- id[32] = '\n';
- id[33] = 0;
- return 0;
-}
-
-static int write_machine_id(int fd, char id[34]) {
- assert(fd >= 0);
- assert(id);
-
- if (lseek(fd, 0, SEEK_SET) < 0)
- return -errno;
-
- return loop_write(fd, id, 33, false);
-}
-
-static int generate_machine_id(char id[34], const char *root) {
- int fd, r;
- unsigned char *p;
- sd_id128_t buf;
- char *q;
- const char *vm_id, *dbus_machine_id;
-
- assert(id);
-
- if (isempty(root))
- dbus_machine_id = "/var/lib/dbus/machine-id";
- else
- dbus_machine_id = strjoina(root, "/var/lib/dbus/machine-id");
+ assert(ret);
/* First, try reading the D-Bus machine id, unless it is a symlink */
+ dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id");
fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
if (fd >= 0) {
- r = read_machine_id(fd, id);
- safe_close(fd);
-
- if (r >= 0) {
+ if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0) {
log_info("Initializing machine ID from D-Bus machine ID.");
return 0;
}
+
+ fd = safe_close(fd);
}
if (isempty(root)) {
/* If that didn't work, see if we are running in a container,
* and a machine ID was passed in via $container_uuid the way
* libvirt/LXC does it */
- r = detect_container(NULL);
- if (r > 0) {
+
+ if (detect_container() > 0) {
_cleanup_free_ char *e = NULL;
- r = getenv_for_pid(1, "container_uuid", &e);
- if (r > 0) {
- r = shorten_uuid(id, e);
- if (r >= 0) {
- log_info("Initializing machine ID from container UUID.");
- return 0;
- }
+ if (getenv_for_pid(1, "container_uuid", &e) > 0 &&
+ sd_id128_from_string(e, ret) >= 0) {
+ log_info("Initializing machine ID from container UUID.");
+ return 0;
}
- } else {
+ } else if (detect_vm() == VIRTUALIZATION_KVM) {
+
/* If we are not running in a container, see if we are
* running in qemu/kvm and a machine ID was passed in
* via -uuid on the qemu/kvm command line */
- r = detect_vm(&vm_id);
- if (r > 0 && streq(vm_id, "kvm")) {
- char uuid[36];
-
- fd = open("/sys/class/dmi/id/product_uuid", O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fd >= 0) {
- r = loop_read_exact(fd, uuid, 36, false);
- safe_close(fd);
-
- if (r >= 0) {
- r = shorten_uuid(id, uuid);
- if (r >= 0) {
- log_info("Initializing machine ID from KVM UUID.");
- return 0;
- }
- }
- }
+ if (id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, ret) >= 0) {
+ log_info("Initializing machine ID from KVM UUID.");
+ return 0;
}
}
}
/* If that didn't work, generate a random machine id */
- r = sd_id128_randomize(&buf);
+ r = sd_id128_randomize(ret);
if (r < 0)
- return log_error_errno(r, "Failed to open /dev/urandom: %m");
-
- for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) {
- q[0] = hexchar(*p >> 4);
- q[1] = hexchar(*p & 15);
- }
-
- id[32] = '\n';
- id[33] = 0;
+ return log_error_errno(r, "Failed to generate randomized : %m");
log_info("Initializing machine ID from random generator.");
-
return 0;
}
-int machine_id_setup(const char *root) {
+int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) {
const char *etc_machine_id, *run_machine_id;
_cleanup_close_ int fd = -1;
- bool writable = true;
- char id[34]; /* 32 + \n + \0 */
+ bool writable;
int r;
- if (isempty(root)) {
- etc_machine_id = "/etc/machine-id";
- run_machine_id = "/run/machine-id";
- } else {
- char *x;
-
- x = strjoina(root, "/etc/machine-id");
- etc_machine_id = path_kill_slashes(x);
-
- x = strjoina(root, "/run/machine-id");
- run_machine_id = path_kill_slashes(x);
- }
+ etc_machine_id = prefix_roota(root, "/etc/machine-id");
RUN_WITH_UMASK(0000) {
/* We create this 0444, to indicate that this isn't really
@@ -216,7 +110,7 @@ int machine_id_setup(const char *root) {
* will be owned by root it doesn't matter much, but maybe
* people look. */
- mkdir_parents(etc_machine_id, 0755);
+ (void) mkdir_parents(etc_machine_id, 0755);
fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
if (fd < 0) {
int old_errno = errno;
@@ -237,31 +131,38 @@ int machine_id_setup(const char *root) {
}
writable = false;
- }
+ } else
+ writable = true;
}
- if (read_machine_id(fd, id) >= 0)
- return 0;
+ /* A we got a valid machine ID argument, that's what counts */
+ if (sd_id128_is_null(machine_id)) {
- /* Hmm, so, the id currently stored is not useful, then let's
- * generate one */
+ /* Try to read any existing machine ID */
+ if (id128_read_fd(fd, ID128_PLAIN, ret) >= 0)
+ return 0;
- r = generate_machine_id(id, root);
- if (r < 0)
- return r;
+ /* Hmm, so, the id currently stored is not useful, then let's generate one */
+ r = generate_machine_id(root, &machine_id);
+ if (r < 0)
+ return r;
+
+ if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to seek: %m");
+ }
if (writable)
- if (write_machine_id(fd, id) >= 0)
- return 0;
+ if (id128_write_fd(fd, ID128_PLAIN, machine_id, true) >= 0)
+ goto finish;
fd = safe_close(fd);
- /* Hmm, we couldn't write it? So let's write it to
- * /run/machine-id as a replacement */
+ /* Hmm, we couldn't write it? So let's write it to /run/machine-id as a replacement */
- RUN_WITH_UMASK(0022) {
- r = write_string_file(run_machine_id, id, WRITE_STRING_FILE_CREATE);
- }
+ run_machine_id = prefix_roota(root, "/run/machine-id");
+
+ RUN_WITH_UMASK(0022)
+ r = id128_write(run_machine_id, ID128_PLAIN, machine_id, false);
if (r < 0) {
(void) unlink(run_machine_id);
return log_error_errno(r, "Cannot write %s: %m", run_machine_id);
@@ -277,7 +178,11 @@ int machine_id_setup(const char *root) {
/* Mark the mount read-only */
if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0)
- log_warning_errno(errno, "Failed to make transient %s read-only: %m", etc_machine_id);
+ log_warning_errno(errno, "Failed to make transient %s read-only, ignoring: %m", etc_machine_id);
+
+finish:
+ if (ret)
+ *ret = machine_id;
return 0;
}
@@ -285,23 +190,20 @@ int machine_id_setup(const char *root) {
int machine_id_commit(const char *root) {
_cleanup_close_ int fd = -1, initial_mntns_fd = -1;
const char *etc_machine_id;
- char id[34]; /* 32 + \n + \0 */
+ sd_id128_t id;
int r;
- if (isempty(root))
- etc_machine_id = "/etc/machine-id";
- else {
- char *x;
+ /* Replaces a tmpfs bind mount of /etc/machine-id by a proper file, atomically. For this, the umount is removed
+ * in a mount namespace, a new file is created at the right place. Afterwards the mount is also removed in the
+ * original mount namespace, thus revealing the file that was just created. */
- x = strjoina(root, "/etc/machine-id");
- etc_machine_id = path_kill_slashes(x);
- }
+ etc_machine_id = prefix_roota(root, "/etc/machine-id");
r = path_is_mount_point(etc_machine_id, 0);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
if (r == 0) {
- log_debug("%s is is not a mount point. Nothing to do.", etc_machine_id);
+ log_debug("%s is not a mount point. Nothing to do.", etc_machine_id);
return 0;
}
@@ -310,10 +212,6 @@ int machine_id_commit(const char *root) {
if (fd < 0)
return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id);
- r = read_machine_id(fd, id);
- if (r < 0)
- return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id);
-
r = fd_is_temporary_fs(fd);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id);
@@ -322,10 +220,14 @@ int machine_id_commit(const char *root) {
return -EROFS;
}
+ r = id128_read_fd(fd, ID128_PLAIN, &id);
+ if (r < 0)
+ return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id);
+
fd = safe_close(fd);
/* Store current mount namespace */
- r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL);
+ r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Can't fetch current mount namespace: %m");
@@ -340,18 +242,12 @@ int machine_id_commit(const char *root) {
return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id);
/* Update a persistent version of etc_machine_id */
- fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444);
- if (fd < 0)
- return log_error_errno(errno, "Cannot open for writing %s. This is mandatory to get a persistent machine-id: %m", etc_machine_id);
-
- r = write_machine_id(fd, id);
+ r = id128_write(etc_machine_id, ID128_PLAIN, id, true);
if (r < 0)
- return log_error_errno(r, "Cannot write %s: %m", etc_machine_id);
-
- fd = safe_close(fd);
+ return log_error_errno(r, "Cannot write %s. This is mandatory to get a persistent machine ID: %m", etc_machine_id);
/* Return to initial namespace and proceed a lazy tmpfs unmount */
- r = namespace_enter(-1, initial_mntns_fd, -1, -1);
+ r = namespace_enter(-1, initial_mntns_fd, -1, -1, -1);
if (r < 0)
return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id);
diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h
index f7707c3bf9..29f4620646 100644
--- a/src/core/machine-id-setup.h
+++ b/src/core/machine-id-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,4 +20,4 @@
***/
int machine_id_commit(const char *root);
-int machine_id_setup(const char *root);
+int machine_id_setup(const char *root, sd_id128_t requested, sd_id128_t *ret);
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index bea6ef1da3..6e8a3b3e3d 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -1,4 +1,4 @@
-# -*- Mode: makefile; indent-tabs-mode: t -*- */
+# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */
#
# This file is part of systemd.
#
@@ -29,6 +29,8 @@
%_sysusersdir @sysusersdir@
%_sysctldir @sysctldir@
%_binfmtdir @binfmtdir@
+%_systemdgeneratordir @systemgeneratordir@
+%_systemdusergeneratordir @usergeneratordir@
%systemd_requires \
Requires(post): systemd \
@@ -36,20 +38,25 @@ Requires(preun): systemd \
Requires(postun): systemd \
%{nil}
+%systemd_ordering \
+OrderWithRequires(post): systemd \
+OrderWithRequires(preun): systemd \
+OrderWithRequires(postun): systemd \
+%{nil}
+
%systemd_post() \
if [ $1 -eq 1 ] ; then \
# Initial installation \
- systemctl preset %{?*} >/dev/null 2>&1 || : \
+ systemctl --no-reload preset %{?*} >/dev/null 2>&1 || : \
fi \
%{nil}
-%systemd_user_post() %systemd_post --user --global %{?*}
+%systemd_user_post() %{expand:%systemd_post \\--user \\--global %%{?*}}
%systemd_preun() \
if [ $1 -eq 0 ] ; then \
# Package removal, not upgrade \
- systemctl --no-reload disable %{?*} > /dev/null 2>&1 || : \
- systemctl stop %{?*} > /dev/null 2>&1 || : \
+ systemctl --no-reload disable --now %{?*} > /dev/null 2>&1 || : \
fi \
%{nil}
@@ -60,14 +67,11 @@ if [ $1 -eq 0 ] ; then \
fi \
%{nil}
-%systemd_postun() \
-systemctl daemon-reload >/dev/null 2>&1 || : \
-%{nil}
+%systemd_postun() %{nil}
%systemd_user_postun() %{nil}
%systemd_postun_with_restart() \
-systemctl daemon-reload >/dev/null 2>&1 || : \
if [ $1 -ge 1 ] ; then \
# Package upgrade, not uninstall \
systemctl try-restart %{?*} >/dev/null 2>&1 || : \
diff --git a/src/core/main.c b/src/core/main.c
index 6ae8b51544..61f3828a36 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,81 +17,96 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/stat.h>
+#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
-#include <fcntl.h>
-#include <sys/prctl.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/mount.h>
-
-#ifdef HAVE_VALGRIND_VALGRIND_H
-#include <valgrind/valgrind.h>
-#endif
+#include <sys/prctl.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <unistd.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
#endif
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
-#include "sd-daemon.h"
#include "sd-bus.h"
-#include "log.h"
-#include "fdset.h"
-#include "special.h"
-#include "conf-parser.h"
-#include "missing.h"
-#include "pager.h"
+#include "sd-daemon.h"
+
+#include "alloc-util.h"
+#include "architecture.h"
#include "build.h"
-#include "strv.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "capability-util.h"
+#include "clock-util.h"
+#include "conf-parser.h"
+#include "cpu-set-util.h"
+#include "dbus-manager.h"
#include "def.h"
-#include "virt.h"
-#include "architecture.h"
-#include "watchdog.h"
-#include "switch-root.h"
-#include "capability.h"
-#include "killall.h"
#include "env-util.h"
-#include "clock-util.h"
+#include "fd-util.h"
+#include "fdset.h"
#include "fileio.h"
-#include "bus-error.h"
-#include "bus-util.h"
-#include "selinux-util.h"
#include "formats-util.h"
-#include "process-util.h"
-#include "terminal-util.h"
-#include "signal-util.h"
-#include "manager.h"
-#include "dbus-manager.h"
+#include "fs-util.h"
+#include "hostname-setup.h"
+#include "ima-setup.h"
+#include "killall.h"
+#include "kmod-setup.h"
#include "load-fragment.h"
-
-#include "mount-setup.h"
+#include "log.h"
#include "loopback-setup.h"
-#include "hostname-setup.h"
#include "machine-id-setup.h"
+#include "manager.h"
+#include "missing.h"
+#include "mount-setup.h"
+#include "pager.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "process-util.h"
+#include "raw-clone.h"
+#include "rlimit-util.h"
+#ifdef HAVE_SECCOMP
+#include "seccomp-util.h"
+#endif
#include "selinux-setup.h"
-#include "ima-setup.h"
+#include "selinux-util.h"
+#include "signal-util.h"
#include "smack-setup.h"
-#include "kmod-setup.h"
+#include "special.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "strv.h"
+#include "switch-root.h"
+#include "terminal-util.h"
+#include "umask-util.h"
+#include "user-util.h"
+#include "virt.h"
+#include "watchdog.h"
static enum {
ACTION_RUN,
ACTION_HELP,
ACTION_VERSION,
ACTION_TEST,
- ACTION_DUMP_CONFIGURATION_ITEMS,
- ACTION_DONE
+ ACTION_DUMP_CONFIGURATION_ITEMS
} arg_action = ACTION_RUN;
static char *arg_default_unit = NULL;
-static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
+static bool arg_system = false;
static bool arg_dump_core = true;
-static bool arg_crash_shell = false;
static int arg_crash_chvt = -1;
+static bool arg_crash_shell = false;
+static bool arg_crash_reboot = false;
static bool arg_confirm_spawn = false;
static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
static bool arg_switched_root = false;
-static int arg_no_pager = -1;
+static bool arg_no_pager = false;
static char ***arg_join_controllers = NULL;
static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
@@ -106,68 +119,76 @@ static usec_t arg_runtime_watchdog = 0;
static usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
static char **arg_default_environment = NULL;
static struct rlimit *arg_default_rlimit[_RLIMIT_MAX] = {};
-static uint64_t arg_capability_bounding_set_drop = 0;
+static uint64_t arg_capability_bounding_set = CAP_ALL;
static nsec_t arg_timer_slack_nsec = NSEC_INFINITY;
static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE;
static Set* arg_syscall_archs = NULL;
static FILE* arg_serialization = NULL;
static bool arg_default_cpu_accounting = false;
+static bool arg_default_io_accounting = false;
static bool arg_default_blockio_accounting = false;
static bool arg_default_memory_accounting = false;
+static bool arg_default_tasks_accounting = true;
+static uint64_t arg_default_tasks_max = UINT64_MAX;
+static sd_id128_t arg_machine_id = {};
+static CADBurstAction arg_cad_burst_action = CAD_BURST_ACTION_REBOOT;
-static void nop_handler(int sig) {}
+noreturn static void freeze_or_reboot(void) {
-static void pager_open_if_enabled(void) {
+ if (arg_crash_reboot) {
+ log_notice("Rebooting in 10s...");
+ (void) sleep(10);
- if (arg_no_pager <= 0)
- return;
+ log_notice("Rebooting now...");
+ (void) reboot(RB_AUTOBOOT);
+ log_emergency_errno(errno, "Failed to reboot: %m");
+ }
- pager_open(false);
+ log_emergency("Freezing execution.");
+ freeze();
}
noreturn static void crash(int sig) {
+ struct sigaction sa;
+ pid_t pid;
if (getpid() != 1)
/* Pass this on immediately, if this is not PID 1 */
- raise(sig);
+ (void) raise(sig);
else if (!arg_dump_core)
log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig));
else {
- struct sigaction sa = {
- .sa_handler = nop_handler,
+ sa = (struct sigaction) {
+ .sa_handler = nop_signal_handler,
.sa_flags = SA_NOCLDSTOP|SA_RESTART,
};
- pid_t pid;
/* We want to wait for the core process, hence let's enable SIGCHLD */
- sigaction(SIGCHLD, &sa, NULL);
+ (void) sigaction(SIGCHLD, &sa, NULL);
- pid = raw_clone(SIGCHLD, NULL);
+ pid = raw_clone(SIGCHLD);
if (pid < 0)
log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig));
-
else if (pid == 0) {
- struct rlimit rl = {};
-
/* Enable default signal handler for core dump */
- zero(sa);
- sa.sa_handler = SIG_DFL;
- sigaction(sig, &sa, NULL);
- /* Don't limit the core dump size */
- rl.rlim_cur = RLIM_INFINITY;
- rl.rlim_max = RLIM_INFINITY;
- setrlimit(RLIMIT_CORE, &rl);
+ sa = (struct sigaction) {
+ .sa_handler = SIG_DFL,
+ };
+ (void) sigaction(sig, &sa, NULL);
+
+ /* Don't limit the coredump size */
+ (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY));
/* Just to be sure... */
(void) chdir("/");
/* Raise the signal again */
pid = raw_getpid();
- kill(pid, sig); /* raise() would kill the parent */
+ (void) kill(pid, sig); /* raise() would kill the parent */
assert_not_reached("We shouldn't be here...");
- _exit(1);
+ _exit(EXIT_FAILURE);
} else {
siginfo_t status;
int r;
@@ -182,44 +203,45 @@ noreturn static void crash(int sig) {
pid, sigchld_code_to_string(status.si_code),
status.si_status,
strna(status.si_code == CLD_EXITED
- ? exit_status_to_string(status.si_status, EXIT_STATUS_FULL)
+ ? exit_status_to_string(status.si_status, EXIT_STATUS_MINIMAL)
: signal_to_string(status.si_status)));
else
log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid);
}
}
- if (arg_crash_chvt)
- chvt(arg_crash_chvt);
+ if (arg_crash_chvt >= 0)
+ (void) chvt(arg_crash_chvt);
- if (arg_crash_shell) {
- struct sigaction sa = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
- };
- pid_t pid;
+ sa = (struct sigaction) {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART,
+ };
- log_info("Executing crash shell in 10s...");
- sleep(10);
+ /* Let the kernel reap children for us */
+ (void) sigaction(SIGCHLD, &sa, NULL);
- /* Let the kernel reap children for us */
- assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+ if (arg_crash_shell) {
+ log_notice("Executing crash shell in 10s...");
+ (void) sleep(10);
- pid = raw_clone(SIGCHLD, NULL);
+ pid = raw_clone(SIGCHLD);
if (pid < 0)
log_emergency_errno(errno, "Failed to fork off crash shell: %m");
else if (pid == 0) {
- make_console_stdio();
- execle("/bin/sh", "/bin/sh", NULL, environ);
+ (void) setsid();
+ (void) make_console_stdio();
+ (void) execle("/bin/sh", "/bin/sh", NULL, environ);
log_emergency_errno(errno, "execle() failed: %m");
- _exit(1);
- } else
- log_info("Successfully spawned crash shell as PID "PID_FMT".", pid);
+ _exit(EXIT_FAILURE);
+ } else {
+ log_info("Spawned crash shell as PID "PID_FMT".", pid);
+ (void) wait_for_terminate(pid, NULL);
+ }
}
- log_emergency("Freezing execution.");
- freeze();
+ freeze_or_reboot();
}
static void install_crash_handler(void) {
@@ -253,37 +275,40 @@ static int console_setup(void) {
return 0;
}
-static int set_default_unit(const char *u) {
- char *c;
+static int parse_crash_chvt(const char *value) {
+ int b;
- assert(u);
+ if (safe_atoi(value, &arg_crash_chvt) >= 0)
+ return 0;
- c = strdup(u);
- if (!c)
- return -ENOMEM;
+ b = parse_boolean(value);
+ if (b < 0)
+ return b;
+
+ if (b > 0)
+ arg_crash_chvt = 0; /* switch to where kmsg goes */
+ else
+ arg_crash_chvt = -1; /* turn off switching */
+
+ return 0;
+}
+
+static int set_machine_id(const char *m) {
+ sd_id128_t t;
+ assert(m);
+
+ if (sd_id128_from_string(m, &t) < 0)
+ return -EINVAL;
- free(arg_default_unit);
- arg_default_unit = c;
+ if (sd_id128_is_null(t))
+ return -EINVAL;
+ arg_machine_id = t;
return 0;
}
static int parse_proc_cmdline_item(const char *key, const char *value) {
- static const char * const rlmap[] = {
- "emergency", SPECIAL_EMERGENCY_TARGET,
- "-b", SPECIAL_EMERGENCY_TARGET,
- "rescue", SPECIAL_RESCUE_TARGET,
- "single", SPECIAL_RESCUE_TARGET,
- "-s", SPECIAL_RESCUE_TARGET,
- "s", SPECIAL_RESCUE_TARGET,
- "S", SPECIAL_RESCUE_TARGET,
- "1", SPECIAL_RESCUE_TARGET,
- "2", SPECIAL_MULTI_USER_TARGET,
- "3", SPECIAL_MULTI_USER_TARGET,
- "4", SPECIAL_MULTI_USER_TARGET,
- "5", SPECIAL_GRAPHICAL_TARGET,
- };
int r;
assert(key);
@@ -291,12 +316,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
if (streq(key, "systemd.unit") && value) {
if (!in_initrd())
- return set_default_unit(value);
+ return free_and_strdup(&arg_default_unit, value);
} else if (streq(key, "rd.systemd.unit") && value) {
if (in_initrd())
- return set_default_unit(value);
+ return free_and_strdup(&arg_default_unit, value);
} else if (streq(key, "systemd.dump_core") && value) {
@@ -306,6 +331,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
else
arg_dump_core = r;
+ } else if (streq(key, "systemd.crash_chvt") && value) {
+
+ if (parse_crash_chvt(value) < 0)
+ log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
+
} else if (streq(key, "systemd.crash_shell") && value) {
r = parse_boolean(value);
@@ -314,12 +344,13 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
else
arg_crash_shell = r;
- } else if (streq(key, "systemd.crash_chvt") && value) {
+ } else if (streq(key, "systemd.crash_reboot") && value) {
- if (safe_atoi(value, &r) < 0)
- log_warning("Failed to parse crash chvt switch %s. Ignoring.", value);
+ r = parse_boolean(value);
+ if (r < 0)
+ log_warning("Failed to parse crash reboot switch %s. Ignoring.", value);
else
- arg_crash_chvt = r;
+ arg_crash_reboot = r;
} else if (streq(key, "systemd.confirm_spawn") && value) {
@@ -364,6 +395,12 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
} else
log_warning("Environment variable name '%s' is not valid. Ignoring.", value);
+ } else if (streq(key, "systemd.machine_id") && value) {
+
+ r = set_machine_id(value);
+ if (r < 0)
+ log_warning("MachineID '%s' is not valid. Ignoring.", value);
+
} else if (streq(key, "quiet") && !value) {
if (arg_show_status == _SHOW_STATUS_UNSET)
@@ -374,16 +411,25 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
/* Note that log_parse_environment() handles 'debug'
* too, and sets the log level to LOG_DEBUG. */
- if (detect_container(NULL) > 0)
+ if (detect_container() > 0)
log_set_target(LOG_TARGET_CONSOLE);
- } else if (!in_initrd() && !value) {
- unsigned i;
+ } else if (!value) {
+ const char *target;
/* SysV compatibility */
- for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
- if (streq(key, rlmap[i]))
- return set_default_unit(rlmap[i+1]);
+ target = runlevel_to_target(key);
+ if (target)
+ return free_and_strdup(&arg_default_unit, target);
+
+ } else if (streq(key, "systemd.default_timeout_start_sec") && value) {
+
+ r = parse_sec(value, &arg_default_timeout_start_usec);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value);
+
+ if (arg_default_timeout_start_usec <= 0)
+ arg_default_timeout_start_usec = USEC_INFINITY;
}
return 0;
@@ -409,9 +455,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
\
r = func(rvalue); \
if (r < 0) \
- log_syntax(unit, LOG_ERR, filename, line, -r, \
- "Invalid " descr "'%s': %s", \
- rvalue, strerror(-r)); \
+ log_syntax(unit, LOG_ERR, filename, line, r, \
+ "Invalid " descr "'%s': %m", \
+ rvalue); \
\
return 0; \
}
@@ -433,49 +479,15 @@ static int config_parse_cpu_affinity2(
void *data,
void *userdata) {
- const char *word, *state;
- size_t l;
- cpu_set_t *c = NULL;
- unsigned ncpus = 0;
+ _cleanup_cpu_free_ cpu_set_t *c = NULL;
+ int ncpus;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
+ ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue);
+ if (ncpus < 0)
+ return ncpus;
- FOREACH_WORD_QUOTED(word, l, rvalue, state) {
- char *t;
- int r;
- unsigned cpu;
-
- if (!(t = strndup(word, l)))
- return log_oom();
-
- r = safe_atou(t, &cpu);
- free(t);
-
- if (!c)
- if (!(c = cpu_set_malloc(&ncpus)))
- return log_oom();
-
- if (r < 0 || cpu >= ncpus) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse CPU affinity '%s'", rvalue);
- CPU_FREE(c);
- return -EBADMSG;
- }
-
- CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
- }
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
-
- if (c) {
- if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
- log_warning("Failed to set CPU affinity: %m");
-
- CPU_FREE(c);
- }
+ if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
+ log_warning("Failed to set CPU affinity: %m");
return 0;
}
@@ -502,29 +514,38 @@ static int config_parse_show_status(
k = parse_show_status(rvalue, b);
if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -k,
- "Failed to parse show status setting, ignoring: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
return 0;
}
return 0;
}
-static void strv_free_free(char ***l) {
- char ***i;
+static int config_parse_crash_chvt(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- if (!l)
- return;
+ int r;
- for (i = l; *i; i++)
- strv_free(*i);
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
- free(l);
-}
+ r = parse_crash_chvt(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
+ return 0;
+ }
-static void free_join_controllers(void) {
- strv_free_free(arg_join_controllers);
- arg_join_controllers = NULL;
+ return 0;
}
static int config_parse_join_controllers(const char *unit,
@@ -538,26 +559,31 @@ static int config_parse_join_controllers(const char *unit,
void *data,
void *userdata) {
+ const char *whole_rvalue = rvalue;
unsigned n = 0;
- const char *word, *state;
- size_t length;
assert(filename);
assert(lvalue);
assert(rvalue);
- free_join_controllers();
+ arg_join_controllers = strv_free_free(arg_join_controllers);
- FOREACH_WORD_QUOTED(word, length, rvalue, state) {
- char *s, **l;
-
- s = strndup(word, length);
- if (!s)
- return log_oom();
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ char **l;
+ int r;
- l = strv_split(s, ",");
- free(s);
+ r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
+ return r;
+ }
+ if (r == 0)
+ break;
+ l = strv_split(word, ",");
+ if (!l)
+ return log_oom();
strv_uniq(l);
if (strv_length(l) <= 1) {
@@ -591,7 +617,7 @@ static int config_parse_join_controllers(const char *unit,
for (a = arg_join_controllers; *a; a++) {
if (strv_overlap(*a, l)) {
- if (strv_extend_strv(&l, *a) < 0) {
+ if (strv_extend_strv(&l, *a, false) < 0) {
strv_free(l);
strv_free_free(t);
return log_oom();
@@ -617,13 +643,14 @@ static int config_parse_join_controllers(const char *unit,
arg_join_controllers = t;
}
}
- if (!isempty(state))
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Trailing garbage, ignoring.");
+ if (!isempty(rvalue))
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
return 0;
}
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_cad_burst_action, cad_burst_action, CADBurstAction, "Failed to parse service restart specifier");
+
static int parse_config_file(void) {
const ConfigTableItem items[] = {
@@ -632,14 +659,16 @@ static int parse_config_file(void) {
{ "Manager", "LogColor", config_parse_color, 0, NULL },
{ "Manager", "LogLocation", config_parse_location, 0, NULL },
{ "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core },
+ { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, NULL },
+ { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, NULL },
{ "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell },
+ { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot },
{ "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status },
- { "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt },
{ "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL },
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
{ "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
- { "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
+ { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
#ifdef HAVE_SECCOMP
{ "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
#endif
@@ -650,37 +679,54 @@ static int parse_config_file(void) {
{ "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
{ "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
{ "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
- { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval },
+ { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */
+ { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval },
{ "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst },
{ "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment },
- { "Manager", "DefaultLimitCPU", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CPU] },
- { "Manager", "DefaultLimitFSIZE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_FSIZE] },
- { "Manager", "DefaultLimitDATA", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_DATA] },
- { "Manager", "DefaultLimitSTACK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_STACK] },
- { "Manager", "DefaultLimitCORE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_CORE] },
- { "Manager", "DefaultLimitRSS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RSS] },
- { "Manager", "DefaultLimitNOFILE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NOFILE] },
- { "Manager", "DefaultLimitAS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_AS] },
- { "Manager", "DefaultLimitNPROC", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NPROC] },
- { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MEMLOCK] },
- { "Manager", "DefaultLimitLOCKS", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_LOCKS] },
- { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_SIGPENDING] },
- { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_MSGQUEUE] },
- { "Manager", "DefaultLimitNICE", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_NICE] },
- { "Manager", "DefaultLimitRTPRIO", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTPRIO] },
- { "Manager", "DefaultLimitRTTIME", config_parse_limit, 0, &arg_default_rlimit[RLIMIT_RTTIME] },
+ { "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit },
+ { "Manager", "DefaultLimitFSIZE", config_parse_limit, RLIMIT_FSIZE, arg_default_rlimit },
+ { "Manager", "DefaultLimitDATA", config_parse_limit, RLIMIT_DATA, arg_default_rlimit },
+ { "Manager", "DefaultLimitSTACK", config_parse_limit, RLIMIT_STACK, arg_default_rlimit },
+ { "Manager", "DefaultLimitCORE", config_parse_limit, RLIMIT_CORE, arg_default_rlimit },
+ { "Manager", "DefaultLimitRSS", config_parse_limit, RLIMIT_RSS, arg_default_rlimit },
+ { "Manager", "DefaultLimitNOFILE", config_parse_limit, RLIMIT_NOFILE, arg_default_rlimit },
+ { "Manager", "DefaultLimitAS", config_parse_limit, RLIMIT_AS, arg_default_rlimit },
+ { "Manager", "DefaultLimitNPROC", config_parse_limit, RLIMIT_NPROC, arg_default_rlimit },
+ { "Manager", "DefaultLimitMEMLOCK", config_parse_limit, RLIMIT_MEMLOCK, arg_default_rlimit },
+ { "Manager", "DefaultLimitLOCKS", config_parse_limit, RLIMIT_LOCKS, arg_default_rlimit },
+ { "Manager", "DefaultLimitSIGPENDING", config_parse_limit, RLIMIT_SIGPENDING, arg_default_rlimit },
+ { "Manager", "DefaultLimitMSGQUEUE", config_parse_limit, RLIMIT_MSGQUEUE, arg_default_rlimit },
+ { "Manager", "DefaultLimitNICE", config_parse_limit, RLIMIT_NICE, arg_default_rlimit },
+ { "Manager", "DefaultLimitRTPRIO", config_parse_limit, RLIMIT_RTPRIO, arg_default_rlimit },
+ { "Manager", "DefaultLimitRTTIME", config_parse_limit, RLIMIT_RTTIME, arg_default_rlimit },
{ "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting },
+ { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting },
{ "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
{ "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
+ { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting },
+ { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max },
+ { "Manager", "CtrlAltDelBurstAction", config_parse_cad_burst_action, 0, &arg_cad_burst_action},
{}
};
const char *fn, *conf_dirs_nulstr;
- fn = arg_running_as == MANAGER_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf";
- conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ? CONF_DIRS_NULSTR("systemd/system.conf") : CONF_DIRS_NULSTR("systemd/user.conf");
- config_parse_many(fn, conf_dirs_nulstr, "Manager\0",
- config_item_table_lookup, items, false, NULL);
+ fn = arg_system ?
+ PKGSYSCONFDIR "/system.conf" :
+ PKGSYSCONFDIR "/user.conf";
+
+ conf_dirs_nulstr = arg_system ?
+ CONF_PATHS_NULSTR("systemd/system.conf.d") :
+ CONF_PATHS_NULSTR("systemd/user.conf.d");
+
+ config_parse_many_nulstr(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, false, NULL);
+
+ /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY
+ * like everywhere else. */
+ if (arg_default_timeout_start_usec <= 0)
+ arg_default_timeout_start_usec = USEC_INFINITY;
+ if (arg_default_timeout_stop_usec <= 0)
+ arg_default_timeout_stop_usec = USEC_INFINITY;
return 0;
}
@@ -698,8 +744,11 @@ static void manager_set_defaults(Manager *m) {
m->default_start_limit_interval = arg_default_start_limit_interval;
m->default_start_limit_burst = arg_default_start_limit_burst;
m->default_cpu_accounting = arg_default_cpu_accounting;
+ m->default_io_accounting = arg_default_io_accounting;
m->default_blockio_accounting = arg_default_blockio_accounting;
m->default_memory_accounting = arg_default_memory_accounting;
+ m->default_tasks_accounting = arg_default_tasks_accounting;
+ m->default_tasks_max = arg_default_tasks_max;
manager_set_default_rlimits(m, arg_default_rlimit);
manager_environment_add(m, NULL, arg_default_environment);
@@ -720,13 +769,16 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION,
ARG_DUMP_CONFIGURATION_ITEMS,
ARG_DUMP_CORE,
+ ARG_CRASH_CHVT,
ARG_CRASH_SHELL,
+ ARG_CRASH_REBOOT,
ARG_CONFIRM_SPAWN,
ARG_SHOW_STATUS,
ARG_DESERIALIZE,
ARG_SWITCHED_ROOT,
ARG_DEFAULT_STD_OUTPUT,
- ARG_DEFAULT_STD_ERROR
+ ARG_DEFAULT_STD_ERROR,
+ ARG_MACHINE_ID
};
static const struct option options[] = {
@@ -743,13 +795,16 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
{ "dump-core", optional_argument, NULL, ARG_DUMP_CORE },
+ { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT },
{ "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL },
+ { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT },
{ "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN },
{ "show-status", optional_argument, NULL, ARG_SHOW_STATUS },
{ "deserialize", required_argument, NULL, ARG_DESERIALIZE },
{ "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT },
{ "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, },
{ "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, },
+ { "machine-id", required_argument, NULL, ARG_MACHINE_ID },
{}
};
@@ -828,24 +883,22 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_UNIT:
- r = set_default_unit(optarg);
+ r = free_and_strdup(&arg_default_unit, optarg);
if (r < 0)
return log_error_errno(r, "Failed to set default unit %s: %m", optarg);
break;
case ARG_SYSTEM:
- arg_running_as = MANAGER_SYSTEM;
+ arg_system = true;
break;
case ARG_USER:
- arg_running_as = MANAGER_USER;
+ arg_system = false;
break;
case ARG_TEST:
arg_action = ACTION_TEST;
- if (arg_no_pager < 0)
- arg_no_pager = true;
break;
case ARG_NO_PAGER:
@@ -861,21 +914,42 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_DUMP_CORE:
- r = optarg ? parse_boolean(optarg) : 1;
- if (r < 0) {
- log_error("Failed to parse dump core boolean %s.", optarg);
- return r;
+ if (!optarg)
+ arg_dump_core = true;
+ else {
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse dump core boolean: %s", optarg);
+ arg_dump_core = r;
}
- arg_dump_core = r;
+ break;
+
+ case ARG_CRASH_CHVT:
+ r = parse_crash_chvt(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse crash virtual terminal index: %s", optarg);
break;
case ARG_CRASH_SHELL:
- r = optarg ? parse_boolean(optarg) : 1;
- if (r < 0) {
- log_error("Failed to parse crash shell boolean %s.", optarg);
- return r;
+ if (!optarg)
+ arg_crash_shell = true;
+ else {
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+ arg_crash_shell = r;
+ }
+ break;
+
+ case ARG_CRASH_REBOOT:
+ if (!optarg)
+ arg_crash_reboot = true;
+ else {
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse crash shell boolean: %s", optarg);
+ arg_crash_reboot = r;
}
- arg_crash_shell = r;
break;
case ARG_CONFIRM_SPAWN:
@@ -905,18 +979,16 @@ static int parse_argv(int argc, char *argv[]) {
r = safe_atoi(optarg, &fd);
if (r < 0 || fd < 0) {
log_error("Failed to parse deserialize option %s.", optarg);
- return r < 0 ? r : -EINVAL;
+ return -EINVAL;
}
- fd_cloexec(fd, true);
+ (void) fd_cloexec(fd, true);
f = fdopen(fd, "r");
if (!f)
return log_error_errno(errno, "Failed to open serialization fd: %m");
- if (arg_serialization)
- fclose(arg_serialization);
-
+ safe_fclose(arg_serialization);
arg_serialization = f;
break;
@@ -926,10 +998,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_switched_root = true;
break;
+ case ARG_MACHINE_ID:
+ r = set_machine_id(optarg);
+ if (r < 0)
+ return log_error_errno(r, "MachineID '%s' is not valid.", optarg);
+ break;
+
case 'h':
arg_action = ACTION_HELP;
- if (arg_no_pager < 0)
- arg_no_pager = true;
break;
case 'D':
@@ -976,14 +1052,16 @@ static int help(void) {
" --unit=UNIT Set default unit\n"
" --system Run a system instance, even if PID != 1\n"
" --user Run a user instance\n"
- " --dump-core[=0|1] Dump core on crash\n"
- " --crash-shell[=0|1] Run shell on crash\n"
- " --confirm-spawn[=0|1] Ask for confirmation when spawning processes\n"
- " --show-status[=0|1] Show status updates on the console during bootup\n"
+ " --dump-core[=BOOL] Dump core on crash\n"
+ " --crash-vt=NR Change to specified VT on crash\n"
+ " --crash-reboot[=BOOL] Reboot on crash\n"
+ " --crash-shell[=BOOL] Run shell on crash\n"
+ " --confirm-spawn[=BOOL] Ask for confirmation when spawning processes\n"
+ " --show-status[=BOOL] Show status updates on the console during bootup\n"
" --log-target=TARGET Set log target (console, journal, kmsg, journal-or-kmsg, null)\n"
" --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n"
- " --log-color[=0|1] Highlight important log messages\n"
- " --log-location[=0|1] Include code location in log messages\n"
+ " --log-color[=BOOL] Highlight important log messages\n"
+ " --log-location[=BOOL] Include code location in log messages\n"
" --default-standard-output= Set default standard output for services\n"
" --default-standard-error= Set default standard error output for services\n",
program_invocation_short_name);
@@ -991,16 +1069,9 @@ static int help(void) {
return 0;
}
-static int version(void) {
- puts(PACKAGE_STRING);
- puts(SYSTEMD_FEATURES);
-
- return 0;
-}
-
static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching_root) {
- FILE *f = NULL;
- FDSet *fds = NULL;
+ _cleanup_fdset_free_ FDSet *fds = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
assert(m);
@@ -1008,57 +1079,39 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching
assert(_fds);
r = manager_open_serialization(m, &f);
- if (r < 0) {
- log_error_errno(r, "Failed to create serialization file: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create serialization file: %m");
/* Make sure nothing is really destructed when we shut down */
- m->n_reloading ++;
+ m->n_reloading++;
bus_manager_send_reloading(m, true);
fds = fdset_new();
- if (!fds) {
- r = -ENOMEM;
- log_error_errno(r, "Failed to allocate fd set: %m");
- goto fail;
- }
+ if (!fds)
+ return log_oom();
r = manager_serialize(m, f, fds, switching_root);
- if (r < 0) {
- log_error_errno(r, "Failed to serialize state: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to serialize state: %m");
- if (fseeko(f, 0, SEEK_SET) < 0) {
- log_error_errno(errno, "Failed to rewind serialization fd: %m");
- goto fail;
- }
+ if (fseeko(f, 0, SEEK_SET) == (off_t) -1)
+ return log_error_errno(errno, "Failed to rewind serialization fd: %m");
r = fd_cloexec(fileno(f), false);
- if (r < 0) {
- log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m");
r = fdset_cloexec(fds, false);
- if (r < 0) {
- log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m");
- goto fail;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m");
*_f = f;
*_fds = fds;
- return 0;
-
-fail:
- fdset_free(fds);
-
- if (f)
- fclose(f);
+ f = NULL;
+ fds = NULL;
- return r;
+ return 0;
}
static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
@@ -1093,32 +1146,6 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
return 0;
}
-static void test_mtab(void) {
-
- static const char ok[] =
- "/proc/self/mounts\0"
- "/proc/mounts\0"
- "../proc/self/mounts\0"
- "../proc/mounts\0";
-
- _cleanup_free_ char *p = NULL;
- int r;
-
- /* Check that /etc/mtab is a symlink to the right place or
- * non-existing. But certainly not a file, or a symlink to
- * some weird place... */
-
- r = readlink_malloc("/etc/mtab", &p);
- if (r == -ENOENT)
- return;
- if (r >= 0 && nulstr_contains(ok, p))
- return;
-
- log_warning("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. "
- "This is not supported anymore. "
- "Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output.");
-}
-
static void test_usr(void) {
/* Check that /usr is not a separate fs */
@@ -1142,15 +1169,19 @@ static int initialize_join_controllers(void) {
return -ENOMEM;
arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
- arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
- arg_join_controllers[2] = NULL;
+ if (!arg_join_controllers[0])
+ goto oom;
- if (!arg_join_controllers[0] || !arg_join_controllers[1]) {
- free_join_controllers();
- return -ENOMEM;
- }
+ arg_join_controllers[1] = strv_new("net_cls", "net_prio", NULL);
+ if (!arg_join_controllers[1])
+ goto oom;
+ arg_join_controllers[2] = NULL;
return 0;
+
+oom:
+ arg_join_controllers = strv_free_free(arg_join_controllers);
+ return -ENOMEM;
}
static int enforce_syscall_archs(Set *archs) {
@@ -1160,6 +1191,9 @@ static int enforce_syscall_archs(Set *archs) {
void *id;
int r;
+ if (!is_seccomp_available())
+ return 0;
+
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return log_oom();
@@ -1200,30 +1234,107 @@ static int status_welcome(void) {
"PRETTY_NAME", &pretty_name,
"ANSI_COLOR", &ansi_color,
NULL);
- if (r == -ENOENT) {
+ if (r == -ENOENT)
r = parse_env_file("/usr/lib/os-release", NEWLINE,
"PRETTY_NAME", &pretty_name,
"ANSI_COLOR", &ansi_color,
NULL);
- }
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read os-release file: %m");
- return status_printf(NULL, false, false,
- "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
- isempty(ansi_color) ? "1" : ansi_color,
- isempty(pretty_name) ? "Linux" : pretty_name);
+ if (log_get_show_color())
+ return status_printf(NULL, false, false,
+ "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
+ isempty(ansi_color) ? "1" : ansi_color,
+ isempty(pretty_name) ? "Linux" : pretty_name);
+ else
+ return status_printf(NULL, false, false,
+ "\nWelcome to %s!\n",
+ isempty(pretty_name) ? "Linux" : pretty_name);
}
static int write_container_id(void) {
const char *c;
+ int r;
c = getenv("container");
if (isempty(c))
return 0;
- return write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
+ RUN_WITH_UMASK(0022)
+ r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m");
+
+ return 1;
+}
+
+static int bump_unix_max_dgram_qlen(void) {
+ _cleanup_free_ char *qlen = NULL;
+ unsigned long v;
+ int r;
+
+ /* Let's bump the net.unix.max_dgram_qlen sysctl. The kernel
+ * default of 16 is simply too low. We set the value really
+ * really early during boot, so that it is actually applied to
+ * all our sockets, including the $NOTIFY_SOCKET one. */
+
+ r = read_one_line_file("/proc/sys/net/unix/max_dgram_qlen", &qlen);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to read AF_UNIX datagram queue length, ignoring: %m");
+
+ r = safe_atolu(qlen, &v);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length, ignoring: %m");
+
+ if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN)
+ return 0;
+
+ qlen = mfree(qlen);
+ if (asprintf(&qlen, "%lu\n", DEFAULT_UNIX_MAX_DGRAM_QLEN) < 0)
+ return log_oom();
+
+ r = write_string_file("/proc/sys/net/unix/max_dgram_qlen", qlen, 0);
+ if (r < 0)
+ return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to bump AF_UNIX datagram queue length, ignoring: %m");
+
+ return 1;
+}
+
+static int fixup_environment(void) {
+ _cleanup_free_ char *term = NULL;
+ int r;
+
+ /* We expect the environment to be set correctly
+ * if run inside a container. */
+ if (detect_container() > 0)
+ return 0;
+
+ /* When started as PID1, the kernel uses /dev/console
+ * for our stdios and uses TERM=linux whatever the
+ * backend device used by the console. We try to make
+ * a better guess here since some consoles might not
+ * have support for color mode for example.
+ *
+ * However if TERM was configured through the kernel
+ * command line then leave it alone. */
+
+ r = get_proc_cmdline_key("TERM=", &term);
+ if (r < 0)
+ return r;
+
+ if (r == 0) {
+ term = strdup(default_term_for_tty("/dev/console"));
+ if (!term)
+ return -ENOMEM;
+ }
+
+ if (setenv("TERM", term, 1) < 0)
+ return -errno;
+
+ return 0;
}
int main(int argc, char *argv[]) {
@@ -1255,7 +1366,6 @@ int main(int argc, char *argv[]) {
/* This is compatibility support for SysV, where
* calling init as a user is identical to telinit. */
- errno = -ENOENT;
execv(SYSTEMCTL_BINARY_PATH, argv);
log_error_errno(errno, "Failed to exec " SYSTEMCTL_BINARY_PATH ": %m");
return 1;
@@ -1286,18 +1396,16 @@ int main(int argc, char *argv[]) {
saved_argv = argv;
saved_argc = argc;
- log_show_color(isatty(STDERR_FILENO) > 0);
log_set_upgrade_syslog_to_journal(true);
/* Disable the umask logic */
if (getpid() == 1)
umask(0);
- if (getpid() == 1 && detect_container(NULL) <= 0) {
+ if (getpid() == 1 && detect_container() <= 0) {
/* Running outside of a container as PID 1 */
- arg_running_as = MANAGER_SYSTEM;
- make_null_stdio();
+ arg_system = true;
log_set_target(LOG_TARGET_KMSG);
log_open();
@@ -1305,28 +1413,32 @@ int main(int argc, char *argv[]) {
initrd_timestamp = userspace_timestamp;
if (!skip_setup) {
- mount_setup_early();
+ r = mount_setup_early();
+ if (r < 0) {
+ error_message = "Failed to early mount API filesystems";
+ goto finish;
+ }
dual_timestamp_get(&security_start_timestamp);
if (mac_selinux_setup(&loaded_policy) < 0) {
error_message = "Failed to load SELinux policy";
goto finish;
- } else if (ima_setup() < 0) {
- error_message = "Failed to load IMA policy";
- goto finish;
} else if (mac_smack_setup(&loaded_policy) < 0) {
error_message = "Failed to load SMACK policy";
goto finish;
+ } else if (ima_setup() < 0) {
+ error_message = "Failed to load IMA policy";
+ goto finish;
}
dual_timestamp_get(&security_finish_timestamp);
}
- if (mac_selinux_init(NULL) < 0) {
+ if (mac_selinux_init() < 0) {
error_message = "Failed to initialize SELinux policy";
goto finish;
}
if (!skip_setup) {
- if (clock_is_localtime() > 0) {
+ if (clock_is_localtime(NULL) > 0) {
int min;
/*
@@ -1346,7 +1458,7 @@ int main(int argc, char *argv[]) {
/*
* Do a dummy very first call to seal the kernel's time warp magic.
*
- * Do not call this this from inside the initrd. The initrd might not
+ * Do not call 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.
@@ -1356,8 +1468,14 @@ int main(int argc, char *argv[]) {
* saving time change. All kernel local time concepts will be treated
* as UTC that way.
*/
- clock_reset_timewarp();
+ (void) clock_reset_timewarp();
}
+
+ r = clock_apply_epoch();
+ if (r < 0)
+ log_error_errno(r, "Current system time is before build time, but cannot correct: %m");
+ else if (r > 0)
+ log_info("System time before build time, advancing clock.");
}
/* Set the default for later on, but don't actually
@@ -1370,7 +1488,7 @@ int main(int argc, char *argv[]) {
} else if (getpid() == 1) {
/* Running inside a container, as PID 1 */
- arg_running_as = MANAGER_SYSTEM;
+ arg_system = true;
log_set_target(LOG_TARGET_CONSOLE);
log_close_console(); /* force reopen of /dev/console */
log_open();
@@ -1380,27 +1498,43 @@ int main(int argc, char *argv[]) {
/* clear the kernel timestamp,
* because we are in a container */
- kernel_timestamp.monotonic = 0ULL;
- kernel_timestamp.realtime = 0ULL;
-
+ kernel_timestamp = DUAL_TIMESTAMP_NULL;
} else {
/* Running as user instance */
- arg_running_as = MANAGER_USER;
+ arg_system = false;
log_set_target(LOG_TARGET_AUTO);
log_open();
/* clear the kernel timestamp,
* because we are not PID 1 */
- kernel_timestamp.monotonic = 0ULL;
- kernel_timestamp.realtime = 0ULL;
+ kernel_timestamp = DUAL_TIMESTAMP_NULL;
}
- /* Initialize default unit */
- r = set_default_unit(SPECIAL_DEFAULT_TARGET);
- if (r < 0) {
- log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET);
- error_message = "Failed to set default unit";
- goto finish;
+ if (getpid() == 1) {
+ /* Don't limit the core dump size, so that coredump handlers such as systemd-coredump (which honour the limit)
+ * will process core dumps for system services by default. */
+ if (setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)) < 0)
+ log_warning_errno(errno, "Failed to set RLIMIT_CORE: %m");
+
+ /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored
+ * until the systemd-coredump tool is enabled via sysctl. */
+ if (!skip_setup)
+ (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ }
+
+ if (arg_system) {
+ if (fixup_environment() < 0) {
+ error_message = "Failed to fix up PID1 environment";
+ goto finish;
+ }
+
+ /* Try to figure out if we can use colors with the console. No
+ * need to do that for user instances since they never log
+ * into the console. */
+ log_show_color(colors_enabled());
+ r = make_null_stdio();
+ if (r < 0)
+ log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m");
}
r = initialize_join_controllers();
@@ -1428,12 +1562,14 @@ int main(int argc, char *argv[]) {
(void) reset_all_signal_handlers();
(void) ignore_signals(SIGNALS_IGNORE, -1);
+ arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U);
+
if (parse_config_file() < 0) {
error_message = "Failed to parse config file";
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_system) {
r = parse_proc_cmdline(parse_proc_cmdline_item);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@@ -1448,30 +1584,40 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ /* Initialize default unit */
+ if (!arg_default_unit) {
+ arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
+ if (!arg_default_unit) {
+ r = log_oom();
+ error_message = "Failed to set default unit";
+ goto finish;
+ }
+ }
+
if (arg_action == ACTION_TEST &&
geteuid() == 0) {
log_error("Don't run test mode as root.");
goto finish;
}
- if (arg_running_as == MANAGER_USER &&
+ if (!arg_system &&
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 == MANAGER_SYSTEM &&
+ if (arg_system &&
arg_action == ACTION_RUN &&
running_in_chroot() > 0) {
log_error("Cannot be run in a chroot() environment.");
goto finish;
}
- if (arg_action == ACTION_TEST)
+ if (arg_action == ACTION_TEST || arg_action == ACTION_HELP) {
+ pager_open(arg_no_pager, false);
skip_setup = true;
-
- pager_open_if_enabled();
+ }
if (arg_action == ACTION_HELP) {
retval = help();
@@ -1480,15 +1626,13 @@ int main(int argc, char *argv[]) {
retval = version();
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
+ pager_open(arg_no_pager, false);
unit_dump_config_items(stdout);
retval = EXIT_SUCCESS;
goto finish;
- } else if (arg_action == ACTION_DONE) {
- retval = EXIT_SUCCESS;
- goto finish;
}
- if (arg_running_as == MANAGER_USER &&
+ if (!arg_system &&
!getenv("XDG_RUNTIME_DIR")) {
log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set.");
goto finish;
@@ -1511,7 +1655,7 @@ int main(int argc, char *argv[]) {
if (arg_serialization)
assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0);
- if (arg_running_as == MANAGER_SYSTEM)
+ if (arg_system)
/* Become a session leader if we aren't one yet. */
setsid();
@@ -1520,7 +1664,7 @@ 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_system && arg_action == ACTION_RUN) {
/* If we are init, we connect stdin/stdout/stderr to
* /dev/null and make sure we don't have a controlling
@@ -1547,15 +1691,15 @@ int main(int argc, char *argv[]) {
goto finish;
}
- if (arg_running_as == MANAGER_SYSTEM) {
- const char *virtualization = NULL;
+ if (arg_system) {
+ int v;
log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")",
arg_action == ACTION_TEST ? "test " : "" );
- detect_virtualization(&virtualization);
- if (virtualization)
- log_info("Detected virtualization %s.", virtualization);
+ v = detect_virtualization();
+ if (v > 0)
+ log_info("Detected virtualization %s.", virtualization_to_string(v));
write_container_id();
@@ -1583,33 +1727,33 @@ int main(int argc, char *argv[]) {
arg_action == ACTION_TEST ? " test" : "", getuid(), t);
}
- if (arg_running_as == MANAGER_SYSTEM && !skip_setup) {
+ if (arg_system && !skip_setup) {
if (arg_show_status > 0)
status_welcome();
hostname_setup();
- machine_id_setup(NULL);
+ machine_id_setup(NULL, arg_machine_id, NULL);
loopback_setup();
+ bump_unix_max_dgram_qlen();
- test_mtab();
test_usr();
}
- if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0)
+ if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
watchdog_set_timeout(&arg_runtime_watchdog);
if (arg_timer_slack_nsec != NSEC_INFINITY)
if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0)
log_error_errno(errno, "Failed to adjust timer slack: %m");
- if (arg_capability_bounding_set_drop) {
- r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
+ if (!cap_test_all(arg_capability_bounding_set)) {
+ r = capability_bounding_set_drop_usermode(arg_capability_bounding_set);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m");
error_message = "Failed to drop capability bounding set of usermode helpers";
goto finish;
}
- r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
+ r = capability_bounding_set_drop(arg_capability_bounding_set, true);
if (r < 0) {
log_emergency_errno(r, "Failed to drop capability bounding set: %m");
error_message = "Failed to drop capability bounding set";
@@ -1625,24 +1769,24 @@ int main(int argc, char *argv[]) {
}
}
- if (arg_running_as == MANAGER_USER)
+ if (!arg_system)
/* Become reaper of our children */
if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0)
log_warning_errno(errno, "Failed to make us a subreaper: %m");
- if (arg_running_as == MANAGER_SYSTEM) {
+ if (arg_system) {
bump_rlimit_nofile(&saved_rlimit_nofile);
if (empty_etc) {
r = unit_file_preset_all(UNIT_FILE_SYSTEM, false, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, false, NULL, 0);
if (r < 0)
- log_warning_errno(r, "Failed to populate /etc with preset unit settings, ignoring: %m");
+ log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, "Failed to populate /etc with preset unit settings, ignoring: %m");
else
log_info("Populated /etc with preset unit settings.");
}
}
- r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m);
+ r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m);
if (r < 0) {
log_emergency_errno(r, "Failed to allocate manager object: %m");
error_message = "Failed to allocate manager object";
@@ -1657,6 +1801,7 @@ int main(int argc, char *argv[]) {
m->initrd_timestamp = initrd_timestamp;
m->security_start_timestamp = security_start_timestamp;
m->security_finish_timestamp = security_finish_timestamp;
+ m->cad_burst_action = arg_cad_burst_action;
manager_set_defaults(m);
manager_set_show_status(m, arg_show_status);
@@ -1673,16 +1818,12 @@ int main(int argc, char *argv[]) {
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
- fdset_free(fds);
- fds = NULL;
+ fds = fdset_free(fds);
- if (arg_serialization) {
- fclose(arg_serialization);
- arg_serialization = NULL;
- }
+ arg_serialization = safe_fclose(arg_serialization);
if (queue_default_job) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Unit *target = NULL;
Job *default_unit_job;
@@ -1722,11 +1863,13 @@ int main(int argc, char *argv[]) {
manager_dump_units(m, stdout, "\t");
}
- r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, false, &error, &default_unit_job);
+ r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, &error, &default_unit_job);
if (r == -EPERM) {
log_debug("Default target could not be isolated, starting instead: %s", bus_error_message(&error, r));
- r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job);
+ sd_bus_error_free(&error);
+
+ r = manager_add_job(m, JOB_START, target, JOB_REPLACE, &error, &default_unit_job);
if (r < 0) {
log_emergency("Failed to start default target: %s", bus_error_message(&error, r));
error_message = "Failed to start default target";
@@ -1763,11 +1906,6 @@ int main(int argc, char *argv[]) {
switch (m->exit_code) {
- case MANAGER_EXIT:
- retval = EXIT_SUCCESS;
- log_debug("Exit.");
- goto finish;
-
case MANAGER_RELOAD:
log_info("Reloading.");
@@ -1785,7 +1923,7 @@ int main(int argc, char *argv[]) {
case MANAGER_REEXECUTE:
if (prepare_reexecute(m, &arg_serialization, &fds, false) < 0) {
- error_message = "Failed to prepare for reexection";
+ error_message = "Failed to prepare for reexecution";
goto finish;
}
@@ -1801,7 +1939,7 @@ int main(int argc, char *argv[]) {
if (!switch_root_init)
if (prepare_reexecute(m, &arg_serialization, &fds, true) < 0) {
- error_message = "Failed to prepare for reexection";
+ error_message = "Failed to prepare for reexecution";
goto finish;
}
@@ -1809,11 +1947,21 @@ int main(int argc, char *argv[]) {
log_notice("Switching root.");
goto finish;
+ case MANAGER_EXIT:
+ retval = m->return_value;
+
+ if (MANAGER_IS_USER(m)) {
+ log_debug("Exit.");
+ goto finish;
+ }
+
+ /* fallthrough */
case MANAGER_REBOOT:
case MANAGER_POWEROFF:
case MANAGER_HALT:
case MANAGER_KEXEC: {
static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
+ [MANAGER_EXIT] = "exit",
[MANAGER_REBOOT] = "reboot",
[MANAGER_POWEROFF] = "poweroff",
[MANAGER_HALT] = "halt",
@@ -1837,23 +1985,16 @@ finish:
if (m)
arg_shutdown_watchdog = m->shutdown_watchdog;
- m = manager_free(m);
-
- for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) {
- free(arg_default_rlimit[j]);
- arg_default_rlimit[j] = NULL;
- }
- free(arg_default_unit);
- arg_default_unit = NULL;
-
- free_join_controllers();
+ m = manager_free(m);
- strv_free(arg_default_environment);
- arg_default_environment = NULL;
+ for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++)
+ arg_default_rlimit[j] = mfree(arg_default_rlimit[j]);
- set_free(arg_syscall_archs);
- arg_syscall_archs = NULL;
+ arg_default_unit = mfree(arg_default_unit);
+ arg_join_controllers = strv_free_free(arg_join_controllers);
+ arg_default_environment = strv_free(arg_default_environment);
+ arg_syscall_archs = set_free(arg_syscall_archs);
mac_selinux_finish();
@@ -1870,7 +2011,7 @@ finish:
* 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);
+ (void) setrlimit(RLIMIT_NOFILE, &saved_rlimit_nofile);
if (switch_root_dir) {
/* Kill all remaining processes from the
@@ -1905,17 +2046,22 @@ 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_system ? "--system" : "--user";
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
- /* do not pass along the environment we inherit from the kernel or initrd */
- if (switch_root_dir)
- clearenv();
-
assert(i <= args_size);
- execv(args[0], (char* const*) args);
+
+ /*
+ * We want valgrind to print its memory usage summary before reexecution.
+ * Valgrind won't do this is on its own on exec(), but it will do it on exit().
+ * Hence, to ensure we get a summary here, fork() off a child, let it exit() cleanly,
+ * so that it prints the summary, and wait() for it in the parent, before proceeding into the exec().
+ */
+ valgrind_summary_hack();
+
+ (void) execv(args[0], (char* const*) args);
}
/* Try the fallback, if there is any, without any
@@ -1924,18 +2070,11 @@ finish:
* getopt() in argv[], and some cleanups in envp[],
* but let's hope that doesn't matter.) */
- if (arg_serialization) {
- fclose(arg_serialization);
- arg_serialization = NULL;
- }
-
- if (fds) {
- fdset_free(fds);
- fds = NULL;
- }
+ arg_serialization = safe_fclose(arg_serialization);
+ fds = fdset_free(fds);
/* Reopen the console */
- make_console_stdio();
+ (void) make_console_stdio();
for (j = 1, i = 1; j < (unsigned) argc; j++)
args[i++] = argv[j];
@@ -1949,33 +2088,26 @@ finish:
if (switch_root_init) {
args[0] = switch_root_init;
- execv(args[0], (char* const*) args);
+ (void) execv(args[0], (char* const*) args);
log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m");
}
args[0] = "/sbin/init";
- execv(args[0], (char* const*) args);
+ (void) execv(args[0], (char* const*) args);
if (errno == ENOENT) {
log_warning("No /sbin/init, trying fallback");
args[0] = "/bin/sh";
args[1] = NULL;
- execv(args[0], (char* const*) args);
+ (void) execv(args[0], (char* const*) args);
log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m");
} else
log_warning_errno(errno, "Failed to execute /sbin/init, giving up: %m");
}
- if (arg_serialization) {
- fclose(arg_serialization);
- arg_serialization = NULL;
- }
-
- if (fds) {
- fdset_free(fds);
- fds = NULL;
- }
+ arg_serialization = safe_fclose(arg_serialization);
+ fds = fdset_free(fds);
#ifdef HAVE_VALGRIND_VALGRIND_H
/* If we are PID 1 and running under valgrind, then let's exit
@@ -1988,7 +2120,8 @@ finish:
if (shutdown_verb) {
char log_level[DECIMAL_STR_MAX(int) + 1];
- const char* command_line[9] = {
+ char exit_code[DECIMAL_STR_MAX(uint8_t) + 1];
+ const char* command_line[11] = {
SYSTEMD_SHUTDOWN_BINARY_PATH,
shutdown_verb,
"--log-level", log_level,
@@ -2003,6 +2136,7 @@ finish:
xsprintf(log_level, "%d", log_get_max_level());
switch (log_get_target()) {
+
case LOG_TARGET_KMSG:
case LOG_TARGET_JOURNAL_OR_KMSG:
case LOG_TARGET_SYSLOG_OR_KMSG:
@@ -2025,9 +2159,15 @@ finish:
if (log_get_show_location())
command_line[pos++] = "--log-location";
+ if (streq(shutdown_verb, "exit")) {
+ command_line[pos++] = "--exit-code";
+ command_line[pos++] = exit_code;
+ xsprintf(exit_code, "%d", retval);
+ }
+
assert(pos < ELEMENTSOF(command_line));
- if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) {
+ if (arm_reboot_watchdog && arg_shutdown_watchdog > 0 && arg_shutdown_watchdog != USEC_INFINITY) {
char *e;
/* If we reboot let's set the shutdown
@@ -2038,15 +2178,15 @@ finish:
/* Tell the binary how often to ping, ignore failure */
if (asprintf(&e, "WATCHDOG_USEC="USEC_FMT, arg_shutdown_watchdog) > 0)
- strv_push(&env_block, e);
+ (void) strv_push(&env_block, e);
} else
watchdog_close(true);
/* Avoid the creation of new processes forked by the
* kernel; at this point, we will not listen to the
* signals anyway */
- if (detect_container(NULL) <= 0)
- cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER);
+ if (detect_container() <= 0)
+ (void) cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER);
execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block);
log_error_errno(errno, "Failed to execute shutdown binary, %s: %m",
@@ -2056,9 +2196,9 @@ finish:
if (getpid() == 1) {
if (error_message)
manager_status_printf(NULL, STATUS_TYPE_EMERGENCY,
- ANSI_HIGHLIGHT_RED_ON "!!!!!!" ANSI_HIGHLIGHT_OFF,
+ ANSI_HIGHLIGHT_RED "!!!!!!" ANSI_NORMAL,
"%s, freezing.", error_message);
- freeze();
+ freeze_or_reboot();
}
return retval;
diff --git a/src/core/manager.c b/src/core/manager.c
index a1f37bbbb3..3569249788 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,19 +17,19 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <dirent.h>
#include <errno.h>
-#include <string.h>
+#include <fcntl.h>
+#include <linux/kd.h>
#include <signal.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <sys/inotify.h>
+#include <string.h>
#include <sys/epoll.h>
-#include <sys/reboot.h>
+#include <sys/inotify.h>
#include <sys/ioctl.h>
-#include <linux/kd.h>
-#include <fcntl.h>
-#include <dirent.h>
+#include <sys/reboot.h>
#include <sys/timerfd.h>
+#include <sys/wait.h>
+#include <unistd.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
@@ -40,40 +38,57 @@
#include "sd-daemon.h"
#include "sd-messages.h"
-#include "hashmap.h"
-#include "macro.h"
-#include "strv.h"
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "ratelimit.h"
-#include "locale-setup.h"
-#include "unit-name.h"
-#include "missing.h"
-#include "rm-rf.h"
-#include "path-lookup.h"
-#include "special.h"
-#include "exit-status.h"
-#include "virt.h"
-#include "watchdog.h"
-#include "path-util.h"
+#include "alloc-util.h"
#include "audit-fd.h"
#include "boot-timestamps.h"
-#include "env-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
-#include "bus-util.h"
#include "bus-kernel.h"
-#include "time-util.h"
-#include "process-util.h"
-#include "terminal-util.h"
-#include "signal-util.h"
-#include "dbus.h"
-#include "dbus-unit.h"
+#include "bus-util.h"
+#include "clean-ipc.h"
#include "dbus-job.h"
#include "dbus-manager.h"
+#include "dbus-unit.h"
+#include "dbus.h"
+#include "dirent-util.h"
+#include "env-util.h"
+#include "escape.h"
+#include "exit-status.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "hashmap.h"
+#include "io-util.h"
+#include "locale-setup.h"
+#include "log.h"
+#include "macro.h"
#include "manager.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-lookup.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "ratelimit.h"
+#include "rm-rf.h"
+#include "signal-util.h"
+#include "special.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "time-util.h"
#include "transaction.h"
+#include "umask-util.h"
+#include "unit-name.h"
+#include "user-util.h"
+#include "util.h"
+#include "virt.h"
+#include "watchdog.h"
+
+#define NOTIFY_RCVBUF_SIZE (8*1024*1024)
+#define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024)
/* Initial delay and the interval for printing status messages about running jobs */
#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
@@ -81,13 +96,14 @@
#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
+static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata);
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata);
static int manager_run_generators(Manager *m);
-static void manager_undo_generators(Manager *m);
static void manager_watch_jobs_in_progress(Manager *m) {
usec_t next;
@@ -111,7 +127,7 @@ static void manager_watch_jobs_in_progress(Manager *m) {
(void) sd_event_source_set_description(m->jobs_in_progress_event_source, "manager-jobs-in-progress");
}
-#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED_ON)-1) + sizeof(ANSI_HIGHLIGHT_RED_ON)-1 + 2*(sizeof(ANSI_HIGHLIGHT_OFF)-1))
+#define CYLON_BUFFER_EXTRA (2*(sizeof(ANSI_RED)-1) + sizeof(ANSI_HIGHLIGHT_RED)-1 + 2*(sizeof(ANSI_NORMAL)-1))
static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) {
char *p = buffer;
@@ -122,23 +138,28 @@ static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned po
if (pos > 1) {
if (pos > 2)
p = mempset(p, ' ', pos-2);
- p = stpcpy(p, ANSI_RED_ON);
+ if (log_get_show_color())
+ p = stpcpy(p, ANSI_RED);
*p++ = '*';
}
if (pos > 0 && pos <= width) {
- p = stpcpy(p, ANSI_HIGHLIGHT_RED_ON);
+ if (log_get_show_color())
+ p = stpcpy(p, ANSI_HIGHLIGHT_RED);
*p++ = '*';
}
- p = stpcpy(p, ANSI_HIGHLIGHT_OFF);
+ if (log_get_show_color())
+ p = stpcpy(p, ANSI_NORMAL);
if (pos < width) {
- p = stpcpy(p, ANSI_RED_ON);
+ if (log_get_show_color())
+ p = stpcpy(p, ANSI_RED);
*p++ = '*';
if (pos < width-1)
p = mempset(p, ' ', width-1-pos);
- strcpy(p, ANSI_HIGHLIGHT_OFF);
+ if (log_get_show_color())
+ strcpy(p, ANSI_NORMAL);
}
}
@@ -220,7 +241,7 @@ static int have_ask_password(void) {
errno = 0;
de = readdir(dir);
- if (!de && errno != 0)
+ if (!de && errno > 0)
return -errno;
if (!de)
return false;
@@ -250,8 +271,8 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source,
static void manager_close_ask_password(Manager *m) {
assert(m);
- m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd);
m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source);
+ m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd);
m->have_ask_password = -EINVAL;
}
@@ -317,6 +338,8 @@ static int manager_watch_idle_pipe(Manager *m) {
static void manager_close_idle_pipe(Manager *m) {
assert(m);
+ m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
+
safe_close_pair(m->idle_pipe);
safe_close_pair(m->idle_pipe + 2);
}
@@ -365,6 +388,9 @@ static int enable_special_signals(Manager *m) {
assert(m);
+ if (m->test_run)
+ return 0;
+
/* Enable that we get SIGINT on control-alt-del. In containers
* this will fail with EPERM (older) or EINVAL (newer), so
* ignore that. */
@@ -467,15 +493,15 @@ static int manager_setup_signals(Manager *m) {
(void) sd_event_source_set_description(m->signal_event_source, "manager-signal");
- /* Process signals a bit earlier than the rest of things, but
- * later than notify_fd processing, so that the notify
- * processing can still figure out to which process/service a
- * message belongs, before we reap the process. */
- r = sd_event_source_set_priority(m->signal_event_source, -5);
+ /* Process signals a bit earlier than the rest of things, but later than notify_fd processing, so that the
+ * notify processing can still figure out to which process/service a message belongs, before we reap the
+ * process. Also, process this before handling cgroup notifications, so that we always collect child exit
+ * status information before detecting that there's no process in a cgroup. */
+ r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-6);
if (r < 0)
return r;
- if (m->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(m))
return enable_special_signals(m);
return 0;
@@ -493,15 +519,17 @@ static void manager_clean_environment(Manager *m) {
"MANAGERPID",
"LISTEN_PID",
"LISTEN_FDS",
+ "LISTEN_FDNAMES",
"WATCHDOG_PID",
"WATCHDOG_USEC",
+ "INVOCATION_ID",
NULL);
}
static int manager_default_environment(Manager *m) {
assert(m);
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* The system manager always starts with a clean
* environment for its children. It does not import
* the kernel or the parents exported variables.
@@ -529,50 +557,55 @@ static int manager_default_environment(Manager *m) {
return 0;
}
-
-int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
-
- static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = {
- [MANAGER_SYSTEM] = "UNIT=",
- [MANAGER_USER] = "USER_UNIT=",
- };
-
- static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = {
- [MANAGER_SYSTEM] = "UNIT=%s",
- [MANAGER_USER] = "USER_UNIT=%s",
- };
-
+int manager_new(UnitFileScope scope, bool test_run, Manager **_m) {
Manager *m;
int r;
assert(_m);
- assert(running_as >= 0);
- assert(running_as < _MANAGER_RUNNING_AS_MAX);
+ assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER));
m = new0(Manager, 1);
if (!m)
return -ENOMEM;
+ m->unit_file_scope = scope;
+ m->exit_code = _MANAGER_EXIT_CODE_INVALID;
+ m->default_timer_accuracy_usec = USEC_PER_MINUTE;
+ m->default_tasks_accounting = true;
+ m->default_tasks_max = UINT64_MAX;
+
#ifdef ENABLE_EFI
- if (running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0)
+ if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0)
boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
#endif
- m->running_as = running_as;
- m->exit_code = _MANAGER_EXIT_CODE_INVALID;
- m->default_timer_accuracy_usec = USEC_PER_MINUTE;
-
/* Prepare log fields we can use for structured logging */
- m->unit_log_field = unit_log_fields[running_as];
- m->unit_log_format_string = unit_log_format_strings[running_as];
+ if (MANAGER_IS_SYSTEM(m)) {
+ m->unit_log_field = "UNIT=";
+ m->unit_log_format_string = "UNIT=%s";
+
+ m->invocation_log_field = "INVOCATION_ID=";
+ m->invocation_log_format_string = "INVOCATION_ID=" SD_ID128_FORMAT_STR;
+ } else {
+ m->unit_log_field = "USER_UNIT=";
+ m->unit_log_format_string = "USER_UNIT=%s";
+
+ m->invocation_log_field = "USER_INVOCATION_ID=";
+ m->invocation_log_format_string = "USER_INVOCATION_ID=" SD_ID128_FORMAT_STR;
+ }
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
- m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = -1;
+ m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd =
+ m->dev_autofs_fd = m->private_listen_fd = m->cgroup_inotify_fd =
+ m->ask_password_inotify_fd = -1;
+
+ m->user_lookup_fds[0] = m->user_lookup_fds[1] = -1;
+
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
- m->ask_password_inotify_fd = -1;
m->have_ask_password = -EINVAL; /* we don't know */
+ m->first_boot = -1;
m->test_run = test_run;
@@ -599,14 +632,6 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
if (r < 0)
goto fail;
- r = set_ensure_allocated(&m->startup_units, NULL);
- if (r < 0)
- goto fail;
-
- r = set_ensure_allocated(&m->failed_units, NULL);
- if (r < 0)
- goto fail;
-
r = sd_event_default(&m->event);
if (r < 0)
goto fail;
@@ -643,9 +668,8 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) {
goto fail;
}
- /* Note that we set up neither kdbus, nor the notify fd
- * here. We do that after deserialization, since they might
- * have gotten serialized across the reexec. */
+ /* Note that we do not set up the notify fd here. We do that after deserialization,
+ * since they might have gotten serialized across the reexec. */
m->taint_usr = dir_is_empty("/usr") > 0;
@@ -669,29 +693,25 @@ static int manager_setup_notify(Manager *m) {
.sa.sa_family = AF_UNIX,
};
static const int one = 1;
+ const char *e;
/* First free all secondary fields */
- free(m->notify_socket);
- m->notify_socket = NULL;
+ m->notify_socket = mfree(m->notify_socket);
m->notify_event_source = sd_event_source_unref(m->notify_event_source);
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return log_error_errno(errno, "Failed to allocate notification socket: %m");
- if (m->running_as == MANAGER_SYSTEM)
- m->notify_socket = strdup("/run/systemd/notify");
- else {
- const char *e;
+ fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
- e = getenv("XDG_RUNTIME_DIR");
- if (!e) {
- log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m");
- return -EINVAL;
- }
-
- m->notify_socket = strappend(e, "/systemd/notify");
+ e = manager_get_runtime_prefix(m);
+ if (!e) {
+ log_error("Failed to determine runtime prefix.");
+ return -EINVAL;
}
+
+ m->notify_socket = strappend(e, "/systemd/notify");
if (!m->notify_socket)
return log_oom();
@@ -699,7 +719,7 @@ static int manager_setup_notify(Manager *m) {
(void) unlink(m->notify_socket);
strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1);
- r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
+ r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
if (r < 0)
return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
@@ -718,9 +738,9 @@ static int manager_setup_notify(Manager *m) {
if (r < 0)
return log_error_errno(r, "Failed to allocate notify event source: %m");
- /* Process signals a bit earlier than SIGCHLD, so that we can
- * still identify to which service an exit message belongs */
- r = sd_event_source_set_priority(m->notify_event_source, -7);
+ /* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which
+ * service an exit message belongs. */
+ r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7);
if (r < 0)
return log_error_errno(r, "Failed to set priority of notify event source: %m");
@@ -730,24 +750,128 @@ static int manager_setup_notify(Manager *m) {
return 0;
}
-static int manager_setup_kdbus(Manager *m) {
- _cleanup_free_ char *p = NULL;
+static int manager_setup_cgroups_agent(Manager *m) {
- assert(m);
+ static const union sockaddr_union sa = {
+ .un.sun_family = AF_UNIX,
+ .un.sun_path = "/run/systemd/cgroups-agent",
+ };
+ int r;
+
+ /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering
+ * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and
+ * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on
+ * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number
+ * of D-Bus connections may be queued until the kernel will start dropping further incoming connections,
+ * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX
+ * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and
+ * we thus won't lose messages.
+ *
+ * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen
+ * to it. The system instance hence listens on this special socket, but the user instances listen on the system
+ * bus for these messages. */
+
+ if (m->test_run)
+ return 0;
- if (m->test_run || m->kdbus_fd >= 0)
+ if (!MANAGER_IS_SYSTEM(m))
return 0;
- if (!is_kdbus_available())
- return -ESOCKTNOSUPPORT;
- m->kdbus_fd = bus_kernel_create_bus(
- m->running_as == MANAGER_SYSTEM ? "system" : "user",
- m->running_as == MANAGER_SYSTEM, &p);
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* We don't need this anymore on the unified hierarchy */
+ return 0;
+
+ if (m->cgroups_agent_fd < 0) {
+ _cleanup_close_ int fd = -1;
+
+ /* First free all secondary fields */
+ m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
- if (m->kdbus_fd < 0)
- return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m");
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m");
+
+ fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE);
- log_debug("Successfully set up kdbus on %s", p);
+ (void) unlink(sa.un.sun_path);
+
+ /* Only allow root to connect to this socket */
+ RUN_WITH_UMASK(0077)
+ r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un));
+ if (r < 0)
+ return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path);
+
+ m->cgroups_agent_fd = fd;
+ fd = -1;
+ }
+
+ if (!m->cgroups_agent_event_source) {
+ r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate cgroups agent event source: %m");
+
+ /* Process cgroups notifications early, but after having processed service notification messages or
+ * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification,
+ * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of
+ * cgroup inotify for the unified cgroup stuff. */
+ r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5);
+ if (r < 0)
+ return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m");
+
+ (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent");
+ }
+
+ return 0;
+}
+
+static int manager_setup_user_lookup_fd(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Set up the socket pair used for passing UID/GID resolution results from forked off processes to PID
+ * 1. Background: we can't do name lookups (NSS) from PID 1, since it might involve IPC and thus activation,
+ * and we might hence deadlock on ourselves. Hence we do all user/group lookups asynchronously from the forked
+ * off processes right before executing the binaries to start. In order to be able to clean up any IPC objects
+ * created by a unit (see RemoveIPC=) we need to know in PID 1 the used UID/GID of the executed processes,
+ * hence we establish this communication channel so that forked off processes can pass their UID/GID
+ * information back to PID 1. The forked off processes send their resolved UID/GID to PID 1 in a simple
+ * datagram, along with their unit name, so that we can share one communication socket pair among all units for
+ * this purpose.
+ *
+ * You might wonder why we need a communication channel for this that is independent of the usual notification
+ * socket scheme (i.e. $NOTIFY_SOCKET). The primary difference is about trust: data sent via the $NOTIFY_SOCKET
+ * channel is only accepted if it originates from the right unit and if reception was enabled for it. The user
+ * lookup socket OTOH is only accessible by PID 1 and its children until they exec(), and always available.
+ *
+ * Note that this function is called under two circumstances: when we first initialize (in which case we
+ * allocate both the socket pair and the event source to listen on it), and when we deserialize after a reload
+ * (in which case the socket pair already exists but we still need to allocate the event source for it). */
+
+ if (m->user_lookup_fds[0] < 0) {
+
+ /* Free all secondary fields */
+ safe_close_pair(m->user_lookup_fds);
+ m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, m->user_lookup_fds) < 0)
+ return log_error_errno(errno, "Failed to allocate user lookup socket: %m");
+
+ (void) fd_inc_rcvbuf(m->user_lookup_fds[0], NOTIFY_RCVBUF_SIZE);
+ }
+
+ if (!m->user_lookup_event_source) {
+ r = sd_event_add_io(m->event, &m->user_lookup_event_source, m->user_lookup_fds[0], EPOLLIN, manager_dispatch_user_lookup_fd, m);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to allocate user lookup event source: %m");
+
+ /* Process even earlier than the notify event source, so that we always know first about valid UID/GID
+ * resolutions */
+ r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m");
+
+ (void) sd_event_source_set_description(m->user_lookup_event_source, "user-lookup");
+ }
return 0;
}
@@ -761,9 +885,8 @@ static int manager_connect_bus(Manager *m, bool reexecuting) {
return 0;
try_bus_connect =
- m->kdbus_fd >= 0 ||
reexecuting ||
- (m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS"));
+ (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS"));
/* Try to connect to the buses, if possible. */
return bus_init(m, try_bus_connect);
@@ -793,6 +916,18 @@ enum {
_GC_OFFSET_MAX
};
+static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
+ Iterator i;
+ Unit *other;
+
+ u->gc_marker = gc_marker + GC_OFFSET_GOOD;
+
+ /* Recursively mark referenced units as GOOD as well */
+ SET_FOREACH(other, u->dependencies[UNIT_REFERENCES], i)
+ if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE)
+ unit_gc_mark_good(other, gc_marker);
+}
+
static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
Iterator i;
Unit *other;
@@ -802,6 +937,7 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
if (u->gc_marker == gc_marker + GC_OFFSET_GOOD ||
u->gc_marker == gc_marker + GC_OFFSET_BAD ||
+ u->gc_marker == gc_marker + GC_OFFSET_UNSURE ||
u->gc_marker == gc_marker + GC_OFFSET_IN_PATH)
return;
@@ -842,7 +978,7 @@ bad:
return;
good:
- u->gc_marker = gc_marker + GC_OFFSET_GOOD;
+ unit_gc_mark_good(u, gc_marker);
}
static unsigned manager_dispatch_gc_queue(Manager *m) {
@@ -925,11 +1061,15 @@ Manager* manager_free(Manager *m) {
* around */
manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE);
- manager_undo_generators(m);
+ lookup_paths_flush_generator(&m->lookup_paths);
bus_done(m);
+ dynamic_user_vacuum(m, false);
+ hashmap_free(m->dynamic_users);
+
hashmap_free(m->units);
+ hashmap_free(m->units_by_invocation_id);
hashmap_free(m->jobs);
hashmap_free(m->watch_pids1);
hashmap_free(m->watch_pids2);
@@ -940,15 +1080,17 @@ Manager* manager_free(Manager *m) {
sd_event_source_unref(m->signal_event_source);
sd_event_source_unref(m->notify_event_source);
+ sd_event_source_unref(m->cgroups_agent_event_source);
sd_event_source_unref(m->time_change_event_source);
sd_event_source_unref(m->jobs_in_progress_event_source);
- sd_event_source_unref(m->idle_pipe_event_source);
sd_event_source_unref(m->run_queue_event_source);
+ sd_event_source_unref(m->user_lookup_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
+ safe_close(m->cgroups_agent_fd);
safe_close(m->time_change_fd);
- safe_close(m->kdbus_fd);
+ safe_close_pair(m->user_lookup_fds);
manager_close_ask_password(m);
@@ -969,17 +1111,19 @@ Manager* manager_free(Manager *m) {
free(m->switch_root_init);
for (i = 0; i < _RLIMIT_MAX; i++)
- free(m->rlimit[i]);
+ m->rlimit[i] = mfree(m->rlimit[i]);
assert(hashmap_isempty(m->units_requiring_mounts_for));
hashmap_free(m->units_requiring_mounts_for);
+ hashmap_free(m->uid_refs);
+ hashmap_free(m->gid_refs);
+
free(m);
return NULL;
}
-int manager_enumerate(Manager *m) {
- int r = 0;
+void manager_enumerate(Manager *m) {
UnitType c;
assert(m);
@@ -987,8 +1131,6 @@ int manager_enumerate(Manager *m) {
/* Let's ask every type to load all units from disk/kernel
* that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
- int q;
-
if (!unit_type_supported(c)) {
log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
continue;
@@ -997,13 +1139,10 @@ int manager_enumerate(Manager *m) {
if (!unit_vtable[c]->enumerate)
continue;
- q = unit_vtable[c]->enumerate(m);
- if (q < 0)
- r = q;
+ unit_vtable[c]->enumerate(m);
}
manager_dispatch_load_queue(m);
- return r;
}
static void manager_coldplug(Manager *m) {
@@ -1029,7 +1168,6 @@ static void manager_coldplug(Manager *m) {
static void manager_build_unit_path_cache(Manager *m) {
char **i;
- _cleanup_closedir_ DIR *d = NULL;
int r;
assert(m);
@@ -1038,29 +1176,27 @@ static void manager_build_unit_path_cache(Manager *m) {
m->unit_path_cache = set_new(&string_hash_ops);
if (!m->unit_path_cache) {
- log_error("Failed to allocate unit path cache.");
- return;
+ r = -ENOMEM;
+ goto fail;
}
/* This simply builds a list of files we know exist, so that
* we don't always have to go to disk */
- STRV_FOREACH(i, m->lookup_paths.unit_path) {
+ STRV_FOREACH(i, m->lookup_paths.search_path) {
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
d = opendir(*i);
if (!d) {
if (errno != ENOENT)
- log_error_errno(errno, "Failed to open directory %s: %m", *i);
+ log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i);
continue;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, r = -errno; goto fail) {
char *p;
- if (hidden_file(de->d_name))
- continue;
-
p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
if (!p) {
r = -ENOMEM;
@@ -1071,25 +1207,18 @@ static void manager_build_unit_path_cache(Manager *m) {
if (r < 0)
goto fail;
}
-
- closedir(d);
- d = NULL;
}
return;
fail:
- log_error_errno(r, "Failed to build unit path cache: %m");
-
- set_free_free(m->unit_path_cache);
- m->unit_path_cache = NULL;
+ log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m");
+ m->unit_path_cache = set_free_free(m->unit_path_cache);
}
-
-static int manager_distribute_fds(Manager *m, FDSet *fds) {
- Unit *u;
+static void manager_distribute_fds(Manager *m, FDSet *fds) {
Iterator i;
- int r;
+ Unit *u;
assert(m);
@@ -1098,14 +1227,11 @@ static int manager_distribute_fds(Manager *m, FDSet *fds) {
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;
- }
- }
+ if (!UNIT_VTABLE(u)->distribute_fds)
+ continue;
- return 0;
+ UNIT_VTABLE(u)->distribute_fds(u, fds);
+ }
}
int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
@@ -1113,32 +1239,35 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
assert(m);
- dual_timestamp_get(&m->generators_start_timestamp);
- r = manager_run_generators(m);
- dual_timestamp_get(&m->generators_finish_timestamp);
+ r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
if (r < 0)
return r;
- r = lookup_paths_init(
- &m->lookup_paths, m->running_as, true,
- NULL,
- m->generator_unit_path,
- m->generator_unit_path_early,
- m->generator_unit_path_late);
+ /* Make sure the transient directory always exists, so that it remains in the search path */
+ if (!m->test_run) {
+ r = mkdir_p_label(m->lookup_paths.transient, 0755);
+ if (r < 0)
+ return r;
+ }
+
+ dual_timestamp_get(&m->generators_start_timestamp);
+ r = manager_run_generators(m);
+ dual_timestamp_get(&m->generators_finish_timestamp);
if (r < 0)
return r;
+ lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
/* If we will deserialize make sure that during enumeration
* this is already known, so we increase the counter here
* already */
if (serialization)
- m->n_reloading ++;
+ m->n_reloading++;
/* First, enumerate what we can from all config files */
dual_timestamp_get(&m->units_load_start_timestamp);
- r = manager_enumerate(m);
+ manager_enumerate(m);
dual_timestamp_get(&m->units_load_finish_timestamp);
/* Second, deserialize if there is something to deserialize */
@@ -1149,11 +1278,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
* 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 == 0)
- r = q;
- }
+ manager_distribute_fds(m, fds);
/* We might have deserialized the notify fd, but if we didn't
* then let's create the bus now */
@@ -1161,18 +1286,33 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (q < 0 && r == 0)
r = q;
- /* We might have deserialized the kdbus control fd, but if we
- * didn't, then let's create the bus now. */
- manager_setup_kdbus(m);
- manager_connect_bus(m, !!serialization);
- bus_track_coldplug(m, &m->subscribed, &m->deserialized_subscribed);
+ q = manager_setup_cgroups_agent(m);
+ if (q < 0 && r == 0)
+ r = q;
+
+ q = manager_setup_user_lookup_fd(m);
+ if (q < 0 && r == 0)
+ r = q;
+
+ /* Let's connect to the bus now. */
+ (void) manager_connect_bus(m, !!serialization);
+
+ (void) bus_track_coldplug(m, &m->subscribed, false, m->deserialized_subscribed);
+ m->deserialized_subscribed = strv_free(m->deserialized_subscribed);
/* Third, fire things up! */
manager_coldplug(m);
+ /* Release any dynamic users no longer referenced */
+ dynamic_user_vacuum(m, true);
+
+ /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+ manager_vacuum_uid_refs(m);
+ manager_vacuum_gid_refs(m);
+
if (serialization) {
assert(m->n_reloading > 0);
- m->n_reloading --;
+ m->n_reloading--;
/* Let's wait for the UnitNew/JobNew messages being
* sent, before we notify that the reload is
@@ -1183,7 +1323,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
return r;
}
-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool override, sd_bus_error *e, Job **_ret) {
+int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) {
int r;
Transaction *tr;
@@ -1206,7 +1346,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
if (!tr)
return -ENOMEM;
- r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, override, false,
+ r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false,
mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
mode == JOB_IGNORE_DEPENDENCIES, e);
if (r < 0)
@@ -1238,7 +1378,7 @@ tr_abort:
return r;
}
-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool override, sd_bus_error *e, Job **_ret) {
+int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) {
Unit *unit;
int r;
@@ -1251,7 +1391,23 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode
if (r < 0)
return r;
- return manager_add_job(m, type, unit, mode, override, e, _ret);
+ return manager_add_job(m, type, unit, mode, e, ret);
+}
+
+int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(m);
+ assert(type < _JOB_TYPE_MAX);
+ assert(name);
+ assert(mode < _JOB_MODE_MAX);
+
+ r = manager_add_job_by_name(m, type, name, mode, &error, ret);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r));
+
+ return r;
}
Job *manager_get_job(Manager *m, uint32_t id) {
@@ -1318,8 +1474,12 @@ int manager_load_unit_prepare(
t = unit_name_to_type(name);
- if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+ if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) {
+ if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
+ return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is missing the instance name.", name);
+
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
+ }
ret = manager_get_unit(m, name);
if (ret) {
@@ -1412,7 +1572,7 @@ void manager_clear_jobs(Manager *m) {
while ((j = hashmap_first(m->jobs)))
/* No need to recurse. We're cancelling all jobs. */
- job_finish_and_invalidate(j, JOB_CANCELED, false);
+ job_finish_and_invalidate(j, JOB_CANCELED, false, false);
}
static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) {
@@ -1478,13 +1638,41 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) {
return n;
}
-static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n, FDSet *fds) {
+static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+ Manager *m = userdata;
+ char buf[PATH_MAX+1];
+ ssize_t n;
+
+ n = recv(fd, buf, sizeof(buf), 0);
+ if (n < 0)
+ return log_error_errno(errno, "Failed to read cgroups agent message: %m");
+ if (n == 0) {
+ log_error("Got zero-length cgroups agent message, ignoring.");
+ return 0;
+ }
+ if ((size_t) n >= sizeof(buf)) {
+ log_error("Got overly long cgroups agent message, ignoring.");
+ return 0;
+ }
+
+ if (memchr(buf, 0, n)) {
+ log_error("Got cgroups agent message with embedded NUL byte, ignoring.");
+ return 0;
+ }
+ buf[n] = 0;
+
+ manager_notify_cgroup_empty(m, buf);
+ bus_forward_agent_released(m, buf);
+
+ return 0;
+}
+
+static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, FDSet *fds) {
_cleanup_strv_free_ char **tags = NULL;
assert(m);
assert(u);
assert(buf);
- assert(n > 0);
tags = strv_split(buf, "\n\r");
if (!tags) {
@@ -1494,14 +1682,43 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *
if (UNIT_VTABLE(u)->notify_message)
UNIT_VTABLE(u)->notify_message(u, pid, tags, fds);
- else
- log_unit_debug(u, "Got notification message for unit. Ignoring.");
+ else if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
+ _cleanup_free_ char *x = NULL, *y = NULL;
+
+ x = cescape(buf);
+ if (x)
+ y = ellipsize(x, 20, 90);
+ log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y));
+ }
}
static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+
+ _cleanup_fdset_free_ FDSet *fds = NULL;
Manager *m = userdata;
+ char buf[NOTIFY_BUFFER_MAX+1];
+ struct iovec iovec = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)-1,
+ };
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
+ } control = {};
+ struct msghdr msghdr = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof(control),
+ };
+
+ struct cmsghdr *cmsg;
+ struct ucred *ucred = NULL;
+ Unit *u1, *u2, *u3;
+ int r, *fd_array = NULL;
+ unsigned n_fds = 0;
ssize_t n;
- int r;
assert(m);
assert(m->notify_fd == fd);
@@ -1511,114 +1728,108 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
return 0;
}
- for (;;) {
- _cleanup_fdset_free_ FDSet *fds = NULL;
- char buf[NOTIFY_BUFFER_MAX+1];
- struct iovec iovec = {
- .iov_base = buf,
- .iov_len = sizeof(buf)-1,
- };
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
- CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
- } control = {};
- struct msghdr msghdr = {
- .msg_iov = &iovec,
- .msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- struct ucred *ucred = NULL;
- bool found = false;
- Unit *u1, *u2, *u3;
- int *fd_array = NULL;
- unsigned n_fds = 0;
-
- n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
- if (n < 0) {
- if (errno == EAGAIN || errno == EINTR)
- break;
+ n = recvmsg(m->notify_fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC|MSG_TRUNC);
+ if (n < 0) {
+ if (IN_SET(errno, EAGAIN, EINTR))
+ return 0; /* Spurious wakeup, try again */
- return -errno;
- }
+ /* If this is any other, real error, then let's stop processing this socket. This of course means we
+ * won't take notification messages anymore, but that's still better than busy looping around this:
+ * being woken up over and over again but being unable to actually read the message off the socket. */
+ return log_error_errno(errno, "Failed to receive notification message: %m");
+ }
- CMSG_FOREACH(cmsg, &msghdr) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ CMSG_FOREACH(cmsg, &msghdr) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- fd_array = (int*) CMSG_DATA(cmsg);
- n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+ fd_array = (int*) CMSG_DATA(cmsg);
+ n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
- } else if (cmsg->cmsg_level == SOL_SOCKET &&
- cmsg->cmsg_type == SCM_CREDENTIALS &&
- cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
+ } else if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
- ucred = (struct ucred*) CMSG_DATA(cmsg);
- }
+ ucred = (struct ucred*) CMSG_DATA(cmsg);
}
+ }
- if (n_fds > 0) {
- assert(fd_array);
+ if (n_fds > 0) {
+ assert(fd_array);
- r = fdset_new_array(&fds, fd_array, n_fds);
- if (r < 0) {
- close_many(fd_array, n_fds);
- return log_oom();
- }
+ r = fdset_new_array(&fds, fd_array, n_fds);
+ if (r < 0) {
+ close_many(fd_array, n_fds);
+ log_oom();
+ return 0;
}
+ }
- if (!ucred || ucred->pid <= 0) {
- log_warning("Received notify message without valid credentials. Ignoring.");
- continue;
- }
+ if (!ucred || ucred->pid <= 0) {
+ log_warning("Received notify message without valid credentials. Ignoring.");
+ return 0;
+ }
- if ((size_t) n >= sizeof(buf)) {
- log_warning("Received notify message exceeded maximum size. Ignoring.");
- continue;
- }
+ if ((size_t) n >= sizeof(buf) || (msghdr.msg_flags & MSG_TRUNC)) {
+ log_warning("Received notify message exceeded maximum size. Ignoring.");
+ return 0;
+ }
+
+ /* As extra safety check, let's make sure the string we get doesn't contain embedded NUL bytes. We permit one
+ * trailing NUL byte in the message, but don't expect it. */
+ if (n > 1 && memchr(buf, 0, n-1)) {
+ log_warning("Received notify message with embedded NUL bytes. Ignoring.");
+ return 0;
+ }
- buf[n] = 0;
+ /* Make sure it's NUL-terminated. */
+ buf[n] = 0;
- /* Notify every unit that might be interested, but try
- * to avoid notifying the same one multiple times. */
- u1 = manager_get_unit_by_pid(m, ucred->pid);
- if (u1) {
- manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds);
- found = true;
- }
+ /* Notify every unit that might be interested, but try
+ * to avoid notifying the same one multiple times. */
+ u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid);
+ if (u1)
+ manager_invoke_notify_message(m, u1, ucred->pid, buf, fds);
- u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid));
- if (u2 && u2 != u1) {
- manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds);
- found = true;
- }
+ u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid));
+ if (u2 && u2 != u1)
+ manager_invoke_notify_message(m, u2, ucred->pid, buf, fds);
- u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid));
- if (u3 && u3 != u2 && u3 != u1) {
- manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds);
- found = true;
- }
+ u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid));
+ if (u3 && u3 != u2 && u3 != u1)
+ manager_invoke_notify_message(m, u3, ucred->pid, buf, fds);
- if (!found)
- log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
+ if (!u1 && !u2 && !u3)
+ log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid);
- if (fdset_size(fds) > 0)
- log_warning("Got auxiliary fds with notification message, closing all.");
- }
+ if (fdset_size(fds) > 0)
+ log_warning("Got extra auxiliary fds with notification message, closing them.");
return 0;
}
-static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) {
+static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) {
+ uint64_t iteration;
+
assert(m);
assert(u);
assert(si);
+ sd_event_get_iteration(m->event, &iteration);
+
log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id);
unit_unwatch_pid(u, si->si_pid);
- UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
+
+ if (UNIT_VTABLE(u)->sigchld_event) {
+ if (set_size(u->pids) <= 1 ||
+ iteration != u->sigchldgen ||
+ unit_main_pid(u) == si->si_pid ||
+ unit_control_pid(u) == si->si_pid) {
+ UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status);
+ u->sigchldgen = iteration;
+ } else
+ log_debug("%s already issued a sigchld this iteration %" PRIu64 ", skipping. Pids still being watched %d", u->id, iteration, set_size(u->pids));
+ }
}
static int manager_dispatch_sigchld(Manager *m) {
@@ -1660,13 +1871,13 @@ static int manager_dispatch_sigchld(Manager *m) {
/* And now figure out the unit this belongs
* to, it might be multiple... */
- u1 = manager_get_unit_by_pid(m, si.si_pid);
+ u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid);
if (u1)
invoke_sigchld_event(m, u1, &si);
- u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid));
+ u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid));
if (u2 && u2 != u1)
invoke_sigchld_event(m, u2, &si);
- u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid));
+ u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid));
if (u3 && u3 != u2 && u3 != u1)
invoke_sigchld_event(m, u3, &si);
}
@@ -1684,23 +1895,53 @@ static int manager_dispatch_sigchld(Manager *m) {
}
static int manager_start_target(Manager *m, const char *name, JobMode mode) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
log_debug("Activating special unit %s", name);
- r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL);
+ r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL);
if (r < 0)
log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r));
return r;
}
+static void manager_handle_ctrl_alt_del(Manager *m) {
+ /* If the user presses C-A-D more than
+ * 7 times within 2s, we reboot/shutdown immediately,
+ * unless it was disabled in system.conf */
+
+ if (ratelimit_test(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == CAD_BURST_ACTION_IGNORE)
+ manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
+ else {
+ switch (m->cad_burst_action) {
+
+ case CAD_BURST_ACTION_REBOOT:
+ m->exit_code = MANAGER_REBOOT;
+ break;
+
+ case CAD_BURST_ACTION_POWEROFF:
+ m->exit_code = MANAGER_POWEROFF;
+ break;
+
+ default:
+ assert_not_reached("Unknown action.");
+ }
+
+ log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.",
+ cad_burst_action_to_string(m->cad_burst_action));
+ status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.",
+ cad_burst_action_to_string(m->cad_burst_action));
+ }
+}
+
static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
ssize_t n;
struct signalfd_siginfo sfsi;
bool sigchld = false;
+ int r;
assert(m);
assert(m->signal_fd == fd);
@@ -1713,18 +1954,21 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
for (;;) {
n = read(m->signal_fd, &sfsi, sizeof(sfsi));
if (n != sizeof(sfsi)) {
+ if (n >= 0) {
+ log_warning("Truncated read from signal fd (%zu bytes)!", n);
+ return 0;
+ }
- if (n >= 0)
- return -EIO;
-
- if (errno == EINTR || errno == EAGAIN)
+ if (IN_SET(errno, EINTR, EAGAIN))
break;
- return -errno;
+ /* We return an error here, which will kill this handler,
+ * to avoid a busy loop on read error. */
+ return log_error_errno(errno, "Reading from signal fd failed: %m");
}
log_received_signal(sfsi.ssi_signo == SIGCHLD ||
- (sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER)
+ (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m))
? LOG_DEBUG : LOG_INFO,
&sfsi);
@@ -1735,7 +1979,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case SIGTERM:
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
/* This is for compatibility with the
* original sysvinit */
m->exit_code = MANAGER_REEXECUTE;
@@ -1745,20 +1989,8 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
/* Fall through */
case SIGINT:
- if (m->running_as == MANAGER_SYSTEM) {
-
- /* If the user presses C-A-D more than
- * 7 times within 2s, we reboot
- * immediately. */
-
- if (ratelimit_test(&m->ctrl_alt_del_ratelimit))
- manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
- else {
- log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
- status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, rebooting immediately.");
- m->exit_code = MANAGER_REBOOT;
- }
-
+ if (MANAGER_IS_SYSTEM(m)) {
+ manager_handle_ctrl_alt_del(m);
break;
}
@@ -1771,14 +2003,14 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
break;
case SIGWINCH:
- if (m->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(m))
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 (MANAGER_IS_SYSTEM(m))
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
/* This is a nop on non-init */
@@ -1809,20 +2041,16 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
f = open_memstream(&dump, &size);
if (!f) {
- log_warning("Failed to allocate memory stream.");
+ log_warning_errno(errno, "Failed to allocate memory stream: %m");
break;
}
manager_dump_units(m, f, "\t");
manager_dump_jobs(m, f, "\t");
- if (ferror(f)) {
- log_warning("Failed to write status stream");
- break;
- }
-
- if (fflush(f)) {
- log_warning("Failed to flush status stream");
+ r = fflush_and_check(f);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to write status stream: %m");
break;
}
@@ -1872,27 +2100,25 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
switch (sfsi.ssi_signo - SIGRTMIN) {
case 20:
- log_debug("Enabling showing of status.");
manager_set_show_status(m, SHOW_STATUS_YES);
break;
case 21:
- log_debug("Disabling showing of status.");
manager_set_show_status(m, SHOW_STATUS_NO);
break;
case 22:
log_set_max_level(LOG_DEBUG);
- log_notice("Setting log level to debug.");
+ log_info("Setting log level to debug.");
break;
case 23:
log_set_max_level(LOG_INFO);
- log_notice("Setting log level to info.");
+ log_info("Setting log level to info.");
break;
case 24:
- if (m->running_as == MANAGER_USER) {
+ if (MANAGER_IS_USER(m)) {
m->exit_code = MANAGER_EXIT;
return 0;
}
@@ -1963,7 +2189,6 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32
m->no_console_output = m->n_on_console > 0;
- m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
manager_close_idle_pipe(m);
return 0;
@@ -1996,8 +2221,7 @@ int manager_loop(Manager *m) {
m->exit_code = MANAGER_OK;
/* Release the path cache */
- set_free_free(m->unit_path_cache);
- m->unit_path_cache = NULL;
+ m->unit_path_cache = set_free_free(m->unit_path_cache);
manager_check_finished(m);
@@ -2010,14 +2234,13 @@ int manager_loop(Manager *m) {
while (m->exit_code == MANAGER_OK) {
usec_t wait_usec;
- if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM)
+ if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m))
watchdog_ping();
if (!ratelimit_test(&rl)) {
/* Yay, something is going seriously wrong, pause a little */
log_warning("Looping too fast. Throttling execution a little.");
sleep(1);
- continue;
}
if (manager_dispatch_load_queue(m) > 0)
@@ -2036,7 +2259,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->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) {
wait_usec = m->runtime_watchdog / 2;
if (wait_usec <= 0)
wait_usec = 1;
@@ -2053,6 +2276,7 @@ int manager_loop(Manager *m) {
int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u) {
_cleanup_free_ char *n = NULL;
+ sd_id128_t invocation_id;
Unit *u;
int r;
@@ -2064,12 +2288,25 @@ int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e,
if (r < 0)
return r;
+ /* Permit addressing units by invocation ID: if the passed bus path is suffixed by a 128bit ID then we use it
+ * as invocation ID. */
+ r = sd_id128_from_string(n, &invocation_id);
+ if (r >= 0) {
+ u = hashmap_get(m->units_by_invocation_id, &invocation_id);
+ if (u) {
+ *_u = u;
+ return 0;
+ }
+
+ return sd_bus_error_setf(e, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", SD_ID128_FORMAT_VAL(invocation_id));
+ }
+
+ /* If this didn't work, we use the suffix as unit name. */
r = manager_load_unit(m, n, NULL, e, &u);
if (r < 0)
return r;
*_u = u;
-
return 0;
}
@@ -2107,16 +2344,16 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
const char *msg;
int audit_fd, r;
+ if (!MANAGER_IS_SYSTEM(m))
+ return;
+
audit_fd = get_audit_fd();
if (audit_fd < 0)
return;
/* Don't generate audit events if the service was already
* started and we're just deserializing */
- if (m->n_reloading > 0)
- return;
-
- if (m->running_as != MANAGER_SYSTEM)
+ if (MANAGER_IS_RELOADING(m))
return;
if (u->type != UNIT_SERVICE)
@@ -2142,21 +2379,20 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
}
void manager_send_unit_plymouth(Manager *m, Unit *u) {
- union sockaddr_union sa = PLYMOUTH_SOCKET;
-
- int n = 0;
+ static const union sockaddr_union sa = PLYMOUTH_SOCKET;
_cleanup_free_ char *message = NULL;
_cleanup_close_ int fd = -1;
+ int n = 0;
/* Don't generate plymouth events if the service was already
* started and we're just deserializing */
- if (m->n_reloading > 0)
+ if (MANAGER_IS_RELOADING(m))
return;
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
- if (detect_container(NULL) > 0)
+ if (detect_container() > 0)
return;
if (u->type != UNIT_SERVICE &&
@@ -2172,7 +2408,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
return;
}
- if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
+ if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED))
log_error_errno(errno, "connect() failed: %m");
@@ -2190,24 +2426,6 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
log_error_errno(errno, "Failed to write Plymouth message: %m");
}
-void manager_dispatch_bus_name_owner_changed(
- Manager *m,
- const char *name,
- const char* old_owner,
- const char *new_owner) {
-
- Unit *u;
-
- assert(m);
- assert(name);
-
- u = hashmap_get(m->watch_bus, name);
- if (!u)
- return;
-
- UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
-}
-
int manager_open_serialization(Manager *m, FILE **_f) {
const char *path;
int fd = -1;
@@ -2215,8 +2433,8 @@ int manager_open_serialization(Manager *m, FILE **_f) {
assert(_f);
- path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp";
- fd = open_tmpfile(path, O_RDWR|O_CLOEXEC);
+ path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp";
+ fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -2244,7 +2462,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
assert(f);
assert(fds);
- m->n_reloading ++;
+ m->n_reloading++;
fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id);
fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
@@ -2290,17 +2508,38 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fprintf(f, "notify-socket=%s\n", m->notify_socket);
}
- if (m->kdbus_fd >= 0) {
+ if (m->cgroups_agent_fd >= 0) {
int copy;
- copy = fdset_put_dup(fds, m->kdbus_fd);
+ copy = fdset_put_dup(fds, m->cgroups_agent_fd);
if (copy < 0)
return copy;
- fprintf(f, "kdbus-fd=%i\n", copy);
+ fprintf(f, "cgroups-agent-fd=%i\n", copy);
+ }
+
+ if (m->user_lookup_fds[0] >= 0) {
+ int copy0, copy1;
+
+ copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]);
+ if (copy0 < 0)
+ return copy0;
+
+ copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]);
+ if (copy1 < 0)
+ return copy1;
+
+ fprintf(f, "user-lookup=%i %i\n", copy0, copy1);
}
- bus_track_serialize(m->subscribed, f);
+ bus_track_serialize(m->subscribed, f, "subscribed");
+
+ r = dynamic_user_serialize(m, f, fds);
+ if (r < 0)
+ return r;
+
+ manager_serialize_uid_refs(m, f);
+ manager_serialize_gid_refs(m, f);
fputc('\n', f);
@@ -2314,13 +2553,13 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
r = unit_serialize(u, f, fds, !switching_root);
if (r < 0) {
- m->n_reloading --;
+ m->n_reloading--;
return r;
}
}
assert(m->n_reloading > 0);
- m->n_reloading --;
+ m->n_reloading--;
if (ferror(f))
return -EIO;
@@ -2340,7 +2579,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
log_debug("Deserializing state...");
- m->n_reloading ++;
+ m->n_reloading++;
for (;;) {
char line[LINE_MAX], *l;
@@ -2457,25 +2696,42 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
free(m->notify_socket);
m->notify_socket = n;
- } else if (startswith(l, "kdbus-fd=")) {
+ } else if (startswith(l, "cgroups-agent-fd=")) {
int fd;
- if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_debug("Failed to parse kdbus fd: %s", l + 9);
+ if (safe_atoi(l + 17, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_debug("Failed to parse cgroups agent fd: %s", l + 10);
else {
- safe_close(m->kdbus_fd);
- m->kdbus_fd = fdset_remove(fds, fd);
+ m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source);
+ safe_close(m->cgroups_agent_fd);
+ m->cgroups_agent_fd = fdset_remove(fds, fd);
}
- } else {
- int k;
+ } else if (startswith(l, "user-lookup=")) {
+ int fd0, fd1;
- k = bus_track_deserialize_item(&m->deserialized_subscribed, l);
- if (k < 0)
- log_debug_errno(k, "Failed to deserialize bus tracker object: %m");
- else if (k == 0)
- log_debug("Unknown serialization item '%s'", l);
- }
+ if (sscanf(l + 12, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1))
+ log_debug("Failed to parse user lookup fd: %s", l + 12);
+ else {
+ m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source);
+ safe_close_pair(m->user_lookup_fds);
+ m->user_lookup_fds[0] = fdset_remove(fds, fd0);
+ m->user_lookup_fds[1] = fdset_remove(fds, fd1);
+ }
+
+ } else if (startswith(l, "dynamic-user="))
+ dynamic_user_deserialize_one(m, l + 13, fds);
+ else if (startswith(l, "destroy-ipc-uid="))
+ manager_deserialize_uid_refs_one(m, l + 16);
+ else if (startswith(l, "destroy-ipc-gid="))
+ manager_deserialize_gid_refs_one(m, l + 16);
+ else if (startswith(l, "subscribed=")) {
+
+ if (strv_extend(&m->deserialized_subscribed, l+11) < 0)
+ log_oom();
+
+ } else if (!startswith(l, "kdbus-fd=")) /* ignore this one */
+ log_debug("Unknown serialization item '%s'", l);
}
for (;;) {
@@ -2508,7 +2764,7 @@ finish:
r = -EIO;
assert(m->n_reloading > 0);
- m->n_reloading --;
+ m->n_reloading--;
return r;
}
@@ -2524,51 +2780,48 @@ int manager_reload(Manager *m) {
if (r < 0)
return r;
- m->n_reloading ++;
+ m->n_reloading++;
bus_manager_send_reloading(m, true);
fds = fdset_new();
if (!fds) {
- m->n_reloading --;
+ m->n_reloading--;
return -ENOMEM;
}
r = manager_serialize(m, f, fds, false);
if (r < 0) {
- m->n_reloading --;
+ m->n_reloading--;
return r;
}
if (fseeko(f, 0, SEEK_SET) < 0) {
- m->n_reloading --;
+ m->n_reloading--;
return -errno;
}
/* From here on there is no way back. */
manager_clear_jobs_and_units(m);
- manager_undo_generators(m);
+ lookup_paths_flush_generator(&m->lookup_paths);
lookup_paths_free(&m->lookup_paths);
+ dynamic_user_vacuum(m, false);
+ m->uid_refs = hashmap_free(m->uid_refs);
+ m->gid_refs = hashmap_free(m->gid_refs);
- /* Find new unit paths */
- q = manager_run_generators(m);
+ q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL);
if (q < 0 && r >= 0)
r = q;
- q = lookup_paths_init(
- &m->lookup_paths, m->running_as, true,
- NULL,
- m->generator_unit_path,
- m->generator_unit_path_early,
- m->generator_unit_path_late);
+ /* Find new unit paths */
+ q = manager_run_generators(m);
if (q < 0 && r >= 0)
r = q;
+ lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
/* First, enumerate what we can from all config files */
- q = manager_enumerate(m);
- if (q < 0 && r >= 0)
- r = q;
+ manager_enumerate(m);
/* Second, deserialize our stored data */
q = manager_deserialize(m, f, fds);
@@ -2583,9 +2836,28 @@ int manager_reload(Manager *m) {
if (q < 0 && r >= 0)
r = q;
+ q = manager_setup_cgroups_agent(m);
+ if (q < 0 && r >= 0)
+ r = q;
+
+ q = manager_setup_user_lookup_fd(m);
+ if (q < 0 && r >= 0)
+ r = q;
+
/* Third, fire things up! */
manager_coldplug(m);
+ /* Release any dynamic users no longer referenced */
+ dynamic_user_vacuum(m, true);
+
+ /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+ manager_vacuum_uid_refs(m);
+ manager_vacuum_gid_refs(m);
+
+ /* Sync current state of bus names with our set of listening units */
+ if (m->api_bus)
+ manager_sync_bus_names(m, m->api_bus);
+
assert(m->n_reloading > 0);
m->n_reloading--;
@@ -2594,12 +2866,6 @@ int manager_reload(Manager *m) {
return r;
}
-bool manager_is_reloading_or_reexecuting(Manager *m) {
- assert(m);
-
- return m->n_reloading != 0;
-}
-
void manager_reset_failed(Manager *m) {
Unit *u;
Iterator i;
@@ -2631,7 +2897,7 @@ static void manager_notify_finished(Manager *m) {
if (m->test_run)
return;
- if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) {
+ if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) {
/* Note that m->kernel_usec.monotonic is always at 0,
* and m->firmware_usec.monotonic and
@@ -2694,12 +2960,9 @@ static void manager_notify_finished(Manager *m) {
}
void manager_check_finished(Manager *m) {
- Unit *u = NULL;
- Iterator i;
-
assert(m);
- if (m->n_reloading > 0)
+ if (MANAGER_IS_RELOADING(m))
return;
/* Verify that we are actually running currently. Initially
@@ -2709,11 +2972,9 @@ void manager_check_finished(Manager *m) {
return;
if (hashmap_size(m->jobs) > 0) {
-
if (m->jobs_in_progress_event_source)
/* Ignore any failure, this is only for feedback */
- (void) sd_event_source_set_time(m->jobs_in_progress_event_source,
- now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
+ (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC);
return;
}
@@ -2721,7 +2982,6 @@ void manager_check_finished(Manager *m) {
manager_flip_auto_status(m, false);
/* Notify Type=idle units that we are done now */
- m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source);
manager_close_idle_pipe(m);
/* Turn off confirm spawn now */
@@ -2740,83 +3000,7 @@ void manager_check_finished(Manager *m) {
manager_notify_finished(m);
- SET_FOREACH(u, m->startup_units, i)
- if (u->cgroup_path)
- cgroup_context_apply(unit_get_cgroup_context(u), unit_get_cgroup_mask(u), u->cgroup_path, manager_state(m));
-}
-
-static int create_generator_dir(Manager *m, char **generator, const char *name) {
- char *p;
- int r;
-
- assert(m);
- assert(generator);
- assert(name);
-
- if (*generator)
- return 0;
-
- if (m->running_as == MANAGER_SYSTEM && getpid() == 1) {
- /* systemd --system, not running --test */
-
- p = strappend("/run/systemd/", name);
- if (!p)
- return log_oom();
-
- r = mkdir_p_label(p, 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create generator directory %s: %m", p);
- free(p);
- return r;
- }
- } else if (m->running_as == MANAGER_USER) {
- const char *s = NULL;
-
- s = getenv("XDG_RUNTIME_DIR");
- if (!s)
- return -EINVAL;
- p = strjoin(s, "/systemd/", name, NULL);
- if (!p)
- return log_oom();
-
- r = mkdir_p_label(p, 0755);
- if (r < 0) {
- log_error_errno(r, "Failed to create generator directory %s: %m", p);
- free(p);
- return r;
- }
- } else {
- /* systemd --system --test */
-
- p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL);
- if (!p)
- return log_oom();
-
- if (!mkdtemp(p)) {
- log_error_errno(errno, "Failed to create generator directory %s: %m",
- p);
- free(p);
- return -errno;
- }
- }
-
- *generator = p;
- return 0;
-}
-
-static void trim_generator_dir(Manager *m, char **generator) {
- assert(m);
- assert(generator);
-
- if (!*generator)
- return;
-
- if (rmdir(*generator) >= 0) {
- free(*generator);
- *generator = NULL;
- }
-
- return;
+ manager_invalidate_startup_units(m);
}
static int manager_run_generators(Manager *m) {
@@ -2830,72 +3014,40 @@ static int manager_run_generators(Manager *m) {
if (m->test_run)
return 0;
- paths = generator_paths(m->running_as);
+ paths = generator_binary_paths(m->unit_file_scope);
if (!paths)
return log_oom();
/* Optimize by skipping the whole process by not creating output directories
* if no generators are found. */
STRV_FOREACH(path, paths) {
- r = access(*path, F_OK);
- if (r == 0)
+ if (access(*path, F_OK) >= 0)
goto found;
if (errno != ENOENT)
log_warning_errno(errno, "Failed to open generator directory %s: %m", *path);
}
+
return 0;
found:
- r = create_generator_dir(m, &m->generator_unit_path, "generator");
- if (r < 0)
- goto finish;
-
- r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early");
- if (r < 0)
- goto finish;
-
- r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late");
+ r = lookup_paths_mkdir_generator(&m->lookup_paths);
if (r < 0)
goto finish;
argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
- argv[1] = m->generator_unit_path;
- argv[2] = m->generator_unit_path_early;
- argv[3] = m->generator_unit_path_late;
+ argv[1] = m->lookup_paths.generator;
+ argv[2] = m->lookup_paths.generator_early;
+ argv[3] = m->lookup_paths.generator_late;
argv[4] = NULL;
RUN_WITH_UMASK(0022)
execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv);
finish:
- trim_generator_dir(m, &m->generator_unit_path);
- trim_generator_dir(m, &m->generator_unit_path_early);
- trim_generator_dir(m, &m->generator_unit_path_late);
+ lookup_paths_trim_generator(&m->lookup_paths);
return r;
}
-static void remove_generator_dir(Manager *m, char **generator) {
- assert(m);
- assert(generator);
-
- if (!*generator)
- return;
-
- strv_remove(m->lookup_paths.unit_path, *generator);
- (void) rm_rf(*generator, REMOVE_ROOT);
-
- free(*generator);
- *generator = NULL;
-}
-
-static void manager_undo_generators(Manager *m) {
- assert(m);
-
- remove_generator_dir(m, &m->generator_unit_path);
- remove_generator_dir(m, &m->generator_unit_path_early);
- remove_generator_dir(m, &m->generator_unit_path_late);
-}
-
int manager_environment_add(Manager *m, char **minus, char **plus) {
char **a = NULL, **b = NULL, **l;
assert(m);
@@ -2940,6 +3092,8 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) {
assert(m);
for (i = 0; i < _RLIMIT_MAX; i++) {
+ m->rlimit[i] = mfree(m->rlimit[i]);
+
if (!default_rlimit[i])
continue;
@@ -2956,7 +3110,7 @@ void manager_recheck_journal(Manager *m) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET);
@@ -2980,21 +3134,24 @@ void manager_set_show_status(Manager *m, ShowStatus mode) {
assert(m);
assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY));
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
+ if (m->show_status != mode)
+ log_debug("%s showing of status.",
+ mode == SHOW_STATUS_NO ? "Disabling" : "Enabling");
m->show_status = mode;
if (mode > 0)
- touch("/run/systemd/show-status");
+ (void) touch("/run/systemd/show-status");
else
- unlink("/run/systemd/show-status");
+ (void) unlink("/run/systemd/show-status");
}
static bool manager_get_show_status(Manager *m, StatusType type) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return false;
if (m->no_console_output)
@@ -3016,15 +3173,17 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
void manager_set_first_boot(Manager *m, bool b) {
assert(m);
- if (m->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(m))
return;
- m->first_boot = b;
+ if (m->first_boot != (int) b) {
+ if (b)
+ (void) touch("/run/systemd/first-boot");
+ else
+ (void) unlink("/run/systemd/first-boot");
+ }
- if (m->first_boot)
- touch("/run/systemd/first-boot");
- else
- unlink("/run/systemd/first-boot");
+ m->first_boot = b;
}
void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) {
@@ -3045,30 +3204,6 @@ void manager_status_printf(Manager *m, StatusType type, const char *status, cons
va_end(ap);
}
-int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found) {
- _cleanup_free_ char *p = NULL;
- Unit *found;
- int r;
-
- assert(m);
- assert(path);
- assert(suffix);
- assert(_found);
-
- r = unit_name_from_path(path, suffix, &p);
- if (r < 0)
- return r;
-
- found = manager_get_unit(m, p);
- if (!found) {
- *_found = NULL;
- return 0;
- }
-
- *_found = found;
- return 1;
-}
-
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
char p[strlen(path)+1];
@@ -3084,13 +3219,14 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
const char *manager_get_runtime_prefix(Manager *m) {
assert(m);
- return m->running_as == MANAGER_SYSTEM ?
+ return MANAGER_IS_SYSTEM(m) ?
"/run" :
getenv("XDG_RUNTIME_DIR");
}
-void manager_update_failed_units(Manager *m, Unit *u, bool failed) {
+int manager_update_failed_units(Manager *m, Unit *u, bool failed) {
unsigned size;
+ int r;
assert(m);
assert(u->manager == m);
@@ -3098,13 +3234,19 @@ void manager_update_failed_units(Manager *m, Unit *u, bool failed) {
size = set_size(m->failed_units);
if (failed) {
+ r = set_ensure_allocated(&m->failed_units, NULL);
+ if (r < 0)
+ return log_oom();
+
if (set_put(m->failed_units, u) < 0)
- log_oom();
+ return log_oom();
} else
- set_remove(m->failed_units, u);
+ (void) set_remove(m->failed_units, u);
if (set_size(m->failed_units) != size)
bus_manager_send_change_signal(m);
+
+ return 0;
}
ManagerState manager_state(Manager *m) {
@@ -3124,18 +3266,18 @@ ManagerState manager_state(Manager *m) {
/* Is the special shutdown target queued? If so, we are in shutdown state */
u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
- if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))
+ if (u && u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))
return MANAGER_STOPPING;
/* Are the rescue or emergency targets active or queued? If so we are in maintenance state */
u = manager_get_unit(m, SPECIAL_RESCUE_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
- (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))))
+ (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
u = manager_get_unit(m, SPECIAL_EMERGENCY_TARGET);
if (u && (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)) ||
- (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_TRY_RESTART, JOB_RELOAD_OR_START))))
+ (u->job && IN_SET(u->job->type, JOB_START, JOB_RESTART, JOB_RELOAD_OR_START))))
return MANAGER_MAINTENANCE;
/* Are there any failed units? If so, we are in degraded mode */
@@ -3145,6 +3287,300 @@ ManagerState manager_state(Manager *m) {
return MANAGER_RUNNING;
}
+#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
+
+static void manager_unref_uid_internal(
+ Manager *m,
+ Hashmap **uid_refs,
+ uid_t uid,
+ bool destroy_now,
+ int (*_clean_ipc)(uid_t uid)) {
+
+ uint32_t c, n;
+
+ assert(m);
+ assert(uid_refs);
+ assert(uid_is_valid(uid));
+ assert(_clean_ipc);
+
+ /* A generic implementation, covering both manager_unref_uid() and manager_unref_gid(), under the assumption
+ * that uid_t and gid_t are actually defined the same way, with the same validity rules.
+ *
+ * We store a hashmap where the UID/GID is they key and the value is a 32bit reference counter, whose highest
+ * bit is used as flag for marking UIDs/GIDs whose IPC objects to remove when the last reference to the UID/GID
+ * is dropped. The flag is set to on, once at least one reference from a unit where RemoveIPC= is set is added
+ * on a UID/GID. It is reset when the UID's/GID's reference counter drops to 0 again. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (uid == 0) /* We don't keep track of root, and will never destroy it */
+ return;
+
+ c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+
+ n = c & ~DESTROY_IPC_FLAG;
+ assert(n > 0);
+ n--;
+
+ if (destroy_now && n == 0) {
+ hashmap_remove(*uid_refs, UID_TO_PTR(uid));
+
+ if (c & DESTROY_IPC_FLAG) {
+ log_debug("%s " UID_FMT " is no longer referenced, cleaning up its IPC.",
+ _clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
+ uid);
+ (void) _clean_ipc(uid);
+ }
+ } else {
+ c = n | (c & DESTROY_IPC_FLAG);
+ assert_se(hashmap_update(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c)) >= 0);
+ }
+}
+
+void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now) {
+ manager_unref_uid_internal(m, &m->uid_refs, uid, destroy_now, clean_ipc_by_uid);
+}
+
+void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now) {
+ manager_unref_uid_internal(m, &m->gid_refs, (uid_t) gid, destroy_now, clean_ipc_by_gid);
+}
+
+static int manager_ref_uid_internal(
+ Manager *m,
+ Hashmap **uid_refs,
+ uid_t uid,
+ bool clean_ipc) {
+
+ uint32_t c, n;
+ int r;
+
+ assert(m);
+ assert(uid_refs);
+ assert(uid_is_valid(uid));
+
+ /* A generic implementation, covering both manager_ref_uid() and manager_ref_gid(), under the assumption
+ * that uid_t and gid_t are actually defined the same way, with the same validity rules. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (uid == 0) /* We don't keep track of root, and will never destroy it */
+ return 0;
+
+ r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+ if (r < 0)
+ return r;
+
+ c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+
+ n = c & ~DESTROY_IPC_FLAG;
+ n++;
+
+ if (n & DESTROY_IPC_FLAG) /* check for overflow */
+ return -EOVERFLOW;
+
+ c = n | (c & DESTROY_IPC_FLAG) | (clean_ipc ? DESTROY_IPC_FLAG : 0);
+
+ return hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+}
+
+int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc) {
+ return manager_ref_uid_internal(m, &m->uid_refs, uid, clean_ipc);
+}
+
+int manager_ref_gid(Manager *m, gid_t gid, bool clean_ipc) {
+ return manager_ref_uid_internal(m, &m->gid_refs, (uid_t) gid, clean_ipc);
+}
+
+static void manager_vacuum_uid_refs_internal(
+ Manager *m,
+ Hashmap **uid_refs,
+ int (*_clean_ipc)(uid_t uid)) {
+
+ Iterator i;
+ void *p, *k;
+
+ assert(m);
+ assert(uid_refs);
+ assert(_clean_ipc);
+
+ HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+ uint32_t c, n;
+ uid_t uid;
+
+ uid = PTR_TO_UID(k);
+ c = PTR_TO_UINT32(p);
+
+ n = c & ~DESTROY_IPC_FLAG;
+ if (n > 0)
+ continue;
+
+ if (c & DESTROY_IPC_FLAG) {
+ log_debug("Found unreferenced %s " UID_FMT " after reload/reexec. Cleaning up.",
+ _clean_ipc == clean_ipc_by_uid ? "UID" : "GID",
+ uid);
+ (void) _clean_ipc(uid);
+ }
+
+ assert_se(hashmap_remove(*uid_refs, k) == p);
+ }
+}
+
+void manager_vacuum_uid_refs(Manager *m) {
+ manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid);
+}
+
+void manager_vacuum_gid_refs(Manager *m) {
+ manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid);
+}
+
+static void manager_serialize_uid_refs_internal(
+ Manager *m,
+ FILE *f,
+ Hashmap **uid_refs,
+ const char *field_name) {
+
+ Iterator i;
+ void *p, *k;
+
+ assert(m);
+ assert(f);
+ assert(uid_refs);
+ assert(field_name);
+
+ /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter
+ * of it is better rebuild after a reload/reexec. */
+
+ HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+ uint32_t c;
+ uid_t uid;
+
+ uid = PTR_TO_UID(k);
+ c = PTR_TO_UINT32(p);
+
+ if (!(c & DESTROY_IPC_FLAG))
+ continue;
+
+ fprintf(f, "%s=" UID_FMT "\n", field_name, uid);
+ }
+}
+
+void manager_serialize_uid_refs(Manager *m, FILE *f) {
+ manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
+}
+
+void manager_serialize_gid_refs(Manager *m, FILE *f) {
+ manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
+}
+
+static void manager_deserialize_uid_refs_one_internal(
+ Manager *m,
+ Hashmap** uid_refs,
+ const char *value) {
+
+ uid_t uid;
+ uint32_t c;
+ int r;
+
+ assert(m);
+ assert(uid_refs);
+ assert(value);
+
+ r = parse_uid(value, &uid);
+ if (r < 0 || uid == 0) {
+ log_debug("Unable to parse UID reference serialization");
+ return;
+ }
+
+ r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+ if (r < 0) {
+ log_oom();
+ return;
+ }
+
+ c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+ if (c & DESTROY_IPC_FLAG)
+ return;
+
+ c |= DESTROY_IPC_FLAG;
+
+ r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+ if (r < 0) {
+ log_debug("Failed to add UID reference entry");
+ return;
+ }
+}
+
+void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
+ manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
+}
+
+void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
+ manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
+}
+
+int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+ struct buffer {
+ uid_t uid;
+ gid_t gid;
+ char unit_name[UNIT_NAME_MAX+1];
+ } _packed_ buffer;
+
+ Manager *m = userdata;
+ ssize_t l;
+ size_t n;
+ Unit *u;
+
+ assert_se(source);
+ assert_se(m);
+
+ /* Invoked whenever a child process succeeded resolving its user/group to use and sent us the resulting UID/GID
+ * in a datagram. We parse the datagram here and pass it off to the unit, so that it can add a reference to the
+ * UID/GID so that it can destroy the UID/GID's IPC objects when the reference counter drops to 0. */
+
+ l = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
+ if (l < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ return log_error_errno(errno, "Failed to read from user lookup fd: %m");
+ }
+
+ if ((size_t) l <= offsetof(struct buffer, unit_name)) {
+ log_warning("Received too short user lookup message, ignoring.");
+ return 0;
+ }
+
+ if ((size_t) l > offsetof(struct buffer, unit_name) + UNIT_NAME_MAX) {
+ log_warning("Received too long user lookup message, ignoring.");
+ return 0;
+ }
+
+ if (!uid_is_valid(buffer.uid) && !gid_is_valid(buffer.gid)) {
+ log_warning("Got user lookup message with invalid UID/GID pair, ignoring.");
+ return 0;
+ }
+
+ n = (size_t) l - offsetof(struct buffer, unit_name);
+ if (memchr(buffer.unit_name, 0, n)) {
+ log_warning("Received lookup message with embedded NUL character, ignoring.");
+ return 0;
+ }
+
+ buffer.unit_name[n] = 0;
+ u = manager_get_unit(m, buffer.unit_name);
+ if (!u) {
+ log_debug("Got user lookup message but unit doesn't exist, ignoring.");
+ return 0;
+ }
+
+ log_unit_debug(u, "User lookup succeeded: uid=" UID_FMT " gid=" GID_FMT, buffer.uid, buffer.gid);
+
+ unit_notify_user_lookup(u, buffer.uid, buffer.gid);
+ return 0;
+}
+
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",
@@ -3155,3 +3591,11 @@ static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState);
+
+static const char *const cad_burst_action_table[_CAD_BURST_ACTION_MAX] = {
+ [CAD_BURST_ACTION_IGNORE] = "ignore",
+ [CAD_BURST_ACTION_REBOOT] = "reboot-force",
+ [CAD_BURST_ACTION_POWEROFF] = "poweroff-force",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(cad_burst_action, CADBurstAction);
diff --git a/src/core/manager.h b/src/core/manager.h
index 4ef869d14a..29fe14e10b 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -21,13 +19,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <libmount.h>
#include <stdbool.h>
#include <stdio.h>
#include "sd-bus.h"
#include "sd-event.h"
-#include "fdset.h"
+
#include "cgroup-util.h"
+#include "fdset.h"
#include "hashmap.h"
#include "list.h"
#include "ratelimit.h"
@@ -62,17 +62,25 @@ typedef enum ManagerExitCode {
_MANAGER_EXIT_CODE_INVALID = -1
} ManagerExitCode;
+typedef enum CADBurstAction {
+ CAD_BURST_ACTION_IGNORE,
+ CAD_BURST_ACTION_REBOOT,
+ CAD_BURST_ACTION_POWEROFF,
+ _CAD_BURST_ACTION_MAX,
+ _CAD_BURST_ACTION_INVALID = -1
+} CADBurstAction;
+
typedef enum StatusType {
STATUS_TYPE_EPHEMERAL,
STATUS_TYPE_NORMAL,
STATUS_TYPE_EMERGENCY,
} StatusType;
+#include "execute.h"
#include "job.h"
#include "path-lookup.h"
-#include "execute.h"
-#include "unit-name.h"
#include "show-status.h"
+#include "unit-name.h"
struct Manager {
/* Note that the set of units we know of is allowed to be
@@ -81,6 +89,7 @@ struct Manager {
/* Active jobs and units */
Hashmap *units; /* name string => Unit object n:1 */
+ Hashmap *units_by_invocation_id;
Hashmap *jobs; /* job id => Job object 1:1 */
/* To make it easy to iterate through the units of a specific
@@ -132,6 +141,9 @@ struct Manager {
int notify_fd;
sd_event_source *notify_event_source;
+ int cgroups_agent_fd;
+ sd_event_source *cgroups_agent_event_source;
+
int signal_fd;
sd_event_source *signal_event_source;
@@ -140,8 +152,10 @@ struct Manager {
sd_event_source *jobs_in_progress_event_source;
- unsigned n_snapshots;
+ int user_lookup_fds[2];
+ sd_event_source *user_lookup_event_source;
+ UnitFileScope unit_file_scope;
LookupPaths lookup_paths;
Set *unit_path_cache;
@@ -164,10 +178,6 @@ struct Manager {
dual_timestamp units_load_start_timestamp;
dual_timestamp units_load_finish_timestamp;
- char *generator_unit_path;
- char *generator_unit_path_early;
- char *generator_unit_path_late;
-
struct udev* udev;
/* Data specific to the device subsystem */
@@ -176,10 +186,8 @@ struct Manager {
Hashmap *devices_by_sysfs;
/* Data specific to the mount subsystem */
- FILE *proc_self_mountinfo;
+ struct libmnt_monitor *mount_monitor;
sd_event_source *mount_event_source;
- int utab_inotify_fd;
- sd_event_source *mount_utab_event_source;
/* Data specific to the swap filesystem */
FILE *proc_swaps;
@@ -215,28 +223,36 @@ struct Manager {
/* Data specific to the cgroup subsystem */
Hashmap *cgroup_unit;
- CGroupControllerMask cgroup_supported;
+ CGroupMask cgroup_supported;
char *cgroup_root;
- int gc_marker;
- unsigned n_in_gc_queue;
+ /* Notifications from cgroups, when the unified hierarchy is
+ * used is done via inotify. */
+ int cgroup_inotify_fd;
+ sd_event_source *cgroup_inotify_event_source;
+ Hashmap *cgroup_inotify_wd_unit;
/* Make sure the user cannot accidentally unmount our cgroup
* file system */
int pin_cgroupfs_fd;
+ int gc_marker;
+ unsigned n_in_gc_queue;
+
/* Flags */
- ManagerRunningAs running_as;
ManagerExitCode exit_code:5;
bool dispatching_load_queue:1;
bool dispatching_dbus_queue:1;
bool taint_usr:1;
- bool first_boot:1;
-
bool test_run:1;
+ /* If non-zero, exit with the following value when the systemd
+ * process terminate. Useful for containers: systemd-nspawn could get
+ * the return value. */
+ uint8_t return_value;
+
ShowStatus show_status;
bool confirm_spawn;
bool no_console_output;
@@ -250,8 +266,11 @@ struct Manager {
bool default_cpu_accounting;
bool default_memory_accounting;
+ bool default_io_accounting;
bool default_blockio_accounting;
+ bool default_tasks_accounting;
+ uint64_t default_tasks_max;
usec_t default_timer_accuracy_usec;
struct rlimit *rlimit[_RLIMIT_MAX];
@@ -284,38 +303,52 @@ struct Manager {
* value where Unit objects are contained. */
Hashmap *units_requiring_mounts_for;
- /* Reference to the kdbus bus control fd */
- int kdbus_fd;
-
/* Used for processing polkit authorization responses */
Hashmap *polkit_registry;
- /* When the user hits C-A-D more than 7 times per 2s, reboot immediately... */
+ /* Dynamic users/groups, indexed by their name */
+ Hashmap *dynamic_users;
+
+ /* Keep track of all UIDs and GIDs any of our services currently use. This is useful for the RemoveIPC= logic. */
+ Hashmap *uid_refs;
+ Hashmap *gid_refs;
+
+ /* When the user hits C-A-D more than 7 times per 2s, do something immediately... */
RateLimit ctrl_alt_del_ratelimit;
+ CADBurstAction cad_burst_action;
const char *unit_log_field;
const char *unit_log_format_string;
+
+ const char *invocation_log_field;
+ const char *invocation_log_format_string;
+
+ int first_boot; /* tri-state */
};
-int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
+#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM)
+#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM)
+
+#define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0)
+
+int manager_new(UnitFileScope scope, bool test_run, Manager **m);
Manager* manager_free(Manager *m);
-int manager_enumerate(Manager *m);
+void manager_enumerate(Manager *m);
int manager_startup(Manager *m, FILE *serialization, FDSet *fds);
Job *manager_get_job(Manager *m, uint32_t id);
Unit *manager_get_unit(Manager *m, const char *name);
-int manager_get_unit_by_path(Manager *m, const char *path, const char *suffix, Unit **_found);
-
int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u);
-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, sd_bus_error *e, Job **_ret);
-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, bool force, sd_bus_error *e, Job **_ret);
+int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret);
+int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **_ret);
+int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret);
void manager_dump_units(Manager *s, FILE *f, const char *prefix);
void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
@@ -329,8 +362,6 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
int manager_loop(Manager *m);
-void manager_dispatch_bus_name_owner_changed(Manager *m, const char *name, const char* old_owner, const char *new_owner);
-
int manager_open_serialization(Manager *m, FILE **_f);
int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root);
@@ -338,8 +369,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
int manager_reload(Manager *m);
-bool manager_is_reloading_or_reexecuting(Manager *m) _pure_;
-
void manager_reset_failed(Manager *m);
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
@@ -363,7 +392,25 @@ const char *manager_get_runtime_prefix(Manager *m);
ManagerState manager_state(Manager *m);
-void manager_update_failed_units(Manager *m, Unit *u, bool failed);
+int manager_update_failed_units(Manager *m, Unit *u, bool failed);
+
+void manager_unref_uid(Manager *m, uid_t uid, bool destroy_now);
+int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc);
+
+void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now);
+int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now);
+
+void manager_vacuum_uid_refs(Manager *m);
+void manager_vacuum_gid_refs(Manager *m);
+
+void manager_serialize_uid_refs(Manager *m, FILE *f);
+void manager_deserialize_uid_refs_one(Manager *m, const char *value);
+
+void manager_serialize_gid_refs(Manager *m, FILE *f);
+void manager_deserialize_gid_refs_one(Manager *m, const char *value);
const char *manager_state_to_string(ManagerState m) _const_;
ManagerState manager_state_from_string(const char *s) _pure_;
+
+const char *cad_burst_action_to_string(CADBurstAction a) _const_;
+CADBurstAction cad_burst_action_from_string(const char *s) _pure_;
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 1782d40720..ca63a93e8b 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,28 +17,32 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/mount.h>
#include <errno.h>
+#include <ftw.h>
#include <stdlib.h>
+#include <sys/mount.h>
#include <unistd.h>
-#include <ftw.h>
-#include "mount-setup.h"
-#include "dev-setup.h"
+#include "alloc-util.h"
#include "bus-util.h"
+#include "cgroup-util.h"
+#include "dev-setup.h"
+#include "efivars.h"
+#include "fs-util.h"
+#include "label.h"
#include "log.h"
#include "macro.h"
-#include "util.h"
-#include "label.h"
-#include "set.h"
-#include "strv.h"
+#include "missing.h"
#include "mkdir.h"
+#include "mount-setup.h"
+#include "mount-util.h"
#include "path-util.h"
-#include "missing.h"
-#include "virt.h"
-#include "efivars.h"
+#include "set.h"
#include "smack-util.h"
-#include "cgroup-util.h"
+#include "strv.h"
+#include "user-util.h"
+#include "util.h"
+#include "virt.h"
typedef enum MountMode {
MNT_NONE = 0,
@@ -93,20 +95,22 @@ static const MountPoint mount_table[] = {
#endif
{ "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ { "cgroup", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
- NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
+ { "cgroup", "/sys/fs/cgroup/systemd", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ cg_is_unified_systemd_controller_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- NULL, MNT_IN_CONTAINER },
+ cg_is_legacy_systemd_controller_wanted, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
- NULL, MNT_FATAL|MNT_IN_CONTAINER },
+ cg_is_legacy_systemd_controller_wanted, MNT_FATAL|MNT_IN_CONTAINER },
{ "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
#ifdef ENABLE_EFI
{ "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
is_efi_boot, MNT_NONE },
#endif
- { "kdbusfs", "/sys/fs/kdbus", "kdbusfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- is_kdbus_wanted, MNT_IN_CONTAINER },
};
/* These are API file systems that might be mounted by other software,
@@ -153,24 +157,26 @@ static int mount_one(const MountPoint *p, bool relabel) {
/* Relabel first, just in case */
if (relabel)
- label_fix(p->where, true, true);
+ (void) label_fix(p->where, true, true);
r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW);
- if (r < 0 && r != -ENOENT)
- return r;
+ if (r < 0 && r != -ENOENT) {
+ log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where);
+ return (p->mode & MNT_FATAL) ? r : 0;
+ }
if (r > 0)
return 0;
/* Skip securityfs in a container */
- if (!(p->mode & MNT_IN_CONTAINER) && detect_container(NULL) > 0)
+ if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0)
return 0;
/* The access mode here doesn't really matter too much, since
* the mounted file system will take precedence anyway. */
if (relabel)
- mkdir_p_label(p->where, 0755);
+ (void) mkdir_p_label(p->where, 0755);
else
- mkdir_p(p->where, 0755);
+ (void) mkdir_p(p->where, 0755);
log_debug("Mounting %s to %s of type %s with options %s.",
p->what,
@@ -183,40 +189,47 @@ static int mount_one(const MountPoint *p, bool relabel) {
p->type,
p->flags,
p->options) < 0) {
- log_full((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, "Failed to mount %s at %s: %m", p->type, p->where);
+ log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where);
return (p->mode & MNT_FATAL) ? -errno : 0;
}
/* Relabel again, since we now mounted something fresh here */
if (relabel)
- label_fix(p->where, false, false);
+ (void) label_fix(p->where, false, false);
return 1;
}
-int mount_setup_early(void) {
+static int mount_points_setup(unsigned n, bool loaded_policy) {
unsigned i;
int r = 0;
- assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
-
- /* Do a minimal mount of /proc and friends to enable the most
- * basic stuff, such as SELinux */
- for (i = 0; i < N_EARLY_MOUNT; i ++) {
+ for (i = 0; i < n; i ++) {
int j;
- j = mount_one(mount_table + i, false);
- if (r == 0)
+ j = mount_one(mount_table + i, loaded_policy);
+ if (j != 0 && r >= 0)
r = j;
}
return r;
}
+int mount_setup_early(void) {
+ assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
+
+ /* Do a minimal mount of /proc and friends to enable the most
+ * basic stuff, such as SELinux */
+ return mount_points_setup(N_EARLY_MOUNT, false);
+}
+
int mount_cgroup_controllers(char ***join_controllers) {
_cleanup_set_free_free_ Set *controllers = NULL;
int r;
+ if (!cg_is_legacy_wanted())
+ return 0;
+
/* Mount all available cgroup controllers that are built into the kernel. */
controllers = set_new(&string_hash_ops);
@@ -296,7 +309,17 @@ int mount_cgroup_controllers(char ***join_controllers) {
return log_oom();
r = symlink(options, t);
- if (r < 0 && errno != EEXIST)
+ if (r >= 0) {
+#ifdef SMACK_RUN_LABEL
+ _cleanup_free_ char *src;
+ src = strappend("/sys/fs/cgroup/", options);
+ if (!src)
+ return log_oom();
+ r = mac_smack_copy(t, src);
+ if (r < 0 && r != -EOPNOTSUPP)
+ return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", src, t);
+#endif
+ } else if (errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink %s: %m", t);
}
}
@@ -334,16 +357,9 @@ static int nftw_cb(
#endif
int mount_setup(bool loaded_policy) {
- unsigned i;
int r = 0;
- for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
- int j;
-
- j = mount_one(mount_table + i, loaded_policy);
- if (r == 0)
- r = j;
- }
+ r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
if (r < 0)
return r;
@@ -360,6 +376,7 @@ int mount_setup(bool loaded_policy) {
before_relabel = now(CLOCK_MONOTONIC);
nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
+ nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
after_relabel = now(CLOCK_MONOTONIC);
@@ -380,7 +397,7 @@ int mount_setup(bool loaded_policy) {
* 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 (detect_container() <= 0)
if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
@@ -389,9 +406,16 @@ int mount_setup(bool loaded_policy) {
* really needs to stay for good, otherwise software that
* copied sd-daemon.c into their sources will misdetect
* systemd. */
- mkdir_label("/run/systemd", 0755);
- mkdir_label("/run/systemd/system", 0755);
- mkdir_label("/run/systemd/inaccessible", 0000);
+ (void) mkdir_label("/run/systemd", 0755);
+ (void) mkdir_label("/run/systemd/system", 0755);
+ (void) mkdir_label("/run/systemd/inaccessible", 0000);
+ /* Set up inaccessible items */
+ (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0);
+ (void) mkdir_label("/run/systemd/inaccessible/dir", 0000);
+ (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0));
+ (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0));
+ (void) mkfifo("/run/systemd/inaccessible/fifo", 0000);
+ (void) mknod("/run/systemd/inaccessible/sock", S_IFSOCK | 0000, 0);
return 0;
}
diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h
index b32fbc5a52..647bd770ae 100644
--- a/src/core/mount-setup.h
+++ b/src/core/mount-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/mount.c b/src/core/mount.c
index 851b41351e..15619dffe3 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,27 +18,33 @@
***/
#include <errno.h>
+#include <signal.h>
#include <stdio.h>
#include <sys/epoll.h>
-#include <signal.h>
-#include <libmount.h>
-#include <sys/inotify.h>
-#include "manager.h"
-#include "unit.h"
-#include "mount.h"
-#include "log.h"
#include "sd-messages.h"
-#include "strv.h"
-#include "mkdir.h"
-#include "path-util.h"
-#include "mount-setup.h"
-#include "unit-name.h"
+
+#include "alloc-util.h"
#include "dbus-mount.h"
-#include "special.h"
+#include "escape.h"
#include "exit-status.h"
-#include "fstab-util.h"
#include "formats-util.h"
+#include "fstab-util.h"
+#include "log.h"
+#include "manager.h"
+#include "mkdir.h"
+#include "mount-setup.h"
+#include "mount-util.h"
+#include "mount.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "process-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit.h"
#define RETRY_UMOUNT_MAX 32
@@ -82,6 +86,15 @@ static bool mount_is_network(const MountParameters *p) {
return mount_needs_network(p->options, p->fstype);
}
+static bool mount_is_loop(const MountParameters *p) {
+ assert(p);
+
+ if (fstab_test_option(p->options, "loop\0"))
+ return true;
+
+ return false;
+}
+
static bool mount_is_bind(const MountParameters *p) {
assert(p);
@@ -100,6 +113,28 @@ static bool mount_is_auto(const MountParameters *p) {
return !fstab_test_option(p->options, "noauto\0");
}
+static bool mount_is_automount(const MountParameters *p) {
+ assert(p);
+
+ return fstab_test_option(p->options,
+ "comment=systemd.automount\0"
+ "x-systemd.automount\0");
+}
+
+static bool mount_state_active(MountState state) {
+ return IN_SET(state,
+ MOUNT_MOUNTING,
+ MOUNT_MOUNTING_DONE,
+ MOUNT_REMOUNTING,
+ MOUNT_UNMOUNTING,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL);
+}
+
static bool needs_quota(const MountParameters *p) {
assert(p);
@@ -146,29 +181,27 @@ static void mount_init(Unit *u) {
u->ignore_on_isolate = true;
}
-static int mount_arm_timer(Mount *m) {
+static int mount_arm_timer(Mount *m, usec_t usec) {
int r;
assert(m);
- if (m->timeout_usec <= 0) {
- m->timer_event_source = sd_event_source_unref(m->timer_event_source);
- return 0;
- }
-
if (m->timer_event_source) {
- r = sd_event_source_set_time(m->timer_event_source, now(CLOCK_MONOTONIC) + m->timeout_usec);
+ r = sd_event_source_set_time(m->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(m->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(m)->manager->event,
&m->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + m->timeout_usec, 0,
+ usec, 0,
mount_dispatch_timer, m);
if (r < 0)
return r;
@@ -203,8 +236,7 @@ static void mount_done(Unit *u) {
assert(m);
- free(m->where);
- m->where = NULL;
+ m->where = mfree(m->where);
mount_parameters_done(&m->parameters_proc_self_mountinfo);
mount_parameters_done(&m->parameters_fragment);
@@ -213,6 +245,8 @@ static void mount_done(Unit *u) {
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
+ dynamic_creds_unref(&m->dynamic_creds);
+
mount_unwatch_control_pid(m);
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
@@ -249,9 +283,10 @@ static int mount_add_mount_links(Mount *m) {
if (!path_equal(m->where, "/")) {
/* Adds in links to other mount points that might lie further
* up in the hierarchy */
- r = path_get_parent(m->where, &parent);
- if (r < 0)
- return r;
+
+ parent = dirname_malloc(m->where);
+ if (!parent)
+ return -ENOMEM;
r = unit_require_mounts_for(UNIT(m), parent);
if (r < 0)
@@ -259,12 +294,12 @@ static int mount_add_mount_links(Mount *m) {
}
/* Adds in links to other mount points that might be needed
- * for the source path (if this is a bind mount) to be
+ * for the source path (if this is a bind mount or a loop mount) to be
* available. */
pm = get_mount_parameters_fragment(m);
if (pm && pm->what &&
path_is_absolute(pm->what) &&
- !mount_is_network(pm)) {
+ (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) {
r = unit_require_mounts_for(UNIT(m), pm->what);
if (r < 0)
@@ -326,10 +361,10 @@ static int mount_add_device_links(Mount *m) {
if (path_equal(m->where, "/"))
return 0;
- if (mount_is_auto(p) && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
+ if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager))
device_wants_mount = true;
- r = unit_add_node_link(UNIT(m), p->what, device_wants_mount);
+ r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES);
if (r < 0)
return r;
@@ -342,7 +377,7 @@ static int mount_add_quota_links(Mount *m) {
assert(m);
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
return 0;
p = get_mount_parameters_fragment(m);
@@ -366,8 +401,8 @@ static int mount_add_quota_links(Mount *m) {
static bool should_umount(Mount *m) {
MountParameters *p;
- if (path_equal(m->where, "/") ||
- path_equal(m->where, "/usr"))
+ if (PATH_IN_SET(m->where, "/", "/usr") ||
+ path_startswith(m->where, "/run/initramfs"))
return false;
p = get_mount_parameters(m);
@@ -379,22 +414,26 @@ static bool should_umount(Mount *m) {
}
static int mount_add_default_dependencies(Mount *m) {
- const char *after, *after2, *online;
MountParameters *p;
+ const char *after;
int r;
assert(m);
- if (UNIT(m)->manager->running_as != MANAGER_SYSTEM)
+ if (!UNIT(m)->default_dependencies)
+ return 0;
+
+ if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
return 0;
- /* We do not add any default dependencies to / and /usr, since
- * they are guaranteed to stay mounted the whole time, since
- * our system is on it. Also, don't bother with anything
- * mounted below virtual file systems, it's also going to be
- * virtual, and hence not worth the effort. */
- if (path_equal(m->where, "/") ||
- path_equal(m->where, "/usr") ||
+ /* We do not add any default dependencies to /, /usr or
+ * /run/initramfs/, since they are guaranteed to stay
+ * mounted the whole time, since our system is on it.
+ * Also, don't bother with anything mounted below virtual
+ * file systems, it's also going to be virtual, and hence
+ * not worth the effort. */
+ if (PATH_IN_SET(m->where, "/", "/usr") ||
+ path_startswith(m->where, "/run/initramfs") ||
path_startswith(m->where, "/proc") ||
path_startswith(m->where, "/sys") ||
path_startswith(m->where, "/dev"))
@@ -405,30 +444,34 @@ static int mount_add_default_dependencies(Mount *m) {
return 0;
if (mount_is_network(p)) {
- after = SPECIAL_REMOTE_FS_PRE_TARGET;
- after2 = SPECIAL_NETWORK_TARGET;
- online = SPECIAL_NETWORK_ONLINE_TARGET;
- } else {
- after = SPECIAL_LOCAL_FS_PRE_TARGET;
- after2 = NULL;
- online = NULL;
- }
-
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
- if (r < 0)
- return r;
+ /* We order ourselves after network.target. This is
+ * primarily useful at shutdown: services that take
+ * down the network should order themselves before
+ * network.target, so that they are shut down only
+ * after this mount unit is stopped. */
- if (after2) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after2, NULL, true);
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true);
if (r < 0)
return r;
- }
- if (online) {
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, online, NULL, true);
+ /* We pull in network-online.target, and order
+ * ourselves after it. This is useful at start-up to
+ * actively pull in tools that want to be started
+ * before we start mounting network file systems, and
+ * whose purpose it is to delay this until the network
+ * is "up". */
+
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true);
if (r < 0)
return r;
- }
+
+ after = SPECIAL_REMOTE_FS_PRE_TARGET;
+ } else
+ after = SPECIAL_LOCAL_FS_PRE_TARGET;
+
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
+ if (r < 0)
+ return r;
if (should_umount(m)) {
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -441,6 +484,7 @@ static int mount_add_default_dependencies(Mount *m) {
static int mount_verify(Mount *m) {
_cleanup_free_ char *e = NULL;
+ MountParameters *p;
int r;
assert(m);
@@ -465,7 +509,8 @@ static int mount_verify(Mount *m) {
return -EINVAL;
}
- if (UNIT(m)->fragment_path && !m->parameters_fragment.what) {
+ p = get_mount_parameters_fragment(m);
+ if (p && !p->what) {
log_unit_error(UNIT(m), "What= setting is missing. Refusing.");
return -EBADMSG;
}
@@ -521,15 +566,13 @@ static int mount_add_extras(Mount *m) {
if (r < 0)
return r;
- r = unit_add_default_slice(u, &m->cgroup_context);
+ r = unit_set_default_slice(u);
if (r < 0)
return r;
- if (u->default_dependencies) {
- r = mount_add_default_dependencies(m);
- if (r < 0)
- return r;
- }
+ r = mount_add_default_dependencies(m);
+ if (r < 0)
+ return r;
return 0;
}
@@ -559,23 +602,6 @@ static int mount_load(Unit *u) {
return mount_verify(m);
}
-static int mount_notify_automount(Mount *m, MountState old_state, MountState state) {
- Unit *p;
- int r;
- Iterator i;
-
- assert(m);
-
- SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i)
- if (p->type == UNIT_AUTOMOUNT) {
- r = automount_update_mount(AUTOMOUNT(p), old_state, state);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
static void mount_set_state(Mount *m, MountState state) {
MountState old_state;
assert(m);
@@ -583,24 +609,13 @@ static void mount_set_state(Mount *m, MountState state) {
old_state = m->state;
m->state = state;
- if (state != MOUNT_MOUNTING &&
- state != MOUNT_MOUNTING_DONE &&
- state != MOUNT_REMOUNTING &&
- state != MOUNT_UNMOUNTING &&
- state != MOUNT_MOUNTING_SIGTERM &&
- state != MOUNT_MOUNTING_SIGKILL &&
- state != MOUNT_UNMOUNTING_SIGTERM &&
- state != MOUNT_UNMOUNTING_SIGKILL &&
- state != MOUNT_REMOUNTING_SIGTERM &&
- state != MOUNT_REMOUNTING_SIGKILL) {
+ if (!mount_state_active(state)) {
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
mount_unwatch_control_pid(m);
m->control_command = NULL;
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
- mount_notify_automount(m, old_state, state);
-
if (state != old_state)
log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state));
@@ -624,29 +639,22 @@ static int mount_coldplug(Unit *u) {
if (new_state == m->state)
return 0;
- if (new_state == MOUNT_MOUNTING ||
- new_state == MOUNT_MOUNTING_DONE ||
- new_state == MOUNT_REMOUNTING ||
- new_state == MOUNT_UNMOUNTING ||
- new_state == MOUNT_MOUNTING_SIGTERM ||
- new_state == MOUNT_MOUNTING_SIGKILL ||
- new_state == MOUNT_UNMOUNTING_SIGTERM ||
- new_state == MOUNT_UNMOUNTING_SIGKILL ||
- new_state == MOUNT_REMOUNTING_SIGTERM ||
- new_state == MOUNT_REMOUNTING_SIGKILL) {
-
- if (m->control_pid <= 0)
- return -EBADMSG;
+ if (m->control_pid > 0 &&
+ pid_is_unwaited(m->control_pid) &&
+ mount_state_active(new_state)) {
r = unit_watch_pid(UNIT(m), m->control_pid);
if (r < 0)
return r;
- r = mount_arm_timer(m);
+ r = mount_arm_timer(m, usec_add(u->state_change_timestamp.monotonic, m->timeout_usec));
if (r < 0)
return r;
}
+ if (!IN_SET(new_state, MOUNT_DEAD, MOUNT_FAILED))
+ (void) unit_setup_dynamic_creds(u);
+
mount_set_state(m, new_state);
return 0;
}
@@ -669,7 +677,10 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
"%sOptions: %s\n"
"%sFrom /proc/self/mountinfo: %s\n"
"%sFrom fragment: %s\n"
- "%sDirectoryMode: %04o\n",
+ "%sDirectoryMode: %04o\n"
+ "%sSloppyOptions: %s\n"
+ "%sLazyUnmount: %s\n"
+ "%sForceUnmount: %s\n",
prefix, mount_state_to_string(m->state),
prefix, mount_result_to_string(m->result),
prefix, m->where,
@@ -678,7 +689,10 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
prefix, p ? strna(p->options) : "n/a",
prefix, yes_no(m->from_proc_self_mountinfo),
prefix, yes_no(m->from_fragment),
- prefix, m->directory_mode);
+ prefix, m->directory_mode,
+ prefix, yes_no(m->sloppy_options),
+ prefix, yes_no(m->lazy_unmount),
+ prefix, yes_no(m->force_unmount));
if (m->control_pid > 0)
fprintf(f,
@@ -693,10 +707,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
- .apply_permissions = true,
- .apply_chroot = true,
- .apply_tty_stdin = true,
- .bus_endpoint_fd = -1,
+ .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(m);
@@ -711,14 +725,18 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
r = unit_setup_exec_runtime(UNIT(m));
if (r < 0)
- goto fail;
+ return r;
- r = mount_arm_timer(m);
+ r = unit_setup_dynamic_creds(UNIT(m));
if (r < 0)
- goto fail;
+ return r;
+
+ r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec));
+ if (r < 0)
+ return r;
exec_params.environment = UNIT(m)->manager->environment;
- exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(m)->cgroup_path;
exec_params.cgroup_delegate = m->cgroup_context.delegate;
@@ -729,43 +747,43 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
&m->exec_context,
&exec_params,
m->exec_runtime,
+ &m->dynamic_creds,
&pid);
if (r < 0)
- goto fail;
+ return r;
r = unit_watch_pid(UNIT(m), pid);
if (r < 0)
/* FIXME: we need to do something here */
- goto fail;
+ return r;
*_pid = pid;
return 0;
-
-fail:
- m->timer_event_source = sd_event_source_unref(m->timer_event_source);
-
- return r;
}
static void mount_enter_dead(Mount *m, MountResult f) {
assert(m);
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
+ mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
+
exec_runtime_destroy(m->exec_runtime);
m->exec_runtime = exec_runtime_unref(m->exec_runtime);
exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager));
- mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
+ unit_unref_uid_gid(UNIT(m), true);
+
+ dynamic_creds_destroy(&m->dynamic_creds);
}
static void mount_enter_mounted(Mount *m, MountResult f) {
assert(m);
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
mount_set_state(m, MOUNT_MOUNTED);
@@ -776,7 +794,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
assert(m);
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
r = unit_kill_context(
@@ -791,7 +809,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
goto fail;
if (r > 0) {
- r = mount_arm_timer(m);
+ r = mount_arm_timer(m, usec_add(now(CLOCK_MONOTONIC), m->timeout_usec));
if (r < 0)
goto fail;
@@ -812,7 +830,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
fail:
log_unit_warning_errno(UNIT(m), r, "Failed to kill processes: %m");
- if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL)
+ if (IN_SET(state, MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL))
mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES);
else
mount_enter_dead(m, MOUNT_FAILURE_RESOURCES);
@@ -834,8 +852,10 @@ static void mount_enter_unmounting(Mount *m) {
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
+ if (r >= 0 && m->lazy_unmount)
+ r = exec_command_append(m->control_command, "-l", NULL);
+ if (r >= 0 && m->force_unmount)
+ r = exec_command_append(m->control_command, "-f", NULL);
if (r < 0)
goto fail;
@@ -876,22 +896,18 @@ static void mount_enter_mounting(Mount *m) {
if (p && mount_is_bind(p))
(void) mkdir_p_label(p->what, m->directory_mode);
- if (m->from_fragment) {
+ if (p) {
_cleanup_free_ char *opts = NULL;
- r = fstab_filter_options(m->parameters_fragment.options,
- "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
+ r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
if (r < 0)
goto fail;
- r = exec_command_set(m->control_command, MOUNT_PATH,
- m->parameters_fragment.what, m->where, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
+ r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
- if (r >= 0 && m->parameters_fragment.fstype)
- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+ if (r >= 0 && p->fstype)
+ r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
if (r >= 0 && !isempty(opts))
r = exec_command_append(m->control_command, "-o", opts, NULL);
} else
@@ -917,29 +933,29 @@ fail:
static void mount_enter_remounting(Mount *m) {
int r;
+ MountParameters *p;
assert(m);
m->control_command_id = MOUNT_EXEC_REMOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT;
- if (m->from_fragment) {
+ p = get_mount_parameters_fragment(m);
+ if (p) {
const char *o;
- if (m->parameters_fragment.options)
- o = strjoina("remount,", m->parameters_fragment.options);
+ if (p->options)
+ o = strjoina("remount,", p->options);
else
o = "remount";
r = exec_command_set(m->control_command, MOUNT_PATH,
- m->parameters_fragment.what, m->where,
+ p->what, m->where,
"-o", o, NULL);
- if (r >= 0 && UNIT(m)->manager->running_as == MANAGER_SYSTEM)
- r = exec_command_append(m->control_command, "-n", NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
- if (r >= 0 && m->parameters_fragment.fstype)
- r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+ if (r >= 0 && p->fstype)
+ r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
} else
r = -ENOENT;
@@ -964,23 +980,35 @@ fail:
static int mount_start(Unit *u) {
Mount *m = MOUNT(u);
+ int r;
assert(m);
/* We cannot fulfill this request right now, try again later
* please! */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
+ if (IN_SET(m->state,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL))
return -EAGAIN;
/* Already on it! */
if (m->state == MOUNT_MOUNTING)
return 0;
- assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED);
+ assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED));
+
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
m->result = MOUNT_SUCCESS;
m->reload_result = MOUNT_SUCCESS;
@@ -996,19 +1024,21 @@ static int mount_stop(Unit *u) {
assert(m);
/* Already on it */
- if (m->state == MOUNT_UNMOUNTING ||
- m->state == MOUNT_UNMOUNTING_SIGKILL ||
- m->state == MOUNT_UNMOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGTERM ||
- m->state == MOUNT_MOUNTING_SIGKILL)
+ if (IN_SET(m->state,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGTERM,
+ MOUNT_MOUNTING_SIGKILL))
return 0;
- assert(m->state == MOUNT_MOUNTING ||
- m->state == MOUNT_MOUNTING_DONE ||
- m->state == MOUNT_MOUNTED ||
- m->state == MOUNT_REMOUNTING ||
- m->state == MOUNT_REMOUNTING_SIGTERM ||
- m->state == MOUNT_REMOUNTING_SIGKILL);
+ assert(IN_SET(m->state,
+ MOUNT_MOUNTING,
+ MOUNT_MOUNTING_DONE,
+ MOUNT_MOUNTED,
+ MOUNT_REMOUNTING,
+ MOUNT_REMOUNTING_SIGTERM,
+ MOUNT_REMOUNTING_SIGKILL));
mount_enter_unmounting(m);
return 1;
@@ -1025,7 +1055,7 @@ static int mount_reload(Unit *u) {
assert(m->state == MOUNT_MOUNTED);
mount_enter_remounting(m);
- return 0;
+ return 1;
}
static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
@@ -1136,7 +1166,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, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = MOUNT_SUCCESS;
else if (code == CLD_EXITED)
f = MOUNT_FAILURE_EXIT_CODE;
@@ -1147,7 +1177,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
assert_not_reached("Unknown code");
- if (f != MOUNT_SUCCESS)
+ if (m->result == MOUNT_SUCCESS)
m->result = f;
if (m->control_command) {
@@ -1174,9 +1204,10 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case MOUNT_MOUNTING_SIGKILL:
case MOUNT_MOUNTING_SIGTERM:
- if (f == MOUNT_SUCCESS)
- mount_enter_mounted(m, f);
- else if (m->from_proc_self_mountinfo)
+ if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo)
+ /* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are
+ * happy. If we see the first condition first, we should see the the second condition
+ * immediately after – or /bin/mount lies to us and is broken. */
mount_enter_mounted(m, f);
else
mount_enter_dead(m, f);
@@ -1382,7 +1413,7 @@ static int mount_setup_unit(
goto fail;
}
- if (m->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(m)) {
const char* target;
target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
@@ -1410,7 +1441,7 @@ static int mount_setup_unit(
}
}
- if (m->running_as == MANAGER_SYSTEM &&
+ if (MANAGER_IS_SYSTEM(m) &&
mount_needs_network(options, fstype)) {
/* _netdev option may have shown up late, or on a
* remount. Add remote-fs dependencies, even though
@@ -1542,92 +1573,91 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
}
static void mount_shutdown(Manager *m) {
+
assert(m);
m->mount_event_source = sd_event_source_unref(m->mount_event_source);
- m->mount_utab_event_source = sd_event_source_unref(m->mount_utab_event_source);
- if (m->proc_self_mountinfo) {
- fclose(m->proc_self_mountinfo);
- m->proc_self_mountinfo = NULL;
- }
- m->utab_inotify_fd = safe_close(m->utab_inotify_fd);
+ mnt_unref_monitor(m->mount_monitor);
+ m->mount_monitor = NULL;
}
-static int mount_get_timeout(Unit *u, uint64_t *timeout) {
+static int mount_get_timeout(Unit *u, usec_t *timeout) {
Mount *m = MOUNT(u);
+ usec_t t;
int r;
if (!m->timer_event_source)
return 0;
- r = sd_event_source_get_time(m->timer_event_source, timeout);
+ r = sd_event_source_get_time(m->timer_event_source, &t);
if (r < 0)
return r;
+ if (t == USEC_INFINITY)
+ return 0;
+ *timeout = t;
return 1;
}
-static int mount_enumerate(Manager *m) {
+static void mount_enumerate(Manager *m) {
int r;
+
assert(m);
mnt_init_debug(0);
- if (!m->proc_self_mountinfo) {
- m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
- if (!m->proc_self_mountinfo)
- return -errno;
+ if (!m->mount_monitor) {
+ int fd;
- r = sd_event_add_io(m->event, &m->mount_event_source, fileno(m->proc_self_mountinfo), EPOLLPRI, mount_dispatch_io, m);
- if (r < 0)
+ m->mount_monitor = mnt_new_monitor();
+ if (!m->mount_monitor) {
+ log_oom();
goto fail;
+ }
- /* Dispatch this before we dispatch SIGCHLD, so that
- * we always get the events from /proc/self/mountinfo
- * before the SIGCHLD of /usr/bin/mount. */
- r = sd_event_source_set_priority(m->mount_event_source, -10);
- if (r < 0)
+ r = mnt_monitor_enable_kernel(m->mount_monitor, 1);
+ if (r < 0) {
+ log_error_errno(r, "Failed to enable watching of kernel mount events: %m");
goto fail;
+ }
- (void) sd_event_source_set_description(m->mount_event_source, "mount-mountinfo-dispatch");
- }
-
- if (m->utab_inotify_fd < 0) {
- m->utab_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
- if (m->utab_inotify_fd < 0) {
- r = -errno;
+ r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to enable watching of userspace mount events: %m");
goto fail;
}
- (void) mkdir_p_label("/run/mount", 0755);
-
- r = inotify_add_watch(m->utab_inotify_fd, "/run/mount", IN_MOVED_TO);
+ /* mnt_unref_monitor() will close the fd */
+ fd = r = mnt_monitor_get_fd(m->mount_monitor);
if (r < 0) {
- r = -errno;
+ log_error_errno(r, "Failed to acquire watch file descriptor: %m");
goto fail;
}
- r = sd_event_add_io(m->event, &m->mount_utab_event_source, m->utab_inotify_fd, EPOLLIN, mount_dispatch_io, m);
- if (r < 0)
+ r = sd_event_add_io(m->event, &m->mount_event_source, fd, EPOLLIN, mount_dispatch_io, m);
+ if (r < 0) {
+ log_error_errno(r, "Failed to watch mount file descriptor: %m");
goto fail;
+ }
- r = sd_event_source_set_priority(m->mount_utab_event_source, -10);
- if (r < 0)
+ r = sd_event_source_set_priority(m->mount_event_source, -10);
+ if (r < 0) {
+ log_error_errno(r, "Failed to adjust mount watch priority: %m");
goto fail;
+ }
- (void) sd_event_source_set_description(m->mount_utab_event_source, "mount-utab-dispatch");
+ (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch");
}
r = mount_load_proc_self_mountinfo(m, false);
if (r < 0)
goto fail;
- return 0;
+ return;
fail:
mount_shutdown(m);
- return r;
}
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
@@ -1639,45 +1669,27 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
int r;
assert(m);
- assert(revents & (EPOLLPRI | EPOLLIN));
-
- /* The manager calls this for every fd event happening on the
- * /proc/self/mountinfo file, which informs us about mounting
- * table changes, and for /run/mount events which we watch
- * for mount options. */
+ assert(revents & EPOLLIN);
- if (fd == m->utab_inotify_fd) {
+ if (fd == mnt_monitor_get_fd(m->mount_monitor)) {
bool rescan = false;
- /* FIXME: We *really* need to replace this with
- * libmount's own API for this, we should not hardcode
- * internal behaviour of libmount here. */
-
- for (;;) {
- union inotify_event_buffer buffer;
- struct inotify_event *e;
- ssize_t l;
-
- l = read(fd, &buffer, sizeof(buffer));
- if (l < 0) {
- if (errno == EAGAIN || errno == EINTR)
- break;
-
- log_error_errno(errno, "Failed to read utab inotify: %m");
- break;
- }
-
- FOREACH_INOTIFY_EVENT(e, buffer, l) {
- /* Only care about changes to utab,
- * but we have to monitor the
- * directory to reliably get
- * notifications about when utab is
- * replaced using rename(2) */
- if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab"))
- rescan = true;
- }
- }
-
+ /* Drain all events and verify that the event is valid.
+ *
+ * Note that libmount also monitors /run/mount mkdir if the
+ * directory does not exist yet. The mkdir may generate event
+ * which is irrelevant for us.
+ *
+ * error: r < 0; valid: r == 0, false positive: rc == 1 */
+ do {
+ r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL);
+ if (r == 0)
+ rescan = true;
+ else if (r < 0)
+ return log_error_errno(r, "Failed to drain libmount events");
+ } while (r == 0);
+
+ log_debug("libmount event [rescan: %s]", yes_no(rescan));
if (!rescan)
return 0;
}
@@ -1722,6 +1734,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
/* This has just been unmounted by
* somebody else, follow the state
* change. */
+ mount->result = MOUNT_SUCCESS; /* make sure we forget any earlier umount failures */
mount_enter_dead(mount, MOUNT_SUCCESS);
break;
@@ -1737,9 +1750,10 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
case MOUNT_DEAD:
case MOUNT_FAILED:
- /* This has just been mounted by
- * somebody else, follow the state
- * change. */
+
+ /* This has just been mounted by somebody else, follow the state change, but let's
+ * generate a new invocation ID for this implicitly and automatically. */
+ (void) unit_acquire_invocation_id(UNIT(mount));
mount_enter_mounted(mount, MOUNT_SUCCESS);
break;
@@ -1798,23 +1812,13 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error);
}
-static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
- [MOUNT_DEAD] = "dead",
- [MOUNT_MOUNTING] = "mounting",
- [MOUNT_MOUNTING_DONE] = "mounting-done",
- [MOUNT_MOUNTED] = "mounted",
- [MOUNT_REMOUNTING] = "remounting",
- [MOUNT_UNMOUNTING] = "unmounting",
- [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm",
- [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill",
- [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm",
- [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill",
- [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm",
- [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill",
- [MOUNT_FAILED] = "failed"
-};
+static int mount_control_pid(Unit *u) {
+ Mount *m = MOUNT(u);
-DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState);
+ assert(m);
+
+ return m->control_pid;
+}
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
[MOUNT_EXEC_MOUNT] = "ExecMount",
@@ -1830,7 +1834,8 @@ static const char* const mount_result_table[_MOUNT_RESULT_MAX] = {
[MOUNT_FAILURE_TIMEOUT] = "timeout",
[MOUNT_FAILURE_EXIT_CODE] = "exit-code",
[MOUNT_FAILURE_SIGNAL] = "signal",
- [MOUNT_FAILURE_CORE_DUMP] = "core-dump"
+ [MOUNT_FAILURE_CORE_DUMP] = "core-dump",
+ [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);
@@ -1841,6 +1846,7 @@ const UnitVTable mount_vtable = {
.cgroup_context_offset = offsetof(Mount, cgroup_context),
.kill_context_offset = offsetof(Mount, kill_context),
.exec_runtime_offset = offsetof(Mount, exec_runtime),
+ .dynamic_creds_offset = offsetof(Mount, dynamic_creds),
.sections =
"Unit\0"
@@ -1848,9 +1854,6 @@ const UnitVTable mount_vtable = {
"Install\0",
.private_section = "Mount",
- .no_alias = true,
- .no_instances = true,
-
.init = mount_init,
.load = mount_load,
.done = mount_done,
@@ -1877,7 +1880,8 @@ const UnitVTable mount_vtable = {
.reset_failed = mount_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Mount",
+ .control_pid = mount_control_pid,
+
.bus_vtable = bus_mount_vtable,
.bus_set_property = bus_mount_set_property,
.bus_commit_properties = bus_mount_commit_properties,
@@ -1897,7 +1901,6 @@ const UnitVTable mount_vtable = {
.finished_start_job = {
[JOB_DONE] = "Mounted %s.",
[JOB_FAILED] = "Failed to mount %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
[JOB_TIMEOUT] = "Timed out mounting %s.",
},
.finished_stop_job = {
diff --git a/src/core/mount.h b/src/core/mount.h
index 280ea0d638..9f7326ba6a 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -24,25 +22,7 @@
typedef struct Mount Mount;
#include "kill.h"
-#include "execute.h"
-
-typedef enum MountState {
- MOUNT_DEAD,
- MOUNT_MOUNTING, /* /usr/bin/mount is running, but the mount is not done yet. */
- MOUNT_MOUNTING_DONE, /* /usr/bin/mount is running, and the mount is done. */
- MOUNT_MOUNTED,
- MOUNT_REMOUNTING,
- MOUNT_UNMOUNTING,
- MOUNT_MOUNTING_SIGTERM,
- MOUNT_MOUNTING_SIGKILL,
- MOUNT_REMOUNTING_SIGTERM,
- MOUNT_REMOUNTING_SIGKILL,
- MOUNT_UNMOUNTING_SIGTERM,
- MOUNT_UNMOUNTING_SIGKILL,
- MOUNT_FAILED,
- _MOUNT_STATE_MAX,
- _MOUNT_STATE_INVALID = -1
-} MountState;
+#include "dynamic-user.h"
typedef enum MountExecCommand {
MOUNT_EXEC_MOUNT,
@@ -59,6 +39,7 @@ typedef enum MountResult {
MOUNT_FAILURE_EXIT_CODE,
MOUNT_FAILURE_SIGNAL,
MOUNT_FAILURE_CORE_DUMP,
+ MOUNT_FAILURE_START_LIMIT_HIT,
_MOUNT_RESULT_MAX,
_MOUNT_RESULT_INVALID = -1
} MountResult;
@@ -90,6 +71,9 @@ struct Mount {
bool sloppy_options;
+ bool lazy_unmount;
+ bool force_unmount;
+
MountResult result;
MountResult reload_result;
@@ -104,6 +88,7 @@ struct Mount {
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
+ DynamicCreds dynamic_creds;
MountState state, deserialized_state;
@@ -120,9 +105,6 @@ extern const UnitVTable mount_vtable;
void mount_fd_event(Manager *m, int events);
-const char* mount_state_to_string(MountState i) _const_;
-MountState mount_state_from_string(const char *s) _pure_;
-
const char* mount_exec_command_to_string(MountExecCommand i) _const_;
MountExecCommand mount_exec_command_from_string(const char *s) _pure_;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 045321e1d4..43a2f4ba6e 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,23 +18,34 @@
***/
#include <errno.h>
-#include <sys/mount.h>
-#include <string.h>
+#include <sched.h>
#include <stdio.h>
-#include <unistd.h>
+#include <string.h>
+#include <sys/mount.h>
#include <sys/stat.h>
-#include <sched.h>
+#include <unistd.h>
#include <linux/fs.h>
-#include "strv.h"
-#include "util.h"
-#include "path-util.h"
-#include "missing.h"
-#include "loopback-setup.h"
+#include "alloc-util.h"
#include "dev-setup.h"
-#include "selinux-util.h"
-#include "namespace.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "loopback-setup.h"
+#include "missing.h"
#include "mkdir.h"
+#include "mount-util.h"
+#include "namespace.h"
+#include "path-util.h"
+#include "selinux-util.h"
+#include "socket-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "umask-util.h"
+#include "user-util.h"
+#include "util.h"
+
+#define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC)
typedef enum MountMode {
/* This is ordered by priority! */
@@ -45,62 +54,230 @@ typedef enum MountMode {
PRIVATE_TMP,
PRIVATE_VAR_TMP,
PRIVATE_DEV,
- PRIVATE_BUS_ENDPOINT,
- READWRITE
+ READWRITE,
} MountMode;
typedef struct BindMount {
- const char *path;
+ const char *path; /* stack memory, doesn't need to be freed explicitly */
+ char *chased; /* malloc()ed memory, needs to be freed */
MountMode mode;
- bool done;
- bool ignore;
+ bool ignore; /* Ignore if path does not exist */
} BindMount;
+typedef struct TargetMount {
+ const char *path;
+ MountMode mode;
+ bool ignore; /* Ignore if path does not exist */
+} TargetMount;
+
+/*
+ * The following Protect tables are to protect paths and mark some of them
+ * READONLY, in case a path is covered by an option from another table, then
+ * it is marked READWRITE in the current one, and the more restrictive mode is
+ * applied from that other table. This way all options can be combined in a
+ * safe and comprehensible way for users.
+ */
+
+/* ProtectKernelTunables= option and the related filesystem APIs */
+static const TargetMount protect_kernel_tunables_table[] = {
+ { "/proc/sys", READONLY, false },
+ { "/proc/sysrq-trigger", READONLY, true },
+ { "/proc/latency_stats", READONLY, true },
+ { "/proc/mtrr", READONLY, true },
+ { "/proc/apm", READONLY, true },
+ { "/proc/acpi", READONLY, true },
+ { "/proc/timer_stats", READONLY, true },
+ { "/proc/asound", READONLY, true },
+ { "/proc/bus", READONLY, true },
+ { "/proc/fs", READONLY, true },
+ { "/proc/irq", READONLY, true },
+ { "/sys", READONLY, false },
+ { "/sys/kernel/debug", READONLY, true },
+ { "/sys/kernel/tracing", READONLY, true },
+ { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */
+};
+
+/*
+ * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of
+ * system should be protected by ProtectSystem=
+ */
+static const TargetMount protect_home_read_only_table[] = {
+ { "/home", READONLY, true },
+ { "/run/user", READONLY, true },
+ { "/root", READONLY, true },
+};
+
+/* ProtectHome=yes table */
+static const TargetMount protect_home_yes_table[] = {
+ { "/home", INACCESSIBLE, true },
+ { "/run/user", INACCESSIBLE, true },
+ { "/root", INACCESSIBLE, true },
+};
+
+/* ProtectSystem=yes table */
+static const TargetMount protect_system_yes_table[] = {
+ { "/usr", READONLY, false },
+ { "/boot", READONLY, true },
+ { "/efi", READONLY, true },
+};
+
+/* ProtectSystem=full includes ProtectSystem=yes */
+static const TargetMount protect_system_full_table[] = {
+ { "/usr", READONLY, false },
+ { "/boot", READONLY, true },
+ { "/efi", READONLY, true },
+ { "/etc", READONLY, false },
+};
+
+/*
+ * ProtectSystem=strict table. In this strict mode, we mount everything
+ * read-only, except for /proc, /dev, /sys which are the kernel API VFS,
+ * which are left writable, but PrivateDevices= + ProtectKernelTunables=
+ * protect those, and these options should be fully orthogonal.
+ * (And of course /home and friends are also left writable, as ProtectHome=
+ * shall manage those, orthogonally).
+ */
+static const TargetMount protect_system_strict_table[] = {
+ { "/", READONLY, false },
+ { "/proc", READWRITE, false }, /* ProtectKernelTunables= */
+ { "/sys", READWRITE, false }, /* ProtectKernelTunables= */
+ { "/dev", READWRITE, false }, /* PrivateDevices= */
+ { "/home", READWRITE, true }, /* ProtectHome= */
+ { "/run/user", READWRITE, true }, /* ProtectHome= */
+ { "/root", READWRITE, true }, /* ProtectHome= */
+};
+
+static void set_bind_mount(BindMount **p, const char *path, MountMode mode, bool ignore) {
+ (*p)->path = path;
+ (*p)->mode = mode;
+ (*p)->ignore = ignore;
+}
+
static int append_mounts(BindMount **p, char **strv, MountMode mode) {
char **i;
assert(p);
STRV_FOREACH(i, strv) {
+ bool ignore = false;
- (*p)->ignore = false;
- (*p)->done = false;
-
- if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') {
- (*p)->ignore = true;
+ if (IN_SET(mode, INACCESSIBLE, READONLY, READWRITE) && startswith(*i, "-")) {
(*i)++;
+ ignore = true;
}
if (!path_is_absolute(*i))
return -EINVAL;
- (*p)->path = *i;
- (*p)->mode = mode;
+ set_bind_mount(p, *i, mode, ignore);
(*p)++;
}
return 0;
}
-static int mount_path_compare(const void *a, const void *b) {
- const BindMount *p = a, *q = b;
- int d;
+static int append_target_mounts(BindMount **p, const char *root_directory, const TargetMount *mounts, const size_t size) {
+ unsigned i;
- d = path_compare(p->path, q->path);
+ assert(p);
+ assert(mounts);
+
+ for (i = 0; i < size; i++) {
+ /*
+ * Here we assume that the ignore field is set during
+ * declaration we do not support "-" at the beginning.
+ */
+ const TargetMount *m = &mounts[i];
+ const char *path = prefix_roota(root_directory, m->path);
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ set_bind_mount(p, path, m->mode, m->ignore);
+ (*p)++;
+ }
+
+ return 0;
+}
+
+static int append_protect_kernel_tunables(BindMount **p, const char *root_directory) {
+ assert(p);
+
+ return append_target_mounts(p, root_directory, protect_kernel_tunables_table,
+ ELEMENTSOF(protect_kernel_tunables_table));
+}
+
+static int append_protect_home(BindMount **p, const char *root_directory, ProtectHome protect_home) {
+ int r = 0;
+
+ assert(p);
+
+ if (protect_home == PROTECT_HOME_NO)
+ return 0;
+
+ switch (protect_home) {
+ case PROTECT_HOME_READ_ONLY:
+ r = append_target_mounts(p, root_directory, protect_home_read_only_table,
+ ELEMENTSOF(protect_home_read_only_table));
+ break;
+ case PROTECT_HOME_YES:
+ r = append_target_mounts(p, root_directory, protect_home_yes_table,
+ ELEMENTSOF(protect_home_yes_table));
+ break;
+ default:
+ r = -EINVAL;
+ break;
+ }
+
+ return r;
+}
- if (d == 0) {
- /* If the paths are equal, check the mode */
- if (p->mode < q->mode)
- return -1;
+static int append_protect_system(BindMount **p, const char *root_directory, ProtectSystem protect_system) {
+ int r = 0;
- if (p->mode > q->mode)
- return 1;
+ assert(p);
+ if (protect_system == PROTECT_SYSTEM_NO)
return 0;
+
+ switch (protect_system) {
+ case PROTECT_SYSTEM_STRICT:
+ r = append_target_mounts(p, root_directory, protect_system_strict_table,
+ ELEMENTSOF(protect_system_strict_table));
+ break;
+ case PROTECT_SYSTEM_YES:
+ r = append_target_mounts(p, root_directory, protect_system_yes_table,
+ ELEMENTSOF(protect_system_yes_table));
+ break;
+ case PROTECT_SYSTEM_FULL:
+ r = append_target_mounts(p, root_directory, protect_system_full_table,
+ ELEMENTSOF(protect_system_full_table));
+ break;
+ default:
+ r = -EINVAL;
+ break;
}
+ return r;
+}
+
+static int mount_path_compare(const void *a, const void *b) {
+ const BindMount *p = a, *q = b;
+ int d;
+
/* If the paths are not equal, then order prefixes first */
- return d;
+ d = path_compare(p->path, q->path);
+ if (d != 0)
+ return d;
+
+ /* If the paths are equal, check the mode */
+ if (p->mode < q->mode)
+ return -1;
+
+ if (p->mode > q->mode)
+ return 1;
+
+ return 0;
}
static void drop_duplicates(BindMount *m, unsigned *n) {
@@ -109,16 +286,110 @@ static void drop_duplicates(BindMount *m, unsigned *n) {
assert(m);
assert(n);
+ /* Drops duplicate entries. Expects that the array is properly ordered already. */
+
for (f = m, t = m, previous = NULL; f < m+*n; f++) {
- /* The first one wins */
- if (previous && path_equal(f->path, previous->path))
+ /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
+ * above. */
+ if (previous && path_equal(f->path, previous->path)) {
+ log_debug("%s is duplicate.", f->path);
continue;
+ }
*t = *f;
-
previous = t;
+ t++;
+ }
+
+ *n = t - m;
+}
+
+static void drop_inaccessible(BindMount *m, unsigned *n) {
+ BindMount *f, *t;
+ const char *clear = NULL;
+
+ assert(m);
+ assert(n);
+
+ /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly
+ * ordered already. */
+
+ for (f = m, t = m; f < m+*n; f++) {
+
+ /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop
+ * it, as inaccessible paths really should drop the entire subtree. */
+ if (clear && path_startswith(f->path, clear)) {
+ log_debug("%s is masked by %s.", f->path, clear);
+ continue;
+ }
+
+ clear = f->mode == INACCESSIBLE ? f->path : NULL;
+
+ *t = *f;
+ t++;
+ }
+
+ *n = t - m;
+}
+
+static void drop_nop(BindMount *m, unsigned *n) {
+ BindMount *f, *t;
+
+ assert(m);
+ assert(n);
+
+ /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the
+ * list is ordered by prefixes. */
+
+ for (f = m, t = m; f < m+*n; f++) {
+
+ /* Only suppress such subtrees for READONLY and READWRITE entries */
+ if (IN_SET(f->mode, READONLY, READWRITE)) {
+ BindMount *p;
+ bool found = false;
+
+ /* Now let's find the first parent of the entry we are looking at. */
+ for (p = t-1; p >= m; p--) {
+ if (path_startswith(f->path, p->path)) {
+ found = true;
+ break;
+ }
+ }
+
+ /* We found it, let's see if it's the same mode, if so, we can drop this entry */
+ if (found && p->mode == f->mode) {
+ log_debug("%s is redundant by %s", f->path, p->path);
+ continue;
+ }
+ }
+
+ *t = *f;
+ t++;
+ }
+
+ *n = t - m;
+}
+
+static void drop_outside_root(const char *root_directory, BindMount *m, unsigned *n) {
+ BindMount *f, *t;
+
+ assert(m);
+ assert(n);
+
+ if (!root_directory)
+ return;
+
+ /* Drops all mounts that are outside of the root directory. */
+ for (f = m, t = m; f < m+*n; f++) {
+
+ if (!path_startswith(f->path, root_directory)) {
+ log_debug("%s is outside of root directory.", f->path);
+ continue;
+ }
+
+ *t = *f;
t++;
}
@@ -148,7 +419,7 @@ static int mount_dev(BindMount *m) {
dev = strjoina(temporary_mount, "/dev");
(void) mkdir(dev, 0755);
- if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) {
+ if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) {
r = -errno;
goto fail;
}
@@ -232,6 +503,8 @@ static int mount_dev(BindMount *m) {
*/
(void) mkdir_p_label(m->path, 0755);
+ /* Unmount everything in old /dev */
+ umount_recursive(m->path, 0);
if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) {
r = -errno;
goto fail;
@@ -262,79 +535,6 @@ fail:
return r;
}
-static int mount_kdbus(BindMount *m) {
-
- char temporary_mount[] = "/tmp/kdbus-dev-XXXXXX";
- _cleanup_free_ char *basepath = NULL;
- _cleanup_umask_ mode_t u;
- char *busnode = NULL, *root;
- struct stat st;
- int r;
-
- assert(m);
-
- u = umask(0000);
-
- if (!mkdtemp(temporary_mount))
- return log_error_errno(errno, "Failed create temp dir: %m");
-
- root = strjoina(temporary_mount, "/kdbus");
- (void) mkdir(root, 0755);
- if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=777") < 0) {
- r = -errno;
- goto fail;
- }
-
- /* create a new /dev/null dev node copy so we have some fodder to
- * bind-mount the custom endpoint over. */
- if (stat("/dev/null", &st) < 0) {
- log_error_errno(errno, "Failed to stat /dev/null: %m");
- r = -errno;
- goto fail;
- }
-
- busnode = strjoina(root, "/bus");
- if (mknod(busnode, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) {
- log_error_errno(errno, "mknod() for %s failed: %m", busnode);
- r = -errno;
- goto fail;
- }
-
- r = mount(m->path, busnode, NULL, MS_BIND, NULL);
- if (r < 0) {
- log_error_errno(errno, "bind mount of %s failed: %m", m->path);
- r = -errno;
- goto fail;
- }
-
- basepath = dirname_malloc(m->path);
- if (!basepath) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (mount(root, basepath, NULL, MS_MOVE, NULL) < 0) {
- log_error_errno(errno, "bind mount of %s failed: %m", basepath);
- r = -errno;
- goto fail;
- }
-
- rmdir(temporary_mount);
- return 0;
-
-fail:
- if (busnode) {
- umount(busnode);
- unlink(busnode);
- }
-
- umount(root);
- rmdir(root);
- rmdir(temporary_mount);
-
- return r;
-}
-
static int apply_mount(
BindMount *m,
const char *tmp_dir,
@@ -345,23 +545,41 @@ static int apply_mount(
assert(m);
+ log_debug("Applying namespace mount on %s", m->path);
+
switch (m->mode) {
- case INACCESSIBLE:
+ case INACCESSIBLE: {
+ struct stat target;
/* First, get rid of everything that is below if there
* is anything... Then, overmount it with an
- * inaccessible directory. */
- umount_recursive(m->path, 0);
+ * inaccessible path. */
+ (void) umount_recursive(m->path, 0);
- what = "/run/systemd/inaccessible";
+ if (lstat(m->path, &target) < 0)
+ return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", m->path);
+
+ what = mode_to_inaccessible_node(target.st_mode);
+ if (!what) {
+ log_debug("File type not supported for inaccessible mounts. Note that symlinks are not allowed");
+ return -ELOOP;
+ }
break;
+ }
case READONLY:
case READWRITE:
- /* Nothing to mount here, we just later toggle the
- * MS_RDONLY bit for the mount point */
- return 0;
+
+ r = path_is_mount_point(m->path, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", m->path);
+ if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
+ return 0;
+
+ /* This isn't a mount point yet, let's make it one. */
+ what = m->path;
+ break;
case PRIVATE_TMP:
what = tmp_dir;
@@ -374,85 +592,155 @@ static int apply_mount(
case PRIVATE_DEV:
return mount_dev(m);
- case PRIVATE_BUS_ENDPOINT:
- return mount_kdbus(m);
-
default:
assert_not_reached("Unknown mode");
}
assert(what);
- r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
- if (r >= 0)
- log_debug("Successfully mounted %s to %s", what, m->path);
- else if (m->ignore && errno == ENOENT)
+ if (mount(what, m->path, NULL, MS_BIND|MS_REC, NULL) < 0)
+ return log_debug_errno(errno, "Failed to mount %s to %s: %m", what, m->path);
+
+ log_debug("Successfully mounted %s to %s", what, m->path);
+ return 0;
+}
+
+static int make_read_only(BindMount *m, char **blacklist) {
+ int r = 0;
+
+ assert(m);
+
+ if (IN_SET(m->mode, INACCESSIBLE, READONLY))
+ r = bind_remount_recursive(m->path, true, blacklist);
+ else if (m->mode == PRIVATE_DEV) { /* Can be readonly but the submounts can't*/
+ if (mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0)
+ r = -errno;
+ } else
return 0;
+ /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked read-only
+ * already stays this way. This improves compatibility with container managers, where we won't attempt to undo
+ * read-only mounts already applied. */
+
return r;
}
-static int make_read_only(BindMount *m) {
+static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) {
+ BindMount *f, *t;
int r;
assert(m);
+ assert(n);
- if (IN_SET(m->mode, INACCESSIBLE, READONLY))
- r = bind_remount_recursive(m->path, true);
- else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV))
- r = bind_remount_recursive(m->path, false);
- else
- r = 0;
+ /* Since mount() will always follow symlinks and we need to take the different root directory into account we
+ * chase the symlinks on our own first. This call wil do so for all entries and remove all entries where we
+ * can't resolve the path, and which have been marked for such removal. */
- if (m->ignore && r == -ENOENT)
- return 0;
+ for (f = m, t = m; f < m+*n; f++) {
- return r;
+ r = chase_symlinks(f->path, root_directory, &f->chased);
+ if (r == -ENOENT && f->ignore) /* Doesn't exist? Then remove it! */
+ continue;
+ if (r < 0)
+ return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path);
+
+ if (path_equal(f->path, f->chased))
+ f->chased = mfree(f->chased);
+ else {
+ log_debug("Chased %s → %s", f->path, f->chased);
+ f->path = f->chased;
+ }
+
+ *t = *f;
+ t++;
+ }
+
+ *n = t - m;
+ return 0;
+}
+
+static unsigned namespace_calculate_mounts(
+ char** read_write_paths,
+ char** read_only_paths,
+ char** inaccessible_paths,
+ const char* tmp_dir,
+ const char* var_tmp_dir,
+ bool private_dev,
+ bool protect_sysctl,
+ bool protect_cgroups,
+ ProtectHome protect_home,
+ ProtectSystem protect_system) {
+
+ unsigned protect_home_cnt;
+ unsigned protect_system_cnt =
+ (protect_system == PROTECT_SYSTEM_STRICT ?
+ ELEMENTSOF(protect_system_strict_table) :
+ ((protect_system == PROTECT_SYSTEM_FULL) ?
+ ELEMENTSOF(protect_system_full_table) :
+ ((protect_system == PROTECT_SYSTEM_YES) ?
+ ELEMENTSOF(protect_system_yes_table) : 0)));
+
+ protect_home_cnt =
+ (protect_home == PROTECT_HOME_YES ?
+ ELEMENTSOF(protect_home_yes_table) :
+ ((protect_home == PROTECT_HOME_READ_ONLY) ?
+ ELEMENTSOF(protect_home_read_only_table) : 0));
+
+ return !!tmp_dir + !!var_tmp_dir +
+ strv_length(read_write_paths) +
+ strv_length(read_only_paths) +
+ strv_length(inaccessible_paths) +
+ private_dev +
+ (protect_sysctl ? ELEMENTSOF(protect_kernel_tunables_table) : 0) +
+ (protect_cgroups ? 1 : 0) +
+ protect_home_cnt + protect_system_cnt;
}
int setup_namespace(
const char* root_directory,
- char** read_write_dirs,
- char** read_only_dirs,
- char** inaccessible_dirs,
+ char** read_write_paths,
+ char** read_only_paths,
+ char** inaccessible_paths,
const char* tmp_dir,
const char* var_tmp_dir,
- const char* bus_endpoint_path,
bool private_dev,
+ bool protect_sysctl,
+ bool protect_cgroups,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags) {
BindMount *m, *mounts = NULL;
+ bool make_slave = false;
unsigned n;
int r = 0;
if (mount_flags == 0)
mount_flags = MS_SHARED;
- if (unshare(CLONE_NEWNS) < 0)
- return -errno;
+ n = namespace_calculate_mounts(read_write_paths,
+ read_only_paths,
+ inaccessible_paths,
+ tmp_dir, var_tmp_dir,
+ private_dev, protect_sysctl,
+ protect_cgroups, protect_home,
+ protect_system);
- n = !!tmp_dir + !!var_tmp_dir + !!bus_endpoint_path +
- strv_length(read_write_dirs) +
- strv_length(read_only_dirs) +
- strv_length(inaccessible_dirs) +
- private_dev +
- (protect_home != PROTECT_HOME_NO ? 3 : 0) +
- (protect_system != PROTECT_SYSTEM_NO ? 2 : 0) +
- (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0);
+ /* Set mount slave mode */
+ if (root_directory || n > 0)
+ make_slave = true;
if (n > 0) {
m = mounts = (BindMount *) alloca0(n * sizeof(BindMount));
- r = append_mounts(&m, read_write_dirs, READWRITE);
+ r = append_mounts(&m, read_write_paths, READWRITE);
if (r < 0)
return r;
- r = append_mounts(&m, read_only_dirs, READONLY);
+ r = append_mounts(&m, read_only_paths, READONLY);
if (r < 0)
return r;
- r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE);
+ r = append_mounts(&m, inaccessible_paths, INACCESSIBLE);
if (r < 0)
return r;
@@ -474,101 +762,112 @@ int setup_namespace(
m++;
}
- if (bus_endpoint_path) {
- m->path = prefix_roota(root_directory, bus_endpoint_path);
- m->mode = PRIVATE_BUS_ENDPOINT;
- m++;
- }
-
- if (protect_home != PROTECT_HOME_NO) {
- const char *home_dir, *run_user_dir, *root_dir;
+ if (protect_sysctl)
+ append_protect_kernel_tunables(&m, root_directory);
- home_dir = prefix_roota(root_directory, "/home");
- home_dir = strjoina("-", home_dir);
- run_user_dir = prefix_roota(root_directory, "/run/user");
- run_user_dir = strjoina("-", run_user_dir);
- root_dir = prefix_roota(root_directory, "/root");
- root_dir = strjoina("-", root_dir);
-
- r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir),
- protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
- if (r < 0)
- return r;
+ if (protect_cgroups) {
+ m->path = prefix_roota(root_directory, "/sys/fs/cgroup");
+ m->mode = READONLY;
+ m++;
}
- if (protect_system != PROTECT_SYSTEM_NO) {
- const char *usr_dir, *boot_dir, *etc_dir;
-
- usr_dir = prefix_roota(root_directory, "/usr");
- boot_dir = prefix_roota(root_directory, "/boot");
- boot_dir = strjoina("-", boot_dir);
- etc_dir = prefix_roota(root_directory, "/etc");
+ r = append_protect_home(&m, root_directory, protect_home);
+ if (r < 0)
+ return r;
- r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL
- ? STRV_MAKE(usr_dir, boot_dir, etc_dir)
- : STRV_MAKE(usr_dir, boot_dir), READONLY);
- if (r < 0)
- return r;
- }
+ r = append_protect_system(&m, root_directory, protect_system);
+ if (r < 0)
+ return r;
assert(mounts + n == m);
+ /* Resolve symlinks manually first, as mount() will always follow them relative to the host's
+ * root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit
+ * racy. */
+ r = chase_all_symlinks(root_directory, mounts, &n);
+ if (r < 0)
+ goto finish;
+
qsort(mounts, n, sizeof(BindMount), mount_path_compare);
+
drop_duplicates(mounts, &n);
+ drop_outside_root(root_directory, mounts, &n);
+ drop_inaccessible(mounts, &n);
+ drop_nop(mounts, &n);
+ }
+
+ if (unshare(CLONE_NEWNS) < 0) {
+ r = -errno;
+ goto finish;
}
- if (n > 0 || root_directory) {
+ if (make_slave) {
/* 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)
- return -errno;
+ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
+ r = -errno;
+ goto finish;
+ }
}
if (root_directory) {
- /* Turn directory into bind mount */
- if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0)
- return -errno;
+ /* Turn directory into bind mount, if it isn't one yet */
+ r = path_is_mount_point(root_directory, AT_SYMLINK_FOLLOW);
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
+ if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0) {
+ r = -errno;
+ goto finish;
+ }
+ }
}
if (n > 0) {
+ char **blacklist;
+ unsigned j;
+
+ /* First round, add in all special mounts we need */
for (m = mounts; m < mounts + n; ++m) {
r = apply_mount(m, tmp_dir, var_tmp_dir);
if (r < 0)
- goto fail;
+ goto finish;
}
+ /* Create a blacklist we can pass to bind_mount_recursive() */
+ blacklist = newa(char*, n+1);
+ for (j = 0; j < n; j++)
+ blacklist[j] = (char*) mounts[j].path;
+ blacklist[j] = NULL;
+
+ /* Second round, flip the ro bits if necessary. */
for (m = mounts; m < mounts + n; ++m) {
- r = make_read_only(m);
+ r = make_read_only(m, blacklist);
if (r < 0)
- goto fail;
+ goto finish;
}
}
if (root_directory) {
/* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
r = mount_move_root(root_directory);
-
- /* at this point, we cannot rollback */
if (r < 0)
- return r;
+ goto finish;
}
/* Remount / as the desired mode. Not that this will not
* reestablish propagation from our side to the host, since
* what's disconnected is disconnected. */
if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
- /* at this point, we cannot rollback */
- return -errno;
+ r = -errno;
+ goto finish;
}
- return 0;
+ r = 0;
-fail:
- if (n > 0) {
- for (m = mounts; m < mounts + n; ++m)
- if (m->done)
- (void) umount2(m->path, MNT_DETACH);
- }
+finish:
+ for (m = mounts; m < mounts + n; m++)
+ free(m->chased);
return r;
}
@@ -645,16 +944,7 @@ int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
int setup_netns(int netns_storage_socket[2]) {
_cleanup_close_ int netns = -1;
- union {
- struct cmsghdr cmsghdr;
- uint8_t buf[CMSG_SPACE(sizeof(int))];
- } control = {};
- struct msghdr mh = {
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- int r;
+ int r, q;
assert(netns_storage_socket);
assert(netns_storage_socket[0] >= 0);
@@ -671,12 +961,8 @@ int setup_netns(int netns_storage_socket[2]) {
if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
return -errno;
- if (recvmsg(netns_storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) < 0) {
- if (errno != EAGAIN) {
- r = -errno;
- goto fail;
- }
-
+ netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT);
+ if (netns == -EAGAIN) {
/* Nothing stored yet, so let's create a new namespace */
if (unshare(CLONE_NEWNET) < 0) {
@@ -693,15 +979,13 @@ int setup_netns(int netns_storage_socket[2]) {
}
r = 1;
- } else {
- /* Yay, found something, so let's join the namespace */
- CMSG_FOREACH(cmsg, &mh)
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
- assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
- netns = *(int*) CMSG_DATA(cmsg);
- }
+ } else if (netns < 0) {
+ r = netns;
+ goto fail;
+ } else {
+ /* Yay, found something, so let's join the namespace */
if (setns(netns, CLONE_NEWNET) < 0) {
r = -errno;
goto fail;
@@ -710,21 +994,14 @@ int setup_netns(int netns_storage_socket[2]) {
r = 0;
}
- cmsg = CMSG_FIRSTHDR(&mh);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- memcpy(CMSG_DATA(cmsg), &netns, sizeof(int));
- mh.msg_controllen = cmsg->cmsg_len;
-
- if (sendmsg(netns_storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL) < 0) {
- r = -errno;
+ q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT);
+ if (q < 0) {
+ r = q;
goto fail;
}
fail:
- lockf(netns_storage_socket[0], F_ULOCK, 0);
-
+ (void) lockf(netns_storage_socket[0], F_ULOCK, 0);
return r;
}
@@ -740,6 +1017,7 @@ static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
[PROTECT_SYSTEM_NO] = "no",
[PROTECT_SYSTEM_YES] = "yes",
[PROTECT_SYSTEM_FULL] = "full",
+ [PROTECT_SYSTEM_STRICT] = "strict",
};
DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);
diff --git a/src/core/namespace.h b/src/core/namespace.h
index 00ab22bf2e..6505bcc499 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -37,18 +35,20 @@ typedef enum ProtectSystem {
PROTECT_SYSTEM_NO,
PROTECT_SYSTEM_YES,
PROTECT_SYSTEM_FULL,
+ PROTECT_SYSTEM_STRICT,
_PROTECT_SYSTEM_MAX,
_PROTECT_SYSTEM_INVALID = -1
} ProtectSystem;
int setup_namespace(const char *chroot,
- char **read_write_dirs,
- char **read_only_dirs,
- char **inaccessible_dirs,
+ char **read_write_paths,
+ char **read_only_paths,
+ char **inaccessible_paths,
const char *tmp_dir,
const char *var_tmp_dir,
- const char *endpoint_path,
bool private_dev,
+ bool protect_sysctl,
+ bool protect_cgroups,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags);
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index 6a7a37ee92..6caa15b0b8 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -54,6 +54,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetUnitByInvocationID"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="LoadUnit"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -70,14 +74,26 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitsByPatterns"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitFilesByPatterns"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="GetUnitFileState"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetUnitProcesses"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListJobs"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -96,6 +112,14 @@
send_interface="org.freedesktop.systemd1.Manager"
send_member="GetDefaultTarget"/>
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="LookupDynamicUserByName"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="LookupDynamicUserByUID"/>
+
<!-- Managed via polkit or other criteria -->
<allow send_destination="org.freedesktop.systemd1"
@@ -144,6 +168,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitsByNames"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="StartTransientUnit"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -160,6 +188,14 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="RefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="UnrefUnit"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="EnableUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
@@ -176,6 +212,10 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="RevertUnitFiles"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="PresetUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/path.c b/src/core/path.c
index 20995d920c..83f794be89 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,20 +17,26 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/inotify.h>
-#include <sys/epoll.h>
#include <errno.h>
+#include <sys/epoll.h>
+#include <sys/inotify.h>
#include <unistd.h>
-#include "unit.h"
-#include "unit-name.h"
-#include "path.h"
-#include "mkdir.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "dbus-path.h"
-#include "special.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "glob-util.h"
#include "macro.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "mkdir.h"
+#include "path.h"
+#include "special.h"
+#include "stat-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "unit-name.h"
+#include "unit.h"
static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
[PATH_DEAD] = UNIT_INACTIVE,
@@ -106,16 +110,14 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
} else {
exists = true;
- /* Path exists, we don't need to watch parent
- too closely. */
+ /* Path exists, we don't need to watch parent too closely. */
if (oldslash) {
char *cut2 = oldslash + (oldslash == s->path);
char tmp2 = *cut2;
*cut2 = '\0';
- inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
- /* Error is ignored, the worst can happen is
- we get spurious events. */
+ (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
+ /* Error is ignored, the worst can happen is we get spurious events. */
*cut2 = tmp2;
}
@@ -309,20 +311,20 @@ static int path_add_default_dependencies(Path *p) {
assert(p);
- r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
- SPECIAL_PATHS_TARGET, NULL, true);
+ if (!UNIT(p)->default_dependencies)
+ return 0;
+
+ r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, NULL, true);
if (r < 0)
return r;
- if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) {
- r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
- SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
+ r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
}
- return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
- SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
static int path_load(Unit *u) {
@@ -354,11 +356,9 @@ static int path_load(Unit *u) {
if (r < 0)
return r;
- if (UNIT(p)->default_dependencies) {
- r = path_add_default_dependencies(p);
- if (r < 0)
- return r;
- }
+ r = path_add_default_dependencies(p);
+ if (r < 0)
+ return r;
}
return path_verify(p);
@@ -454,14 +454,15 @@ static int path_coldplug(Unit *u) {
static void path_enter_dead(Path *p, PathResult f) {
assert(p);
- if (f != PATH_SUCCESS)
+ if (p->result == PATH_SUCCESS)
p->result = f;
path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
}
static void path_enter_running(Path *p) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ Unit *trigger;
int r;
assert(p);
@@ -470,8 +471,14 @@ static void path_enter_running(Path *p) {
if (unit_stop_pending(UNIT(p)))
return;
- r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
- JOB_REPLACE, true, &error, NULL);
+ trigger = UNIT_TRIGGER(UNIT(p));
+ if (!trigger) {
+ log_unit_error(UNIT(p), "Unit to trigger vanished.");
+ path_enter_dead(p, PATH_FAILURE_RESOURCES);
+ return;
+ }
+
+ r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
@@ -552,12 +559,27 @@ static void path_mkdir(Path *p) {
static int path_start(Unit *u) {
Path *p = PATH(u);
+ Unit *trigger;
+ int r;
assert(p);
assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
- if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
+ trigger = UNIT_TRIGGER(u);
+ if (!trigger || trigger->load_state != UNIT_LOADED) {
+ log_unit_error(u, "Refusing to start, unit to trigger not loaded.");
return -ENOENT;
+ }
+
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
path_mkdir(p);
@@ -715,15 +737,6 @@ static void path_reset_failed(Unit *u) {
p->result = PATH_SUCCESS;
}
-static const char* const path_state_table[_PATH_STATE_MAX] = {
- [PATH_DEAD] = "dead",
- [PATH_WAITING] = "waiting",
- [PATH_RUNNING] = "running",
- [PATH_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
-
static const char* const path_type_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = "PathExists",
[PATH_EXISTS_GLOB] = "PathExistsGlob",
@@ -737,6 +750,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
static const char* const path_result_table[_PATH_RESULT_MAX] = {
[PATH_SUCCESS] = "success",
[PATH_FAILURE_RESOURCES] = "resources",
+ [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
@@ -770,6 +784,5 @@ const UnitVTable path_vtable = {
.reset_failed = path_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Path",
.bus_vtable = bus_path_vtable
};
diff --git a/src/core/path.h b/src/core/path.h
index dec39333e4..4230c8fb99 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -26,15 +24,6 @@ typedef struct PathSpec PathSpec;
#include "unit.h"
-typedef enum PathState {
- PATH_DEAD,
- PATH_WAITING,
- PATH_RUNNING,
- PATH_FAILED,
- _PATH_STATE_MAX,
- _PATH_STATE_INVALID = -1
-} PathState;
-
typedef enum PathType {
PATH_EXISTS,
PATH_EXISTS_GLOB,
@@ -73,6 +62,7 @@ static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) {
typedef enum PathResult {
PATH_SUCCESS,
PATH_FAILURE_RESOURCES,
+ PATH_FAILURE_START_LIMIT_HIT,
_PATH_RESULT_MAX,
_PATH_RESULT_INVALID = -1
} PathResult;
@@ -96,9 +86,6 @@ void path_free_specs(Path *p);
extern const UnitVTable path_vtable;
-const char* path_state_to_string(PathState i) _const_;
-PathState path_state_from_string(const char *s) _pure_;
-
const char* path_type_to_string(PathType i) _const_;
PathType path_type_from_string(const char *s) _pure_;
diff --git a/src/core/scope.c b/src/core/scope.c
index ab1769b46b..e7583f6d89 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -22,13 +20,17 @@
#include <errno.h>
#include <unistd.h>
-#include "unit.h"
-#include "scope.h"
-#include "log.h"
+#include "alloc-util.h"
#include "dbus-scope.h"
+#include "load-dropin.h"
+#include "log.h"
+#include "scope.h"
#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
#include "unit-name.h"
-#include "load-dropin.h"
+#include "unit.h"
static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
[SCOPE_DEAD] = UNIT_INACTIVE,
@@ -48,9 +50,7 @@ static void scope_init(Unit *u) {
assert(u->load_state == UNIT_STUB);
s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
-
- UNIT(s)->ignore_on_isolate = true;
- UNIT(s)->ignore_on_snapshot = true;
+ u->ignore_on_isolate = true;
}
static void scope_done(Unit *u) {
@@ -63,29 +63,27 @@ static void scope_done(Unit *u) {
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
-static int scope_arm_timer(Scope *s) {
+static int scope_arm_timer(Scope *s, usec_t usec) {
int r;
assert(s);
- if (s->timeout_stop_usec <= 0) {
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return 0;
- }
-
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_stop_usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + s->timeout_stop_usec, 0,
+ usec, 0,
scope_dispatch_timer, s);
if (r < 0)
return r;
@@ -119,6 +117,9 @@ static int scope_add_default_dependencies(Scope *s) {
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
/* Make sure scopes are unloaded on shutdown */
r = unit_add_two_dependencies_by_name(
UNIT(s),
@@ -136,7 +137,9 @@ static int scope_verify(Scope *s) {
if (UNIT(s)->load_state != UNIT_LOADED)
return 0;
- if (set_isempty(UNIT(s)->pids) && UNIT(s)->manager->n_reloading <= 0) {
+ if (set_isempty(UNIT(s)->pids) &&
+ !MANAGER_IS_RELOADING(UNIT(s)->manager) &&
+ !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) {
log_unit_error(UNIT(s), "Scope has no PIDs. Refusing.");
return -EINVAL;
}
@@ -151,24 +154,23 @@ static int scope_load(Unit *u) {
assert(s);
assert(u->load_state == UNIT_STUB);
- if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
+ if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
+ /* Refuse to load non-transient scope units, but allow them while reloading. */
return -ENOENT;
- u->load_state = UNIT_LOADED;
-
- r = unit_load_dropin(u);
+ r = unit_load_fragment_and_dropin_optional(u);
if (r < 0)
return r;
- r = unit_patch_contexts(u);
- if (r < 0)
- return r;
+ if (u->load_state == UNIT_LOADED) {
+ r = unit_patch_contexts(u);
+ if (r < 0)
+ return r;
- r = unit_add_default_slice(u, &s->cgroup_context);
- if (r < 0)
- return r;
+ r = unit_set_default_slice(u);
+ if (r < 0)
+ return r;
- if (u->default_dependencies) {
r = scope_add_default_dependencies(s);
if (r < 0)
return r;
@@ -184,20 +186,19 @@ static int scope_coldplug(Unit *u) {
assert(s);
assert(s->state == SCOPE_DEAD);
- if (s->deserialized_state != s->state) {
-
- if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
- r = scope_arm_timer(s);
- if (r < 0)
- return r;
- }
-
- if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
- unit_watch_all_pids(UNIT(s));
+ if (s->deserialized_state == s->state)
+ return 0;
- scope_set_state(s, s->deserialized_state);
+ if (IN_SET(s->deserialized_state, SCOPE_STOP_SIGKILL, SCOPE_STOP_SIGTERM)) {
+ r = scope_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_stop_usec));
+ if (r < 0)
+ return r;
}
+ if (!IN_SET(s->deserialized_state, SCOPE_DEAD, SCOPE_FAILED))
+ unit_watch_all_pids(UNIT(s));
+
+ scope_set_state(s, s->deserialized_state);
return 0;
}
@@ -220,7 +221,7 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) {
static void scope_enter_dead(Scope *s, ScopeResult f) {
assert(s);
- if (f != SCOPE_SUCCESS)
+ if (s->result == SCOPE_SUCCESS)
s->result = f;
scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
@@ -232,14 +233,14 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
assert(s);
- if (f != SCOPE_SUCCESS)
+ if (s->result == SCOPE_SUCCESS)
s->result = f;
unit_watch_all_pids(UNIT(s));
/* If we have a controller set let's ask the controller nicely
* to terminate the scope, instead of us going directly into
- * SIGTERM beserk mode */
+ * SIGTERM berserk mode */
if (state == SCOPE_STOP_SIGTERM)
skip_signal = bus_scope_send_request_stop(s) > 0;
@@ -247,7 +248,9 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
r = unit_kill_context(
UNIT(s),
&s->kill_context,
- state != SCOPE_STOP_SIGTERM ? KILL_KILL : KILL_TERMINATE,
+ state != SCOPE_STOP_SIGTERM ? KILL_KILL :
+ s->was_abandoned ? KILL_TERMINATE_AND_LOG :
+ KILL_TERMINATE,
-1, -1, false);
if (r < 0)
goto fail;
@@ -255,7 +258,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
r = 1;
if (r > 0) {
- r = scope_arm_timer(s);
+ r = scope_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
if (r < 0)
goto fail;
@@ -279,6 +282,9 @@ static int scope_start(Unit *u) {
assert(s);
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE))
+ return -EPERM;
+
if (s->state == SCOPE_FAILED)
return -EPERM;
@@ -289,9 +295,13 @@ static int scope_start(Unit *u) {
assert(s->state == SCOPE_DEAD);
- if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
+ if (!u->transient && !MANAGER_IS_RELOADING(u->manager))
return -ENOENT;
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
(void) unit_realize_cgroup(u);
(void) unit_reset_cpu_usage(u);
@@ -339,17 +349,21 @@ static int scope_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, -1, error);
}
-static int scope_get_timeout(Unit *u, uint64_t *timeout) {
+static int scope_get_timeout(Unit *u, usec_t *timeout) {
Scope *s = SCOPE(u);
+ usec_t t;
int r;
if (!s->timer_event_source)
return 0;
- r = sd_event_source_get_time(s->timer_event_source, timeout);
+ r = sd_event_source_get_time(s->timer_event_source, &t);
if (r < 0)
return r;
+ if (t == USEC_INFINITY)
+ return 0;
+ *timeout = t;
return 1;
}
@@ -361,6 +375,7 @@ static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
assert(fds);
unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
+ unit_serialize_item(u, f, "was-abandoned", yes_no(s->was_abandoned));
return 0;
}
@@ -381,6 +396,14 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F
else
s->deserialized_state = state;
+ } else if (streq(key, "was-abandoned")) {
+ int k;
+
+ k = parse_boolean(value);
+ if (k < 0)
+ log_unit_debug(u, "Failed to parse boolean value: %s", value);
+ else
+ s->was_abandoned = k;
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
@@ -393,15 +416,10 @@ static bool scope_check_gc(Unit *u) {
/* Never clean up scopes that still have a process around,
* even if the scope is formally dead. */
- if (u->cgroup_path) {
- int r;
-
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
- if (r <= 0)
- return true;
- }
+ if (!u->cgroup_path)
+ return false;
- return false;
+ return cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path) <= 0;
}
static void scope_notify_cgroup_empty_event(Unit *u) {
@@ -425,8 +443,9 @@ static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
unit_tidy_watch_pids(u, 0, 0);
unit_watch_all_pids(u);
- /* If the PID set is empty now, then let's finish this off */
- if (set_isempty(u->pids))
+ /* If the PID set is empty now, then let's finish this off
+ (On unified we use proper notifications) */
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids))
scope_notify_cgroup_empty_event(u);
}
@@ -464,11 +483,14 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user
int scope_abandon(Scope *s) {
assert(s);
+ if (unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE))
+ return -EPERM;
+
if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED))
return -ESTALE;
- free(s->controller);
- s->controller = NULL;
+ s->was_abandoned = true;
+ s->controller = mfree(s->controller);
/* The client is no longer watching the remaining processes,
* so let's step in here, under the assumption that the
@@ -499,16 +521,51 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) {
return scope_state_to_string(SCOPE(u)->state);
}
-static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
- [SCOPE_DEAD] = "dead",
- [SCOPE_RUNNING] = "running",
- [SCOPE_ABANDONED] = "abandoned",
- [SCOPE_STOP_SIGTERM] = "stop-sigterm",
- [SCOPE_STOP_SIGKILL] = "stop-sigkill",
- [SCOPE_FAILED] = "failed",
-};
+static void scope_enumerate(Manager *m) {
+ Unit *u;
+ int r;
+
+ assert(m);
-DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
+ /* Let's unconditionally add the "init.scope" special unit
+ * that encapsulates PID 1. Note that PID 1 already is in the
+ * cgroup for this, we hence just need to allocate the object
+ * for it and that's it. */
+
+ u = manager_get_unit(m, SPECIAL_INIT_SCOPE);
+ if (!u) {
+ u = unit_new(m, sizeof(Scope));
+ if (!u) {
+ log_oom();
+ return;
+ }
+
+ r = unit_add_name(u, SPECIAL_INIT_SCOPE);
+ if (r < 0) {
+ unit_free(u);
+ log_error_errno(r, "Failed to add init.scope name");
+ return;
+ }
+ }
+
+ u->transient = true;
+ u->default_dependencies = false;
+ u->no_gc = true;
+ u->ignore_on_isolate = true;
+ u->refuse_manual_start = true;
+ u->refuse_manual_stop = true;
+ SCOPE(u)->deserialized_state = SCOPE_RUNNING;
+ SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14;
+
+ /* Prettify things, if we can. */
+ if (!u->description)
+ u->description = strdup("System and Service Manager");
+ if (!u->documentation)
+ (void) strv_extend(&u->documentation, "man:systemd(1)");
+
+ unit_add_to_load_queue(u);
+ unit_add_to_dbus_queue(u);
+}
static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
[SCOPE_SUCCESS] = "success",
@@ -529,8 +586,7 @@ const UnitVTable scope_vtable = {
"Install\0",
.private_section = "Scope",
- .no_alias = true,
- .no_instances = true,
+ .can_transient = true,
.init = scope_init,
.load = scope_load,
@@ -561,10 +617,9 @@ const UnitVTable scope_vtable = {
.notify_cgroup_empty = scope_notify_cgroup_empty_event,
- .bus_interface = "org.freedesktop.systemd1.Scope",
.bus_vtable = bus_scope_vtable,
.bus_set_property = bus_scope_set_property,
.bus_commit_properties = bus_scope_commit_properties,
- .can_transient = true
+ .enumerate = scope_enumerate,
};
diff --git a/src/core/scope.h b/src/core/scope.h
index 4452fe2c94..eaf8e8b447 100644
--- a/src/core/scope.h
+++ b/src/core/scope.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,18 +21,9 @@
typedef struct Scope Scope;
+#include "cgroup.h"
#include "kill.h"
-
-typedef enum ScopeState {
- SCOPE_DEAD,
- SCOPE_RUNNING,
- SCOPE_ABANDONED,
- SCOPE_STOP_SIGTERM,
- SCOPE_STOP_SIGKILL,
- SCOPE_FAILED,
- _SCOPE_STATE_MAX,
- _SCOPE_STATE_INVALID = -1
-} ScopeState;
+#include "unit.h"
typedef enum ScopeResult {
SCOPE_SUCCESS,
@@ -56,6 +45,7 @@ struct Scope {
usec_t timeout_stop_usec;
char *controller;
+ bool was_abandoned;
sd_event_source *timer_event_source;
};
@@ -64,8 +54,5 @@ extern const UnitVTable scope_vtable;
int scope_abandon(Scope *s);
-const char* scope_state_to_string(ScopeState i) _const_;
-ScopeState scope_state_from_string(const char *s) _pure_;
-
const char* scope_result_to_string(ScopeResult i) _const_;
ScopeResult scope_result_from_string(const char *s) _pure_;
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
index e9a9a020de..2b96a9551b 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -23,21 +21,25 @@
#ifdef HAVE_SELINUX
-#include <stdio.h>
#include <errno.h>
-#include <selinux/selinux.h>
#include <selinux/avc.h>
+#include <selinux/selinux.h>
+#include <stdio.h>
#ifdef HAVE_AUDIT
#include <libaudit.h>
#endif
#include "sd-bus.h"
+
+#include "alloc-util.h"
+#include "audit-fd.h"
#include "bus-util.h"
-#include "util.h"
#include "log.h"
+#include "path-util.h"
#include "selinux-util.h"
-#include "audit-fd.h"
+#include "stdio-util.h"
#include "strv.h"
+#include "util.h"
static bool initialized = false;
@@ -108,6 +110,7 @@ static int callback_type_to_priority(int type) {
*/
_printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
va_list ap;
+ const char *fmt2;
#ifdef HAVE_AUDIT
int fd;
@@ -129,63 +132,47 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
}
#endif
+ fmt2 = strjoina("selinux: ", fmt);
+
va_start(ap, fmt);
- log_internalv(LOG_AUTH | callback_type_to_priority(type),
- 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap);
+ log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt2, 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 = 0;
-
- if (avc_open(NULL, 0))
- return log_error_errno(errno, "avc_open() failed: %m");
+static int access_init(sd_bus_error *error) {
- 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){
- r = -errno;
- avc_destroy();
- }
-
- return r;
-}
-
-static int mac_selinux_access_init(sd_bus_error *error) {
- int r;
+ if (!mac_selinux_use())
+ return 0;
if (initialized)
- return 0;
+ return 1;
- if (!mac_selinux_use())
- return 0;
+ if (avc_open(NULL, 0) != 0) {
+ int enforce, saved_errno = errno;
- r = access_init();
- if (r < 0)
- return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to initialize SELinux.");
+ enforce = security_getenforce();
+ log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
- initialized = true;
- return 0;
-}
-#endif
+ /* If enforcement isn't on, then let's suppress this
+ * error, and just don't do any AVC checks. The
+ * warning we printed is hence all the admin will
+ * see. */
+ if (enforce == 0)
+ return 0;
-void mac_selinux_access_free(void) {
+ /* Return an access denied error, if we couldn't load
+ * the AVC but enforcing mode was on, or we couldn't
+ * determine whether it is one. */
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror(saved_errno));
+ }
-#ifdef HAVE_SELINUX
- if (!initialized)
- return;
+ selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
+ selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback);
- avc_destroy();
- initialized = false;
-#endif
+ initialized = true;
+ return 1;
}
/*
@@ -200,12 +187,11 @@ int mac_selinux_generic_access_check(
const char *permission,
sd_bus_error *error) {
-#ifdef HAVE_SELINUX
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *tclass = NULL, *scon = NULL;
struct audit_info audit_info = {};
_cleanup_free_ char *cl = NULL;
- security_context_t fcon = NULL;
+ char *fcon = NULL;
char **cmdline = NULL;
int r = 0;
@@ -213,11 +199,8 @@ int mac_selinux_generic_access_check(
assert(permission);
assert(error);
- if (!mac_selinux_use())
- return 0;
-
- r = mac_selinux_access_init(error);
- if (r < 0)
+ r = access_init(error);
+ if (r <= 0)
return r;
r = sd_bus_query_sender_creds(
@@ -245,7 +228,7 @@ int mac_selinux_generic_access_check(
if (path) {
/* Get the file context of the unit file */
- r = getfilecon(path, &fcon);
+ r = getfilecon_raw(path, &fcon);
if (r < 0) {
r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path);
goto finish;
@@ -253,7 +236,7 @@ int mac_selinux_generic_access_check(
tclass = "service";
} else {
- r = getcon(&fcon);
+ r = getcon_raw(&fcon);
if (r < 0) {
r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context.");
goto finish;
@@ -284,31 +267,17 @@ finish:
}
return r;
-#else
- return 0;
-#endif
}
-int mac_selinux_unit_access_check_strv(
- char **units,
+#else
+
+int mac_selinux_generic_access_check(
sd_bus_message *message,
- Manager *m,
+ const char *path,
const char *permission,
sd_bus_error *error) {
-#ifdef HAVE_SELINUX
- char **i;
- Unit *u;
- int r;
-
- STRV_FOREACH(i, units) {
- u = manager_get_unit(m, *i);
- if (u) {
- r = mac_selinux_unit_access_check(u, message, permission, error);
- if (r < 0)
- return r;
- }
- }
-#endif
return 0;
}
+
+#endif
diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h
index b5758e2e42..f46370d020 100644
--- a/src/core/selinux-access.h
+++ b/src/core/selinux-access.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,15 +20,12 @@
***/
#include "sd-bus.h"
+
#include "bus-util.h"
#include "manager.h"
-void mac_selinux_access_free(void);
-
int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
-int mac_selinux_unit_access_check_strv(char **units, sd_bus_message *message, Manager *m, const char *permission, sd_bus_error *error);
-
#ifdef HAVE_SELINUX
#define mac_selinux_access_check(message, permission, error) \
@@ -38,7 +33,7 @@ int mac_selinux_unit_access_check_strv(char **units, sd_bus_message *message, Ma
#define mac_selinux_unit_access_check(unit, message, permission, error) \
({ \
- Unit *_unit = (unit); \
+ const Unit *_unit = (unit); \
mac_selinux_generic_access_check((message), _unit->source_path ?: _unit->fragment_path, (permission), (error)); \
})
diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c
index a4678500e6..527aa8add0 100644
--- a/src/core/selinux-setup.c
+++ b/src/core/selinux-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,21 +17,23 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
-#include <stdio.h>
#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
+#include "log.h"
+#include "macro.h"
#include "selinux-setup.h"
#include "selinux-util.h"
-#include "macro.h"
+#include "string-util.h"
#include "util.h"
-#include "log.h"
#ifdef HAVE_SELINUX
+_printf_(2,3)
static int null_log(int type, const char *fmt, ...) {
return 0;
}
@@ -44,7 +44,7 @@ int mac_selinux_setup(bool *loaded_policy) {
#ifdef HAVE_SELINUX
int enforce = 0;
usec_t before_load, after_load;
- security_context_t con;
+ char *con;
int r;
union selinux_callback cb;
bool initialized = false;
@@ -77,24 +77,22 @@ int mac_selinux_setup(bool *loaded_policy) {
before_load = now(CLOCK_MONOTONIC);
r = selinux_init_load_policy(&enforce);
if (r == 0) {
+ _cleanup_(mac_selinux_freep) char *label = NULL;
char timespan[FORMAT_TIMESPAN_MAX];
- char *label;
mac_selinux_retest();
/* Transition to the new context */
r = mac_selinux_get_create_label_from_exe(SYSTEMD_BINARY_PATH, &label);
- if (r < 0 || label == NULL) {
+ if (r < 0 || !label) {
log_open();
log_error("Failed to compute init label, ignoring.");
} else {
- r = setcon(label);
+ r = setcon_raw(label);
log_open();
if (r < 0)
log_error("Failed to transition into init label '%s', ignoring.", label);
-
- mac_selinux_free(label);
}
after_load = now(CLOCK_MONOTONIC);
diff --git a/src/core/selinux-setup.h b/src/core/selinux-setup.h
index 9ac2276576..7b613249b0 100644
--- a/src/core/selinux-setup.h
+++ b/src/core/selinux-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/service.c b/src/core/service.c
index d72ff54daa..63045ede55 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -23,31 +21,38 @@
#include <signal.h>
#include <unistd.h>
+#include "alloc-util.h"
#include "async.h"
-#include "manager.h"
-#include "unit.h"
-#include "service.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "strv.h"
-#include "unit-name.h"
-#include "unit-printf.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+#include "bus-util.h"
#include "dbus-service.h"
-#include "special.h"
-#include "exit-status.h"
#include "def.h"
-#include "path-util.h"
-#include "util.h"
-#include "utf8.h"
#include "env-util.h"
+#include "escape.h"
+#include "exit-status.h"
+#include "fd-util.h"
#include "fileio.h"
-#include "bus-error.h"
-#include "bus-util.h"
-#include "bus-kernel.h"
#include "formats-util.h"
+#include "fs-util.h"
+#include "load-dropin.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "manager.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
+#include "service.h"
#include "signal-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "utf8.h"
+#include "util.h"
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = UNIT_INACTIVE,
@@ -105,13 +110,12 @@ static void service_init(Unit *u) {
s->timeout_start_usec = u->manager->default_timeout_start_usec;
s->timeout_stop_usec = u->manager->default_timeout_stop_usec;
s->restart_usec = u->manager->default_restart_usec;
+ s->runtime_max_usec = USEC_INFINITY;
s->type = _SERVICE_TYPE_INVALID;
s->socket_fd = -1;
- s->bus_endpoint_fd = -1;
+ s->stdin_fd = s->stdout_fd = s->stderr_fd = -1;
s->guess_main_pid = true;
- RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst);
-
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
@@ -142,8 +146,7 @@ static void service_unwatch_pid_file(Service *s) {
log_unit_debug(UNIT(s), "Stopping watch for PID file %s", s->pid_file_pathspec->path);
path_spec_unwatch(s->pid_file_pathspec);
path_spec_done(s->pid_file_pathspec);
- free(s->pid_file_pathspec);
- s->pid_file_pathspec = NULL;
+ s->pid_file_pathspec = mfree(s->pid_file_pathspec);
}
static int service_set_main_pid(Service *s, pid_t pid) {
@@ -168,7 +171,7 @@ static int service_set_main_pid(Service *s, pid_t pid) {
s->main_pid = pid;
s->main_pid_known = true;
- if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) {
+ if (get_process_ppid(pid, &ppid) >= 0 && ppid != getpid()) {
log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", pid);
s->main_pid_alien = true;
} else
@@ -177,20 +180,17 @@ static int service_set_main_pid(Service *s, pid_t pid) {
return 0;
}
-static void service_close_socket_fd(Service *s) {
+void service_close_socket_fd(Service *s) {
assert(s);
- s->socket_fd = asynchronous_close(s->socket_fd);
-}
+ /* Undo the effect of service_set_socket_fd(). */
-static void service_connection_unref(Service *s) {
- assert(s);
-
- if (!UNIT_ISSET(s->accept_socket))
- return;
+ s->socket_fd = asynchronous_close(s->socket_fd);
- socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
- unit_ref_unset(&s->accept_socket);
+ if (UNIT_ISSET(s->accept_socket)) {
+ socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
+ unit_ref_unset(&s->accept_socket);
+ }
}
static void service_stop_watchdog(Service *s) {
@@ -200,16 +200,27 @@ static void service_stop_watchdog(Service *s) {
s->watchdog_timestamp = DUAL_TIMESTAMP_NULL;
}
+static usec_t service_get_watchdog_usec(Service *s) {
+ assert(s);
+
+ if (s->watchdog_override_enable)
+ return s->watchdog_override_usec;
+ else
+ return s->watchdog_usec;
+}
+
static void service_start_watchdog(Service *s) {
int r;
+ usec_t watchdog_usec;
assert(s);
- if (s->watchdog_usec <= 0)
+ watchdog_usec = service_get_watchdog_usec(s);
+ if (watchdog_usec == 0 || watchdog_usec == USEC_INFINITY)
return;
if (s->watchdog_event_source) {
- r = sd_event_source_set_time(s->watchdog_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec);
+ r = sd_event_source_set_time(s->watchdog_event_source, usec_add(s->watchdog_timestamp.monotonic, watchdog_usec));
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to reset watchdog timer: %m");
return;
@@ -221,7 +232,7 @@ static void service_start_watchdog(Service *s) {
UNIT(s)->manager->event,
&s->watchdog_event_source,
CLOCK_MONOTONIC,
- s->watchdog_timestamp.monotonic + s->watchdog_usec, 0,
+ usec_add(s->watchdog_timestamp.monotonic, watchdog_usec), 0,
service_dispatch_watchdog, s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to add watchdog timer: %m");
@@ -246,6 +257,17 @@ static void service_reset_watchdog(Service *s) {
service_start_watchdog(s);
}
+static void service_reset_watchdog_timeout(Service *s, usec_t watchdog_override_usec) {
+ assert(s);
+
+ s->watchdog_override_enable = true;
+ s->watchdog_override_usec = watchdog_override_usec;
+ service_reset_watchdog(s);
+
+ log_unit_debug(UNIT(s), "watchdog_usec="USEC_FMT, s->watchdog_usec);
+ log_unit_debug(UNIT(s), "watchdog_override_usec="USEC_FMT, s->watchdog_override_usec);
+}
+
static void service_fd_store_unlink(ServiceFDStore *fs) {
if (!fs)
@@ -262,6 +284,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) {
sd_event_source_unref(fs->event_source);
}
+ free(fs->fdname);
safe_close(fs->fd);
free(fs);
}
@@ -271,11 +294,15 @@ static void service_release_resources(Unit *u) {
assert(s);
- if (!s->fd_store)
+ if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
return;
log_unit_debug(u, "Releasing all resources.");
+ s->stdin_fd = safe_close(s->stdin_fd);
+ s->stdout_fd = safe_close(s->stdout_fd);
+ s->stderr_fd = safe_close(s->stderr_fd);
+
while (s->fd_store)
service_fd_store_unlink(s->fd_store);
@@ -287,20 +314,16 @@ static void service_done(Unit *u) {
assert(s);
- free(s->pid_file);
- s->pid_file = NULL;
-
- free(s->status_text);
- s->status_text = NULL;
-
- free(s->reboot_arg);
- s->reboot_arg = NULL;
+ s->pid_file = mfree(s->pid_file);
+ s->status_text = mfree(s->status_text);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
s->main_command = NULL;
+ dynamic_creds_unref(&s->dynamic_creds);
+
exit_status_set_free(&s->restart_prevent_status);
exit_status_set_free(&s->restart_force_status);
exit_status_set_free(&s->success_status);
@@ -313,13 +336,13 @@ static void service_done(Unit *u) {
if (s->bus_name) {
unit_unwatch_bus_name(u, s->bus_name);
- free(s->bus_name);
- s->bus_name = NULL;
+ s->bus_name = mfree(s->bus_name);
}
- s->bus_endpoint_fd = safe_close(s->bus_endpoint_fd);
+ s->bus_name_owner = mfree(s->bus_name_owner);
+
service_close_socket_fd(s);
- service_connection_unref(s);
+ s->peer = socket_peer_unref(s->peer);
unit_ref_unset(&s->accept_socket);
@@ -341,7 +364,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us
return 0;
}
-static int service_add_fd_store(Service *s, int fd) {
+static int service_add_fd_store(Service *s, int fd, const char *name) {
ServiceFDStore *fs;
int r;
@@ -368,9 +391,15 @@ static int service_add_fd_store(Service *s, int fd) {
fs->fd = fd;
fs->service = s;
+ fs->fdname = strdup(name ?: "stored");
+ if (!fs->fdname) {
+ free(fs);
+ return -ENOMEM;
+ }
r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs);
if (r < 0) {
+ free(fs->fdname);
free(fs);
return r;
}
@@ -383,7 +412,7 @@ static int service_add_fd_store(Service *s, int fd) {
return 1;
}
-static int service_add_fd_store_set(Service *s, FDSet *fds) {
+static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
int r;
assert(s);
@@ -398,10 +427,9 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
if (fd < 0)
break;
- r = service_add_fd_store(s, fd);
+ r = service_add_fd_store(s, fd, name);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m");
-
if (r > 0) {
log_unit_debug(UNIT(s), "Added fd to fd store.");
fd = -1;
@@ -409,7 +437,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
}
if (fdset_size(fds) > 0)
- log_unit_warning(UNIT(s), "Tried to store more fds than FDStoreMax=%u allows, closing remaining.", s->n_fd_store_max);
+ log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max);
return 0;
}
@@ -420,18 +448,21 @@ static int service_arm_timer(Service *s, usec_t usec) {
assert(s);
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + usec, 0,
+ usec, 0,
service_dispatch_timer, s);
if (r < 0)
return r;
@@ -490,6 +521,15 @@ static int service_verify(Service *s) {
return -EINVAL;
}
+ if (s->usb_function_descriptors && !s->usb_function_strings)
+ log_unit_warning(UNIT(s), "Service has USBFunctionDescriptors= setting, but no USBFunctionStrings=. Ignoring.");
+
+ if (!s->usb_function_descriptors && s->usb_function_strings)
+ log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring.");
+
+ if (s->runtime_max_usec != USEC_INFINITY && s->type == SERVICE_ONESHOT)
+ log_unit_warning(UNIT(s), "MaxRuntimeSec= has no effect in combination with Type=oneshot. Ignoring.");
+
return 0;
}
@@ -498,15 +538,38 @@ static int service_add_default_dependencies(Service *s) {
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
/* Add a number of automatic dependencies useful for the
* majority of services. */
- /* First, pull in base system */
- r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true);
+ if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
+ /* First, pull in the really early boot stuff, and
+ * require it, so that we fail if we can't acquire
+ * it. */
+
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ } else {
+
+ /* In the --user instance there's no sysinit.target,
+ * in that case require basic.target instead. */
+
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
+ /* Second, if the rest of the base system is in the same
+ * transaction, order us after it, but do not pull it in or
+ * even require it. */
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true);
if (r < 0)
return r;
- /* Second, activate normal shutdown */
+ /* Third, add us in for normal shutdown. */
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
@@ -528,6 +591,32 @@ static void service_fix_output(Service *s) {
s->exec_context.std_output = UNIT(s)->manager->default_std_output;
}
+static int service_setup_bus_name(Service *s) {
+ int r;
+
+ assert(s);
+
+ if (!s->bus_name)
+ return 0;
+
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
+
+ /* Regardless if kdbus is used or not, we always want to be ordered against dbus.socket if both are in the transaction. */
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, NULL, true);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
+
+ r = unit_watch_bus_name(UNIT(s), s->bus_name);
+ if (r == -EEXIST)
+ return log_unit_error_errno(UNIT(s), r, "Two services allocated for the same bus name %s, refusing operation.", s->bus_name);
+ if (r < 0)
+ return log_unit_error_errno(UNIT(s), r, "Cannot watch bus name %s: %m", s->bus_name);
+
+ return 0;
+}
+
static int service_add_extras(Service *s) {
int r;
@@ -545,7 +634,7 @@ static int service_add_extras(Service *s) {
/* Oneshot services have disabled start timeout by default */
if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
- s->timeout_start_usec = 0;
+ s->timeout_start_usec = USEC_INFINITY;
service_fix_output(s);
@@ -557,7 +646,7 @@ static int service_add_extras(Service *s) {
if (r < 0)
return r;
- r = unit_add_default_slice(UNIT(s), &s->cgroup_context);
+ r = unit_set_default_slice(UNIT(s));
if (r < 0)
return r;
@@ -567,24 +656,13 @@ static int service_add_extras(Service *s) {
if (s->watchdog_usec > 0 && s->notify_access == NOTIFY_NONE)
s->notify_access = NOTIFY_MAIN;
- if (s->bus_name) {
- const char *n;
-
- n = strjoina(s->bus_name, ".busname");
- r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, n, NULL, true);
- if (r < 0)
- return r;
-
- r = unit_watch_bus_name(UNIT(s), s->bus_name);
- if (r < 0)
- return r;
- }
+ r = service_add_default_dependencies(s);
+ if (r < 0)
+ return r;
- if (UNIT(s)->default_dependencies) {
- r = service_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = service_setup_bus_name(s);
+ if (r < 0)
+ return r;
return 0;
}
@@ -683,6 +761,11 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
prefix, s->bus_name,
prefix, yes_no(s->bus_name_good));
+ if (UNIT_ISSET(s->accept_socket))
+ fprintf(f,
+ "%sAccept Socket: %s\n",
+ prefix, UNIT_DEREF(s->accept_socket)->id);
+
kill_context_dump(&s->kill_context, f, prefix);
exec_context_dump(&s->exec_context, f, prefix);
@@ -701,13 +784,12 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
fprintf(f, "%sStatus Text: %s\n",
prefix, s->status_text);
- if (s->n_fd_store_max > 0) {
+ if (s->n_fd_store_max > 0)
fprintf(f,
"%sFile Descriptor Store Max: %u\n"
"%sFile Descriptor Store Current: %u\n",
prefix, s->n_fd_store_max,
prefix, s->n_fd_store);
- }
}
static int service_load_pid_file(Service *s, bool may_warn) {
@@ -765,8 +847,8 @@ static int service_load_pid_file(Service *s, bool may_warn) {
return 0;
}
-static int service_search_main_pid(Service *s) {
- pid_t pid;
+static void service_search_main_pid(Service *s) {
+ pid_t pid = 0;
int r;
assert(s);
@@ -774,30 +856,24 @@ static int service_search_main_pid(Service *s) {
/* If we know it anyway, don't ever fallback to unreliable
* heuristics */
if (s->main_pid_known)
- return 0;
+ return;
if (!s->guess_main_pid)
- return 0;
+ return;
assert(s->main_pid <= 0);
- pid = unit_search_main_pid(UNIT(s));
- if (pid <= 0)
- return -ENOENT;
+ if (unit_search_main_pid(UNIT(s), &pid) < 0)
+ return;
log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid);
- r = service_set_main_pid(s, pid);
- if (r < 0)
- return r;
+ if (service_set_main_pid(s, pid) < 0)
+ return;
r = unit_watch_pid(UNIT(s), pid);
- if (r < 0) {
+ if (r < 0)
/* FIXME: we need to do something here */
log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid);
- return r;
- }
-
- return 0;
}
static void service_set_state(Service *s, ServiceState state) {
@@ -815,6 +891,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING,
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
@@ -848,18 +925,16 @@ static void service_set_state(Service *s, ServiceState state) {
SERVICE_RUNNING, SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
- !(state == SERVICE_DEAD && UNIT(s)->job)) {
+ !(state == SERVICE_DEAD && UNIT(s)->job))
service_close_socket_fd(s);
- service_connection_unref(s);
- }
if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
service_stop_watchdog(s);
/* For the inactive states unit_notify() will trim the cgroup,
* but for exit we have to do that ourselves... */
- if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
- unit_destroy_cgroup_if_empty(UNIT(s));
+ if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager))
+ unit_prune_cgroup(UNIT(s));
/* For remain_after_exit services, let's see if we can "release" the
* hold on the console, since unit_notify() only does that in case of
@@ -874,7 +949,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (ec && exec_context_may_touch_console(ec)) {
Manager *m = UNIT(s)->manager;
- m->n_on_console --;
+ m->n_on_console--;
if (m->n_on_console == 0)
/* unset no_console_output flag, since the console is free */
m->no_console_output = false;
@@ -885,7 +960,37 @@ static void service_set_state(Service *s, ServiceState state) {
log_unit_debug(UNIT(s), "Changed %s -> %s", 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;
+}
+
+static usec_t service_coldplug_timeout(Service *s) {
+ assert(s);
+
+ switch (s->deserialized_state) {
+
+ case SERVICE_START_PRE:
+ case SERVICE_START:
+ case SERVICE_START_POST:
+ case SERVICE_RELOAD:
+ return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_start_usec);
+
+ case SERVICE_RUNNING:
+ return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec);
+
+ case SERVICE_STOP:
+ case SERVICE_STOP_SIGABRT:
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
+ case SERVICE_STOP_POST:
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
+ return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec);
+
+ case SERVICE_AUTO_RESTART:
+ return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec);
+
+ default:
+ return USEC_INFINITY;
+ }
}
static int service_coldplug(Unit *u) {
@@ -895,125 +1000,138 @@ static int service_coldplug(Unit *u) {
assert(s);
assert(s->state == SERVICE_DEAD);
- if (s->deserialized_state != s->state) {
-
- if (IN_SET(s->deserialized_state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
-
- usec_t k;
+ if (s->deserialized_state == s->state)
+ return 0;
- k = IN_SET(s->deserialized_state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec;
+ r = service_arm_timer(s, service_coldplug_timeout(s));
+ if (r < 0)
+ return r;
- /* For the start/stop timeouts 0 means off */
- if (k > 0) {
- r = service_arm_timer(s, k);
- if (r < 0)
- return r;
- }
- }
+ if (s->main_pid > 0 &&
+ pid_is_unwaited(s->main_pid) &&
+ ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) ||
+ IN_SET(s->deserialized_state,
+ SERVICE_START, SERVICE_START_POST,
+ SERVICE_RUNNING, SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
+ r = unit_watch_pid(UNIT(s), s->main_pid);
+ if (r < 0)
+ return r;
+ }
- if (s->deserialized_state == SERVICE_AUTO_RESTART) {
+ if (s->control_pid > 0 &&
+ pid_is_unwaited(s->control_pid) &&
+ IN_SET(s->deserialized_state,
+ SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
+ SERVICE_RELOAD,
+ SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+ r = unit_watch_pid(UNIT(s), s->control_pid);
+ if (r < 0)
+ return r;
+ }
- /* The restart timeouts 0 means immediately */
- r = service_arm_timer(s, s->restart_usec);
- if (r < 0)
- return r;
- }
+ if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
+ unit_watch_all_pids(UNIT(s));
- if (pid_is_unwaited(s->main_pid) &&
- ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) ||
- IN_SET(s->deserialized_state,
- SERVICE_START, SERVICE_START_POST,
- SERVICE_RUNNING, SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
- r = unit_watch_pid(UNIT(s), s->main_pid);
- if (r < 0)
- return r;
- }
+ if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
+ service_start_watchdog(s);
- if (pid_is_unwaited(s->control_pid) &&
- IN_SET(s->deserialized_state,
- SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
- SERVICE_RELOAD,
- SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
- r = unit_watch_pid(UNIT(s), s->control_pid);
- if (r < 0)
- return r;
- }
+ if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
+ (void) unit_setup_dynamic_creds(u);
- if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
- unit_watch_all_pids(UNIT(s));
+ if (UNIT_ISSET(s->accept_socket)) {
+ Socket* socket = SOCKET(UNIT_DEREF(s->accept_socket));
- if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
- service_start_watchdog(s);
+ if (socket->max_connections_per_source > 0) {
+ SocketPeer *peer;
- service_set_state(s, s->deserialized_state);
+ /* Make a best-effort attempt at bumping the connection count */
+ if (socket_acquire_peer(socket, s->socket_fd, &peer) > 0) {
+ socket_peer_unref(s->peer);
+ s->peer = peer;
+ }
+ }
}
+ service_set_state(s, s->deserialized_state);
return 0;
}
-static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
+static int service_collect_fds(Service *s, int **fds, char ***fd_names) {
+ _cleanup_strv_free_ char **rfd_names = NULL;
_cleanup_free_ int *rfds = NULL;
- unsigned rn_fds = 0;
- Iterator i;
- int r;
- Unit *u;
+ int rn_fds = 0, r;
assert(s);
assert(fds);
- assert(n_fds);
+ assert(fd_names);
- if (s->socket_fd >= 0)
- return 0;
+ if (s->socket_fd >= 0) {
- SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
- int *cfds;
- unsigned cn_fds;
- Socket *sock;
+ /* Pass the per-connection socket */
- if (u->type != UNIT_SOCKET)
- continue;
+ rfds = new(int, 1);
+ if (!rfds)
+ return -ENOMEM;
+ rfds[0] = s->socket_fd;
- sock = SOCKET(u);
+ rfd_names = strv_new("connection", NULL);
+ if (!rfd_names)
+ return -ENOMEM;
- r = socket_collect_fds(sock, &cfds, &cn_fds);
- if (r < 0)
- return r;
+ rn_fds = 1;
+ } else {
+ Iterator i;
+ Unit *u;
- if (cn_fds <= 0) {
- free(cfds);
- continue;
- }
+ /* Pass all our configured sockets for singleton services */
- if (!rfds) {
- rfds = cfds;
- rn_fds = cn_fds;
- } else {
- int *t;
+ SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
+ _cleanup_free_ int *cfds = NULL;
+ Socket *sock;
+ int cn_fds;
- t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
- if (!t) {
- free(cfds);
- return -ENOMEM;
- }
+ if (u->type != UNIT_SOCKET)
+ continue;
+
+ sock = SOCKET(u);
+
+ cn_fds = socket_collect_fds(sock, &cfds);
+ if (cn_fds < 0)
+ return cn_fds;
+
+ if (cn_fds <= 0)
+ continue;
+
+ if (!rfds) {
+ rfds = cfds;
+ rn_fds = cn_fds;
- memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
- rfds = t;
- rn_fds += cn_fds;
+ cfds = NULL;
+ } else {
+ int *t;
- free(cfds);
+ t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
+ if (!t)
+ return -ENOMEM;
+
+ memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
+
+ rfds = t;
+ rn_fds += cn_fds;
+ }
+ r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds);
+ if (r < 0)
+ return r;
}
}
if (s->n_fd_store > 0) {
ServiceFDStore *fs;
+ char **nl;
int *t;
t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int));
@@ -1021,49 +1139,68 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
return -ENOMEM;
rfds = t;
- LIST_FOREACH(fd_store, fs, s->fd_store)
- rfds[rn_fds++] = fs->fd;
+
+ nl = realloc(rfd_names, (rn_fds + s->n_fd_store + 1) * sizeof(char*));
+ if (!nl)
+ return -ENOMEM;
+
+ rfd_names = nl;
+
+ LIST_FOREACH(fd_store, fs, s->fd_store) {
+ rfds[rn_fds] = fs->fd;
+ rfd_names[rn_fds] = strdup(strempty(fs->fdname));
+ if (!rfd_names[rn_fds])
+ return -ENOMEM;
+
+ rn_fds++;
+ }
+
+ rfd_names[rn_fds] = NULL;
}
*fds = rfds;
- *n_fds = rn_fds;
+ *fd_names = rfd_names;
rfds = NULL;
- return 0;
+ rfd_names = NULL;
+
+ return rn_fds;
}
static int service_spawn(
Service *s,
ExecCommand *c,
usec_t timeout,
- bool pass_fds,
- bool apply_permissions,
- bool apply_chroot,
- bool apply_tty_stdin,
- bool is_control,
+ ExecFlags flags,
pid_t *_pid) {
- pid_t pid;
- int r;
- int *fds = NULL;
- _cleanup_free_ int *fdsbuf = NULL;
+ _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL;
+ _cleanup_free_ int *fds = NULL;
unsigned n_fds = 0, n_env = 0;
- _cleanup_free_ char *bus_endpoint_path = NULL;
- _cleanup_strv_free_ char
- **argv = NULL, **final_env = NULL, **our_env = NULL;
const char *path;
+ pid_t pid;
+
ExecParameters exec_params = {
- .apply_permissions = apply_permissions,
- .apply_chroot = apply_chroot,
- .apply_tty_stdin = apply_tty_stdin,
- .bus_endpoint_fd = -1,
- .selinux_context_net = s->socket_fd_selinux_context_net
+ .flags = flags,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
+ int r;
+
assert(s);
assert(c);
assert(_pid);
+ if (flags & EXEC_IS_CONTROL) {
+ /* If this is a control process, mask the permissions/chroot application if this is requested. */
+ if (s->permissions_start_only)
+ exec_params.flags &= ~EXEC_APPLY_PERMISSIONS;
+ if (s->root_directory_start_only)
+ exec_params.flags &= ~EXEC_APPLY_CHROOT;
+ }
+
(void) unit_realize_cgroup(UNIT(s));
if (s->reset_cpu_usage) {
(void) unit_reset_cpu_usage(UNIT(s));
@@ -1072,161 +1209,150 @@ static int service_spawn(
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
- goto fail;
+ return r;
+
+ r = unit_setup_dynamic_creds(UNIT(s));
+ if (r < 0)
+ return r;
- if (pass_fds ||
+ if ((flags & EXEC_PASS_FDS) ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
- if (s->socket_fd >= 0) {
- fds = &s->socket_fd;
- n_fds = 1;
- } else {
- r = service_collect_fds(s, &fdsbuf, &n_fds);
- if (r < 0)
- goto fail;
+ r = service_collect_fds(s, &fds, &fd_names);
+ if (r < 0)
+ return r;
- fds = fdsbuf;
- }
+ n_fds = r;
}
- if (timeout > 0) {
- r = service_arm_timer(s, timeout);
- if (r < 0)
- goto fail;
- } else
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout));
+ if (r < 0)
+ return r;
r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
if (r < 0)
- goto fail;
+ return r;
- our_env = new0(char*, 6);
- if (!our_env) {
- r = -ENOMEM;
- goto fail;
- }
+ our_env = new0(char*, 9);
+ if (!our_env)
+ return -ENOMEM;
- if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
- if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if ((flags & EXEC_IS_CONTROL) ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
+ if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
+ return -ENOMEM;
if (s->main_pid > 0)
- if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
+ return -ENOMEM;
- if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
- if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if (MANAGER_IS_USER(UNIT(s)->manager))
+ if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0)
+ return -ENOMEM;
- if (UNIT_DEREF(s->accept_socket)) {
+ if (s->socket_fd >= 0) {
union sockaddr_union sa;
socklen_t salen = sizeof(sa);
r = getpeername(s->socket_fd, &sa.sa, &salen);
if (r < 0) {
r = -errno;
- goto fail;
+
+ /* ENOTCONN is legitimate if the endpoint disappeared on shutdown.
+ * This connection is over, but the socket unit lives on. */
+ if (r != -ENOTCONN || !IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST))
+ return r;
}
- if (IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
+ if (r == 0 && IN_SET(sa.sa.sa_family, AF_INET, AF_INET6)) {
_cleanup_free_ char *addr = NULL;
char *t;
int port;
r = sockaddr_pretty(&sa.sa, salen, true, false, &addr);
if (r < 0)
- goto fail;
+ return r;
t = strappend("REMOTE_ADDR=", addr);
- if (!t) {
- r = -ENOMEM;
- goto fail;
- }
+ if (!t)
+ return -ENOMEM;
our_env[n_env++] = t;
port = sockaddr_port(&sa.sa);
- if (port < 0) {
- r = port;
- goto fail;
- }
+ if (port < 0)
+ return port;
- if (asprintf(&t, "REMOTE_PORT=%u", port) < 0) {
- r = -ENOMEM;
- goto fail;
- }
+ if (asprintf(&t, "REMOTE_PORT=%u", port) < 0)
+ return -ENOMEM;
our_env[n_env++] = t;
}
}
- final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
- if (!final_env) {
- r = -ENOMEM;
- goto fail;
+ if (flags & EXEC_SETENV_RESULT) {
+ if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0)
+ return -ENOMEM;
+
+ if (s->main_exec_status.pid > 0 &&
+ dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
+ if (asprintf(our_env + n_env++, "EXIT_CODE=%s", sigchld_code_to_string(s->main_exec_status.code)) < 0)
+ return -ENOMEM;
+
+ if (s->main_exec_status.code == CLD_EXITED)
+ r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status);
+ else
+ r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status));
+ if (r < 0)
+ return -ENOMEM;
+ }
}
- if (is_control && UNIT(s)->cgroup_path) {
+ final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
+ if (!final_env)
+ return -ENOMEM;
+
+ if ((flags & EXEC_IS_CONTROL) && UNIT(s)->cgroup_path) {
path = strjoina(UNIT(s)->cgroup_path, "/control");
- cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+ (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
} else
path = UNIT(s)->cgroup_path;
- if (s->exec_context.bus_endpoint) {
- r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == MANAGER_SYSTEM ? "system" : "user",
- UNIT(s)->id, &bus_endpoint_path);
- if (r < 0)
- goto fail;
-
- /* Pass the fd to the exec_params so that the child process can upload the policy.
- * Keep a reference to the fd in the service, so the endpoint is kept alive as long
- * as the service is running. */
- exec_params.bus_endpoint_fd = s->bus_endpoint_fd = r;
- }
-
exec_params.argv = argv;
+ exec_params.environment = final_env;
exec_params.fds = fds;
+ exec_params.fd_names = fd_names;
exec_params.n_fds = n_fds;
- exec_params.environment = final_env;
- exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
exec_params.watchdog_usec = s->watchdog_usec;
- exec_params.bus_endpoint_path = bus_endpoint_path;
+ exec_params.selinux_context_net = s->socket_fd_selinux_context_net;
if (s->type == SERVICE_IDLE)
exec_params.idle_pipe = UNIT(s)->manager->idle_pipe;
+ exec_params.stdin_fd = s->stdin_fd;
+ exec_params.stdout_fd = s->stdout_fd;
+ exec_params.stderr_fd = s->stderr_fd;
r = exec_spawn(UNIT(s),
c,
&s->exec_context,
&exec_params,
s->exec_runtime,
+ &s->dynamic_creds,
&pid);
if (r < 0)
- goto fail;
+ return r;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
- goto fail;
+ return r;
*_pid = pid;
return 0;
-
-fail:
- if (timeout)
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
-
- return r;
}
static int main_pid_good(Service *s) {
@@ -1268,7 +1394,7 @@ static int cgroup_good(Service *s) {
if (!UNIT(s)->cgroup_path)
return 0;
- r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path);
if (r < 0)
return r;
@@ -1322,19 +1448,19 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
int r;
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
if (s->result != SERVICE_SUCCESS) {
log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result));
- failure_action(UNIT(s)->manager, s->failure_action, s->reboot_arg);
+ failure_action(UNIT(s)->manager, s->failure_action, UNIT(s)->reboot_arg);
}
if (allow_restart && service_shall_restart(s)) {
- r = service_arm_timer(s, s->restart_usec);
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));
if (r < 0)
goto fail;
@@ -1348,14 +1474,20 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
- /* Also, remove the runtime directory in */
+ /* Also, remove the runtime directory */
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
+ /* Get rid of the IPC bits of the user */
+ unit_unref_uid_gid(UNIT(s), true);
+
+ /* Release the user, and destroy it if we are the only remaining owner */
+ dynamic_creds_destroy(&s->dynamic_creds);
+
/* Try to delete the pid file. At this point it will be
* out-of-date, and some software might be confused by it, so
* let's remove it. */
if (s->pid_file)
- unlink_noerrno(s->pid_file);
+ (void) unlink(s->pid_file);
return;
@@ -1368,7 +1500,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
int r;
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@@ -1381,11 +1513,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- true,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1425,7 +1553,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
unit_watch_all_pids(UNIT(s));
@@ -1442,11 +1570,9 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
goto fail;
if (r > 0) {
- if (s->timeout_stop_usec > 0) {
- r = service_arm_timer(s, s->timeout_stop_usec);
- if (r < 0)
- goto fail;
- }
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
+ if (r < 0)
+ goto fail;
service_set_state(s, state);
} else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
@@ -1474,8 +1600,7 @@ static void service_enter_stop_by_notify(Service *s) {
unit_watch_all_pids(UNIT(s));
- if (s->timeout_stop_usec > 0)
- service_arm_timer(s, s->timeout_stop_usec);
+ service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_stop_usec));
/* The service told us it's stopping, so it's as if we SIGTERM'd it. */
service_set_state(s, SERVICE_STOP_SIGTERM);
@@ -1486,7 +1611,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@@ -1499,11 +1624,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1519,18 +1640,35 @@ fail:
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
}
+static bool service_good(Service *s) {
+ int main_pid_ok;
+ assert(s);
+
+ if (s->type == SERVICE_DBUS && !s->bus_name_good)
+ return false;
+
+ main_pid_ok = main_pid_good(s);
+ if (main_pid_ok > 0) /* It's alive */
+ return true;
+ if (main_pid_ok == 0) /* It's dead */
+ return false;
+
+ /* OK, we don't know anything about the main PID, maybe
+ * because there is none. Let's check the control group
+ * instead. */
+
+ return cgroup_good(s) != 0;
+}
+
static void service_enter_running(Service *s, ServiceResult f) {
- int main_pid_ok, cgroup_ok;
assert(s);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
- main_pid_ok = main_pid_good(s);
- cgroup_ok = cgroup_good(s);
+ service_unwatch_control_pid(s);
- if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) &&
- (s->bus_name_good || s->type != SERVICE_DBUS)) {
+ if (service_good(s)) {
/* If there are any queued up sd_notify()
* notifications, process them now */
@@ -1538,8 +1676,10 @@ static void service_enter_running(Service *s, ServiceResult f) {
service_enter_reload_by_notify(s);
else if (s->notify_state == NOTIFY_STOPPING)
service_enter_stop_by_notify(s);
- else
+ else {
service_set_state(s, SERVICE_RUNNING);
+ service_arm_timer(s, usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec));
+ }
} else if (s->remain_after_exit)
service_set_state(s, SERVICE_EXITED);
@@ -1561,11 +1701,7 @@ static void service_enter_start_post(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1588,11 +1724,12 @@ static void service_kill_control_processes(Service *s) {
return;
p = strjoina(UNIT(s)->cgroup_path, "/control");
- cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, true, true, true, NULL);
+ cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, CGROUP_SIGCONT|CGROUP_IGNORE_SELF|CGROUP_REMOVE, NULL, NULL, NULL);
}
static void service_enter_start(Service *s) {
ExecCommand *c;
+ usec_t timeout;
pid_t pid;
int r;
@@ -1624,19 +1761,22 @@ static void service_enter_start(Service *s) {
return;
}
+ if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE))
+ /* For simple + idle this is the main process. We don't apply any timeout here, but
+ * service_enter_running() will later apply the .runtime_max_usec timeout. */
+ timeout = USEC_INFINITY;
+ else
+ timeout = s->timeout_start_usec;
+
r = service_spawn(s,
c,
- IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_NOTIFY, SERVICE_ONESHOT) ? s->timeout_start_usec : 0,
- true,
- true,
- true,
- true,
- false,
+ timeout,
+ EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG,
&pid);
if (r < 0)
goto fail;
- if (s->type == SERVICE_SIMPLE || s->type == SERVICE_IDLE) {
+ if (IN_SET(s->type, SERVICE_SIMPLE, SERVICE_IDLE)) {
/* For simple services we immediately start
* the START_POST binaries. */
@@ -1651,9 +1791,7 @@ static void service_enter_start(Service *s) {
s->control_pid = pid;
service_set_state(s, SERVICE_START);
- } else if (s->type == SERVICE_ONESHOT ||
- s->type == SERVICE_DBUS ||
- s->type == SERVICE_NOTIFY) {
+ } else if (IN_SET(s->type, SERVICE_ONESHOT, SERVICE_DBUS, SERVICE_NOTIFY)) {
/* For oneshot services we wait until the start
* process exited, too, but it is our main process. */
@@ -1692,11 +1830,7 @@ static void service_enter_start_pre(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- true,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1713,7 +1847,7 @@ fail:
}
static void service_enter_restart(Service *s) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
assert(s);
@@ -1722,7 +1856,7 @@ static void service_enter_restart(Service *s) {
/* Don't restart things if we are going down anyway */
log_unit_info(UNIT(s), "Stop job pending for unit, delaying automatic restart.");
- r = service_arm_timer(s, s->restart_usec);
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->restart_usec));
if (r < 0)
goto fail;
@@ -1733,7 +1867,7 @@ static void service_enter_restart(Service *s) {
* restarted. We use JOB_RESTART (instead of the more obvious
* JOB_START) here so that those dependency jobs will be added
* as well. */
- r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, false, &error, NULL);
+ r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_FAIL, &error, NULL);
if (r < 0)
goto fail;
@@ -1752,9 +1886,7 @@ fail:
static void service_enter_reload_by_notify(Service *s) {
assert(s);
- if (s->timeout_start_usec > 0)
- service_arm_timer(s, s->timeout_start_usec);
-
+ service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_start_usec));
service_set_state(s, SERVICE_RELOAD);
}
@@ -1764,6 +1896,7 @@ static void service_enter_reload(Service *s) {
assert(s);
service_unwatch_control_pid(s);
+ s->reload_result = SERVICE_SUCCESS;
s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
if (s->control_command) {
@@ -1772,11 +1905,7 @@ static void service_enter_reload(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- false,
- true,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
&s->control_pid);
if (r < 0)
goto fail;
@@ -1794,6 +1923,7 @@ fail:
}
static void service_run_next_control(Service *s) {
+ usec_t timeout;
int r;
assert(s);
@@ -1805,15 +1935,17 @@ static void service_run_next_control(Service *s) {
s->control_command = s->control_command->command_next;
service_unwatch_control_pid(s);
+ if (IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
+ timeout = s->timeout_start_usec;
+ else
+ timeout = s->timeout_stop_usec;
+
r = service_spawn(s,
s->control_command,
- IN_SET(s->state, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD) ? s->timeout_start_usec : s->timeout_stop_usec,
- false,
- !s->permissions_start_only,
- !s->root_directory_start_only,
- s->control_command_id == SERVICE_EXEC_START_PRE ||
- s->control_command_id == SERVICE_EXEC_STOP_POST,
- true,
+ timeout,
+ EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|
+ (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)|
+ (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0),
&s->control_pid);
if (r < 0)
goto fail;
@@ -1851,11 +1983,7 @@ static void service_run_next_main(Service *s) {
r = service_spawn(s,
s->main_command,
s->timeout_start_usec,
- true,
- true,
- true,
- true,
- false,
+ EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG,
&pid);
if (r < 0)
goto fail;
@@ -1869,17 +1997,6 @@ fail:
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
}
-static int service_start_limit_test(Service *s) {
- assert(s);
-
- if (ratelimit_test(&s->start_limit))
- return 0;
-
- log_unit_warning(UNIT(s), "Start request repeated too quickly.");
-
- return failure_action(UNIT(s)->manager, s->start_limit_action, s->reboot_arg);
-}
-
static int service_start(Unit *u) {
Service *s = SERVICE(u);
int r;
@@ -1910,12 +2027,16 @@ static int service_start(Unit *u) {
assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
/* Make sure we don't enter a busy loop of some kind. */
- r = service_start_limit_test(s);
+ r = unit_start_limit_test(u);
if (r < 0) {
- service_enter_dead(s, SERVICE_FAILURE_START_LIMIT, false);
+ service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
return r;
}
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
s->main_pid_known = false;
@@ -1923,12 +2044,14 @@ static int service_start(Unit *u) {
s->forbid_restart = false;
s->reset_cpu_usage = true;
- free(s->status_text);
- s->status_text = NULL;
+ s->status_text = mfree(s->status_text);
s->status_errno = 0;
s->notify_state = NOTIFY_UNKNOWN;
+ s->watchdog_override_enable = false;
+ s->watchdog_override_usec = 0;
+
service_enter_start_pre(s);
return 1;
}
@@ -1974,7 +2097,7 @@ static int service_reload(Unit *u) {
assert(s->state == SERVICE_RUNNING || s->state == SERVICE_EXITED);
service_enter_reload(s);
- return 0;
+ return 1;
}
_pure_ static bool service_can_reload(Unit *u) {
@@ -1988,6 +2111,7 @@ _pure_ static bool service_can_reload(Unit *u) {
static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
Service *s = SERVICE(u);
ServiceFDStore *fs;
+ int r;
assert(u);
assert(f);
@@ -2005,13 +2129,11 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
unit_serialize_item(u, f, "bus-name-good", yes_no(s->bus_name_good));
+ unit_serialize_item(u, f, "bus-name-owner", s->bus_name_owner);
- if (s->status_text) {
- _cleanup_free_ char *c = NULL;
-
- c = cescape(s->status_text);
- unit_serialize_item(u, f, "status-text", strempty(c));
- }
+ r = unit_serialize_item_escaped(u, f, "status-text", s->status_text);
+ if (r < 0)
+ return r;
/* FIXME: There's a minor uncleanliness here: if there are
* multiple commands attached here, we will start from the
@@ -2019,34 +2141,37 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
if (s->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
- if (s->socket_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, s->socket_fd);
- if (copy < 0)
- return copy;
+ r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
+ if (r < 0)
+ return r;
+ r = unit_serialize_item_fd(u, f, fds, "stdout-fd", s->stdout_fd);
+ if (r < 0)
+ return r;
+ r = unit_serialize_item_fd(u, f, fds, "stderr-fd", s->stderr_fd);
+ if (r < 0)
+ return r;
- unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
+ if (UNIT_ISSET(s->accept_socket)) {
+ r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id);
+ if (r < 0)
+ return r;
}
- if (s->bus_endpoint_fd >= 0) {
- int copy;
-
- copy = fdset_put_dup(fds, s->bus_endpoint_fd);
- if (copy < 0)
- return copy;
-
- unit_serialize_item_format(u, f, "endpoint-fd", "%i", copy);
- }
+ r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd);
+ if (r < 0)
+ return r;
LIST_FOREACH(fd_store, fs, s->fd_store) {
+ _cleanup_free_ char *c = NULL;
int copy;
copy = fdset_put_dup(fds, fs->fd);
if (copy < 0)
return copy;
- unit_serialize_item_format(u, f, "fd-store-fd", "%i", copy);
+ c = cescape(fs->fdname);
+
+ unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c));
}
if (s->main_exec_status.pid > 0) {
@@ -2060,11 +2185,12 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
}
}
- if (dual_timestamp_is_set(&s->watchdog_timestamp))
- dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
+ dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
- if (s->forbid_restart)
- unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
+ unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
+
+ if (s->watchdog_override_enable)
+ unit_serialize_item_format(u, f, "watchdog-override-usec", USEC_FMT, s->watchdog_override_usec);
return 0;
}
@@ -2136,6 +2262,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
log_unit_debug(u, "Failed to parse bus-name-good value: %s", value);
else
s->bus_name_good = b;
+ } else if (streq(key, "bus-name-owner")) {
+ r = free_and_strdup(&s->bus_name_owner, value);
+ if (r < 0)
+ log_unit_error_errno(u, r, "Unable to deserialize current bus owner %s: %m", value);
} else if (streq(key, "status-text")) {
char *t;
@@ -2157,6 +2287,17 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
s->control_command_id = id;
s->control_command = s->exec_command[id];
}
+ } else if (streq(key, "accept-socket")) {
+ Unit *socket;
+
+ r = manager_load_unit(u->manager, value, NULL, NULL, &socket);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Failed to load accept-socket unit: %s", value);
+ else {
+ unit_ref_set(&s->accept_socket, socket);
+ SOCKET(socket)->n_connections++;
+ }
+
} else if (streq(key, "socket-fd")) {
int fd;
@@ -2166,22 +2307,25 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
asynchronous_close(s->socket_fd);
s->socket_fd = fdset_remove(fds, fd);
}
- } else if (streq(key, "endpoint-fd")) {
- int fd;
-
- if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
- log_unit_debug(u, "Failed to parse endpoint-fd value: %s", value);
- else {
- safe_close(s->bus_endpoint_fd);
- s->bus_endpoint_fd = fdset_remove(fds, fd);
- }
} else if (streq(key, "fd-store-fd")) {
+ const char *fdv;
+ size_t pf;
int fd;
- if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ pf = strcspn(value, WHITESPACE);
+ fdv = strndupa(value, pf);
+
+ if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value);
else {
- r = service_add_fd_store(s, fd);
+ _cleanup_free_ char *t = NULL;
+ const char *fdn;
+
+ fdn = value + pf;
+ fdn += strspn(fdn, WHITESPACE);
+ (void) cunescape(fdn, 0, &t);
+
+ r = service_add_fd_store(s, fd, t);
if (r < 0)
log_unit_error_errno(u, r, "Failed to add fd to store: %m");
else if (r > 0)
@@ -2223,6 +2367,44 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
log_unit_debug(u, "Failed to parse forbid-restart value: %s", value);
else
s->forbid_restart = b;
+ } else if (streq(key, "stdin-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse stdin-fd value: %s", value);
+ else {
+ asynchronous_close(s->stdin_fd);
+ s->stdin_fd = fdset_remove(fds, fd);
+ s->exec_context.stdio_as_fds = true;
+ }
+ } else if (streq(key, "stdout-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse stdout-fd value: %s", value);
+ else {
+ asynchronous_close(s->stdout_fd);
+ s->stdout_fd = fdset_remove(fds, fd);
+ s->exec_context.stdio_as_fds = true;
+ }
+ } else if (streq(key, "stderr-fd")) {
+ int fd;
+
+ if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse stderr-fd value: %s", value);
+ else {
+ asynchronous_close(s->stderr_fd);
+ s->stderr_fd = fdset_remove(fds, fd);
+ s->exec_context.stdio_as_fds = true;
+ }
+ } else if (streq(key, "watchdog-override-usec")) {
+ usec_t watchdog_override_usec;
+ if (timestamp_deserialize(value, &watchdog_override_usec) < 0)
+ log_unit_debug(u, "Failed to parse watchdog_override_usec value: %s", value);
+ else {
+ s->watchdog_override_enable = true;
+ s->watchdog_override_usec = watchdog_override_usec;
+ }
} else
log_unit_debug(u, "Unknown serialization key: %s", key);
@@ -2260,14 +2442,6 @@ static bool service_check_gc(Unit *u) {
return false;
}
-_pure_ static bool service_check_snapshot(Unit *u) {
- Service *s = SERVICE(u);
-
- assert(s);
-
- return s->socket_fd < 0;
-}
-
static int service_retry_pid_file(Service *s) {
int r;
@@ -2430,8 +2604,7 @@ 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, &s->success_status) :
- is_clean_exit_lsb(code, status, &s->success_status))
+ if (is_clean_exit(code, status, s->type == SERVICE_ONESHOT ? EXIT_CLEAN_COMMAND : EXIT_CLEAN_DAEMON, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)
f = SERVICE_FAILURE_EXIT_CODE;
@@ -2484,7 +2657,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"EXIT_STATUS=%i", status,
NULL);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
if (s->main_command &&
@@ -2565,7 +2738,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"Control process exited, code=%s status=%i",
sigchld_code_to_string(code), status);
- if (f != SERVICE_SUCCESS)
+ if (s->result == SERVICE_SUCCESS)
s->result = f;
/* Immediately get rid of the cgroup, so that the
@@ -2635,7 +2808,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case SERVICE_START_POST:
if (f != SERVICE_SUCCESS) {
- service_enter_stop(s, f);
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
break;
}
@@ -2656,10 +2829,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
case SERVICE_RELOAD:
- if (f == SERVICE_SUCCESS) {
- service_load_pid_file(s, true);
- service_search_main_pid(s);
- }
+ if (f == SERVICE_SUCCESS)
+ if (service_load_pid_file(s, true) < 0)
+ service_search_main_pid(s);
s->reload_result = f;
service_enter_running(s, SERVICE_SUCCESS);
@@ -2704,8 +2876,9 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
unit_tidy_watch_pids(u, s->main_pid, s->control_pid);
unit_watch_all_pids(u);
- /* If the PID set is empty now, then let's finish this off */
- if (set_isempty(u->pids))
+ /* If the PID set is empty now, then let's finish this off
+ (On unified we use proper notifications) */
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids))
service_notify_cgroup_empty_event(u);
}
@@ -2725,11 +2898,17 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
case SERVICE_START_POST:
log_unit_warning(UNIT(s), "Start-post operation timed out. Stopping.");
+ service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+ break;
+
+ case SERVICE_RUNNING:
+ log_unit_warning(UNIT(s), "Service reached runtime time limit. Stopping.");
service_enter_stop(s, SERVICE_FAILURE_TIMEOUT);
break;
case SERVICE_RELOAD:
- log_unit_warning(UNIT(s), "Reload operation timed out. Stopping.");
+ log_unit_warning(UNIT(s), "Reload operation timed out. Killing reload process.");
+ service_kill_control_processes(s);
s->reload_result = SERVICE_FAILURE_TIMEOUT;
service_enter_running(s, SERVICE_SUCCESS);
break;
@@ -2803,12 +2982,15 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void *userdata) {
Service *s = SERVICE(userdata);
char t[FORMAT_TIMESPAN_MAX];
+ usec_t watchdog_usec;
assert(s);
assert(source == s->watchdog_event_source);
+ watchdog_usec = service_get_watchdog_usec(s);
+
log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!",
- format_timespan(t, sizeof(t), s->watchdog_usec, 1));
+ format_timespan(t, sizeof(t), watchdog_usec, 1));
service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG);
@@ -2930,13 +3112,28 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
}
/* Interpret WATCHDOG= */
- if (strv_find(tags, "WATCHDOG=1")) {
+ if (strv_find(tags, "WATCHDOG=1"))
service_reset_watchdog(s);
- }
- /* Add the passed fds to the fd store */
if (strv_find(tags, "FDSTORE=1")) {
- service_add_fd_store_set(s, fds);
+ const char *name;
+
+ name = strv_find_startswith(tags, "FDNAME=");
+ if (name && !fdname_is_valid(name)) {
+ log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring.");
+ name = NULL;
+ }
+
+ service_add_fd_store_set(s, fds, name);
+ }
+
+ e = strv_find_startswith(tags, "WATCHDOG_USEC=");
+ if (e) {
+ usec_t watchdog_override_usec;
+ if (safe_atou64(e, &watchdog_override_usec) < 0)
+ log_unit_warning(u, "Failed to parse WATCHDOG_USEC=%s", e);
+ else
+ service_reset_watchdog_timeout(s, watchdog_override_usec);
}
/* Notify clients about changed status or main pid */
@@ -2944,17 +3141,21 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
unit_add_to_dbus_queue(u);
}
-static int service_get_timeout(Unit *u, uint64_t *timeout) {
+static int service_get_timeout(Unit *u, usec_t *timeout) {
Service *s = SERVICE(u);
+ uint64_t t;
int r;
if (!s->timer_event_source)
return 0;
- r = sd_event_source_get_time(s->timer_event_source, timeout);
+ r = sd_event_source_get_time(s->timer_event_source, &t);
if (r < 0)
return r;
+ if (t == USEC_INFINITY)
+ return 0;
+ *timeout = t;
return 1;
}
@@ -2982,6 +3183,13 @@ static void service_bus_name_owner_change(
s->bus_name_good = !!new_owner;
+ /* Track the current owner, so we can reconstruct changes after a daemon reload */
+ r = free_and_strdup(&s->bus_name_owner, new_owner);
+ if (r < 0) {
+ log_unit_error_errno(u, r, "Unable to set new bus name owner %s: %m", new_owner);
+ return;
+ }
+
if (s->type == SERVICE_DBUS) {
/* service_enter_running() will figure out what to
@@ -2998,7 +3206,7 @@ static void service_bus_name_owner_change(
s->state == SERVICE_RUNNING ||
s->state == SERVICE_RELOAD)) {
- _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
pid_t pid;
/* Try to acquire PID from bus service */
@@ -3022,9 +3230,8 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
assert(s);
assert(fd >= 0);
- /* This is called by the socket code when instantiating a new
- * service for a stream socket and the socket needs to be
- * configured. */
+ /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs
+ * to be configured. We take ownership of the passed fd on success. */
if (UNIT(s)->load_state != UNIT_LOADED)
return -EINVAL;
@@ -3035,7 +3242,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
if (s->state != SERVICE_DEAD)
return -EAGAIN;
- if (getpeername_pretty(fd, &peer) >= 0) {
+ if (getpeername_pretty(fd, true, &peer) >= 0) {
if (UNIT(s)->description) {
_cleanup_free_ char *a;
@@ -3052,12 +3259,15 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
return r;
}
+ r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
+ if (r < 0)
+ return r;
+
s->socket_fd = fd;
s->socket_fd_selinux_context_net = selinux_context_net;
unit_ref_set(&s->accept_socket, UNIT(sock));
-
- return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
+ return 0;
}
static void service_reset_failed(Unit *u) {
@@ -3070,8 +3280,6 @@ static void service_reset_failed(Unit *u) {
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
-
- RATELIMIT_RESET(s->start_limit);
}
static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
@@ -3080,26 +3288,21 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
}
-static const char* const service_state_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = "dead",
- [SERVICE_START_PRE] = "start-pre",
- [SERVICE_START] = "start",
- [SERVICE_START_POST] = "start-post",
- [SERVICE_RUNNING] = "running",
- [SERVICE_EXITED] = "exited",
- [SERVICE_RELOAD] = "reload",
- [SERVICE_STOP] = "stop",
- [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
- [SERVICE_STOP_SIGTERM] = "stop-sigterm",
- [SERVICE_STOP_SIGKILL] = "stop-sigkill",
- [SERVICE_STOP_POST] = "stop-post",
- [SERVICE_FINAL_SIGTERM] = "final-sigterm",
- [SERVICE_FINAL_SIGKILL] = "final-sigkill",
- [SERVICE_FAILED] = "failed",
- [SERVICE_AUTO_RESTART] = "auto-restart",
-};
+static int service_main_pid(Unit *u) {
+ Service *s = SERVICE(u);
-DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
+ assert(s);
+
+ return s->main_pid;
+}
+
+static int service_control_pid(Unit *u) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
@@ -3160,7 +3363,7 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
[SERVICE_FAILURE_SIGNAL] = "signal",
[SERVICE_FAILURE_CORE_DUMP] = "core-dump",
[SERVICE_FAILURE_WATCHDOG] = "watchdog",
- [SERVICE_FAILURE_START_LIMIT] = "start-limit"
+ [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
@@ -3171,6 +3374,7 @@ const UnitVTable service_vtable = {
.cgroup_context_offset = offsetof(Service, cgroup_context),
.kill_context_offset = offsetof(Service, kill_context),
.exec_runtime_offset = offsetof(Service, exec_runtime),
+ .dynamic_creds_offset = offsetof(Service, dynamic_creds),
.sections =
"Unit\0"
@@ -3202,7 +3406,6 @@ const UnitVTable service_vtable = {
.sub_state_to_string = service_sub_state_to_string,
.check_gc = service_check_gc,
- .check_snapshot = service_check_snapshot,
.sigchld_event = service_sigchld_event,
@@ -3211,9 +3414,11 @@ const UnitVTable service_vtable = {
.notify_cgroup_empty = service_notify_cgroup_empty_event,
.notify_message = service_notify_message,
+ .main_pid = service_main_pid,
+ .control_pid = service_control_pid,
+
.bus_name_owner_change = service_bus_name_owner_change,
- .bus_interface = "org.freedesktop.systemd1.Service",
.bus_vtable = bus_service_vtable,
.bus_set_property = bus_service_set_property,
.bus_commit_properties = bus_service_commit_properties,
@@ -3229,13 +3434,10 @@ const UnitVTable service_vtable = {
.finished_start_job = {
[JOB_DONE] = "Started %s.",
[JOB_FAILED] = "Failed to start %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
- [JOB_TIMEOUT] = "Timed out starting %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Stopped %s.",
[JOB_FAILED] = "Stopped (with error) %s.",
- [JOB_TIMEOUT] = "Timed out stopping %s.",
},
},
};
diff --git a/src/core/service.h b/src/core/service.h
index 7da0a93961..888007cc0b 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -24,31 +22,10 @@
typedef struct Service Service;
typedef struct ServiceFDStore ServiceFDStore;
+#include "exit-status.h"
+#include "kill.h"
#include "path.h"
#include "ratelimit.h"
-#include "kill.h"
-#include "exit-status.h"
-
-typedef enum ServiceState {
- SERVICE_DEAD,
- SERVICE_START_PRE,
- SERVICE_START,
- SERVICE_START_POST,
- SERVICE_RUNNING,
- SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
- SERVICE_RELOAD,
- SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */
- SERVICE_STOP_SIGABRT, /* Watchdog timeout */
- SERVICE_STOP_SIGTERM,
- SERVICE_STOP_SIGKILL,
- SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
- SERVICE_FINAL_SIGKILL,
- SERVICE_FAILED,
- SERVICE_AUTO_RESTART,
- _SERVICE_STATE_MAX,
- _SERVICE_STATE_INVALID = -1
-} ServiceState;
typedef enum ServiceRestart {
SERVICE_RESTART_NO,
@@ -103,13 +80,13 @@ typedef enum NotifyState {
typedef enum ServiceResult {
SERVICE_SUCCESS,
- SERVICE_FAILURE_RESOURCES,
+ SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */
SERVICE_FAILURE_TIMEOUT,
SERVICE_FAILURE_EXIT_CODE,
SERVICE_FAILURE_SIGNAL,
SERVICE_FAILURE_CORE_DUMP,
SERVICE_FAILURE_WATCHDOG,
- SERVICE_FAILURE_START_LIMIT,
+ SERVICE_FAILURE_START_LIMIT_HIT,
_SERVICE_RESULT_MAX,
_SERVICE_RESULT_INVALID = -1
} ServiceResult;
@@ -118,6 +95,7 @@ struct ServiceFDStore {
Service *service;
int fd;
+ char *fdname;
sd_event_source *event_source;
LIST_FIELDS(ServiceFDStore, fd_store);
@@ -138,9 +116,12 @@ struct Service {
usec_t restart_usec;
usec_t timeout_start_usec;
usec_t timeout_stop_usec;
+ usec_t runtime_max_usec;
dual_timestamp watchdog_timestamp;
usec_t watchdog_usec;
+ usec_t watchdog_override_usec;
+ bool watchdog_override_enable;
sd_event_source *watchdog_event_source;
ExecCommand* exec_command[_SERVICE_EXEC_COMMAND_MAX];
@@ -167,13 +148,13 @@ struct Service {
/* Runtime data of the execution context */
ExecRuntime *exec_runtime;
+ DynamicCreds dynamic_creds;
pid_t main_pid, control_pid;
int socket_fd;
+ SocketPeer *peer;
bool socket_fd_selinux_context_net;
- int bus_endpoint_fd;
-
bool permissions_start_only;
bool root_directory_start_only;
bool remain_after_exit;
@@ -192,14 +173,12 @@ struct Service {
bool reset_cpu_usage:1;
char *bus_name;
+ char *bus_name_owner; /* unique name of the current owner */
char *status_text;
int status_errno;
- RateLimit start_limit;
- FailureAction start_limit_action;
FailureAction failure_action;
- char *reboot_arg;
UnitRef accept_socket;
@@ -212,14 +191,19 @@ struct Service {
ServiceFDStore *fd_store;
unsigned n_fd_store;
unsigned n_fd_store_max;
+
+ char *usb_function_descriptors;
+ char *usb_function_strings;
+
+ int stdin_fd;
+ int stdout_fd;
+ int stderr_fd;
};
extern const UnitVTable service_vtable;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
-
-const char* service_state_to_string(ServiceState i) _const_;
-ServiceState service_state_from_string(const char *s) _pure_;
+void service_close_socket_fd(Service *s);
const char* service_restart_to_string(ServiceRestart i) _const_;
ServiceRestart service_restart_from_string(const char *s) _pure_;
diff --git a/src/core/show-status.c b/src/core/show-status.c
index 02b1be73e3..59ebdc7219 100644
--- a/src/core/show-status.c
+++ b/src/core/show-status.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,7 +17,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "io-util.h"
+#include "parse-util.h"
#include "show-status.h"
+#include "string-util.h"
+#include "terminal-util.h"
#include "util.h"
int parse_show_status(const char *v, ShowStatus *ret) {
@@ -40,3 +44,81 @@ int parse_show_status(const char *v, ShowStatus *ret) {
*ret = r ? SHOW_STATUS_YES : SHOW_STATUS_NO;
return 0;
}
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
+ static const char status_indent[] = " "; /* "[" STATUS "] " */
+ _cleanup_free_ char *s = NULL;
+ _cleanup_close_ int fd = -1;
+ struct iovec iovec[6] = {};
+ int n = 0;
+ static bool prev_ephemeral;
+
+ assert(format);
+
+ /* This is independent of logging, as status messages are
+ * optional and go exclusively to the console. */
+
+ if (vasprintf(&s, format, ap) < 0)
+ return log_oom();
+
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ if (ellipse) {
+ char *e;
+ size_t emax, sl;
+ int c;
+
+ c = fd_columns(fd);
+ if (c <= 0)
+ c = 80;
+
+ sl = status ? sizeof(status_indent)-1 : 0;
+
+ emax = c - sl - 1;
+ if (emax < 3)
+ emax = 3;
+
+ e = ellipsize(s, emax, 50);
+ if (e) {
+ free(s);
+ s = e;
+ }
+ }
+
+ if (prev_ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+ prev_ephemeral = ephemeral;
+
+ if (status) {
+ if (!isempty(status)) {
+ IOVEC_SET_STRING(iovec[n++], "[");
+ IOVEC_SET_STRING(iovec[n++], status);
+ IOVEC_SET_STRING(iovec[n++], "] ");
+ } else
+ IOVEC_SET_STRING(iovec[n++], status_indent);
+ }
+
+ IOVEC_SET_STRING(iovec[n++], s);
+ if (!ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ if (writev(fd, iovec, n) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
+ va_list ap;
+ int r;
+
+ assert(format);
+
+ va_start(ap, format);
+ r = status_vprintf(status, ellipse, ephemeral, format, ap);
+ va_end(ap);
+
+ return r;
+}
diff --git a/src/core/show-status.h b/src/core/show-status.h
index a2b2153746..9a29e72645 100644
--- a/src/core/show-status.h
+++ b/src/core/show-status.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -21,6 +19,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
+#include "macro.h"
+
/* Manager status */
typedef enum ShowStatus {
@@ -32,3 +34,6 @@ typedef enum ShowStatus {
} ShowStatus;
int parse_show_status(const char *v, ShowStatus *ret);
+
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) _printf_(4,0);
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) _printf_(4,5);
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index aba16b4689..a795d875bb 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,35 +17,39 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/mman.h>
-#include <sys/reboot.h>
-#include <linux/reboot.h>
-#include <sys/stat.h>
-#include <sys/mount.h>
#include <errno.h>
-#include <unistd.h>
+#include <getopt.h>
+#include <linux/reboot.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <getopt.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <unistd.h>
-#include "missing.h"
-#include "log.h"
+#include "alloc-util.h"
+#include "cgroup-util.h"
+#include "def.h"
#include "fileio.h"
+#include "killall.h"
+#include "log.h"
+#include "missing.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "string-util.h"
+#include "switch-root.h"
+#include "terminal-util.h"
#include "umount.h"
#include "util.h"
#include "virt.h"
#include "watchdog.h"
-#include "killall.h"
-#include "cgroup-util.h"
-#include "def.h"
-#include "switch-root.h"
-#include "process-util.h"
-#include "terminal-util.h"
#define FINALIZE_ATTEMPTS 50
static char* arg_verb;
+static uint8_t arg_exit_code;
static int parse_argv(int argc, char *argv[]) {
enum {
@@ -55,6 +57,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_LOG_TARGET,
ARG_LOG_COLOR,
ARG_LOG_LOCATION,
+ ARG_EXIT_CODE,
};
static const struct option options[] = {
@@ -62,6 +65,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
{ "log-color", optional_argument, NULL, ARG_LOG_COLOR },
{ "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
+ { "exit-code", required_argument, NULL, ARG_EXIT_CODE },
{}
};
@@ -110,6 +114,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_EXIT_CODE:
+ r = safe_atou8(optarg, &arg_exit_code);
+ if (r < 0)
+ log_error("Failed to parse exit code %s, ignoring", optarg);
+
+ break;
+
case '\001':
if (!arg_verb)
arg_verb = optarg;
@@ -146,7 +157,6 @@ static int switch_root_initramfs(void) {
return switch_root("/run/initramfs", "/oldroot", false, MS_BIND);
}
-
int main(int argc, char *argv[]) {
bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
bool in_container, use_watchdog = false;
@@ -183,27 +193,34 @@ int main(int argc, char *argv[]) {
cmd = RB_HALT_SYSTEM;
else if (streq(arg_verb, "kexec"))
cmd = LINUX_REBOOT_CMD_KEXEC;
+ else if (streq(arg_verb, "exit"))
+ cmd = 0; /* ignored, just checking that arg_verb is valid */
else {
r = -EINVAL;
log_error("Unknown action '%s'.", arg_verb);
goto error;
}
- cg_get_root_path(&cgroup);
+ (void) cg_get_root_path(&cgroup);
+ in_container = detect_container() > 0;
use_watchdog = !!getenv("WATCHDOG_USEC");
- /* lock us into memory */
+ /* Lock us into memory */
mlockall(MCL_CURRENT|MCL_FUTURE);
+ /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that
+ * slow IO is processed here already and the final process killing spree is not impacted by processes
+ * desperately trying to sync IO to disk within their timeout. */
+ if (!in_container)
+ sync();
+
log_info("Sending SIGTERM to remaining processes...");
broadcast_signal(SIGTERM, true, true);
log_info("Sending SIGKILL to remaining processes...");
broadcast_signal(SIGKILL, true, false);
- in_container = detect_container(NULL) > 0;
-
need_umount = !in_container;
need_swapoff = !in_container;
need_loop_detach = !in_container;
@@ -332,13 +349,23 @@ int main(int argc, char *argv[]) {
need_loop_detach ? " loop devices," : "",
need_dm_detach ? " DM devices," : "");
- /* 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. */
+ /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
+ * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
+ * sync'ed things already once above, but we did some more work since then which might have caused IO, hence
+ * let's doit once more. */
if (!in_container)
sync();
+ if (streq(arg_verb, "exit")) {
+ if (in_container)
+ exit(arg_exit_code);
+ else {
+ /* We cannot exit() on the host, fallback on another
+ * method. */
+ cmd = RB_POWER_OFF;
+ }
+ }
+
switch (cmd) {
case LINUX_REBOOT_CMD_KEXEC:
@@ -374,9 +401,14 @@ int main(int argc, char *argv[]) {
if (!in_container) {
_cleanup_free_ char *param = NULL;
- if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
+ r = read_one_line_file("/run/systemd/reboot-param", &param);
+ if (r < 0)
+ log_warning_errno(r, "Failed to read reboot parameter file: %m");
+
+ if (!isempty(param)) {
log_info("Rebooting with argument '%s'.", param);
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param);
+ log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m");
}
}
@@ -404,11 +436,9 @@ int main(int argc, char *argv[]) {
exit(0);
}
- log_error_errno(errno, "Failed to invoke reboot(): %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to invoke reboot(): %m");
error:
log_emergency_errno(r, "Critical error while doing system shutdown: %m");
-
freeze();
}
diff --git a/src/core/slice.c b/src/core/slice.c
index e52bf71515..03fe797f27 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,18 +19,28 @@
#include <errno.h>
-#include "unit.h"
-#include "slice.h"
-#include "log.h"
+#include "alloc-util.h"
#include "dbus-slice.h"
+#include "log.h"
+#include "slice.h"
#include "special.h"
+#include "string-util.h"
+#include "strv.h"
#include "unit-name.h"
+#include "unit.h"
static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
[SLICE_DEAD] = UNIT_INACTIVE,
[SLICE_ACTIVE] = UNIT_ACTIVE
};
+static void slice_init(Unit *u) {
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ u->ignore_on_isolate = true;
+}
+
static void slice_set_state(Slice *t, SliceState state) {
SliceState old_state;
assert(t);
@@ -82,6 +90,9 @@ static int slice_add_default_dependencies(Slice *s) {
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
/* Make sure slices are unloaded on shutdown */
r = unit_add_two_dependencies_by_name(
UNIT(s),
@@ -93,7 +104,6 @@ static int slice_add_default_dependencies(Slice *s) {
return 0;
}
-
static int slice_verify(Slice *s) {
_cleanup_free_ char *parent = NULL;
int r;
@@ -125,6 +135,7 @@ static int slice_load(Unit *u) {
int r;
assert(s);
+ assert(u->load_state == UNIT_STUB);
r = unit_load_fragment_and_dropin_optional(u);
if (r < 0)
@@ -141,11 +152,9 @@ static int slice_load(Unit *u) {
if (r < 0)
return r;
- if (u->default_dependencies) {
- r = slice_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = slice_add_default_dependencies(s);
+ if (r < 0)
+ return r;
}
return slice_verify(s);
@@ -178,10 +187,15 @@ static void slice_dump(Unit *u, FILE *f, const char *prefix) {
static int slice_start(Unit *u) {
Slice *t = SLICE(u);
+ int r;
assert(t);
assert(t->state == SLICE_DEAD);
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
(void) unit_realize_cgroup(u);
(void) unit_reset_cpu_usage(u);
@@ -252,12 +266,43 @@ _pure_ static const char *slice_sub_state_to_string(Unit *u) {
return slice_state_to_string(SLICE(u)->state);
}
-static const char* const slice_state_table[_SLICE_STATE_MAX] = {
- [SLICE_DEAD] = "dead",
- [SLICE_ACTIVE] = "active"
-};
+static void slice_enumerate(Manager *m) {
+ Unit *u;
+ int r;
+
+ assert(m);
+
+ u = manager_get_unit(m, SPECIAL_ROOT_SLICE);
+ if (!u) {
+ u = unit_new(m, sizeof(Slice));
+ if (!u) {
+ log_oom();
+ return;
+ }
+
+ r = unit_add_name(u, SPECIAL_ROOT_SLICE);
+ if (r < 0) {
+ unit_free(u);
+ log_error_errno(r, "Failed to add -.slice name");
+ return;
+ }
+ }
-DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
+ u->default_dependencies = false;
+ u->no_gc = true;
+ u->ignore_on_isolate = true;
+ u->refuse_manual_start = true;
+ u->refuse_manual_stop = true;
+ SLICE(u)->deserialized_state = SLICE_ACTIVE;
+
+ if (!u->description)
+ u->description = strdup("Root Slice");
+ if (!u->documentation)
+ (void) strv_extend(&u->documentation, "man:systemd.special(7)");
+
+ unit_add_to_load_queue(u);
+ unit_add_to_dbus_queue(u);
+}
const UnitVTable slice_vtable = {
.object_size = sizeof(Slice),
@@ -269,9 +314,9 @@ const UnitVTable slice_vtable = {
"Install\0",
.private_section = "Slice",
- .no_alias = true,
- .no_instances = true,
+ .can_transient = true,
+ .init = slice_init,
.load = slice_load,
.coldplug = slice_coldplug,
@@ -289,15 +334,15 @@ const UnitVTable slice_vtable = {
.active_state = slice_active_state,
.sub_state_to_string = slice_sub_state_to_string,
- .bus_interface = "org.freedesktop.systemd1.Slice",
.bus_vtable = bus_slice_vtable,
.bus_set_property = bus_slice_set_property,
.bus_commit_properties = bus_slice_commit_properties,
+ .enumerate = slice_enumerate,
+
.status_message_formats = {
.finished_start_job = {
[JOB_DONE] = "Created slice %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Removed slice %s.",
diff --git a/src/core/slice.h b/src/core/slice.h
index ac648e56f8..c9f3f61067 100644
--- a/src/core/slice.h
+++ b/src/core/slice.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,14 +21,6 @@
typedef struct Slice Slice;
-
-typedef enum SliceState {
- SLICE_DEAD,
- SLICE_ACTIVE,
- _SLICE_STATE_MAX,
- _SLICE_STATE_INVALID = -1
-} SliceState;
-
struct Slice {
Unit meta;
@@ -40,6 +30,3 @@ struct Slice {
};
extern const UnitVTable slice_vtable;
-
-const char* slice_state_to_string(SliceState i) _const_;
-SliceState slice_state_from_string(const char *s) _pure_;
diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
index cbe7d0b4a9..5a6d11cfa1 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,18 +19,22 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <stdio.h>
+#include <dirent.h>
#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
#include <fcntl.h>
-#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "alloc-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "log.h"
#include "macro.h"
#include "smack-setup.h"
+#include "string-util.h"
#include "util.h"
-#include "fileio.h"
-#include "log.h"
#ifdef HAVE_SMACK
@@ -193,6 +195,75 @@ static int write_cipso2_rules(const char* srcdir) {
return r;
}
+static int write_netlabel_rules(const char* srcdir) {
+ _cleanup_fclose_ FILE *dst = NULL;
+ _cleanup_closedir_ DIR *dir = NULL;
+ struct dirent *entry;
+ char buf[NAME_MAX];
+ int dfd = -1;
+ int r = 0;
+
+ dst = fopen("/sys/fs/smackfs/netlabel", "we");
+ if (!dst) {
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Failed to open /sys/fs/smackfs/netlabel: %m");
+ return -errno; /* negative error */
+ }
+
+ /* write rules to dst from every file in the directory */
+ dir = opendir(srcdir);
+ if (!dir) {
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Failed to opendir %s: %m", srcdir);
+ return errno; /* positive on purpose */
+ }
+
+ dfd = dirfd(dir);
+ assert(dfd >= 0);
+
+ FOREACH_DIRENT(entry, dir, return 0) {
+ int fd;
+ _cleanup_fclose_ FILE *policy = NULL;
+
+ fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ if (r == 0)
+ r = -errno;
+ log_warning_errno(errno, "Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ policy = fdopen(fd, "re");
+ if (!policy) {
+ if (r == 0)
+ r = -errno;
+ safe_close(fd);
+ log_error_errno(errno, "Failed to open %s: %m", entry->d_name);
+ continue;
+ }
+
+ /* load2 write rules in the kernel require a line buffered stream */
+ FOREACH_LINE(buf, policy,
+ log_error_errno(errno, "Failed to read line from %s: %m",
+ entry->d_name)) {
+ if (!fputs(buf, dst)) {
+ if (r == 0)
+ r = -EINVAL;
+ log_error_errno(errno, "Failed to write line to /sys/fs/smackfs/netlabel");
+ break;
+ }
+ if (fflush(dst)) {
+ if (r == 0)
+ r = -errno;
+ log_error_errno(errno, "Failed to flush writes to /sys/fs/smackfs/netlabel: %m");
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
#endif
int mac_smack_setup(bool *loaded_policy) {
@@ -215,16 +286,24 @@ int mac_smack_setup(bool *loaded_policy) {
log_info("Successfully loaded Smack policies.");
break;
default:
- log_warning("Failed to load Smack access rules: %s, ignoring.",
- strerror(abs(r)));
+ log_warning_errno(r, "Failed to load Smack access rules, ignoring: %m");
return 0;
}
#ifdef SMACK_RUN_LABEL
r = write_string_file("/proc/self/attr/current", SMACK_RUN_LABEL, 0);
- if (r)
- log_warning("Failed to set SMACK label \"%s\" on self: %s",
- SMACK_RUN_LABEL, strerror(-r));
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK label \"" SMACK_RUN_LABEL "\" on self: %m");
+ r = write_string_file("/sys/fs/smackfs/ambient", SMACK_RUN_LABEL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK ambient label \"" SMACK_RUN_LABEL "\": %m");
+ r = write_string_file("/sys/fs/smackfs/netlabel",
+ "0.0.0.0/0 " SMACK_RUN_LABEL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK netlabel rule \"0.0.0.0/0 " SMACK_RUN_LABEL "\": %m");
+ r = write_string_file("/sys/fs/smackfs/netlabel", "127.0.0.1 -CIPSO", 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set SMACK netlabel rule \"127.0.0.1 -CIPSO\": %m");
#endif
r = write_cipso2_rules("/etc/smack/cipso.d/");
@@ -234,14 +313,29 @@ int mac_smack_setup(bool *loaded_policy) {
return 0;
case ENOENT:
log_debug("Smack/CIPSO access rules directory '/etc/smack/cipso.d/' not found");
- return 0;
+ break;
case 0:
log_info("Successfully loaded Smack/CIPSO policies.");
break;
default:
- log_warning("Failed to load Smack/CIPSO access rules: %s, ignoring.",
- strerror(abs(r)));
+ log_warning_errno(r, "Failed to load Smack/CIPSO access rules, ignoring: %m");
+ break;
+ }
+
+ r = write_netlabel_rules("/etc/smack/netlabel.d/");
+ switch(r) {
+ case -ENOENT:
+ log_debug("Smack/CIPSO is not enabled in the kernel.");
return 0;
+ case ENOENT:
+ log_debug("Smack network host rules directory '/etc/smack/netlabel.d/' not found");
+ break;
+ case 0:
+ log_info("Successfully loaded Smack network host rules.");
+ break;
+ default:
+ log_warning_errno(r, "Failed to load Smack network host rules: %m, ignoring.");
+ break;
}
*loaded_policy = true;
diff --git a/src/core/smack-setup.h b/src/core/smack-setup.h
index 1cab7718ff..78164c85e6 100644
--- a/src/core/smack-setup.h
+++ b/src/core/smack-setup.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
deleted file mode 100644
index 1e634b9bc1..0000000000
--- a/src/core/snapshot.c
+++ /dev/null
@@ -1,308 +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 "unit.h"
-#include "snapshot.h"
-#include "unit-name.h"
-#include "dbus-snapshot.h"
-#include "bus-common-errors.h"
-
-static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
- [SNAPSHOT_DEAD] = UNIT_INACTIVE,
- [SNAPSHOT_ACTIVE] = UNIT_ACTIVE
-};
-
-static void snapshot_init(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(UNIT(s)->load_state == UNIT_STUB);
-
- UNIT(s)->ignore_on_isolate = true;
- UNIT(s)->ignore_on_snapshot = true;
- UNIT(s)->allow_isolate = true;
-}
-
-static void snapshot_set_state(Snapshot *s, SnapshotState state) {
- SnapshotState old_state;
- assert(s);
-
- old_state = s->state;
- s->state = state;
-
- if (state != old_state)
- log_unit_debug(UNIT(s), "Changed %s -> %s", snapshot_state_to_string(old_state), snapshot_state_to_string(state));
-
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
-}
-
-static int snapshot_load(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(u);
- assert(u->load_state == UNIT_STUB);
-
- /* Make sure that only snapshots created via snapshot_create()
- * can be loaded */
- if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
- return -ENOENT;
-
- u->load_state = UNIT_LOADED;
- return 0;
-}
-
-static int snapshot_coldplug(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_DEAD);
-
- if (s->deserialized_state != s->state)
- snapshot_set_state(s, s->deserialized_state);
-
- return 0;
-}
-
-static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(f);
-
- fprintf(f,
- "%sSnapshot State: %s\n"
- "%sClean Up: %s\n",
- prefix, snapshot_state_to_string(s->state),
- prefix, yes_no(s->cleanup));
-}
-
-static int snapshot_start(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_DEAD);
-
- snapshot_set_state(s, SNAPSHOT_ACTIVE);
-
- if (s->cleanup)
- unit_add_to_cleanup_queue(u);
-
- return 1;
-}
-
-static int snapshot_stop(Unit *u) {
- Snapshot *s = SNAPSHOT(u);
-
- assert(s);
- assert(s->state == SNAPSHOT_ACTIVE);
-
- snapshot_set_state(s, SNAPSHOT_DEAD);
- return 1;
-}
-
-static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
- Snapshot *s = SNAPSHOT(u);
- Unit *other;
- Iterator i;
-
- assert(s);
- assert(f);
- assert(fds);
-
- unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
- unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
- SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
- unit_serialize_item(u, f, "wants", other->id);
-
- return 0;
-}
-
-static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
- Snapshot *s = SNAPSHOT(u);
- int r;
-
- assert(u);
- assert(key);
- assert(value);
- assert(fds);
-
- if (streq(key, "state")) {
- SnapshotState state;
-
- state = snapshot_state_from_string(value);
- if (state < 0)
- log_unit_debug(u, "Failed to parse state value: %s", value);
- else
- s->deserialized_state = state;
-
- } else if (streq(key, "cleanup")) {
-
- r = parse_boolean(value);
- if (r < 0)
- log_unit_debug(u, "Failed to parse cleanup value: %s", value);
- else
- s->cleanup = r;
-
- } else if (streq(key, "wants")) {
-
- r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true);
- if (r < 0)
- return r;
- } else
- log_unit_debug(u, "Unknown serialization key: %s", key);
-
- return 0;
-}
-
-_pure_ static UnitActiveState snapshot_active_state(Unit *u) {
- assert(u);
-
- return state_translation_table[SNAPSHOT(u)->state];
-}
-
-_pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
- assert(u);
-
- return snapshot_state_to_string(SNAPSHOT(u)->state);
-}
-
-int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **_s) {
- _cleanup_free_ char *n = NULL;
- Unit *other, *u = NULL;
- Iterator i;
- int r;
- const char *k;
-
- assert(m);
- assert(_s);
-
- if (name) {
- if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
- return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
-
- if (!endswith(name, ".snapshot"))
- return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name);
-
- if (manager_get_unit(m, name))
- return sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
-
- } else {
-
- for (;;) {
- if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
- return -ENOMEM;
-
- if (!manager_get_unit(m, n)) {
- name = n;
- break;
- }
-
- free(n);
- n = NULL;
- }
- }
-
- r = manager_load_unit_prepare(m, name, NULL, e, &u);
- if (r < 0)
- goto fail;
-
- u->transient = true;
- manager_dispatch_load_queue(m);
- assert(u->load_state == UNIT_LOADED);
-
- HASHMAP_FOREACH_KEY(other, k, m->units, i) {
-
- if (other->ignore_on_snapshot ||
- other->transient)
- continue;
-
- if (k != other->id)
- continue;
-
- if (UNIT_VTABLE(other)->check_snapshot)
- if (!UNIT_VTABLE(other)->check_snapshot(other))
- continue;
-
- if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- continue;
-
- r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
- if (r < 0)
- goto fail;
- }
-
- SNAPSHOT(u)->cleanup = cleanup;
- *_s = SNAPSHOT(u);
-
- log_unit_info(u, "Created snapshot.");
-
- return 0;
-
-fail:
- if (u)
- unit_add_to_cleanup_queue(u);
-
- return r;
-}
-
-void snapshot_remove(Snapshot *s) {
- assert(s);
-
- log_unit_info(UNIT(s), "Removing snapshot.");
-
- unit_add_to_cleanup_queue(UNIT(s));
-}
-
-static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
- [SNAPSHOT_DEAD] = "dead",
- [SNAPSHOT_ACTIVE] = "active"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
-
-const UnitVTable snapshot_vtable = {
- .object_size = sizeof(Snapshot),
-
- .no_alias = true,
- .no_instances = true,
- .no_gc = true,
-
- .init = snapshot_init,
- .load = snapshot_load,
-
- .coldplug = snapshot_coldplug,
-
- .dump = snapshot_dump,
-
- .start = snapshot_start,
- .stop = snapshot_stop,
-
- .serialize = snapshot_serialize,
- .deserialize_item = snapshot_deserialize_item,
-
- .active_state = snapshot_active_state,
- .sub_state_to_string = snapshot_sub_state_to_string,
-
- .bus_interface = "org.freedesktop.systemd1.Snapshot",
- .bus_vtable = bus_snapshot_vtable
-};
diff --git a/src/core/snapshot.h b/src/core/snapshot.h
deleted file mode 100644
index f2451b1193..0000000000
--- a/src/core/snapshot.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- 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/>.
-***/
-
-typedef struct Snapshot Snapshot;
-
-
-typedef enum SnapshotState {
- SNAPSHOT_DEAD,
- SNAPSHOT_ACTIVE,
- _SNAPSHOT_STATE_MAX,
- _SNAPSHOT_STATE_INVALID = -1
-} SnapshotState;
-
-struct Snapshot {
- Unit meta;
-
- SnapshotState state, deserialized_state;
-
- bool cleanup;
-};
-
-extern const UnitVTable snapshot_vtable;
-
-int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **s);
-void snapshot_remove(Snapshot *s);
-
-const char* snapshot_state_to_string(SnapshotState i) _const_;
-SnapshotState snapshot_state_from_string(const char *s) _pure_;
diff --git a/src/core/socket.c b/src/core/socket.c
index 693cbc6080..0b1c4acfec 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,37 +17,54 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <sys/stat.h>
-#include <unistd.h>
+#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
-#include <sys/epoll.h>
-#include <signal.h>
-#include <arpa/inet.h>
-#include <netinet/tcp.h>
#include <mqueue.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+#include <sys/epoll.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <linux/sctp.h>
-#include "sd-event.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "copy.h"
+#include "dbus-socket.h"
+#include "def.h"
+#include "exit-status.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "io-util.h"
+#include "label.h"
#include "log.h"
-#include "strv.h"
+#include "missing.h"
#include "mkdir.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "unit-name.h"
-#include "unit-printf.h"
-#include "missing.h"
-#include "special.h"
-#include "label.h"
-#include "exit-status.h"
-#include "def.h"
-#include "smack-util.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "process-util.h"
#include "selinux-util.h"
-#include "dbus-socket.h"
-#include "unit.h"
-#include "formats-util.h"
#include "signal-util.h"
+#include "smack-util.h"
#include "socket.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "user-util.h"
+#include "in-addr-util.h"
+
+struct SocketPeer {
+ unsigned n_ref;
+
+ Socket *socket;
+ union sockaddr_union peer;
+};
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = UNIT_INACTIVE,
@@ -92,6 +107,9 @@ static void socket_init(Unit *u) {
s->exec_context.std_error = u->manager->default_std_error;
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+
+ s->trigger_limit.interval = USEC_INFINITY;
+ s->trigger_limit.burst = (unsigned) -1;
}
static void socket_unwatch_control_pid(Socket *s) {
@@ -104,6 +122,14 @@ static void socket_unwatch_control_pid(Socket *s) {
s->control_pid = 0;
}
+static void socket_cleanup_fd_list(SocketPort *p) {
+ assert(p);
+
+ close_many(p->auxiliary_fds, p->n_auxiliary_fds);
+ p->auxiliary_fds = mfree(p->auxiliary_fds);
+ p->n_auxiliary_fds = 0;
+}
+
void socket_free_ports(Socket *s) {
SocketPort *p;
@@ -114,6 +140,7 @@ void socket_free_ports(Socket *s) {
sd_event_source_unref(p->event_source);
+ socket_cleanup_fd_list(p);
safe_close(p->fd);
free(p->path);
free(p);
@@ -122,60 +149,65 @@ void socket_free_ports(Socket *s) {
static void socket_done(Unit *u) {
Socket *s = SOCKET(u);
+ SocketPeer *p;
assert(s);
socket_free_ports(s);
+ while ((p = set_steal_first(s->peers_by_address)))
+ p->socket = NULL;
+
+ s->peers_by_address = set_free(s->peers_by_address);
+
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
+ dynamic_creds_unref(&s->dynamic_creds);
+
socket_unwatch_control_pid(s);
unit_ref_unset(&s->service);
- free(s->tcp_congestion);
- s->tcp_congestion = NULL;
-
- free(s->bind_to_device);
- s->bind_to_device = NULL;
+ s->tcp_congestion = mfree(s->tcp_congestion);
+ s->bind_to_device = mfree(s->bind_to_device);
- free(s->smack);
- free(s->smack_ip_in);
- free(s->smack_ip_out);
+ s->smack = mfree(s->smack);
+ s->smack_ip_in = mfree(s->smack_ip_in);
+ s->smack_ip_out = mfree(s->smack_ip_out);
strv_free(s->symlinks);
- free(s->user);
- free(s->group);
+ s->user = mfree(s->user);
+ s->group = mfree(s->group);
+
+ s->fdname = mfree(s->fdname);
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
-static int socket_arm_timer(Socket *s) {
+static int socket_arm_timer(Socket *s, usec_t usec) {
int r;
assert(s);
- if (s->timeout_usec <= 0) {
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return 0;
- }
-
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
+ usec, 0,
socket_dispatch_timer, s);
if (r < 0)
return r;
@@ -214,7 +246,6 @@ int socket_instantiate_service(Socket *s) {
if (r < 0)
return r;
- u->no_gc = true;
unit_ref_set(&s->service, u);
return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false);
@@ -251,7 +282,7 @@ static int socket_add_mount_links(Socket *s) {
if (p->type == SOCKET_SOCKET)
path = socket_address_get_path(&p->address);
- else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL)
+ else if (IN_SET(p->type, SOCKET_FIFO, SOCKET_SPECIAL, SOCKET_USB_FUNCTION))
path = p->path;
if (!path)
@@ -274,18 +305,21 @@ static int socket_add_device_link(Socket *s) {
return 0;
t = strjoina("/sys/subsystem/net/devices/", s->bind_to_device);
- return unit_add_node_link(UNIT(s), t, false);
+ return unit_add_node_link(UNIT(s), t, false, UNIT_BINDS_TO);
}
static int socket_add_default_dependencies(Socket *s) {
int r;
assert(s);
+ if (!UNIT(s)->default_dependencies)
+ return 0;
+
r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true);
if (r < 0)
return r;
- if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -311,6 +345,25 @@ static int socket_add_extras(Socket *s) {
assert(s);
+ /* Pick defaults for the trigger limit, if nothing was explicitly configured. We pick a relatively high limit
+ * in Accept=yes mode, and a lower limit for Accept=no. Reason: in Accept=yes mode we are invoking accept()
+ * ourselves before the trigger limit can hit, thus incoming connections are taken off the socket queue quickly
+ * and reliably. This is different for Accept=no, where the spawned service has to take the incoming traffic
+ * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to
+ * process whatever is queued in one go, and thus should normally never have to be started frequently. This is
+ * different for Accept=yes where each connection is processed by a new service instance, and thus frequent
+ * service starts are typical. */
+
+ if (s->trigger_limit.interval == USEC_INFINITY)
+ s->trigger_limit.interval = 2 * USEC_PER_SEC;
+
+ if (s->trigger_limit.burst == (unsigned) -1) {
+ if (s->accept)
+ s->trigger_limit.burst = 200;
+ else
+ s->trigger_limit.burst = 20;
+ }
+
if (have_non_accept_socket(s)) {
if (!UNIT_DEREF(s->service)) {
@@ -345,16 +398,14 @@ static int socket_add_extras(Socket *s) {
if (r < 0)
return r;
- r = unit_add_default_slice(u, &s->cgroup_context);
+ r = unit_set_default_slice(u);
if (r < 0)
return r;
}
- if (u->default_dependencies) {
- r = socket_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = socket_add_default_dependencies(s);
+ if (r < 0)
+ return r;
return 0;
}
@@ -431,6 +482,40 @@ static int socket_verify(Socket *s) {
return 0;
}
+static void peer_address_hash_func(const void *p, struct siphash *state) {
+ const SocketPeer *s = p;
+
+ assert(s);
+ assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6));
+
+ if (s->peer.sa.sa_family == AF_INET)
+ siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state);
+ else
+ siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state);
+}
+
+static int peer_address_compare_func(const void *a, const void *b) {
+ const SocketPeer *x = a, *y = b;
+
+ if (x->peer.sa.sa_family < y->peer.sa.sa_family)
+ return -1;
+ if (x->peer.sa.sa_family > y->peer.sa.sa_family)
+ return 1;
+
+ switch(x->peer.sa.sa_family) {
+ case AF_INET:
+ return memcmp(&x->peer.in.sin_addr, &y->peer.in.sin_addr, sizeof(x->peer.in.sin_addr));
+ case AF_INET6:
+ return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr));
+ }
+ assert_not_reached("Black sheep in the family!");
+}
+
+const struct hash_ops peer_address_hash_ops = {
+ .hash = peer_address_hash_func,
+ .compare = peer_address_compare_func
+};
+
static int socket_load(Unit *u) {
Socket *s = SOCKET(u);
int r;
@@ -438,6 +523,10 @@ static int socket_load(Unit *u) {
assert(u);
assert(u->load_state == UNIT_STUB);
+ r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops);
+ if (r < 0)
+ return r;
+
r = unit_load_fragment_and_dropin(u);
if (r < 0)
return r;
@@ -452,6 +541,87 @@ static int socket_load(Unit *u) {
return socket_verify(s);
}
+static SocketPeer *socket_peer_new(void) {
+ SocketPeer *p;
+
+ p = new0(SocketPeer, 1);
+ if (!p)
+ return NULL;
+
+ p->n_ref = 1;
+
+ return p;
+}
+
+SocketPeer *socket_peer_ref(SocketPeer *p) {
+ if (!p)
+ return NULL;
+
+ assert(p->n_ref > 0);
+ p->n_ref++;
+
+ return p;
+}
+
+SocketPeer *socket_peer_unref(SocketPeer *p) {
+ if (!p)
+ return NULL;
+
+ assert(p->n_ref > 0);
+
+ p->n_ref--;
+
+ if (p->n_ref > 0)
+ return NULL;
+
+ if (p->socket)
+ set_remove(p->socket->peers_by_address, p);
+
+ return mfree(p);
+}
+
+int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) {
+ _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL;
+ SocketPeer sa = {}, *i;
+ socklen_t salen = sizeof(sa.peer);
+ int r;
+
+ assert(fd >= 0);
+ assert(s);
+
+ r = getpeername(fd, &sa.peer.sa, &salen);
+ if (r < 0)
+ return log_error_errno(errno, "getpeername failed: %m");
+
+ if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6)) {
+ *p = NULL;
+ return 0;
+ }
+
+ i = set_get(s->peers_by_address, &sa);
+ if (i) {
+ *p = socket_peer_ref(i);
+ return 1;
+ }
+
+ remote = socket_peer_new();
+ if (!remote)
+ return log_oom();
+
+ remote->peer = sa.peer;
+
+ r = set_put(s->peers_by_address, remote);
+ if (r < 0)
+ return r;
+
+ remote->socket = s;
+
+ *p = remote;
+ remote = NULL;
+
+ return 1;
+}
+
_const_ static const char* listen_lookup(int family, int type) {
if (family == AF_NETLINK)
@@ -497,6 +667,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sPassSecurity: %s\n"
"%sTCPCongestion: %s\n"
"%sRemoveOnStop: %s\n"
+ "%sWritable: %s\n"
+ "%sFDName: %s\n"
"%sSELinuxContextFromNet: %s\n",
prefix, socket_state_to_string(s->state),
prefix, socket_result_to_string(s->result),
@@ -513,6 +685,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(s->pass_sec),
prefix, strna(s->tcp_congestion),
prefix, yes_no(s->remove_on_stop),
+ prefix, yes_no(s->writable),
+ prefix, socket_fdname(s),
prefix, yes_no(s->selinux_context_from_net));
if (s->control_pid > 0)
@@ -601,8 +775,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
if (!isempty(s->user) || !isempty(s->group))
fprintf(f,
- "%sOwnerUser: %s\n"
- "%sOwnerGroup: %s\n",
+ "%sSocketUser: %s\n"
+ "%sSocketGroup: %s\n",
prefix, strna(s->user),
prefix, strna(s->group));
@@ -633,7 +807,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
int r;
char *k = NULL;
- if ((r = socket_address_print(&p->address, &k)) < 0)
+ r = socket_address_print(&p->address, &k);
+ if (r < 0)
t = strerror(-r);
else
t = k;
@@ -642,12 +817,20 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
free(k);
} else if (p->type == SOCKET_SPECIAL)
fprintf(f, "%sListenSpecial: %s\n", prefix, p->path);
+ else if (p->type == SOCKET_USB_FUNCTION)
+ fprintf(f, "%sListenUSBFunction: %s\n", prefix, p->path);
else if (p->type == SOCKET_MQUEUE)
fprintf(f, "%sListenMessageQueue: %s\n", prefix, p->path);
else
fprintf(f, "%sListenFIFO: %s\n", prefix, p->path);
}
+ fprintf(f,
+ "%sTriggerLimitIntervalSec: %s\n"
+ "%sTriggerLimitBurst: %u\n",
+ prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC),
+ prefix, s->trigger_limit.burst);
+
exec_context_dump(&s->exec_context, f, prefix);
kill_context_dump(&s->kill_context, f, prefix);
@@ -682,16 +865,16 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
case AF_INET: {
uint32_t
- a = ntohl(local.in.sin_addr.s_addr),
- b = ntohl(remote.in.sin_addr.s_addr);
+ a = be32toh(local.in.sin_addr.s_addr),
+ b = be32toh(remote.in.sin_addr.s_addr);
if (asprintf(&r,
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
nr,
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
- ntohs(local.in.sin_port),
+ be16toh(local.in.sin_port),
b >> 24, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
- ntohs(remote.in.sin_port)) < 0)
+ be16toh(remote.in.sin_port)) < 0)
return -ENOMEM;
break;
@@ -712,9 +895,9 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
nr,
a[0], a[1], a[2], a[3],
- ntohs(local.in6.sin6_port),
+ be16toh(local.in6.sin6_port),
b[0], b[1], b[2], b[3],
- ntohs(remote.in6.sin6_port)) < 0)
+ be16toh(remote.in6.sin6_port)) < 0)
return -ENOMEM;
} else {
char a[INET6_ADDRSTRLEN], b[INET6_ADDRSTRLEN];
@@ -723,9 +906,9 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
"%u-%s:%u-%s:%u",
nr,
inet_ntop(AF_INET6, &local.in6.sin6_addr, a, sizeof(a)),
- ntohs(local.in6.sin6_port),
+ be16toh(local.in6.sin6_port),
inet_ntop(AF_INET6, &remote.in6.sin6_addr, b, sizeof(b)),
- ntohs(remote.in6.sin6_port)) < 0)
+ be16toh(remote.in6.sin6_port)) < 0)
return -ENOMEM;
}
@@ -771,46 +954,45 @@ static void socket_close_fds(Socket *s) {
assert(s);
LIST_FOREACH(port, p, s->ports) {
+ bool was_open;
- p->event_source = sd_event_source_unref(p->event_source);
-
- if (p->fd < 0)
- continue;
+ was_open = p->fd >= 0;
+ p->event_source = sd_event_source_unref(p->event_source);
p->fd = safe_close(p->fd);
+ socket_cleanup_fd_list(p);
- /* One little note: we should normally not delete any
- * sockets in the file system here! After all some
- * other process we spawned might still have a
- * reference of this fd and wants to continue to use
- * it. Therefore we delete sockets in the file system
- * before we create a new one, not after we stopped
- * using one! */
+ /* One little note: we should normally not delete any sockets in the file system here! After all some
+ * other process we spawned might still have a reference of this fd and wants to continue to use
+ * it. Therefore we normally delete sockets in the file system before we create a new one, not after we
+ * stopped using one! That all said, if the user explicitly requested this, we'll delete them here
+ * anyway, but only then. */
- if (s->remove_on_stop) {
- switch (p->type) {
+ if (!was_open || !s->remove_on_stop)
+ continue;
- case SOCKET_FIFO:
- unlink(p->path);
- break;
+ switch (p->type) {
- case SOCKET_MQUEUE:
- mq_unlink(p->path);
- break;
+ case SOCKET_FIFO:
+ (void) unlink(p->path);
+ break;
- case SOCKET_SOCKET:
- socket_address_unlink(&p->address);
- break;
+ case SOCKET_MQUEUE:
+ (void) mq_unlink(p->path);
+ break;
- default:
- break;
- }
+ case SOCKET_SOCKET:
+ (void) socket_address_unlink(&p->address);
+ break;
+
+ default:
+ break;
}
}
if (s->remove_on_stop)
STRV_FOREACH(i, s->symlinks)
- unlink(*i);
+ (void) unlink(*i);
}
static void socket_apply_socket_options(Socket *s, int fd) {
@@ -839,7 +1021,7 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (s->keep_alive_cnt) {
int value = s->keep_alive_cnt;
- if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0)
+ if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0)
log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m");
}
@@ -851,8 +1033,14 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (s->no_delay) {
int b = s->no_delay;
- if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
- log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
+
+ if (s->socket_protocol == IPPROTO_SCTP) {
+ if (setsockopt(fd, SOL_SCTP, SCTP_NODELAY, &b, sizeof(b)) < 0)
+ log_unit_warning_errno(UNIT(s), errno, "SCTP_NODELAY failed: %m");
+ } else {
+ if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0)
+ log_unit_warning_errno(UNIT(s), errno, "TCP_NODELAY failed: %m");
+ }
}
if (s->broadcast) {
@@ -923,13 +1111,13 @@ static void socket_apply_socket_options(Socket *s, int fd) {
log_unit_warning_errno(UNIT(s), errno, "TCP_CONGESTION failed: %m");
if (s->smack_ip_in) {
- r = mac_smack_apply_ip_in_fd(fd, s->smack_ip_in);
+ r = mac_smack_apply_fd(fd, SMACK_ATTR_IPIN, s->smack_ip_in);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_in_fd: %m");
}
if (s->smack_ip_out) {
- r = mac_smack_apply_ip_out_fd(fd, s->smack_ip_out);
+ r = mac_smack_apply_fd(fd, SMACK_ATTR_IPOUT, s->smack_ip_out);
if (r < 0)
log_unit_error_errno(UNIT(s), r, "mac_smack_apply_ip_out_fd: %m");
}
@@ -943,49 +1131,49 @@ 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_unit_warning_errno(UNIT(s), errno, "F_SETPIPE_SZ: %m");
+ log_unit_warning_errno(UNIT(s), errno, "Setting pipe size failed, ignoring: %m");
if (s->smack) {
- r = mac_smack_apply_fd(fd, s->smack);
+ r = mac_smack_apply_fd(fd, SMACK_ATTR_ACCESS, s->smack);
if (r < 0)
- log_unit_error_errno(UNIT(s), r, "mac_smack_apply_fd: %m");
+ log_unit_error_errno(UNIT(s), r, "SMACK relabelling failed, ignoring: %m");
}
}
static int fifo_address_create(
const char *path,
mode_t directory_mode,
- mode_t socket_mode,
- int *_fd) {
+ mode_t socket_mode) {
- int fd = -1, r = 0;
- struct stat st;
+ _cleanup_close_ int fd = -1;
mode_t old_mask;
+ struct stat st;
+ int r;
assert(path);
- assert(_fd);
mkdir_parents_label(path, directory_mode);
r = mac_selinux_create_file_prepare(path, S_IFIFO);
if (r < 0)
- goto fail;
+ return r;
/* Enforce the right access mode for the fifo */
old_mask = umask(~ socket_mode);
/* Include the original umask in our mask */
- umask(~socket_mode | old_mask);
+ (void) umask(~socket_mode | old_mask);
r = mkfifo(path, socket_mode);
- umask(old_mask);
+ (void) umask(old_mask);
if (r < 0 && errno != EEXIST) {
r = -errno;
goto fail;
}
- if ((fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
+ fd = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK | O_NOFOLLOW);
+ if (fd < 0) {
r = -errno;
goto fail;
}
@@ -1001,53 +1189,64 @@ static int fifo_address_create(
(st.st_mode & 0777) != (socket_mode & ~old_mask) ||
st.st_uid != getuid() ||
st.st_gid != getgid()) {
-
r = -EEXIST;
goto fail;
}
- *_fd = fd;
- return 0;
+ r = fd;
+ fd = -1;
+
+ return r;
fail:
mac_selinux_create_file_clear();
- safe_close(fd);
-
return r;
}
-static int special_address_create(
- const char *path,
- int *_fd) {
-
- int fd = -1, r = 0;
+static int special_address_create(const char *path, bool writable) {
+ _cleanup_close_ int fd = -1;
struct stat st;
+ int r;
assert(path);
- assert(_fd);
- fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
- if (fd < 0) {
- r = -errno;
- goto fail;
- }
+ fd = open(path, (writable ? O_RDWR : O_RDONLY)|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
- if (fstat(fd, &st) < 0) {
- r = -errno;
- goto fail;
- }
+ if (fstat(fd, &st) < 0)
+ return -errno;
/* Check whether this is a /proc, /sys or /dev file or char device */
- if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) {
- r = -EEXIST;
- goto fail;
- }
+ if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode))
+ return -EEXIST;
- *_fd = fd;
- return 0;
+ r = fd;
+ fd = -1;
-fail:
- safe_close(fd);
+ return r;
+}
+
+static int usbffs_address_create(const char *path) {
+ _cleanup_close_ int fd = -1;
+ struct stat st;
+ int r;
+
+ assert(path);
+
+ fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
+ if (fd < 0)
+ return -errno;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ /* Check whether this is a regular file (ffs endpoint)*/
+ if (!S_ISREG(st.st_mode))
+ return -EEXIST;
+
+ r = fd;
+ fd = -1;
return r;
}
@@ -1056,22 +1255,22 @@ static int mq_address_create(
const char *path,
mode_t mq_mode,
long maxmsg,
- long msgsize,
- int *_fd) {
+ long msgsize) {
- int fd = -1, r = 0;
+ _cleanup_close_ int fd = -1;
struct stat st;
mode_t old_mask;
struct mq_attr _attr, *attr = NULL;
+ int r;
assert(path);
- assert(_fd);
if (maxmsg > 0 && msgsize > 0) {
- zero(_attr);
- _attr.mq_flags = O_NONBLOCK;
- _attr.mq_maxmsg = maxmsg;
- _attr.mq_msgsize = msgsize;
+ _attr = (struct mq_attr) {
+ .mq_flags = O_NONBLOCK,
+ .mq_maxmsg = maxmsg,
+ .mq_msgsize = msgsize,
+ };
attr = &_attr;
}
@@ -1079,33 +1278,24 @@ static int mq_address_create(
old_mask = umask(~ mq_mode);
/* Include the original umask in our mask */
- umask(~mq_mode | old_mask);
+ (void) umask(~mq_mode | old_mask);
fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr);
- umask(old_mask);
+ (void) umask(old_mask);
- if (fd < 0) {
- r = -errno;
- goto fail;
- }
+ if (fd < 0)
+ return -errno;
- if (fstat(fd, &st) < 0) {
- r = -errno;
- goto fail;
- }
+ if (fstat(fd, &st) < 0)
+ return -errno;
if ((st.st_mode & 0777) != (mq_mode & ~old_mask) ||
st.st_uid != getuid() ||
- st.st_gid != getgid()) {
+ st.st_gid != getgid())
+ return -EEXIST;
- r = -EEXIST;
- goto fail;
- }
+ r = fd;
+ fd = -1;
- *_fd = fd;
- return 0;
-
-fail:
- safe_close(fd);
return r;
}
@@ -1125,11 +1315,112 @@ static int socket_symlink(Socket *s) {
return 0;
}
+static int usbffs_write_descs(int fd, Service *s) {
+ int r;
+
+ if (!s->usb_function_descriptors || !s->usb_function_strings)
+ return -EINVAL;
+
+ r = copy_file_fd(s->usb_function_descriptors, fd, false);
+ if (r < 0)
+ return r;
+
+ return copy_file_fd(s->usb_function_strings, fd, false);
+}
+
+static int usbffs_select_ep(const struct dirent *d) {
+ return d->d_name[0] != '.' && !streq(d->d_name, "ep0");
+}
+
+static int usbffs_dispatch_eps(SocketPort *p) {
+ _cleanup_free_ struct dirent **ent = NULL;
+ int r, i, n, k;
+
+ r = scandir(p->path, &ent, usbffs_select_ep, alphasort);
+ if (r < 0)
+ return -errno;
+
+ n = r;
+ p->auxiliary_fds = new(int, n);
+ if (!p->auxiliary_fds)
+ return -ENOMEM;
+
+ p->n_auxiliary_fds = n;
+
+ k = 0;
+ for (i = 0; i < n; ++i) {
+ _cleanup_free_ char *ep = NULL;
+
+ ep = path_make_absolute(ent[i]->d_name, p->path);
+ if (!ep)
+ return -ENOMEM;
+
+ path_kill_slashes(ep);
+
+ r = usbffs_address_create(ep);
+ if (r < 0)
+ goto fail;
+
+ p->auxiliary_fds[k] = r;
+
+ ++k;
+ free(ent[i]);
+ }
+
+ return r;
+
+fail:
+ close_many(p->auxiliary_fds, k);
+ p->auxiliary_fds = mfree(p->auxiliary_fds);
+ p->n_auxiliary_fds = 0;
+
+ return r;
+}
+
+static int socket_determine_selinux_label(Socket *s, char **ret) {
+ ExecCommand *c;
+ int r;
+
+ assert(s);
+ assert(ret);
+
+ if (s->selinux_context_from_net) {
+ /* If this is requested, get label from the network label */
+
+ r = mac_selinux_get_our_label(ret);
+ if (r == -EOPNOTSUPP)
+ goto no_label;
+
+ } else {
+ /* Otherwise, get it from the executable we are about to start */
+ r = socket_instantiate_service(s);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_ISSET(s->service))
+ goto no_label;
+
+ c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START];
+ if (!c)
+ goto no_label;
+
+ r = mac_selinux_get_create_label_from_exe(c->path, ret);
+ if (r == -EPERM || r == -EOPNOTSUPP)
+ goto no_label;
+ }
+
+ return r;
+
+no_label:
+ *ret = NULL;
+ return 0;
+}
+
static int socket_open_fds(Socket *s) {
+ _cleanup_(mac_selinux_freep) char *label = NULL;
+ bool know_label = false;
SocketPort *p;
int r;
- char *label = NULL;
- bool know_label = false;
assert(s);
@@ -1138,39 +1429,36 @@ static int socket_open_fds(Socket *s) {
if (p->fd >= 0)
continue;
- if (p->type == SOCKET_SOCKET) {
+ switch (p->type) {
+
+ case SOCKET_SOCKET:
if (!know_label) {
- /* Figure out label, if we don't it know
- * yet. We do it once, for the first
- * socket where we need this and
- * remember it for the rest. */
-
- if (s->selinux_context_from_net) {
- /* Get it from the network label */
-
- r = mac_selinux_get_our_label(&label);
- if (r < 0 && r != -EOPNOTSUPP)
- goto rollback;
-
- } else {
- /* Get it from the executable we are about to start */
-
- r = socket_instantiate_service(s);
- if (r < 0)
- goto rollback;
-
- if (UNIT_ISSET(s->service) &&
- SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) {
- r = mac_selinux_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
- if (r < 0 && r != -EPERM && r != -EOPNOTSUPP)
- goto rollback;
- }
- }
+ /* Figure out label, if we don't it know yet. We do it once, for the first socket where
+ * we need this and remember it for the rest. */
+
+ r = socket_determine_selinux_label(s, &label);
+ if (r < 0)
+ goto rollback;
know_label = true;
}
+ /* Apply the socket protocol */
+ switch (p->address.type) {
+
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ if (s->socket_protocol == IPPROTO_SCTP)
+ p->address.protocol = s->socket_protocol;
+ break;
+
+ case SOCK_DGRAM:
+ if (s->socket_protocol == IPPROTO_UDPLITE)
+ p->address.protocol = s->socket_protocol;
+ break;
+ }
+
r = socket_address_listen(
&p->address,
SOCK_CLOEXEC|SOCK_NONBLOCK,
@@ -1189,49 +1477,75 @@ static int socket_open_fds(Socket *s) {
p->fd = r;
socket_apply_socket_options(s, p->fd);
socket_symlink(s);
+ break;
- } else if (p->type == SOCKET_SPECIAL) {
+ case SOCKET_SPECIAL:
- r = special_address_create(
- p->path,
- &p->fd);
- if (r < 0)
+ p->fd = special_address_create(p->path, s->writable);
+ if (p->fd < 0) {
+ r = p->fd;
goto rollback;
+ }
+ break;
- } else if (p->type == SOCKET_FIFO) {
+ case SOCKET_FIFO:
- r = fifo_address_create(
+ p->fd = fifo_address_create(
p->path,
s->directory_mode,
- s->socket_mode,
- &p->fd);
- if (r < 0)
+ s->socket_mode);
+ if (p->fd < 0) {
+ r = p->fd;
goto rollback;
+ }
socket_apply_fifo_options(s, p->fd);
socket_symlink(s);
+ break;
- } else if (p->type == SOCKET_MQUEUE) {
+ case SOCKET_MQUEUE:
- r = mq_address_create(
+ p->fd = mq_address_create(
p->path,
s->socket_mode,
s->mq_maxmsg,
- s->mq_msgsize,
- &p->fd);
+ s->mq_msgsize);
+ if (p->fd < 0) {
+ r = p->fd;
+ goto rollback;
+ }
+ break;
+
+ case SOCKET_USB_FUNCTION: {
+ _cleanup_free_ char *ep = NULL;
+
+ ep = path_make_absolute("ep0", p->path);
+
+ p->fd = usbffs_address_create(ep);
+ if (p->fd < 0) {
+ r = p->fd;
+ goto rollback;
+ }
+
+ r = usbffs_write_descs(p->fd, SERVICE(UNIT_DEREF(s->service)));
if (r < 0)
goto rollback;
- } else
+
+ r = usbffs_dispatch_eps(p);
+ if (r < 0)
+ goto rollback;
+
+ break;
+ }
+ default:
assert_not_reached("Unknown port type");
+ }
}
- mac_selinux_free(label);
return 0;
rollback:
socket_close_fds(s);
- mac_selinux_free(label);
-
return r;
}
@@ -1285,6 +1599,34 @@ fail:
return r;
}
+enum {
+ SOCKET_OPEN_NONE,
+ SOCKET_OPEN_SOME,
+ SOCKET_OPEN_ALL,
+};
+
+static int socket_check_open(Socket *s) {
+ bool have_open = false, have_closed = false;
+ SocketPort *p;
+
+ assert(s);
+
+ LIST_FOREACH(port, p, s->ports) {
+ if (p->fd < 0)
+ have_closed = true;
+ else
+ have_open = true;
+
+ if (have_open && have_closed)
+ return SOCKET_OPEN_SOME;
+ }
+
+ if (have_open)
+ return SOCKET_OPEN_ALL;
+
+ return SOCKET_OPEN_NONE;
+}
+
static void socket_set_state(Socket *s, SocketState state) {
SocketState old_state;
assert(s);
@@ -1338,7 +1680,9 @@ static int socket_coldplug(Unit *u) {
if (s->deserialized_state == s->state)
return 0;
- if (IN_SET(s->deserialized_state,
+ if (s->control_pid > 0 &&
+ pid_is_unwaited(s->control_pid) &&
+ IN_SET(s->deserialized_state,
SOCKET_START_PRE,
SOCKET_START_CHOWN,
SOCKET_START_POST,
@@ -1349,14 +1693,11 @@ static int socket_coldplug(Unit *u) {
SOCKET_FINAL_SIGTERM,
SOCKET_FINAL_SIGKILL)) {
- if (s->control_pid <= 0)
- return -EBADMSG;
-
r = unit_watch_pid(UNIT(s), s->control_pid);
if (r < 0)
return r;
- r = socket_arm_timer(s);
+ r = socket_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec));
if (r < 0)
return r;
}
@@ -1365,13 +1706,24 @@ static int socket_coldplug(Unit *u) {
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
- SOCKET_RUNNING,
- SOCKET_STOP_PRE,
- SOCKET_STOP_PRE_SIGTERM,
- SOCKET_STOP_PRE_SIGKILL)) {
- r = socket_open_fds(s);
- if (r < 0)
- return r;
+ SOCKET_RUNNING)) {
+
+ /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors
+ * for. However, this is problematic, as we won't traverse throught the SOCKET_START_CHOWN state for
+ * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open,
+ * and if there's a mismatch, warn loudly. */
+
+ r = socket_check_open(s);
+ if (r == SOCKET_OPEN_NONE)
+ log_unit_warning(UNIT(s),
+ "Socket unit configuration has changed while unit has been running, "
+ "no open socket file descriptor left. "
+ "The socket unit is not functional until restarted.");
+ else if (r == SOCKET_OPEN_SOME)
+ log_unit_warning(UNIT(s),
+ "Socket unit configuration has changed while unit has been running, "
+ "and some socket file descriptors have not been opened yet. "
+ "The socket unit is not fully functional until restarted.");
}
if (s->deserialized_state == SOCKET_LISTENING) {
@@ -1380,6 +1732,9 @@ static int socket_coldplug(Unit *u) {
return r;
}
+ if (!IN_SET(s->deserialized_state, SOCKET_DEAD, SOCKET_FAILED))
+ (void) unit_setup_dynamic_creds(u);
+
socket_set_state(s, s->deserialized_state);
return 0;
}
@@ -1389,10 +1744,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
- .apply_permissions = true,
- .apply_chroot = true,
- .apply_tty_stdin = true,
- .bus_endpoint_fd = -1,
+ .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(s);
@@ -1407,19 +1762,23 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
- goto fail;
+ return r;
- r = socket_arm_timer(s);
+ r = unit_setup_dynamic_creds(UNIT(s));
if (r < 0)
- goto fail;
+ return r;
+
+ r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
+ if (r < 0)
+ return r;
r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
if (r < 0)
- goto fail;
+ return r;
exec_params.argv = argv;
exec_params.environment = UNIT(s)->manager->environment;
- exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@@ -1430,28 +1789,25 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
&s->exec_context,
&exec_params,
s->exec_runtime,
+ &s->dynamic_creds,
&pid);
if (r < 0)
- goto fail;
+ return r;
r = unit_watch_pid(UNIT(s), pid);
if (r < 0)
/* FIXME: we need to do something here */
- goto fail;
+ return r;
*_pid = pid;
return 0;
-
-fail:
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return r;
}
static int socket_chown(Socket *s, pid_t *_pid) {
pid_t pid;
int r;
- r = socket_arm_timer(s);
+ r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
@@ -1534,15 +1890,19 @@ fail:
static void socket_enter_dead(Socket *s, SocketResult f) {
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
+ socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
+
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
- socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
+ unit_unref_uid_gid(UNIT(s), true);
+
+ dynamic_creds_destroy(&s->dynamic_creds);
}
static void socket_enter_signal(Socket *s, SocketState state, SocketResult f);
@@ -1551,7 +1911,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
int r;
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
@@ -1579,7 +1939,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
r = unit_kill_context(
@@ -1594,7 +1954,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
goto fail;
if (r > 0) {
- r = socket_arm_timer(s);
+ r = socket_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
@@ -1623,7 +1983,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
int r;
assert(s);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
@@ -1746,38 +2106,47 @@ fail:
socket_enter_dead(s, SOCKET_FAILURE_RESOURCES);
}
+static void flush_ports(Socket *s) {
+ SocketPort *p;
+
+ /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy
+ * anymore */
+
+ LIST_FOREACH(port, p, s->ports) {
+ if (p->fd < 0)
+ continue;
+
+ (void) flush_accept(p->fd);
+ (void) flush_fd(p->fd);
+ }
+}
+
static void socket_enter_running(Socket *s, int cfd) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
+ /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or
+ * close it. */
+
assert(s);
- /* We don't take connections anymore if we are supposed to
- * shut down anyway */
+ /* We don't take connections anymore if we are supposed to shut down anyway */
if (unit_stop_pending(UNIT(s))) {
log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
if (cfd >= 0)
- safe_close(cfd);
- else {
- /* Flush all sockets by closing and reopening them */
- socket_close_fds(s);
-
- r = socket_open_fds(s);
- if (r < 0) {
- log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
- return;
- }
+ cfd = safe_close(cfd);
+ else
+ flush_ports(s);
- r = socket_watch_fds(s);
- if (r < 0) {
- log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m");
- socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
- }
- }
+ return;
+ }
+ if (!ratelimit_test(&s->trigger_limit)) {
+ safe_close(cfd);
+ log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation.");
+ socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT);
return;
}
@@ -1801,7 +2170,7 @@ static void socket_enter_running(Socket *s, int cfd) {
goto fail;
}
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, true, &error, NULL);
+ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
}
@@ -1809,14 +2178,34 @@ static void socket_enter_running(Socket *s, int cfd) {
socket_set_state(s, SOCKET_RUNNING);
} else {
_cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL;
+ _cleanup_(socket_peer_unrefp) SocketPeer *p = NULL;
Service *service;
if (s->n_connections >= s->max_connections) {
- log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections);
+ log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.",
+ s->n_connections);
safe_close(cfd);
return;
}
+ if (s->max_connections_per_source > 0) {
+ r = socket_acquire_peer(s, cfd, &p);
+ if (r < 0) {
+ safe_close(cfd);
+ return;
+ } else if (r > 0 && p->n_ref > s->max_connections_per_source) {
+ _cleanup_free_ char *t = NULL;
+
+ sockaddr_pretty(&p->peer.sa, FAMILY_ADDRESS_SIZE(p->peer.sa.sa_family), true, false, &t);
+
+ log_unit_warning(UNIT(s),
+ "Too many incoming connections (%u) from source %s, dropping connection.",
+ p->n_ref, strnull(t));
+ safe_close(cfd);
+ return;
+ }
+ }
+
r = socket_instantiate_service(s);
if (r < 0)
goto fail;
@@ -1828,6 +2217,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* ENOTCONN is legitimate if TCP RST was received.
* This connection is over, but the socket unit lives on. */
+ log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring.");
safe_close(cfd);
return;
}
@@ -1846,22 +2236,27 @@ static void socket_enter_running(Socket *s, int cfd) {
service = SERVICE(UNIT_DEREF(s->service));
unit_ref_unset(&s->service);
- s->n_accepted ++;
-
- UNIT(service)->no_gc = false;
+ s->n_accepted++;
unit_choose_id(UNIT(service), name);
r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net);
if (r < 0)
goto fail;
- cfd = -1;
- s->n_connections ++;
+ cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */
+ s->n_connections++;
- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL);
- if (r < 0)
+ service->peer = p; /* Pass ownership of the peer reference */
+ p = NULL;
+
+ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL);
+ if (r < 0) {
+ /* We failed to activate the new service, but it still exists. Let's make sure the service
+ * closes and forgets the connection fd again, immediately. */
+ service_close_socket_fd(service);
goto fail;
+ }
/* Notify clients about changed counters */
unit_add_to_dbus_queue(UNIT(s));
@@ -1908,6 +2303,7 @@ fail:
static int socket_start(Unit *u) {
Socket *s = SOCKET(u);
+ int r;
assert(s);
@@ -1952,11 +2348,20 @@ static int socket_start(Unit *u) {
assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
s->result = SOCKET_SUCCESS;
s->reset_cpu_usage = true;
socket_enter_start_pre(s);
-
return 1;
}
@@ -2036,6 +2441,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
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 if (p->type == SOCKET_USB_FUNCTION)
+ unit_serialize_item_format(u, f, "ffs", "%i %s", copy, p->path);
else {
assert(p->type == SOCKET_FIFO);
unit_serialize_item_format(u, f, "fifo", "%i %s", copy, p->path);
@@ -2045,6 +2452,11 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
return 0;
}
+static void socket_port_take_fd(SocketPort *p, FDSet *fds, int fd) {
+ safe_close(p->fd);
+ p->fd = fdset_remove(fds, fd);
+}
+
static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
Socket *s = SOCKET(u);
@@ -2099,18 +2511,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse fifo value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_FIFO &&
- path_equal_or_files_same(p->path, value+skip))
+ path_equal_or_files_same(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "special")) {
int fd, skip = 0;
@@ -2118,18 +2525,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse special value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_SPECIAL &&
- path_equal_or_files_same(p->path, value+skip))
+ path_equal_or_files_same(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "mqueue")) {
int fd, skip = 0;
@@ -2137,18 +2539,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse mqueue value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
if (p->type == SOCKET_MQUEUE &&
- streq(p->path, value+skip))
+ streq(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "socket")) {
int fd, type, skip = 0;
@@ -2156,17 +2553,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse socket value: %s", value);
- else {
-
+ else
LIST_FOREACH(port, p, s->ports)
- if (socket_address_is(&p->address, value+skip, type))
+ if (socket_address_is(&p->address, value+skip, type)) {
+ socket_port_take_fd(p, fds, fd);
break;
-
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
+ }
} else if (streq(key, "netlink")) {
int fd, skip = 0;
@@ -2174,24 +2566,34 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse socket value: %s", value);
- else {
+ else
+ LIST_FOREACH(port, p, s->ports)
+ if (socket_address_is_netlink(&p->address, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
+ break;
+ }
+ } else if (streq(key, "ffs")) {
+ int fd, skip = 0;
+ SocketPort *p;
+
+ if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd))
+ log_unit_debug(u, "Failed to parse ffs value: %s", value);
+ else
LIST_FOREACH(port, p, s->ports)
- if (socket_address_is_netlink(&p->address, value+skip))
+ if (p->type == SOCKET_USB_FUNCTION &&
+ path_equal_or_files_same(p->path, value+skip)) {
+ socket_port_take_fd(p, fds, fd);
break;
+ }
- if (p) {
- safe_close(p->fd);
- p->fd = fdset_remove(fds, fd);
- }
- }
} else
log_unit_debug(UNIT(s), "Unknown serialization key: %s", key);
return 0;
}
-static int socket_distribute_fds(Unit *u, FDSet *fds) {
+static void socket_distribute_fds(Unit *u, FDSet *fds) {
Socket *s = SOCKET(u);
SocketPort *p;
@@ -2215,8 +2617,6 @@ static int socket_distribute_fds(Unit *u, FDSet *fds) {
}
}
}
-
- return 0;
}
_pure_ static UnitActiveState socket_active_state(Unit *u) {
@@ -2267,6 +2667,9 @@ const char* socket_port_type_to_string(SocketPort *p) {
case SOCKET_FIFO:
return "FIFO";
+ case SOCKET_USB_FUNCTION:
+ return "USBFunction";
+
default:
return NULL;
}
@@ -2298,7 +2701,6 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.");
else
log_unit_error(UNIT(p->socket), "Got unexpected poll event (0x%x) on socket.", revents);
-
goto fail;
}
@@ -2344,7 +2746,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, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SOCKET_SUCCESS;
else if (code == CLD_EXITED)
f = SOCKET_FAILURE_EXIT_CODE;
@@ -2366,7 +2768,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"Control process exited, code=%s status=%i",
sigchld_code_to_string(code), status);
- if (f != SOCKET_SUCCESS)
+ if (s->result == SOCKET_SUCCESS)
s->result = f;
if (s->control_command &&
@@ -2494,43 +2896,43 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
return 0;
}
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
- int *rfds;
- unsigned rn_fds, k;
+int socket_collect_fds(Socket *s, int **fds) {
+ int *rfds, k = 0, n = 0;
SocketPort *p;
assert(s);
assert(fds);
- assert(n_fds);
/* Called from the service code for requesting our fds */
- rn_fds = 0;
- LIST_FOREACH(port, p, s->ports)
+ LIST_FOREACH(port, p, s->ports) {
if (p->fd >= 0)
- rn_fds++;
+ n++;
+ n += p->n_auxiliary_fds;
+ }
- if (rn_fds <= 0) {
+ if (n <= 0) {
*fds = NULL;
- *n_fds = 0;
return 0;
}
- rfds = new(int, rn_fds);
+ rfds = new(int, n);
if (!rfds)
return -ENOMEM;
- k = 0;
- LIST_FOREACH(port, p, s->ports)
+ LIST_FOREACH(port, p, s->ports) {
+ int i;
+
if (p->fd >= 0)
rfds[k++] = p->fd;
+ for (i = 0; i < p->n_auxiliary_fds; ++i)
+ rfds[k++] = p->auxiliary_fds[i];
+ }
- assert(k == rn_fds);
+ assert(k == n);
*fds = rfds;
- *n_fds = rn_fds;
-
- return 0;
+ return n;
}
static void socket_reset_failed(Unit *u) {
@@ -2544,23 +2946,6 @@ static void socket_reset_failed(Unit *u) {
s->result = SOCKET_SUCCESS;
}
-static void socket_notify_service_dead(Socket *s, bool failed_permanent) {
- assert(s);
-
- /* The service is dead. Dang!
- *
- * This is strictly for one-instance-for-all-connections
- * services. */
-
- if (s->state == SOCKET_RUNNING) {
- log_unit_debug(UNIT(s), "Got notified about service death (failed permanently: %s)", yes_no(failed_permanent));
- if (failed_permanent)
- socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT);
- else
- socket_enter_listening(s);
- }
-}
-
void socket_connection_unref(Socket *s) {
assert(s);
@@ -2577,34 +2962,39 @@ void socket_connection_unref(Socket *s) {
static void socket_trigger_notify(Unit *u, Unit *other) {
Socket *s = SOCKET(u);
- Service *se;
assert(u);
assert(other);
- /* Don't propagate state changes from the service if we are
- already down or accepting connections */
- if ((s->state != SOCKET_RUNNING &&
- s->state != SOCKET_LISTENING) ||
- s->accept)
+ /* Filter out invocations with bogus state */
+ if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
return;
- if (other->load_state != UNIT_LOADED ||
- other->type != UNIT_SERVICE)
+ /* Don't propagate state changes from the service if we are already down */
+ if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING))
return;
- se = SERVICE(other);
+ /* We don't care for the service state if we are in Accept=yes mode */
+ if (s->accept)
+ return;
+
+ /* Propagate start limit hit state */
+ if (other->start_limit_hit) {
+ socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT);
+ return;
+ }
- if (se->state == SERVICE_FAILED)
- socket_notify_service_dead(s, se->result == SERVICE_FAILURE_START_LIMIT);
+ /* Don't propagate anything if there's still a job queued */
+ if (other->job)
+ return;
- if (se->state == SERVICE_DEAD ||
- se->state == SERVICE_FINAL_SIGTERM ||
- se->state == SERVICE_FINAL_SIGKILL ||
- se->state == SERVICE_AUTO_RESTART)
- socket_notify_service_dead(s, false);
+ if (IN_SET(SERVICE(other)->state,
+ SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+ SERVICE_AUTO_RESTART))
+ socket_enter_listening(s);
- if (se->state == SERVICE_RUNNING)
+ if (SERVICE(other)->state == SERVICE_RUNNING)
socket_set_state(s, SOCKET_RUNNING);
}
@@ -2612,37 +3002,44 @@ static int socket_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error);
}
-static int socket_get_timeout(Unit *u, uint64_t *timeout) {
+static int socket_get_timeout(Unit *u, usec_t *timeout) {
Socket *s = SOCKET(u);
+ usec_t t;
int r;
if (!s->timer_event_source)
return 0;
- r = sd_event_source_get_time(s->timer_event_source, timeout);
+ r = sd_event_source_get_time(s->timer_event_source, &t);
if (r < 0)
return r;
+ if (t == USEC_INFINITY)
+ return 0;
+ *timeout = t;
return 1;
}
-static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
- [SOCKET_DEAD] = "dead",
- [SOCKET_START_PRE] = "start-pre",
- [SOCKET_START_CHOWN] = "start-chown",
- [SOCKET_START_POST] = "start-post",
- [SOCKET_LISTENING] = "listening",
- [SOCKET_RUNNING] = "running",
- [SOCKET_STOP_PRE] = "stop-pre",
- [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm",
- [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill",
- [SOCKET_STOP_POST] = "stop-post",
- [SOCKET_FINAL_SIGTERM] = "final-sigterm",
- [SOCKET_FINAL_SIGKILL] = "final-sigkill",
- [SOCKET_FAILED] = "failed"
-};
+char *socket_fdname(Socket *s) {
+ assert(s);
+
+ /* Returns the name to use for $LISTEN_NAMES. If the user
+ * didn't specify anything specifically, use the socket unit's
+ * name as fallback. */
-DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState);
+ if (s->fdname)
+ return s->fdname;
+
+ return UNIT(s)->id;
+}
+
+static int socket_control_pid(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ return s->control_pid;
+}
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "StartPre",
@@ -2661,7 +3058,9 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
- [SOCKET_FAILURE_SERVICE_FAILED_PERMANENT] = "service-failed-permanent"
+ [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
+ [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
+ [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
};
DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
@@ -2672,6 +3071,7 @@ const UnitVTable socket_vtable = {
.cgroup_context_offset = offsetof(Socket, cgroup_context),
.kill_context_offset = offsetof(Socket, kill_context),
.exec_runtime_offset = offsetof(Socket, exec_runtime),
+ .dynamic_creds_offset = offsetof(Socket, dynamic_creds),
.sections =
"Unit\0"
@@ -2709,7 +3109,8 @@ const UnitVTable socket_vtable = {
.reset_failed = socket_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Socket",
+ .control_pid = socket_control_pid,
+
.bus_vtable = bus_socket_vtable,
.bus_set_property = bus_socket_set_property,
.bus_commit_properties = bus_socket_commit_properties,
@@ -2722,7 +3123,6 @@ const UnitVTable socket_vtable = {
.finished_start_job = {
[JOB_DONE] = "Listening on %s.",
[JOB_FAILED] = "Failed to listen on %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
[JOB_TIMEOUT] = "Timed out starting %s.",
},
.finished_stop_job = {
diff --git a/src/core/socket.h b/src/core/socket.h
index fa3ebdafa0..89f4664510 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,28 +20,11 @@
***/
typedef struct Socket Socket;
+typedef struct SocketPeer SocketPeer;
-#include "socket-util.h"
#include "mount.h"
#include "service.h"
-
-typedef enum SocketState {
- SOCKET_DEAD,
- SOCKET_START_PRE,
- SOCKET_START_CHOWN,
- SOCKET_START_POST,
- SOCKET_LISTENING,
- SOCKET_RUNNING,
- SOCKET_STOP_PRE,
- SOCKET_STOP_PRE_SIGTERM,
- SOCKET_STOP_PRE_SIGKILL,
- SOCKET_STOP_POST,
- SOCKET_FINAL_SIGTERM,
- SOCKET_FINAL_SIGKILL,
- SOCKET_FAILED,
- _SOCKET_STATE_MAX,
- _SOCKET_STATE_INVALID = -1
-} SocketState;
+#include "socket-util.h"
typedef enum SocketExecCommand {
SOCKET_EXEC_START_PRE,
@@ -60,6 +41,7 @@ typedef enum SocketType {
SOCKET_FIFO,
SOCKET_SPECIAL,
SOCKET_MQUEUE,
+ SOCKET_USB_FUNCTION,
_SOCKET_FIFO_MAX,
_SOCKET_FIFO_INVALID = -1
} SocketType;
@@ -71,7 +53,9 @@ typedef enum SocketResult {
SOCKET_FAILURE_EXIT_CODE,
SOCKET_FAILURE_SIGNAL,
SOCKET_FAILURE_CORE_DUMP,
- SOCKET_FAILURE_SERVICE_FAILED_PERMANENT,
+ SOCKET_FAILURE_START_LIMIT_HIT,
+ SOCKET_FAILURE_TRIGGER_LIMIT_HIT,
+ SOCKET_FAILURE_SERVICE_START_LIMIT_HIT,
_SOCKET_RESULT_MAX,
_SOCKET_RESULT_INVALID = -1
} SocketResult;
@@ -81,6 +65,8 @@ typedef struct SocketPort {
SocketType type;
int fd;
+ int *auxiliary_fds;
+ int n_auxiliary_fds;
SocketAddress address;
char *path;
@@ -94,9 +80,12 @@ struct Socket {
LIST_HEAD(SocketPort, ports);
+ Set *peers_by_address;
+
unsigned n_accepted;
unsigned n_connections;
unsigned max_connections;
+ unsigned max_connections_per_source;
unsigned backlog;
unsigned keep_alive_cnt;
@@ -109,7 +98,9 @@ struct Socket {
ExecContext exec_context;
KillContext kill_context;
CGroupContext cgroup_context;
+
ExecRuntime *exec_runtime;
+ DynamicCreds dynamic_creds;
/* For Accept=no sockets refers to the one service we'll
activate. For Accept=yes sockets is either NULL, or filled
@@ -133,6 +124,9 @@ struct Socket {
bool accept;
bool remove_on_stop;
+ bool writable;
+
+ int socket_protocol;
/* Socket options */
bool keep_alive;
@@ -168,20 +162,31 @@ struct Socket {
char *user, *group;
bool reset_cpu_usage:1;
+
+ char *fdname;
+
+ RateLimit trigger_limit;
};
+SocketPeer *socket_peer_ref(SocketPeer *p);
+SocketPeer *socket_peer_unref(SocketPeer *p);
+int socket_acquire_peer(Socket *s, int fd, SocketPeer **p);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref);
+
/* Called from the service code when collecting fds */
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
+int socket_collect_fds(Socket *s, int **fds);
/* Called from the service code when a per-connection service ended */
void socket_connection_unref(Socket *s);
void socket_free_ports(Socket *s);
-extern const UnitVTable socket_vtable;
+int socket_instantiate_service(Socket *s);
-const char* socket_state_to_string(SocketState i) _const_;
-SocketState socket_state_from_string(const char *s) _pure_;
+char *socket_fdname(Socket *s);
+
+extern const UnitVTable socket_vtable;
const char* socket_exec_command_to_string(SocketExecCommand i) _const_;
SocketExecCommand socket_exec_command_from_string(const char *s) _pure_;
@@ -190,5 +195,3 @@ const char* socket_result_to_string(SocketResult i) _const_;
SocketResult socket_result_from_string(const char *s) _pure_;
const char* socket_port_type_to_string(SocketPort *p) _pure_;
-
-int socket_instantiate_service(Socket *s);
diff --git a/src/core/swap.c b/src/core/swap.c
index 193c8c3767..b592abb9fb 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,22 +18,30 @@
***/
#include <errno.h>
-#include <unistd.h>
#include <sys/epoll.h>
#include <sys/stat.h>
-#include <libudev.h>
+#include <unistd.h>
-#include "unit.h"
-#include "swap.h"
-#include "unit-name.h"
+#include "libudev.h"
+
+#include "alloc-util.h"
#include "dbus-swap.h"
-#include "special.h"
+#include "escape.h"
#include "exit-status.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "fstab-util.h"
+#include "parse-util.h"
#include "path-util.h"
-#include "virt.h"
+#include "process-util.h"
+#include "special.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "swap.h"
#include "udev-util.h"
-#include "fstab-util.h"
-#include "formats-util.h"
+#include "unit-name.h"
+#include "unit.h"
+#include "virt.h"
static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
[SWAP_DEAD] = UNIT_INACTIVE,
@@ -59,8 +65,7 @@ static void swap_unset_proc_swaps(Swap *s) {
if (!s->from_proc_swaps)
return;
- free(s->parameters_proc_swaps.what);
- s->parameters_proc_swaps.what = NULL;
+ s->parameters_proc_swaps.what = mfree(s->parameters_proc_swaps.what);
s->from_proc_swaps = false;
}
@@ -87,8 +92,7 @@ static int swap_set_devnode(Swap *s, const char *devnode) {
else
hashmap_remove(swaps, s->devnode);
- free(s->devnode);
- s->devnode = NULL;
+ s->devnode = mfree(s->devnode);
}
if (devnode) {
@@ -141,47 +145,42 @@ static void swap_done(Unit *u) {
swap_unset_proc_swaps(s);
swap_set_devnode(s, NULL);
- free(s->what);
- s->what = NULL;
-
- free(s->parameters_fragment.what);
- s->parameters_fragment.what = NULL;
-
- free(s->parameters_fragment.options);
- s->parameters_fragment.options = NULL;
+ s->what = mfree(s->what);
+ s->parameters_fragment.what = mfree(s->parameters_fragment.what);
+ s->parameters_fragment.options = mfree(s->parameters_fragment.options);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
s->control_command = NULL;
+ dynamic_creds_unref(&s->dynamic_creds);
+
swap_unwatch_control_pid(s);
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
}
-static int swap_arm_timer(Swap *s) {
+static int swap_arm_timer(Swap *s, usec_t usec) {
int r;
assert(s);
- if (s->timeout_usec <= 0) {
- s->timer_event_source = sd_event_source_unref(s->timer_event_source);
- return 0;
- }
-
if (s->timer_event_source) {
- r = sd_event_source_set_time(s->timer_event_source, now(CLOCK_MONOTONIC) + s->timeout_usec);
+ r = sd_event_source_set_time(s->timer_event_source, usec);
if (r < 0)
return r;
return sd_event_source_set_enabled(s->timer_event_source, SD_EVENT_ONESHOT);
}
+ if (usec == USEC_INFINITY)
+ return 0;
+
r = sd_event_add_time(
UNIT(s)->manager->event,
&s->timer_event_source,
CLOCK_MONOTONIC,
- now(CLOCK_MONOTONIC) + s->timeout_usec, 0,
+ usec, 0,
swap_dispatch_timer, s);
if (r < 0)
return r;
@@ -201,7 +200,7 @@ static int swap_add_device_links(Swap *s) {
return 0;
if (is_device_path(s->what))
- return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM);
+ return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO);
else
/* File based swap devices need to be ordered after
* systemd-remount-fs.service, since they might need a
@@ -210,14 +209,25 @@ static int swap_add_device_links(Swap *s) {
}
static int swap_add_default_dependencies(Swap *s) {
+ int r;
+
assert(s);
- if (UNIT(s)->manager->running_as != MANAGER_SYSTEM)
+ if (!UNIT(s)->default_dependencies)
return 0;
- if (detect_container(NULL) > 0)
+ if (!MANAGER_IS_SYSTEM(UNIT(s)->manager))
return 0;
+ if (detect_container() > 0)
+ return 0;
+
+ /* swap units generated for the swap dev links are missing the
+ * ordering dep against the swap target. */
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
}
@@ -326,15 +336,13 @@ static int swap_load(Unit *u) {
if (r < 0)
return r;
- r = unit_add_default_slice(u, &s->cgroup_context);
+ r = unit_set_default_slice(u);
if (r < 0)
return r;
- if (UNIT(s)->default_dependencies) {
- r = swap_add_default_dependencies(s);
- if (r < 0)
- return r;
- }
+ r = swap_add_default_dependencies(s);
+ if (r < 0)
+ return r;
}
return swap_verify(s);
@@ -527,26 +535,29 @@ static int swap_coldplug(Unit *u) {
if (new_state == s->state)
return 0;
- if (new_state == SWAP_ACTIVATING ||
- new_state == SWAP_ACTIVATING_SIGTERM ||
- new_state == SWAP_ACTIVATING_SIGKILL ||
- new_state == SWAP_ACTIVATING_DONE ||
- new_state == SWAP_DEACTIVATING ||
- new_state == SWAP_DEACTIVATING_SIGTERM ||
- new_state == SWAP_DEACTIVATING_SIGKILL) {
-
- if (s->control_pid <= 0)
- return -EBADMSG;
+ if (s->control_pid > 0 &&
+ pid_is_unwaited(s->control_pid) &&
+ IN_SET(new_state,
+ SWAP_ACTIVATING,
+ SWAP_ACTIVATING_SIGTERM,
+ SWAP_ACTIVATING_SIGKILL,
+ SWAP_ACTIVATING_DONE,
+ SWAP_DEACTIVATING,
+ SWAP_DEACTIVATING_SIGTERM,
+ SWAP_DEACTIVATING_SIGKILL)) {
r = unit_watch_pid(UNIT(s), s->control_pid);
if (r < 0)
return r;
- r = swap_arm_timer(s);
+ r = swap_arm_timer(s, usec_add(u->state_change_timestamp.monotonic, s->timeout_usec));
if (r < 0)
return r;
}
+ if (!IN_SET(new_state, SWAP_DEAD, SWAP_FAILED))
+ (void) unit_setup_dynamic_creds(u);
+
swap_set_state(s, new_state);
return 0;
}
@@ -600,10 +611,10 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
- .apply_permissions = true,
- .apply_chroot = true,
- .apply_tty_stdin = true,
- .bus_endpoint_fd = -1,
+ .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
+ .stdin_fd = -1,
+ .stdout_fd = -1,
+ .stderr_fd = -1,
};
assert(s);
@@ -620,12 +631,16 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
if (r < 0)
goto fail;
- r = swap_arm_timer(s);
+ r = unit_setup_dynamic_creds(UNIT(s));
+ if (r < 0)
+ return r;
+
+ r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
exec_params.environment = UNIT(s)->manager->environment;
- exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
+ exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@@ -636,6 +651,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
&s->exec_context,
&exec_params,
s->exec_runtime,
+ &s->dynamic_creds,
&pid);
if (r < 0)
goto fail;
@@ -657,21 +673,25 @@ fail:
static void swap_enter_dead(Swap *s, SwapResult f) {
assert(s);
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
+ swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
+
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
- swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
+ unit_unref_uid_gid(UNIT(s), true);
+
+ dynamic_creds_destroy(&s->dynamic_creds);
}
static void swap_enter_active(Swap *s, SwapResult f) {
assert(s);
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
swap_set_state(s, SWAP_ACTIVE);
@@ -682,7 +702,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
assert(s);
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
r = unit_kill_context(
@@ -697,7 +717,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
goto fail;
if (r > 0) {
- r = swap_arm_timer(s);
+ r = swap_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_usec));
if (r < 0)
goto fail;
@@ -806,6 +826,7 @@ fail:
static int swap_start(Unit *u) {
Swap *s = SWAP(u), *other;
+ int r;
assert(s);
@@ -824,7 +845,7 @@ static int swap_start(Unit *u) {
assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED);
- if (detect_container(NULL) > 0)
+ if (detect_container() > 0)
return -EPERM;
/* If there's a job for another swap unit for the same node
@@ -834,6 +855,16 @@ static int swap_start(Unit *u) {
if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING)
return -EAGAIN;
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
s->result = SWAP_SUCCESS;
s->reset_cpu_usage = true;
@@ -857,7 +888,7 @@ static int swap_stop(Unit *u) {
s->state == SWAP_ACTIVATING_DONE ||
s->state == SWAP_ACTIVE);
- if (detect_container(NULL) > 0)
+ if (detect_container() > 0)
return -EPERM;
swap_enter_deactivating(s);
@@ -961,7 +992,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, NULL))
+ if (is_clean_exit(code, status, EXIT_CLEAN_COMMAND, NULL))
f = SWAP_SUCCESS;
else if (code == CLD_EXITED)
f = SWAP_FAILURE_EXIT_CODE;
@@ -972,7 +1003,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
assert_not_reached("Unknown code");
- if (f != SWAP_SUCCESS)
+ if (s->result == SWAP_SUCCESS)
s->result = f;
if (s->control_command) {
@@ -1162,6 +1193,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
case SWAP_DEAD:
case SWAP_FAILED:
+ (void) unit_acquire_invocation_id(UNIT(swap));
swap_enter_active(swap, SWAP_SUCCESS);
break;
@@ -1202,7 +1234,7 @@ static Unit *swap_following(Unit *u) {
if (other->from_fragment)
return UNIT(other);
- /* Otherwise make everybody follow the unit that's named after
+ /* Otherwise, make everybody follow the unit that's named after
* the swap device in the kernel */
if (streq_ptr(s->what, s->devnode))
@@ -1259,35 +1291,41 @@ static void swap_shutdown(Manager *m) {
m->swap_event_source = sd_event_source_unref(m->swap_event_source);
- if (m->proc_swaps) {
- fclose(m->proc_swaps);
- m->proc_swaps = NULL;
- }
+ m->proc_swaps = safe_fclose(m->proc_swaps);
- hashmap_free(m->swaps_by_devnode);
- m->swaps_by_devnode = NULL;
+ m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode);
}
-static int swap_enumerate(Manager *m) {
+static void swap_enumerate(Manager *m) {
int r;
assert(m);
if (!m->proc_swaps) {
m->proc_swaps = fopen("/proc/swaps", "re");
- if (!m->proc_swaps)
- return errno == ENOENT ? 0 : -errno;
+ if (!m->proc_swaps) {
+ if (errno == ENOENT)
+ log_debug("Not swap enabled, skipping enumeration");
+ else
+ log_error_errno(errno, "Failed to open /proc/swaps: %m");
+
+ return;
+ }
r = sd_event_add_io(m->event, &m->swap_event_source, fileno(m->proc_swaps), EPOLLPRI, swap_dispatch_io, m);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to watch /proc/swaps: %m");
goto fail;
+ }
/* Dispatch this before we dispatch SIGCHLD, so that
* we always get the events from /proc/swaps before
* the SIGCHLD of /sbin/swapon. */
r = sd_event_source_set_priority(m->swap_event_source, -10);
- if (r < 0)
+ if (r < 0) {
+ log_error_errno(r, "Failed to change /proc/swaps priority: %m");
goto fail;
+ }
(void) sd_event_source_set_description(m->swap_event_source, "swap-proc");
}
@@ -1296,11 +1334,10 @@ static int swap_enumerate(Manager *m) {
if (r < 0)
goto fail;
- return 0;
+ return;
fail:
swap_shutdown(m);
- return r;
}
int swap_process_device_new(Manager *m, struct udev_device *dev) {
@@ -1380,17 +1417,21 @@ static int swap_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error);
}
-static int swap_get_timeout(Unit *u, uint64_t *timeout) {
+static int swap_get_timeout(Unit *u, usec_t *timeout) {
Swap *s = SWAP(u);
+ usec_t t;
int r;
if (!s->timer_event_source)
return 0;
- r = sd_event_source_get_time(s->timer_event_source, timeout);
+ r = sd_event_source_get_time(s->timer_event_source, &t);
if (r < 0)
return r;
+ if (t == USEC_INFINITY)
+ return 0;
+ *timeout = t;
return 1;
}
@@ -1404,25 +1445,18 @@ static bool swap_supported(void) {
if (supported < 0)
supported =
access("/proc/swaps", F_OK) >= 0 &&
- detect_container(NULL) <= 0;
+ detect_container() <= 0;
return supported;
}
-static const char* const swap_state_table[_SWAP_STATE_MAX] = {
- [SWAP_DEAD] = "dead",
- [SWAP_ACTIVATING] = "activating",
- [SWAP_ACTIVATING_DONE] = "activating-done",
- [SWAP_ACTIVE] = "active",
- [SWAP_DEACTIVATING] = "deactivating",
- [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm",
- [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill",
- [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm",
- [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill",
- [SWAP_FAILED] = "failed"
-};
+static int swap_control_pid(Unit *u) {
+ Swap *s = SWAP(u);
-DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
+ assert(s);
+
+ return s->control_pid;
+}
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
@@ -1437,7 +1471,8 @@ static const char* const swap_result_table[_SWAP_RESULT_MAX] = {
[SWAP_FAILURE_TIMEOUT] = "timeout",
[SWAP_FAILURE_EXIT_CODE] = "exit-code",
[SWAP_FAILURE_SIGNAL] = "signal",
- [SWAP_FAILURE_CORE_DUMP] = "core-dump"
+ [SWAP_FAILURE_CORE_DUMP] = "core-dump",
+ [SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult);
@@ -1448,6 +1483,7 @@ const UnitVTable swap_vtable = {
.cgroup_context_offset = offsetof(Swap, cgroup_context),
.kill_context_offset = offsetof(Swap, kill_context),
.exec_runtime_offset = offsetof(Swap, exec_runtime),
+ .dynamic_creds_offset = offsetof(Swap, dynamic_creds),
.sections =
"Unit\0"
@@ -1455,9 +1491,6 @@ const UnitVTable swap_vtable = {
"Install\0",
.private_section = "Swap",
- .no_alias = true,
- .no_instances = true,
-
.init = swap_init,
.load = swap_load,
.done = swap_done,
@@ -1485,7 +1518,8 @@ const UnitVTable swap_vtable = {
.reset_failed = swap_reset_failed,
- .bus_interface = "org.freedesktop.systemd1.Swap",
+ .control_pid = swap_control_pid,
+
.bus_vtable = bus_swap_vtable,
.bus_set_property = bus_swap_set_property,
.bus_commit_properties = bus_swap_commit_properties,
@@ -1505,7 +1539,6 @@ const UnitVTable swap_vtable = {
.finished_start_job = {
[JOB_DONE] = "Activated swap %s.",
[JOB_FAILED] = "Failed to activate swap %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
[JOB_TIMEOUT] = "Timed out activating swap %s.",
},
.finished_stop_job = {
diff --git a/src/core/swap.h b/src/core/swap.h
index 9136b9abab..b0ef50f1e8 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -22,26 +20,10 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <libudev.h>
+#include "libudev.h"
typedef struct Swap Swap;
-
-typedef enum SwapState {
- SWAP_DEAD,
- SWAP_ACTIVATING, /* /sbin/swapon is running, but the swap not yet enabled. */
- SWAP_ACTIVATING_DONE, /* /sbin/swapon is running, and the swap is done. */
- SWAP_ACTIVE,
- SWAP_DEACTIVATING,
- SWAP_ACTIVATING_SIGTERM,
- SWAP_ACTIVATING_SIGKILL,
- SWAP_DEACTIVATING_SIGTERM,
- SWAP_DEACTIVATING_SIGKILL,
- SWAP_FAILED,
- _SWAP_STATE_MAX,
- _SWAP_STATE_INVALID = -1
-} SwapState;
-
typedef enum SwapExecCommand {
SWAP_EXEC_ACTIVATE,
SWAP_EXEC_DEACTIVATE,
@@ -56,6 +38,7 @@ typedef enum SwapResult {
SWAP_FAILURE_EXIT_CODE,
SWAP_FAILURE_SIGNAL,
SWAP_FAILURE_CORE_DUMP,
+ SWAP_FAILURE_START_LIMIT_HIT,
_SWAP_RESULT_MAX,
_SWAP_RESULT_INVALID = -1
} SwapResult;
@@ -99,6 +82,7 @@ struct Swap {
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
+ DynamicCreds dynamic_creds;
SwapState state, deserialized_state;
@@ -120,9 +104,6 @@ extern const UnitVTable swap_vtable;
int swap_process_device_new(Manager *m, struct udev_device *dev);
int swap_process_device_remove(Manager *m, struct udev_device *dev);
-const char* swap_state_to_string(SwapState i) _const_;
-SwapState swap_state_from_string(const char *s) _pure_;
-
const char* swap_exec_command_to_string(SwapExecCommand i) _const_;
SwapExecCommand swap_exec_command_from_string(const char *s) _pure_;
diff --git a/src/core/system.conf b/src/core/system.conf
index 231609033b..746572b7ff 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -17,9 +17,11 @@
#LogColor=yes
#LogLocation=no
#DumpCore=yes
-#CrashShell=no
#ShowStatus=yes
-#CrashChVT=1
+#CrashChangeVT=no
+#CrashShell=no
+#CrashReboot=no
+#CtrlAltDelBurstAction=reboot-force
#CPUAffinity=1 2
#JoinControllers=cpu,cpuacct net_cls,net_prio
#RuntimeWatchdogSec=0
@@ -33,12 +35,15 @@
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultRestartSec=100ms
-#DefaultStartLimitInterval=10s
+#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
#DefaultEnvironment=
#DefaultCPUAccounting=no
+#DefaultIOAccounting=no
#DefaultBlockIOAccounting=no
#DefaultMemoryAccounting=no
+#DefaultTasksAccounting=yes
+#DefaultTasksMax=15%
#DefaultLimitCPU=
#DefaultLimitFSIZE=
#DefaultLimitDATA=
diff --git a/src/core/target.c b/src/core/target.c
index 8817ef21c4..765c1f3fa4 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,13 +17,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "unit.h"
-#include "target.h"
-#include "log.h"
#include "dbus-target.h"
+#include "log.h"
#include "special.h"
+#include "string-util.h"
#include "unit-name.h"
+#include "unit.h"
+#include "target.h"
static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
[TARGET_DEAD] = UNIT_INACTIVE,
@@ -52,9 +50,7 @@ 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_PART_OF
@@ -128,10 +124,15 @@ static void target_dump(Unit *u, FILE *f, const char *prefix) {
static int target_start(Unit *u) {
Target *t = TARGET(u);
+ int r;
assert(t);
assert(t->state == TARGET_DEAD);
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
+
target_set_state(t, TARGET_ACTIVE);
return 1;
}
@@ -192,13 +193,6 @@ _pure_ static const char *target_sub_state_to_string(Unit *u) {
return target_state_to_string(TARGET(u)->state);
}
-static const char* const target_state_table[_TARGET_STATE_MAX] = {
- [TARGET_DEAD] = "dead",
- [TARGET_ACTIVE] = "active"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
-
const UnitVTable target_vtable = {
.object_size = sizeof(Target),
@@ -221,13 +215,11 @@ const UnitVTable target_vtable = {
.active_state = target_active_state,
.sub_state_to_string = target_sub_state_to_string,
- .bus_interface = "org.freedesktop.systemd1.Target",
.bus_vtable = bus_target_vtable,
.status_message_formats = {
.finished_start_job = {
[JOB_DONE] = "Reached target %s.",
- [JOB_DEPENDENCY] = "Dependency failed for %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Stopped target %s.",
diff --git a/src/core/target.h b/src/core/target.h
index 0a25ef469a..339aea154e 100644
--- a/src/core/target.h
+++ b/src/core/target.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,14 +21,6 @@
typedef struct Target Target;
-
-typedef enum TargetState {
- TARGET_DEAD,
- TARGET_ACTIVE,
- _TARGET_STATE_MAX,
- _TARGET_STATE_INVALID = -1
-} TargetState;
-
struct Target {
Unit meta;
@@ -38,6 +28,3 @@ struct Target {
};
extern const UnitVTable target_vtable;
-
-const char* target_state_to_string(TargetState i) _const_;
-TargetState target_state_from_string(const char *s) _pure_;
diff --git a/src/core/timer.c b/src/core/timer.c
index 7f4a2eb716..9538059c13 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,13 +19,21 @@
#include <errno.h>
-#include "unit.h"
-#include "unit-name.h"
-#include "timer.h"
+#include "alloc-util.h"
+#include "bus-error.h"
+#include "bus-util.h"
#include "dbus-timer.h"
+#include "fs-util.h"
+#include "parse-util.h"
+#include "random-util.h"
#include "special.h"
-#include "bus-util.h"
-#include "bus-error.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "timer.h"
+#include "unit-name.h"
+#include "unit.h"
+#include "user-util.h"
+#include "virt.h"
static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
[TIMER_DEAD] = UNIT_INACTIVE,
@@ -48,6 +54,7 @@ static void timer_init(Unit *u) {
t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
t->next_elapse_realtime = USEC_INFINITY;
t->accuracy_usec = u->manager->default_timer_accuracy_usec;
+ t->remain_after_elapse = true;
}
void timer_free_values(Timer *t) {
@@ -57,10 +64,7 @@ void timer_free_values(Timer *t) {
while ((v = t->values)) {
LIST_REMOVE(value, t->values, v);
-
- if (v->calendar_spec)
- calendar_spec_free(v->calendar_spec);
-
+ calendar_spec_free(v->calendar_spec);
free(v);
}
}
@@ -98,11 +102,14 @@ static int timer_add_default_dependencies(Timer *t) {
assert(t);
+ if (!UNIT(t)->default_dependencies)
+ return 0;
+
r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
if (r < 0)
return r;
- if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
if (r < 0)
return r;
@@ -128,7 +135,7 @@ static int timer_setup_persistent(Timer *t) {
if (!t->persistent)
return 0;
- if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) {
+ if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
if (r < 0)
@@ -188,11 +195,9 @@ static int timer_load(Unit *u) {
if (r < 0)
return r;
- if (u->default_dependencies) {
- r = timer_add_default_dependencies(t);
- if (r < 0)
- return r;
- }
+ r = timer_add_default_dependencies(t);
+ if (r < 0)
+ return r;
}
return timer_verify(t);
@@ -212,13 +217,15 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) {
"%sUnit: %s\n"
"%sPersistent: %s\n"
"%sWakeSystem: %s\n"
- "%sAccuracy: %s\n",
+ "%sAccuracy: %s\n"
+ "%sRemainAfterElapse: %s\n",
prefix, timer_state_to_string(t->state),
prefix, timer_result_to_string(t->result),
prefix, trigger ? trigger->id : "n/a",
prefix, yes_no(t->persistent),
prefix, yes_no(t->wake_system),
- prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1));
+ prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1),
+ prefix, yes_no(t->remain_after_elapse));
LIST_FOREACH(value, v, t->values) {
@@ -270,13 +277,13 @@ static int timer_coldplug(Unit *u) {
assert(t);
assert(t->state == TIMER_DEAD);
- if (t->deserialized_state != t->state) {
+ if (t->deserialized_state == t->state)
+ return 0;
- if (t->deserialized_state == TIMER_WAITING)
- timer_enter_waiting(t, false);
- else
- timer_set_state(t, t->deserialized_state);
- }
+ if (t->deserialized_state == TIMER_WAITING)
+ timer_enter_waiting(t, false);
+ else
+ timer_set_state(t, t->deserialized_state);
return 0;
}
@@ -284,19 +291,36 @@ static int timer_coldplug(Unit *u) {
static void timer_enter_dead(Timer *t, TimerResult f) {
assert(t);
- if (f != TIMER_SUCCESS)
+ if (t->result == TIMER_SUCCESS)
t->result = f;
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
}
+static void timer_enter_elapsed(Timer *t, bool leave_around) {
+ assert(t);
+
+ /* If a unit is marked with RemainAfterElapse=yes we leave it
+ * around even after it elapsed once, so that starting it
+ * later again does not necessarily mean immediate
+ * retriggering. We unconditionally leave units with
+ * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around,
+ * since they might be restarted automatically at any time
+ * later on. */
+
+ if (t->remain_after_elapse || leave_around)
+ timer_set_state(t, TIMER_ELAPSED);
+ else
+ timer_enter_dead(t, TIMER_SUCCESS);
+}
+
static usec_t monotonic_to_boottime(usec_t t) {
usec_t a, b;
if (t <= 0)
return 0;
- a = now(CLOCK_BOOTTIME);
+ a = now(clock_boottime_or_monotonic());
b = now(CLOCK_MONOTONIC);
if (t + a > b)
@@ -305,18 +329,51 @@ static usec_t monotonic_to_boottime(usec_t t) {
return 0;
}
+static void add_random(Timer *t, usec_t *v) {
+ char s[FORMAT_TIMESPAN_MAX];
+ usec_t add;
+
+ assert(t);
+ assert(v);
+
+ if (t->random_usec == 0)
+ return;
+ if (*v == USEC_INFINITY)
+ return;
+
+ add = random_u64() % t->random_usec;
+
+ if (*v + add < *v) /* overflow */
+ *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
+ else
+ *v += add;
+
+ log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
+}
+
static void timer_enter_waiting(Timer *t, bool initial) {
bool found_monotonic = false, found_realtime = false;
usec_t ts_realtime, ts_monotonic;
usec_t base = 0;
+ bool leave_around = false;
TimerValue *v;
+ Unit *trigger;
int r;
+ assert(t);
+
+ trigger = UNIT_TRIGGER(UNIT(t));
+ if (!trigger) {
+ log_unit_error(UNIT(t), "Unit to trigger vanished.");
+ timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
+ return;
+ }
+
/* If we shall wake the system we use the boottime clock
* rather than the monotonic clock. */
ts_realtime = now(CLOCK_REALTIME);
- ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC);
+ ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC);
t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
LIST_FOREACH(value, v, t->values) {
@@ -356,17 +413,21 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_BOOT:
- /* CLOCK_MONOTONIC equals the uptime on Linux */
- base = 0;
- break;
-
+ if (detect_container() <= 0) {
+ /* CLOCK_MONOTONIC equals the uptime on Linux */
+ base = 0;
+ break;
+ }
+ /* In a container we don't want to include the time the host
+ * was already up when the container started, so count from
+ * our own startup. Fall through. */
case TIMER_STARTUP:
base = UNIT(t)->manager->userspace_timestamp.monotonic;
break;
case TIMER_UNIT_ACTIVE:
-
- base = UNIT_TRIGGER(UNIT(t))->inactive_exit_timestamp.monotonic;
+ leave_around = true;
+ base = trigger->inactive_exit_timestamp.monotonic;
if (base <= 0)
base = t->last_trigger.monotonic;
@@ -377,8 +438,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
break;
case TIMER_UNIT_INACTIVE:
-
- base = UNIT_TRIGGER(UNIT(t))->inactive_enter_timestamp.monotonic;
+ leave_around = true;
+ base = trigger->inactive_enter_timestamp.monotonic;
if (base <= 0)
base = t->last_trigger.monotonic;
@@ -414,14 +475,18 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (!found_monotonic && !found_realtime) {
log_unit_debug(UNIT(t), "Timer is elapsed.");
- timer_set_state(t, TIMER_ELAPSED);
+ timer_enter_elapsed(t, leave_around);
return;
}
if (found_monotonic) {
char buf[FORMAT_TIMESPAN_MAX];
+ usec_t left;
- log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0));
+ add_random(t, &t->next_elapse_monotonic_or_boottime);
+
+ left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
+ log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
if (t->monotonic_event_source) {
r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
@@ -454,6 +519,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
+
+ add_random(t, &t->next_elapse_realtime);
+
log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
if (t->realtime_event_source) {
@@ -493,7 +561,8 @@ fail:
}
static void timer_enter_running(Timer *t) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ Unit *trigger;
int r;
assert(t);
@@ -502,15 +571,21 @@ static void timer_enter_running(Timer *t) {
if (unit_stop_pending(UNIT(t)))
return;
- r = manager_add_job(UNIT(t)->manager, JOB_START, UNIT_TRIGGER(UNIT(t)),
- JOB_REPLACE, true, &error, NULL);
+ trigger = UNIT_TRIGGER(UNIT(t));
+ if (!trigger) {
+ log_unit_error(UNIT(t), "Unit to trigger vanished.");
+ timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
+ return;
+ }
+
+ r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL);
if (r < 0)
goto fail;
dual_timestamp_get(&t->last_trigger);
if (t->stamp_path)
- touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0);
+ touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
timer_set_state(t, TIMER_RUNNING);
return;
@@ -523,12 +598,27 @@ fail:
static int timer_start(Unit *u) {
Timer *t = TIMER(u);
TimerValue *v;
+ Unit *trigger;
+ int r;
assert(t);
assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
- if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
+ trigger = UNIT_TRIGGER(u);
+ if (!trigger || trigger->load_state != UNIT_LOADED) {
+ log_unit_error(u, "Refusing to start, unit to trigger not loaded.");
return -ENOENT;
+ }
+
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
+ r = unit_acquire_invocation_id(u);
+ if (r < 0)
+ return r;
t->last_trigger = DUAL_TIMESTAMP_NULL;
@@ -546,7 +636,7 @@ static int timer_start(Unit *u) {
/* The timer has never run before,
* make sure a stamp file exists.
*/
- touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
+ (void) touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
}
t->result = TIMER_SUCCESS;
@@ -716,16 +806,6 @@ static void timer_time_change(Unit *u) {
timer_enter_waiting(t, false);
}
-static const char* const timer_state_table[_TIMER_STATE_MAX] = {
- [TIMER_DEAD] = "dead",
- [TIMER_WAITING] = "waiting",
- [TIMER_RUNNING] = "running",
- [TIMER_ELAPSED] = "elapsed",
- [TIMER_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState);
-
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
@@ -739,7 +819,8 @@ DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
[TIMER_SUCCESS] = "success",
- [TIMER_FAILURE_RESOURCES] = "resources"
+ [TIMER_FAILURE_RESOURCES] = "resources",
+ [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
@@ -775,7 +856,6 @@ const UnitVTable timer_vtable = {
.reset_failed = timer_reset_failed,
.time_change = timer_time_change,
- .bus_interface = "org.freedesktop.systemd1.Timer",
.bus_vtable = bus_timer_vtable,
.bus_set_property = bus_timer_set_property,
diff --git a/src/core/timer.h b/src/core/timer.h
index 9d919e4d3e..9c4b64f898 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -25,16 +23,6 @@ typedef struct Timer Timer;
#include "calendarspec.h"
-typedef enum TimerState {
- TIMER_DEAD,
- TIMER_WAITING,
- TIMER_RUNNING,
- TIMER_ELAPSED,
- TIMER_FAILED,
- _TIMER_STATE_MAX,
- _TIMER_STATE_INVALID = -1
-} TimerState;
-
typedef enum TimerBase {
TIMER_ACTIVE,
TIMER_BOOT,
@@ -60,6 +48,7 @@ typedef struct TimerValue {
typedef enum TimerResult {
TIMER_SUCCESS,
TIMER_FAILURE_RESOURCES,
+ TIMER_FAILURE_START_LIMIT_HIT,
_TIMER_RESULT_MAX,
_TIMER_RESULT_INVALID = -1
} TimerResult;
@@ -68,6 +57,7 @@ struct Timer {
Unit meta;
usec_t accuracy_usec;
+ usec_t random_usec;
LIST_HEAD(TimerValue, values);
usec_t next_elapse_realtime;
@@ -83,6 +73,7 @@ struct Timer {
bool persistent;
bool wake_system;
+ bool remain_after_elapse;
char *stamp_path;
};
@@ -91,9 +82,6 @@ void timer_free_values(Timer *t);
extern const UnitVTable timer_vtable;
-const char *timer_state_to_string(TimerState i) _const_;
-TimerState timer_state_from_string(const char *s) _pure_;
-
const char *timer_base_to_string(TimerBase i) _const_;
TimerBase timer_base_from_string(const char *s) _pure_;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 090103fbda..8370b864fb 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,13 +17,15 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <unistd.h>
#include <fcntl.h>
+#include <unistd.h>
+#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-error.h"
-#include "transaction.h"
#include "terminal-util.h"
+#include "transaction.h"
+#include "dbus-unit.h"
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
@@ -98,9 +98,7 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other
j->type = t;
j->state = JOB_WAITING;
- j->override = j->override || other->override;
j->irreversible = j->irreversible || other->irreversible;
-
j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
/* Patch us in as new owner of the JobDependency objects */
@@ -375,17 +373,15 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
delete = NULL;
for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
- /* logging for j not k here here to provide consistent narrative */
+ /* logging for j not k here to provide consistent narrative */
log_unit_warning(j->unit,
"Found dependency on %s/%s",
k->unit->id, job_type_to_string(k->type));
- if (!delete && hashmap_get(tr->jobs, k->unit) &&
- !unit_matters_to_anchor(k->unit, k)) {
+ if (!delete && hashmap_get(tr->jobs, k->unit) && !unit_matters_to_anchor(k->unit, k))
/* Ok, we can drop this one, so let's
* do so. */
delete = k;
- }
/* Check if this in fact was the beginning of
* the cycle */
@@ -395,7 +391,8 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
if (delete) {
- /* logging for j not k here here to provide consistent narrative */
+ const char *status;
+ /* logging for j not k here to provide consistent narrative */
log_unit_warning(j->unit,
"Breaking ordering cycle by deleting job %s/%s",
delete->unit->id, job_type_to_string(delete->type));
@@ -403,7 +400,13 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
"Job %s/%s deleted to break ordering cycle starting with %s/%s",
delete->unit->id, job_type_to_string(delete->type),
j->unit->id, job_type_to_string(j->type));
- unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF,
+
+ if (log_get_show_color())
+ status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL;
+ else
+ status = " SKIP ";
+
+ unit_status_printf(delete->unit, status,
"Ordering cycle found, skipping %s");
transaction_delete_unit(tr, delete->unit);
return -EAGAIN;
@@ -464,9 +467,11 @@ static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bu
g = (*generation)++;
- HASHMAP_FOREACH(j, tr->jobs, i)
- if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
+ HASHMAP_FOREACH(j, tr->jobs, i) {
+ r = transaction_verify_order_one(tr, j, NULL, g, e);
+ if (r < 0)
return r;
+ }
return 0;
}
@@ -586,13 +591,16 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
HASHMAP_FOREACH(j, m->jobs, i) {
assert(j->installed);
+ if (j->unit->ignore_on_isolate)
+ continue;
+
if (hashmap_get(tr->jobs, j->unit))
continue;
/* Not invalidating recursively. Avoids triggering
* OnFailure= actions of dependent jobs. Also avoids
* invalidating our iterator. */
- job_finish_and_invalidate(j, JOB_CANCELED, false);
+ job_finish_and_invalidate(j, JOB_CANCELED, false, false);
}
}
@@ -736,15 +744,15 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error
if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 &&
m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) {
- pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
- pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
+ (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
+ (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
}
}
return 0;
}
-static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
+static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) {
Job *j, *f;
assert(tr);
@@ -773,7 +781,6 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b
j->generation = 0;
j->marker = NULL;
j->matters_to_anchor = false;
- j->override = override;
j->irreversible = tr->irreversible;
LIST_PREPEND(transaction, f, j);
@@ -832,7 +839,6 @@ int transaction_add_job_and_dependencies(
Unit *unit,
Job *by,
bool matters,
- bool override,
bool conflicts,
bool ignore_requirements,
bool ignore_order,
@@ -852,7 +858,7 @@ int transaction_add_job_and_dependencies(
* This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()).
* This way, we "recursively" coldplug units, ensuring that we do not look at state of
* not-yet-coldplugged units. */
- if (unit->manager->n_reloading > 0)
+ if (MANAGER_IS_RELOADING(unit->manager))
unit_coldplug(unit);
/* log_debug("Pulling in %s/%s from %s/%s", */
@@ -863,30 +869,12 @@ int transaction_add_job_and_dependencies(
if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED))
return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
- if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
- if (unit->load_error == -ENOENT || unit->manager->test_run)
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s.",
- unit->id,
- strerror(-unit->load_error));
- else
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s. "
- "See system logs and 'systemctl status %s' for details.",
- unit->id,
- strerror(-unit->load_error),
- unit->id);
+ if (type != JOB_STOP) {
+ r = bus_unit_check_load_state(unit, e);
+ if (r < 0)
+ return r;
}
- if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND)
- return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
- "Unit %s failed to load: %s.",
- unit->id, strerror(-unit->load_error));
-
- if (type != JOB_STOP && unit->load_state == UNIT_MASKED)
- return sd_bus_error_setf(e, BUS_ERROR_UNIT_MASKED,
- "Unit %s is masked.", unit->id);
-
if (!unit_job_is_applicable(unit, type))
return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
"Job type %s is not applicable for unit %s.",
@@ -894,7 +882,7 @@ int transaction_add_job_and_dependencies(
/* First add the job. */
- ret = transaction_add_one_job(tr, type, unit, override, &is_new);
+ ret = transaction_add_one_job(tr, type, unit, &is_new);
if (!ret)
return -ENOMEM;
@@ -917,7 +905,7 @@ int transaction_add_job_and_dependencies(
* add all dependencies of everybody following. */
if (unit_following_set(ret->unit, &following) > 0) {
SET_FOREACH(dep, following, i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r));
sd_bus_error_free(e);
@@ -930,9 +918,9 @@ int transaction_add_job_and_dependencies(
/* Finally, recursively add in all dependencies. */
if (type == JOB_START || type == JOB_RESTART) {
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
- if (r != -EBADR)
+ if (r != -EBADR) /* job type not applicable */
goto fail;
sd_bus_error_free(e);
@@ -940,62 +928,41 @@ int transaction_add_job_and_dependencies(
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
- if (r != -EBADR)
+ if (r != -EBADR) /* job type not applicable */
goto fail;
sd_bus_error_free(e);
}
}
- 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_unit_full(dep,
- r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
- "Cannot add dependency job, ignoring: %s",
- bus_error_message(e, r));
- sd_bus_error_free(e);
- }
- }
-
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);
+ r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
+ /* unit masked, job type not applicable and unit not found are not considered as errors. */
log_unit_full(dep,
- r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
- "Cannot add dependency job, ignoring: %s",
+ IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
+ r, "Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
sd_bus_error_free(e);
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
- if (r != -EBADR)
+ if (r != -EBADR) /* job type not applicable */
goto fail;
sd_bus_error_free(e);
}
}
- 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_unit_full(dep,
- r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
- "Cannot add dependency job, ignoring: %s",
- bus_error_message(e, r));
- sd_bus_error_free(e);
- }
- }
-
SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
- r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
if (r < 0) {
- if (r != -EBADR)
+ if (r != -EBADR) /* job type not applicable */
goto fail;
sd_bus_error_free(e);
@@ -1003,7 +970,7 @@ 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);
+ r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep,
"Cannot add dependency job, ignoring: %s",
@@ -1038,9 +1005,9 @@ int transaction_add_job_and_dependencies(
if (nt == JOB_NOP)
continue;
- r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, override, false, false, ignore_order, e);
+ r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
if (r < 0) {
- if (r != -EBADR)
+ if (r != -EBADR) /* job type not applicable */
goto fail;
sd_bus_error_free(e);
@@ -1051,7 +1018,13 @@ int transaction_add_job_and_dependencies(
if (type == JOB_RELOAD) {
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);
+ JobType nt;
+
+ nt = job_type_collapse(JOB_TRY_RELOAD, dep);
+ if (nt == JOB_NOP)
+ continue;
+
+ r = transaction_add_job_and_dependencies(tr, nt, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep,
"Cannot add dependency reload job, ignoring: %s",
@@ -1096,7 +1069,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
if (hashmap_get(tr->jobs, u))
continue;
- r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
+ r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, NULL);
if (r < 0)
log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %m");
}
diff --git a/src/core/transaction.h b/src/core/transaction.h
index d949b21b8d..6a3f927b0f 100644
--- a/src/core/transaction.h
+++ b/src/core/transaction.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -23,10 +21,10 @@
typedef struct Transaction Transaction;
-#include "unit.h"
-#include "manager.h"
-#include "job.h"
#include "hashmap.h"
+#include "job.h"
+#include "manager.h"
+#include "unit.h"
struct Transaction {
/* Jobs to be added */
@@ -44,7 +42,6 @@ int transaction_add_job_and_dependencies(
Unit *unit,
Job *by,
bool matters,
- bool override,
bool conflicts,
bool ignore_requirements,
bool ignore_order,
diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in
new file mode 100644
index 0000000000..0d8c303136
--- /dev/null
+++ b/src/core/triggers.systemd.in
@@ -0,0 +1,66 @@
+# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */
+#
+# This file is part of systemd.
+#
+# Copyright 2015 Zbigniew Jędrzejewski-Szmek
+#
+# 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/>.
+
+# The contents of this are an example to be copied into systemd.spec.
+#
+# Minimum rpm version supported: 4.13.0
+
+%transfiletriggerin -P 900900 -p <lua> -- @systemunitdir@ /etc/systemd/system
+-- This script will run after any package is initially installed or
+-- upgraded. We care about the case where a package is initially
+-- installed, because other cases are covered by the *un scriptlets,
+-- so sometimes we will reload needlessly.
+
+pid = posix.fork()
+if pid == 0 then
+ assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
+elseif pid > 0 then
+ posix.wait(pid)
+end
+
+%transfiletriggerun -p <lua> -- @systemunitdir@ /etc/systemd/system
+-- On removal, we need to run daemon-reload after any units have been
+-- removed. %transfiletriggerpostun would be ideal, but it does not get
+-- executed for some reason.
+-- On upgrade, we need to run daemon-reload after any new unit files
+-- have been installed, but before %postun scripts in packages get
+-- executed. %transfiletriggerun gets the right list of files
+-- but it is invoked too early (before changes happen).
+-- %filetriggerpostun happens at the right time, but it fires for
+-- every package.
+-- To execute the reload at the right time, we create a state
+-- file in %transfiletriggerun and execute the daemon-reload in
+-- the first %filetriggerpostun.
+
+posix.mkdir("%{_localstatedir}/lib")
+posix.mkdir("%{_localstatedir}/lib/rpm-state")
+posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd")
+io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w")
+
+%filetriggerpostun -P 1000100 -p <lua> -- @systemunitdir@ /etc/systemd/system
+if posix.access("%{_localstatedir}/lib/rpm-state/systemd/needs-reload") then
+ posix.unlink("%{_localstatedir}/lib/rpm-state/systemd/needs-reload")
+ posix.rmdir("%{_localstatedir}/lib/rpm-state/systemd")
+ pid = posix.fork()
+ if pid == 0 then
+ assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
+ elseif pid > 0 then
+ posix.wait(pid)
+ end
+end
diff --git a/src/core/umount.c b/src/core/umount.c
index d59b5d0ffb..1e5459ed80 100644
--- a/src/core/umount.c
+++ b/src/core/umount.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -21,23 +19,30 @@
#include <errno.h>
#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/loop.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/swap.h>
-#include <linux/loop.h>
-#include <linux/dm-ioctl.h>
+#include "libudev.h"
+
+#include "alloc-util.h"
+#include "escape.h"
+#include "fd-util.h"
+#include "fstab-util.h"
#include "list.h"
#include "mount-setup.h"
-#include "umount.h"
#include "path-util.h"
+#include "string-util.h"
+#include "udev-util.h"
+#include "umount.h"
#include "util.h"
#include "virt.h"
-#include "libudev.h"
-#include "udev-util.h"
typedef struct MountPoint {
char *path;
+ char *options;
dev_t devnum;
LIST_FIELDS(struct MountPoint, mount_point);
} MountPoint;
@@ -71,7 +76,7 @@ static int mount_points_list_get(MountPoint **head) {
return -errno;
for (i = 1;; i++) {
- _cleanup_free_ char *path = NULL;
+ _cleanup_free_ char *path = NULL, *options = NULL;
char *p = NULL;
MountPoint *m;
int k;
@@ -82,15 +87,15 @@ static int mount_points_list_get(MountPoint **head) {
"%*s " /* (3) major:minor */
"%*s " /* (4) root */
"%ms " /* (5) mount point */
- "%*s" /* (6) mount options */
+ "%*s" /* (6) mount flags */
"%*[^-]" /* (7) optional fields */
"- " /* (8) separator */
"%*s " /* (9) file system type */
"%*s" /* (10) mount source */
- "%*s" /* (11) mount options 2 */
+ "%ms" /* (11) mount options */
"%*[^\n]", /* some rubbish at the end */
- &path);
- if (k != 1) {
+ &path, &options);
+ if (k != 2) {
if (k == EOF)
break;
@@ -125,6 +130,9 @@ static int mount_points_list_get(MountPoint **head) {
}
m->path = p;
+ m->options = options;
+ options = NULL;
+
LIST_PREPEND(mount_point, *head, m);
}
@@ -367,8 +375,16 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
/* 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) {
+ the superblock here, not the bind mount. */
+ if (detect_container() <= 0) {
+ _cleanup_free_ char *options = NULL;
+ /* MS_REMOUNT requires that the data parameter
+ * should be the same from the original mount
+ * except for the desired changes. Since we want
+ * to remount read-only, we should filter out
+ * rw (and ro too, because it confuses the kernel) */
+ (void) fstab_filter_options(m->options, "rw\0ro\0", NULL, NULL, &options);
+
/* We always try to remount directories
* read-only first, before we go on and umount
* them.
@@ -385,7 +401,8 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
* alias read-only we hence should be
* relatively safe regarding keeping the fs we
* can otherwise not see dirty. */
- (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
+ log_info("Remounting '%s' read-only with options '%s'.", m->path, options);
+ (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options);
}
/* Skip / and /usr since we cannot unmount that
@@ -395,6 +412,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
#ifndef HAVE_SPLIT_USR
|| path_equal(m->path, "/usr")
#endif
+ || path_startswith(m->path, "/run/initramfs")
)
continue;
@@ -455,7 +473,7 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) {
major(root_st.st_dev) != 0 &&
lstat(m->path, &loopback_st) >= 0 &&
root_st.st_dev == loopback_st.st_rdev) {
- n_failed ++;
+ n_failed++;
continue;
}
@@ -490,7 +508,7 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) {
if (k >= 0 &&
major(root_st.st_dev) != 0 &&
root_st.st_dev == m->devnum) {
- n_failed ++;
+ n_failed++;
continue;
}
diff --git a/src/core/umount.h b/src/core/umount.h
index 8439ffe58f..4e2215a47d 100644
--- a/src/core/umount.h
+++ b/src/core/umount.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index 0889769d03..f11df42af3 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -19,14 +17,17 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "unit.h"
+#include "alloc-util.h"
+#include "cgroup-util.h"
+#include "formats-util.h"
+#include "macro.h"
#include "specifier.h"
+#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
#include "unit-printf.h"
-#include "macro.h"
-#include "cgroup-util.h"
-#include "formats-util.h"
+#include "unit.h"
+#include "user-util.h"
static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
@@ -63,10 +64,7 @@ static int specifier_instance_unescaped(char specifier, void *data, void *userda
assert(u);
- if (!u->instance)
- return -EINVAL;
-
- return unit_name_unescape(u->instance, ret);
+ return unit_name_unescape(strempty(u->instance), ret);
}
static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
@@ -128,6 +126,8 @@ static int specifier_cgroup_slice(char specifier, void *data, void *userdata, ch
n = unit_default_cgroup_path(slice);
} else
n = strdup(u->manager->cgroup_root);
+ if (!n)
+ return -ENOMEM;
*ret = n;
return 0;
@@ -140,14 +140,9 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char **
assert(u);
- if (u->manager->running_as == MANAGER_SYSTEM)
- e = "/run";
- else {
- e = getenv("XDG_RUNTIME_DIR");
- if (!e)
- return -EOPNOTSUPP;
- }
-
+ e = manager_get_runtime_prefix(u->manager);
+ if (!e)
+ return -EOPNOTSUPP;
n = strdup(e);
if (!n)
return -ENOMEM;
@@ -157,162 +152,43 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char **
}
static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
- char *printed = NULL;
- Unit *u = userdata;
- ExecContext *c;
- int r = 0;
-
- assert(u);
+ char *t;
- c = unit_get_exec_context(u);
- if (!c)
- return -EINVAL;
+ /* If we are UID 0 (root), this will not result in NSS,
+ * otherwise it might. This is good, as we want to be able to
+ * run this in PID 1, where our user ID is 0, but where NSS
+ * lookups are not allowed. */
- if (u->manager->running_as == MANAGER_SYSTEM) {
-
- /* We cannot use NSS from PID 1, hence try to make the
- * best of it in that case, and fail if we can't help
- * it */
-
- if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
- printed = strdup(specifier == 'u' ? "root" : "0");
- else {
- if (specifier == 'u')
- printed = strdup(c->user);
- else {
- uid_t uid;
-
- r = parse_uid(c->user, &uid);
- if (r < 0)
- return -ENODATA;
-
- r = asprintf(&printed, UID_FMT, uid);
- }
- }
-
- } else {
- _cleanup_free_ char *tmp = NULL;
- const char *username = NULL;
- uid_t uid;
-
- if (c->user)
- username = c->user;
- else
- /* get USER env from env or our own uid */
- username = tmp = getusername_malloc();
-
- /* fish username from passwd */
- r = get_user_creds(&username, &uid, NULL, NULL, NULL);
- if (r < 0)
- return r;
-
- if (specifier == 'u')
- printed = strdup(username);
- else
- r = asprintf(&printed, UID_FMT, uid);
- }
-
- if (r < 0 || !printed)
+ t = getusername_malloc();
+ if (!t)
return -ENOMEM;
- *ret = printed;
+ *ret = t;
return 0;
}
-static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
- ExecContext *c;
- char *n;
- int r;
-
- assert(u);
+static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) {
- c = unit_get_exec_context(u);
- if (!c)
- return -EOPNOTSUPP;
-
- if (u->manager->running_as == MANAGER_SYSTEM) {
-
- /* We cannot use NSS from PID 1, hence try to make the
- * best of it if we can, but fail if we can't */
-
- if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
- n = strdup("/root");
- else
- return -EOPNOTSUPP;
-
- } else {
-
- /* return HOME if set, otherwise from passwd */
- if (!c || !c->user) {
- r = get_home_dir(&n);
- if (r < 0)
- return r;
- } else {
- const char *username, *home;
-
- username = c->user;
- r = get_user_creds(&username, NULL, NULL, &home, NULL);
- if (r < 0)
- return r;
-
- n = strdup(home);
- }
- }
-
- if (!n)
+ if (asprintf(ret, UID_FMT, getuid()) < 0)
return -ENOMEM;
- *ret = n;
return 0;
}
-static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
- Unit *u = userdata;
- ExecContext *c;
- char *n;
- int r;
-
- assert(u);
-
- c = unit_get_exec_context(u);
- if (!c)
- return -EOPNOTSUPP;
-
- if (u->manager->running_as == MANAGER_SYSTEM) {
-
- /* We cannot use NSS from PID 1, hence try to make the
- * best of it if we can, but fail if we can't */
-
- if (!c->user || streq(c->user, "root") || streq(c->user, "0"))
- n = strdup("/bin/sh");
- else
- return -EOPNOTSUPP;
-
- } else {
+static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
- /* return /bin/sh for root, otherwise the value from passwd */
- if (!c->user) {
- r = get_shell(&n);
- if (r < 0)
- return r;
- } else {
- const char *username, *shell;
+ /* On PID 1 (which runs as root) this will not result in NSS,
+ * which is good. See above */
- username = c->user;
- r = get_user_creds(&username, NULL, NULL, NULL, &shell);
- if (r < 0)
- return r;
+ return get_home_dir(ret);
+}
- n = strdup(shell);
- }
- }
+static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
- if (!n)
- return -ENOMEM;
+ /* On PID 1 (which runs as root) this will not result in NSS,
+ * which is good. See above */
- *ret = n;
- return 0;
+ return get_shell(ret);
}
int unit_name_printf(Unit *u, const char* format, char **ret) {
@@ -352,10 +228,10 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
* %r where units in this slice are placed in the cgroup tree
* %R the root of this systemd's instance tree
* %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR)
- * %U the UID of the configured user or running user
- * %u the username of the configured user or running user
- * %h the homedir of the configured user or running user
- * %s the shell of the configured user or running user
+ * %U the UID of the running user
+ * %u the username of the running user
+ * %h the homedir of the running user
+ * %s the shell of the running user
* %m the machine ID of the running system
* %H the host name of the running system
* %b the boot ID of the running system
@@ -375,7 +251,8 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
{ 'r', specifier_cgroup_slice, NULL },
{ 'R', specifier_cgroup_root, NULL },
{ 't', specifier_runtime, NULL },
- { 'U', specifier_user_name, NULL },
+
+ { 'U', specifier_user_id, NULL },
{ 'u', specifier_user_name, NULL },
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },
diff --git a/src/core/unit-printf.h b/src/core/unit-printf.h
index 51acad63e9..4fc8531228 100644
--- a/src/core/unit-printf.h
+++ b/src/core/unit-printf.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
diff --git a/src/core/unit.c b/src/core/unit.c
index fac017c57d..690f7f7dd9 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
/***
This file is part of systemd.
@@ -20,41 +18,53 @@
***/
#include <errno.h>
-#include <string.h>
#include <stdlib.h>
-#include <unistd.h>
+#include <string.h>
#include <sys/stat.h>
+#include <unistd.h>
#include "sd-id128.h"
#include "sd-messages.h"
-#include "set.h"
-#include "unit.h"
-#include "macro.h"
-#include "strv.h"
-#include "path-util.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "unit-name.h"
-#include "dbus-unit.h"
-#include "special.h"
-#include "cgroup-util.h"
-#include "missing.h"
-#include "mkdir.h"
-#include "fileio-label.h"
+
+#include "alloc-util.h"
#include "bus-common-errors.h"
+#include "bus-util.h"
+#include "cgroup-util.h"
+#include "dbus-unit.h"
#include "dbus.h"
-#include "execute.h"
#include "dropin.h"
+#include "escape.h"
+#include "execute.h"
+#include "fileio-label.h"
#include "formats-util.h"
+#include "id128-util.h"
+#include "load-dropin.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "macro.h"
+#include "missing.h"
+#include "mkdir.h"
+#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
+#include "set.h"
+#include "signal-util.h"
+#include "special.h"
+#include "stat-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "umask-util.h"
+#include "unit-name.h"
+#include "unit.h"
+#include "user-util.h"
+#include "virt.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
[UNIT_SOCKET] = &socket_vtable,
[UNIT_BUSNAME] = &busname_vtable,
[UNIT_TARGET] = &target_vtable,
- [UNIT_SNAPSHOT] = &snapshot_vtable,
[UNIT_DEVICE] = &device_vtable,
[UNIT_MOUNT] = &mount_vtable,
[UNIT_AUTOMOUNT] = &automount_vtable,
@@ -89,7 +99,13 @@ Unit *unit_new(Manager *m, size_t size) {
u->unit_file_state = _UNIT_FILE_STATE_INVALID;
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
+ u->cgroup_inotify_wd = -1;
+ u->job_timeout = USEC_INFINITY;
+ u->ref_uid = UID_INVALID;
+ u->ref_gid = GID_INVALID;
+ u->cpu_usage_last = NSEC_INFINITY;
+ RATELIMIT_INIT(u->start_limit, m->default_start_limit_interval, m->default_start_limit_burst);
RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
return u;
@@ -99,7 +115,7 @@ bool unit_has_name(Unit *u, const char *name) {
assert(u);
assert(name);
- return !!set_get(u->names, (char*) name);
+ return set_contains(u->names, (char*) name);
}
static void unit_init(Unit *u) {
@@ -120,8 +136,13 @@ static void unit_init(Unit *u) {
* been initialized */
cc->cpu_accounting = u->manager->default_cpu_accounting;
+ cc->io_accounting = u->manager->default_io_accounting;
cc->blockio_accounting = u->manager->default_blockio_accounting;
cc->memory_accounting = u->manager->default_memory_accounting;
+ cc->tasks_accounting = u->manager->default_tasks_accounting;
+
+ if (u->type != UNIT_SLICE)
+ cc->tasks_max = u->manager->default_tasks_max;
}
ec = unit_get_exec_context(u);
@@ -177,7 +198,7 @@ int unit_add_name(Unit *u, const char *text) {
if (r < 0)
return r;
- if (i && unit_vtable[t]->no_instances)
+ if (i && !unit_type_may_template(t))
return -EINVAL;
/* Ensure that this unit is either instanced or not instanced,
@@ -186,7 +207,7 @@ int unit_add_name(Unit *u, const char *text) {
if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
return -EINVAL;
- if (unit_vtable[t]->no_alias && !set_isempty(u->names))
+ if (!unit_type_may_alias(t) && !set_isempty(u->names))
return -EEXIST;
if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
@@ -304,15 +325,15 @@ bool unit_check_gc(Unit *u) {
if (state != UNIT_INACTIVE)
return true;
- if (UNIT_VTABLE(u)->no_gc)
- return true;
-
if (u->no_gc)
return true;
if (u->refs)
return true;
+ if (sd_bus_track_count(u->bus_track) > 0)
+ return true;
+
if (UNIT_VTABLE(u)->check_gc)
if (UNIT_VTABLE(u)->check_gc(u))
return true;
@@ -353,7 +374,7 @@ void unit_add_to_gc_queue(Unit *u) {
LIST_PREPEND(gc_queue, u->manager->gc_queue, u);
u->in_gc_queue = true;
- u->manager->n_in_gc_queue ++;
+ u->manager->n_in_gc_queue++;
}
void unit_add_to_dbus_queue(Unit *u) {
@@ -404,17 +425,25 @@ static void unit_remove_transient(Unit *u) {
return;
if (u->fragment_path)
- unlink(u->fragment_path);
+ (void) unlink(u->fragment_path);
STRV_FOREACH(i, u->dropin_paths) {
- _cleanup_free_ char *p = NULL;
- int r;
+ _cleanup_free_ char *p = NULL, *pp = NULL;
+
+ p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */
+ if (!p)
+ continue;
- unlink(*i);
+ pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */
+ if (!pp)
+ continue;
- r = path_get_parent(*i, &p);
- if (r >= 0)
- rmdir(p);
+ /* Only drop transient drop-ins */
+ if (!path_equal(u->manager->lookup_paths.transient, pp))
+ continue;
+
+ (void) unlink(*i);
+ (void) rmdir(p);
}
}
@@ -442,8 +471,7 @@ static void unit_free_requires_mounts_for(Unit *u) {
}
}
- strv_free(u->requires_mounts_for);
- u->requires_mounts_for = NULL;
+ u->requires_mounts_for = strv_free(u->requires_mounts_for);
}
static void unit_done(Unit *u) {
@@ -474,18 +502,29 @@ void unit_free(Unit *u) {
assert(u);
- if (u->manager->n_reloading <= 0)
+ if (u->transient_file)
+ fclose(u->transient_file);
+
+ if (!MANAGER_IS_RELOADING(u->manager))
unit_remove_transient(u);
bus_unit_send_removed_signal(u);
unit_done(u);
+ sd_bus_slot_unref(u->match_bus_slot);
+
+ sd_bus_track_unref(u->bus_track);
+ u->deserialized_refs = strv_free(u->deserialized_refs);
+
unit_free_requires_mounts_for(u);
SET_FOREACH(t, u->names, i)
hashmap_remove_value(u->manager->units, t, u);
+ if (!sd_id128_is_null(u->invocation_id))
+ hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u);
+
if (u->job) {
Job *j = u->job;
job_uninstall(j);
@@ -521,12 +560,11 @@ void unit_free(Unit *u) {
if (u->in_cgroup_queue)
LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
- if (u->cgroup_path) {
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- free(u->cgroup_path);
- }
+ unit_release_cgroup(u);
+
+ unit_unref_uid_gid(u, false);
- manager_update_failed_units(u->manager, u, false);
+ (void) manager_update_failed_units(u->manager, u, false);
set_remove(u->manager->startup_units, u);
free(u->description);
@@ -545,6 +583,8 @@ void unit_free(Unit *u) {
condition_free_list(u->conditions);
condition_free_list(u->asserts);
+ free(u->reboot_arg);
+
unit_ref_unset(&u->slice);
while (u->refs)
@@ -672,8 +712,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD
/* The move cannot fail. The caller must have performed a reservation. */
assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0);
- set_free(other->dependencies[d]);
- other->dependencies[d] = NULL;
+ other->dependencies[d] = set_free(other->dependencies[d]);
}
int unit_merge(Unit *u, Unit *other) {
@@ -697,6 +736,9 @@ int unit_merge(Unit *u, Unit *other) {
if (!u->instance != !other->instance)
return -EINVAL;
+ if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */
+ return -EEXIST;
+
if (other->load_state != UNIT_STUB &&
other->load_state != UNIT_NOT_FOUND)
return -EEXIST;
@@ -753,9 +795,9 @@ int unit_merge(Unit *u, Unit *other) {
}
int unit_merge_by_name(Unit *u, const char *name) {
+ _cleanup_free_ char *s = NULL;
Unit *other;
int r;
- _cleanup_free_ char *s = NULL;
assert(u);
assert(name);
@@ -805,7 +847,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
return r;
}
- if (u->manager->running_as != MANAGER_SYSTEM)
+ if (!MANAGER_IS_SYSTEM(u->manager))
return 0;
if (c->private_tmp) {
@@ -857,6 +899,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
Iterator i;
const char *prefix2;
char
+ timestamp0[FORMAT_TIMESTAMP_MAX],
timestamp1[FORMAT_TIMESTAMP_MAX],
timestamp2[FORMAT_TIMESTAMP_MAX],
timestamp3[FORMAT_TIMESTAMP_MAX],
@@ -865,6 +908,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
Unit *following;
_cleanup_set_free_ Set *following_set = NULL;
int r;
+ const char *n;
assert(u);
assert(u->type >= 0);
@@ -878,6 +922,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tInstance: %s\n"
"%s\tUnit Load State: %s\n"
"%s\tUnit Active State: %s\n"
+ "%s\tState Change Timestamp: %s\n"
"%s\tInactive Exit Timestamp: %s\n"
"%s\tActive Enter Timestamp: %s\n"
"%s\tActive Exit Timestamp: %s\n"
@@ -895,6 +940,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, strna(u->instance),
prefix, unit_load_state_to_string(u->load_state),
prefix, unit_active_state_to_string(unit_active_state(u)),
+ prefix, strna(format_timestamp(timestamp0, sizeof(timestamp0), u->state_change_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->inactive_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp2, sizeof(timestamp2), u->active_enter_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)),
@@ -911,6 +957,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
SET_FOREACH(t, u->names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);
+ if (!sd_id128_is_null(u->invocation_id))
+ fprintf(f, "%s\tInvocation ID: " SD_ID128_FORMAT_STR "\n",
+ prefix, SD_ID128_FORMAT_VAL(u->invocation_id));
+
STRV_FOREACH(j, u->documentation)
fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
@@ -935,7 +985,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
STRV_FOREACH(j, u->dropin_paths)
fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
- if (u->job_timeout > 0)
+ if (u->job_timeout != USEC_INFINITY)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
if (u->job_timeout_action != FAILURE_ACTION_NONE)
@@ -986,15 +1036,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tRefuseManualStop: %s\n"
"%s\tDefaultDependencies: %s\n"
"%s\tOnFailureJobMode: %s\n"
- "%s\tIgnoreOnIsolate: %s\n"
- "%s\tIgnoreOnSnapshot: %s\n",
+ "%s\tIgnoreOnIsolate: %s\n",
prefix, yes_no(u->stop_when_unneeded),
prefix, yes_no(u->refuse_manual_start),
prefix, yes_no(u->refuse_manual_stop),
prefix, yes_no(u->default_dependencies),
prefix, job_mode_to_string(u->on_failure_job_mode),
- prefix, yes_no(u->ignore_on_isolate),
- prefix, yes_no(u->ignore_on_snapshot));
+ prefix, yes_no(u->ignore_on_isolate));
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
@@ -1006,13 +1054,14 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
else if (u->load_state == UNIT_ERROR)
fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
+ for (n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
+ fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
if (u->job)
job_dump(u->job, f, prefix2);
if (u->nop_job)
job_dump(u->nop_job, f, prefix2);
-
}
/* Common implementation for multiple backends */
@@ -1092,9 +1141,7 @@ static int unit_add_target_dependencies(Unit *u) {
static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
- UNIT_REQUIRED_BY_OVERRIDABLE,
UNIT_REQUISITE_OF,
- UNIT_REQUISITE_OF_OVERRIDABLE,
UNIT_WANTED_BY,
UNIT_BOUND_BY
};
@@ -1119,16 +1166,16 @@ static int unit_add_target_dependencies(Unit *u) {
static int unit_add_slice_dependencies(Unit *u) {
assert(u);
- if (!unit_get_cgroup_context(u))
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
if (UNIT_ISSET(u->slice))
- return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true);
+ return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true);
- if (streq(u->id, SPECIAL_ROOT_SLICE))
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
return 0;
- return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true);
+ return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true);
}
static int unit_add_mount_dependencies(Unit *u) {
@@ -1141,13 +1188,23 @@ static int unit_add_mount_dependencies(Unit *u) {
char prefix[strlen(*i) + 1];
PATH_FOREACH_PREFIX_MORE(prefix, *i) {
+ _cleanup_free_ char *p = NULL;
Unit *m;
- r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m);
+ r = unit_name_from_path(prefix, ".mount", &p);
if (r < 0)
return r;
- if (r == 0)
+
+ m = manager_get_unit(u->manager, p);
+ if (!m) {
+ /* Make sure to load the mount unit if
+ * it exists. If so the dependencies
+ * on this unit will be added later
+ * during the loading of the mount
+ * unit. */
+ (void) manager_load_unit_prepare(u->manager, p, NULL, NULL, &m);
continue;
+ }
if (m == u)
continue;
@@ -1171,15 +1228,21 @@ static int unit_add_mount_dependencies(Unit *u) {
static int unit_add_startup_units(Unit *u) {
CGroupContext *c;
+ int r;
c = unit_get_cgroup_context(u);
if (!c)
return 0;
- if (c->startup_cpu_shares == (unsigned long) -1 &&
- c->startup_blockio_weight == (unsigned long) -1)
+ if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID &&
+ c->startup_io_weight == CGROUP_WEIGHT_INVALID &&
+ c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID)
return 0;
+ r = set_ensure_allocated(&u->manager->startup_units, NULL);
+ if (r < 0)
+ return r;
+
return set_put(u->manager->startup_units, u);
}
@@ -1199,6 +1262,17 @@ int unit_load(Unit *u) {
if (u->load_state != UNIT_STUB)
return 0;
+ if (u->transient_file) {
+ r = fflush_and_check(u->transient_file);
+ if (r < 0)
+ goto fail;
+
+ fclose(u->transient_file);
+ u->transient_file = NULL;
+
+ u->fragment_mtime = now(CLOCK_REALTIME);
+ }
+
if (UNIT_VTABLE(u)->load) {
r = UNIT_VTABLE(u)->load(u);
if (r < 0)
@@ -1317,43 +1391,35 @@ static bool unit_assert_test(Unit *u) {
return u->assert_result;
}
-_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
- const UnitStatusMessageFormats *format_table;
-
- 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 NULL;
-
- return format_table->starting_stopping[t == JOB_STOP];
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+ DISABLE_WARNING_FORMAT_NONLITERAL;
+ manager_status_printf(u->manager, STATUS_TYPE_NORMAL, status, unit_status_msg_format, unit_description(u));
+ REENABLE_WARNING;
}
-_pure_ static const char *unit_get_status_message_format_try_harder(Unit *u, JobType t) {
+_pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) {
const char *format;
+ const UnitStatusMessageFormats *format_table;
assert(u);
- assert(t >= 0);
- assert(t < _JOB_TYPE_MAX);
-
- format = unit_get_status_message_format(u, t);
- if (format)
- return format;
+ assert(IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD));
+
+ if (t != JOB_RELOAD) {
+ format_table = &UNIT_VTABLE(u)->status_message_formats;
+ if (format_table) {
+ format = format_table->starting_stopping[t == JOB_STOP];
+ 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)
+ else
return "Reloading %s.";
-
- return NULL;
}
static void unit_status_print_starting_stopping(Unit *u, JobType t) {
@@ -1361,12 +1427,11 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
assert(u);
- /* We only print status messages for selected units on
- * selected operations. */
+ /* Reload status messages have traditionally not been printed to console. */
+ if (!IN_SET(t, JOB_START, JOB_STOP))
+ return;
format = unit_get_status_message_format(u, t);
- if (!format)
- return;
DISABLE_WARNING_FORMAT_NONLITERAL;
unit_status_printf(u, "", format);
@@ -1380,7 +1445,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
assert(u);
- if (t != JOB_START && t != JOB_STOP && t != JOB_RELOAD)
+ if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD))
return;
if (log_on_console())
@@ -1388,12 +1453,10 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
/* We log status messages for all units and all operations. */
- format = unit_get_status_message_format_try_harder(u, t);
- if (!format)
- return;
+ format = unit_get_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buf, sizeof(buf), format, unit_description(u));
+ xsprintf(buf, format, unit_description(u));
REENABLE_WARNING;
mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING :
@@ -1413,23 +1476,44 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) {
NULL);
}
+void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
+ assert(u);
+ assert(t >= 0);
+ assert(t < _JOB_TYPE_MAX);
+
+ unit_status_log_starting_stopping_reloading(u, t);
+ unit_status_print_starting_stopping(u, t);
+}
+
+int unit_start_limit_test(Unit *u) {
+ assert(u);
+
+ if (ratelimit_test(&u->start_limit)) {
+ u->start_limit_hit = false;
+ return 0;
+ }
+
+ log_unit_warning(u, "Start request repeated too quickly.");
+ u->start_limit_hit = true;
+
+ return failure_action(u->manager, u->start_limit_action, u->reboot_arg);
+}
+
/* Errors:
- * -EBADR: This unit type does not support starting.
- * -EALREADY: Unit is already started.
- * -EAGAIN: An operation is already in progress. Retry later.
- * -ECANCELED: Too many requests for now.
- * -EPROTO: Assert failed
+ * -EBADR: This unit type does not support starting.
+ * -EALREADY: Unit is already started.
+ * -EAGAIN: An operation is already in progress. Retry later.
+ * -ECANCELED: Too many requests for now.
+ * -EPROTO: Assert failed
+ * -EINVAL: Unit not loaded
+ * -EOPNOTSUPP: Unit type not supported
*/
int unit_start(Unit *u) {
UnitActiveState state;
Unit *following;
- int r;
assert(u);
- if (u->load_state != UNIT_LOADED)
- return -EINVAL;
-
/* If this is already started, then this will succeed. Note
* that this will even succeed if this unit is not startable
* by the user. This is relied on to detect when we need to
@@ -1438,6 +1522,10 @@ int unit_start(Unit *u) {
if (UNIT_IS_ACTIVE_OR_RELOADING(state))
return -EALREADY;
+ /* Units that aren't loaded cannot be started */
+ if (u->load_state != UNIT_LOADED)
+ return -EINVAL;
+
/* If the conditions failed, don't do anything at all. If we
* already are activating this call might still be useful to
* speed up activation in case there is some hold-off time,
@@ -1455,6 +1543,15 @@ int unit_start(Unit *u) {
return -EPROTO;
}
+ /* Units of types that aren't supported cannot be
+ * started. Note that we do this test only after the condition
+ * checks, so that we rather return condition check errors
+ * (which are usually not considered a true failure) than "not
+ * supported" errors (which are considered a failure).
+ */
+ if (!unit_supported(u))
+ return -EOPNOTSUPP;
+
/* Forward to the main object, if we aren't it. */
following = unit_following(u);
if (following) {
@@ -1462,9 +1559,6 @@ int unit_start(Unit *u) {
return unit_start(following);
}
- if (!unit_supported(u))
- return -EOPNOTSUPP;
-
/* If it is stopped, but we cannot start it, then fail */
if (!UNIT_VTABLE(u)->start)
return -EBADR;
@@ -1477,19 +1571,18 @@ int unit_start(Unit *u) {
unit_add_to_dbus_queue(u);
- r = UNIT_VTABLE(u)->start(u);
- if (r <= 0)
- return r;
-
- /* Log if the start function actually did something */
- unit_status_log_starting_stopping_reloading(u, JOB_START);
- unit_status_print_starting_stopping(u, JOB_START);
- return r;
+ return UNIT_VTABLE(u)->start(u);
}
bool unit_can_start(Unit *u) {
assert(u);
+ if (u->load_state != UNIT_LOADED)
+ return false;
+
+ if (!unit_supported(u))
+ return false;
+
return !!UNIT_VTABLE(u)->start;
}
@@ -1508,7 +1601,6 @@ bool unit_can_isolate(Unit *u) {
int unit_stop(Unit *u) {
UnitActiveState state;
Unit *following;
- int r;
assert(u);
@@ -1527,13 +1619,7 @@ int unit_stop(Unit *u) {
unit_add_to_dbus_queue(u);
- r = UNIT_VTABLE(u)->stop(u);
- if (r <= 0)
- return r;
-
- unit_status_log_starting_stopping_reloading(u, JOB_STOP);
- unit_status_print_starting_stopping(u, JOB_STOP);
- return r;
+ return UNIT_VTABLE(u)->stop(u);
}
/* Errors:
@@ -1544,7 +1630,6 @@ int unit_stop(Unit *u) {
int unit_reload(Unit *u) {
UnitActiveState state;
Unit *following;
- int r;
assert(u);
@@ -1571,12 +1656,7 @@ int unit_reload(Unit *u) {
unit_add_to_dbus_queue(u);
- r = UNIT_VTABLE(u)->reload(u);
- if (r <= 0)
- return r;
-
- unit_status_log_starting_stopping_reloading(u, JOB_RELOAD);
- return r;
+ return UNIT_VTABLE(u)->reload(u);
}
bool unit_can_reload(Unit *u) {
@@ -1593,11 +1673,11 @@ bool unit_can_reload(Unit *u) {
static void unit_check_unneeded(Unit *u) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+
static const UnitDependency needed_dependencies[] = {
UNIT_REQUIRED_BY,
- UNIT_REQUIRED_BY_OVERRIDABLE,
UNIT_REQUISITE_OF,
- UNIT_REQUISITE_OF_OVERRIDABLE,
UNIT_WANTED_BY,
UNIT_BOUND_BY,
};
@@ -1623,7 +1703,7 @@ static void unit_check_unneeded(Unit *u) {
if (unit_active_or_pending(other))
return;
- /* If stopping a unit fails continously we might enter a stop
+ /* If stopping a unit fails continuously we might enter a stop
* loop here, hence stop acting on the service being
* unnecessary after a while. */
if (!ratelimit_test(&u->auto_stop_ratelimit)) {
@@ -1634,12 +1714,13 @@ static void unit_check_unneeded(Unit *u) {
log_unit_info(u, "Unit not needed anymore. Stopping.");
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
}
static void unit_check_binds_to(Unit *u) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bool stop = false;
Unit *other;
Iterator i;
@@ -1667,7 +1748,7 @@ static void unit_check_binds_to(Unit *u) {
if (!stop)
return;
- /* If stopping a unit fails continously we might enter a stop
+ /* If stopping a unit fails continuously we might enter a stop
* loop here, hence stop acting on the service being
* unnecessary after a while. */
if (!ratelimit_test(&u->auto_stop_ratelimit)) {
@@ -1679,9 +1760,9 @@ static void unit_check_binds_to(Unit *u) {
log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id);
/* A unit we need to run is gone. Sniff. Let's stop this. */
- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL);
if (r < 0)
- log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
}
static void retroactively_start_dependencies(Unit *u) {
@@ -1694,30 +1775,25 @@ static void retroactively_start_dependencies(Unit *u) {
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
- if (!set_get(u->dependencies[UNIT_AFTER], other) &&
- !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
+ manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
static void retroactively_stop_dependencies(Unit *u) {
@@ -1730,7 +1806,7 @@ static void retroactively_stop_dependencies(Unit *u) {
/* Pull down units which are bound to us recursively if enabled */
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
+ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
static void check_unneeded_dependencies(Unit *u) {
@@ -1744,18 +1820,12 @@ static void check_unneeded_dependencies(Unit *u) {
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
- SET_FOREACH(other, u->dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
- if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
- unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
@@ -1775,7 +1845,7 @@ void unit_start_on_failure(Unit *u) {
SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
int r;
- r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL);
if (r < 0)
log_unit_error_errno(u, r, "Failed to enqueue OnFailure= job: %m");
}
@@ -1809,28 +1879,26 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
m = u->manager;
/* Update timestamps for state changes */
- if (m->n_reloading <= 0) {
- dual_timestamp ts;
-
- dual_timestamp_get(&ts);
+ if (!MANAGER_IS_RELOADING(m)) {
+ dual_timestamp_get(&u->state_change_timestamp);
if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns))
- u->inactive_exit_timestamp = ts;
+ u->inactive_exit_timestamp = u->state_change_timestamp;
else if (!UNIT_IS_INACTIVE_OR_FAILED(os) && UNIT_IS_INACTIVE_OR_FAILED(ns))
- u->inactive_enter_timestamp = ts;
+ u->inactive_enter_timestamp = u->state_change_timestamp;
if (!UNIT_IS_ACTIVE_OR_RELOADING(os) && UNIT_IS_ACTIVE_OR_RELOADING(ns))
- u->active_enter_timestamp = ts;
+ u->active_enter_timestamp = u->state_change_timestamp;
else if (UNIT_IS_ACTIVE_OR_RELOADING(os) && !UNIT_IS_ACTIVE_OR_RELOADING(ns))
- u->active_exit_timestamp = ts;
+ u->active_exit_timestamp = u->state_change_timestamp;
}
/* Keep track of failed units */
- manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
+ (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED);
/* Make sure the cgroup is always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- unit_destroy_cgroup_if_empty(u);
+ unit_prune_cgroup(u);
/* Note that this doesn't apply to RemainAfterExit services exiting
* successfully, since there's no change of state in that case. Which is
@@ -1841,13 +1909,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
ec = unit_get_exec_context(u);
if (ec && exec_context_may_touch_console(ec)) {
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
- m->n_on_console --;
+ m->n_on_console--;
if (m->n_on_console == 0)
/* unset no_console_output flag, since the console is free */
m->no_console_output = false;
} else
- m->n_on_console ++;
+ m->n_on_console++;
}
}
@@ -1871,27 +1939,28 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
case JOB_VERIFY_ACTIVE:
if (UNIT_IS_ACTIVE_OR_RELOADING(ns))
- job_finish_and_invalidate(u->job, JOB_DONE, true);
+ job_finish_and_invalidate(u->job, JOB_DONE, true, false);
else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) {
unexpected = true;
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true);
+ job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false);
}
break;
case JOB_RELOAD:
case JOB_RELOAD_OR_START:
+ case JOB_TRY_RELOAD:
if (u->job->state == JOB_RUNNING) {
if (ns == UNIT_ACTIVE)
- job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true);
+ job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true, false);
else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) {
unexpected = true;
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true);
+ job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false);
}
}
@@ -1902,10 +1971,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
case JOB_TRY_RESTART:
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- job_finish_and_invalidate(u->job, JOB_DONE, true);
+ job_finish_and_invalidate(u->job, JOB_DONE, true, false);
else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) {
unexpected = true;
- job_finish_and_invalidate(u->job, JOB_FAILED, true);
+ job_finish_and_invalidate(u->job, JOB_FAILED, true, false);
}
break;
@@ -1917,7 +1986,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
} else
unexpected = true;
- if (m->n_reloading <= 0) {
+ if (!MANAGER_IS_RELOADING(m)) {
/* If this state change happened without being
* requested by a job, then let's retroactively start
@@ -1954,7 +2023,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- m->n_reloading <= 0) {
+ !MANAGER_IS_RELOADING(m)) {
/* Write audit record if we have just finished starting up */
manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
u->in_audit = true;
@@ -1971,7 +2040,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (u->type == UNIT_SERVICE &&
UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os) &&
- m->n_reloading <= 0) {
+ !MANAGER_IS_RELOADING(m)) {
/* Hmm, if there was no start record written
* write it now, so that we always have a nice
@@ -1992,7 +2061,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
manager_recheck_journal(m);
unit_trigger_notify(u);
- if (u->manager->n_reloading <= 0) {
+ if (!MANAGER_IS_RELOADING(u->manager)) {
/* Maybe we finished startup and are now ready for
* being stopped because unneeded? */
unit_check_unneeded(u);
@@ -2026,16 +2095,16 @@ int unit_watch_pid(Unit *u, pid_t pid) {
if (r < 0)
return r;
- r = hashmap_put(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
+ r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u);
if (r == -EEXIST) {
r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL);
if (r < 0)
return r;
- r = hashmap_put(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
+ r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u);
}
- q = set_put(u->pids, LONG_TO_PTR(pid));
+ q = set_put(u->pids, PID_TO_PTR(pid));
if (q < 0)
return q;
@@ -2046,81 +2115,18 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
assert(u);
assert(pid >= 1);
- hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u);
- hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u);
- set_remove(u->pids, LONG_TO_PTR(pid));
+ (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u);
+ (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u);
+ (void) set_remove(u->pids, PID_TO_PTR(pid));
}
void unit_unwatch_all_pids(Unit *u) {
assert(u);
while (!set_isempty(u->pids))
- unit_unwatch_pid(u, PTR_TO_LONG(set_first(u->pids)));
+ unit_unwatch_pid(u, PTR_TO_PID(set_first(u->pids)));
- set_free(u->pids);
- u->pids = NULL;
-}
-
-static int unit_watch_pids_in_path(Unit *u, const char *path) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_fclose_ FILE *f = NULL;
- int ret = 0, r;
-
- assert(u);
- assert(path);
-
- /* Adds all PIDs from a specific cgroup path to the set of PIDs we watch. */
-
- r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f);
- if (r >= 0) {
- pid_t pid;
-
- while ((r = cg_read_pid(f, &pid)) > 0) {
- r = unit_watch_pid(u, pid);
- if (r < 0 && ret >= 0)
- ret = r;
- }
- if (r < 0 && ret >= 0)
- ret = r;
-
- } else if (ret >= 0)
- ret = r;
-
- r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d);
- if (r >= 0) {
- char *fn;
-
- while ((r = cg_read_subgroup(d, &fn)) > 0) {
- _cleanup_free_ char *p = NULL;
-
- p = strjoin(path, "/", fn, NULL);
- free(fn);
-
- if (!p)
- return -ENOMEM;
-
- r = unit_watch_pids_in_path(u, p);
- if (r < 0 && ret >= 0)
- ret = r;
- }
- if (r < 0 && ret >= 0)
- ret = r;
-
- } else if (ret >= 0)
- ret = r;
-
- return ret;
-}
-
-int unit_watch_all_pids(Unit *u) {
- assert(u);
-
- /* Adds all PIDs from our cgroup to the set of PIDs we watch */
-
- if (!u->cgroup_path)
- return -ENOENT;
-
- return unit_watch_pids_in_path(u, u->cgroup_path);
+ u->pids = set_free(u->pids);
}
void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) {
@@ -2132,7 +2138,7 @@ void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) {
/* Cleans dead PIDs from our list */
SET_FOREACH(e, u->pids, i) {
- pid_t pid = PTR_TO_LONG(e);
+ pid_t pid = PTR_TO_PID(e);
if (pid == except1 || pid == except2)
continue;
@@ -2159,6 +2165,7 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
return unit_can_start(u);
case JOB_RELOAD:
+ case JOB_TRY_RELOAD:
return unit_can_reload(u);
case JOB_RELOAD_OR_START:
@@ -2186,16 +2193,12 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = UNIT_REQUIRED_BY,
- [UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
[UNIT_WANTS] = UNIT_WANTED_BY,
[UNIT_REQUISITE] = UNIT_REQUISITE_OF,
- [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUISITE_OF_OVERRIDABLE,
[UNIT_BINDS_TO] = UNIT_BOUND_BY,
[UNIT_PART_OF] = UNIT_CONSISTS_OF,
[UNIT_REQUIRED_BY] = UNIT_REQUIRES,
- [UNIT_REQUIRED_BY_OVERRIDABLE] = UNIT_REQUIRES_OVERRIDABLE,
[UNIT_REQUISITE_OF] = UNIT_REQUISITE,
- [UNIT_REQUISITE_OF_OVERRIDABLE] = UNIT_REQUISITE_OVERRIDABLE,
[UNIT_WANTED_BY] = UNIT_WANTS,
[UNIT_BOUND_BY] = UNIT_BINDS_TO,
[UNIT_CONSISTS_OF] = UNIT_PART_OF,
@@ -2229,6 +2232,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
return 0;
}
+ if (d == UNIT_BEFORE && other->type == UNIT_DEVICE) {
+ log_unit_warning(u, "Dependency Before=%s ignored (.device units cannot be delayed)", other->id);
+ return 0;
+ }
+
r = set_ensure_allocated(&u->dependencies[d], NULL);
if (r < 0)
return r;
@@ -2374,47 +2382,9 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
return unit_add_two_dependencies(u, d, e, other, add_reference);
}
-int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
- _cleanup_free_ char *buf = NULL;
- Unit *other;
- int r;
-
- assert(u);
- assert(name || path);
-
- r = resolve_template(u, name, path, &buf, &name);
- if (r < 0)
- return r;
-
- r = manager_load_unit(u->manager, name, path, NULL, &other);
- if (r < 0)
- return r;
-
- return unit_add_dependency(other, d, u, add_reference);
-}
-
-int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
- _cleanup_free_ char *buf = NULL;
- Unit *other;
- int r;
-
- assert(u);
- assert(name || path);
-
- r = resolve_template(u, name, path, &buf, &name);
- if (r < 0)
- return r;
-
- r = manager_load_unit(u->manager, name, path, NULL, &other);
- if (r < 0)
- return r;
-
- return unit_add_two_dependencies(other, d, e, u, add_reference);
-}
-
int set_unit_path(const char *p) {
/* This is mostly for debug purposes */
- if (setenv("SYSTEMD_UNIT_PATH", p, 0) < 0)
+ if (setenv("SYSTEMD_UNIT_PATH", p, 1) < 0)
return -errno;
return 0;
@@ -2429,39 +2399,60 @@ char *unit_dbus_path(Unit *u) {
return unit_dbus_path_from_name(u->id);
}
-char *unit_default_cgroup_path(Unit *u) {
- _cleanup_free_ char *escaped = NULL, *slice = NULL;
- int r;
+char *unit_dbus_path_invocation_id(Unit *u) {
+ assert(u);
+
+ if (sd_id128_is_null(u->invocation_id))
+ return NULL;
+ return unit_dbus_path_from_name(u->invocation_id_string);
+}
+
+int unit_set_slice(Unit *u, Unit *slice) {
assert(u);
+ assert(slice);
- if (unit_has_name(u, SPECIAL_ROOT_SLICE))
- return strdup(u->manager->cgroup_root);
+ /* Sets the unit slice if it has not been set before. Is extra
+ * careful, to only allow this for units that actually have a
+ * cgroup context. Also, we don't allow to set this for slices
+ * (since the parent slice is derived from the name). Make
+ * sure the unit we set is actually a slice. */
- if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) {
- r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
- if (r < 0)
- return NULL;
- }
+ if (!UNIT_HAS_CGROUP_CONTEXT(u))
+ return -EOPNOTSUPP;
- escaped = cg_escape(u->id);
- if (!escaped)
- return NULL;
+ if (u->type == UNIT_SLICE)
+ return -EINVAL;
- if (slice)
- return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
- else
- return strjoin(u->manager->cgroup_root, "/", escaped, NULL);
+ if (unit_active_state(u) != UNIT_INACTIVE)
+ return -EBUSY;
+
+ if (slice->type != UNIT_SLICE)
+ return -EINVAL;
+
+ if (unit_has_name(u, SPECIAL_INIT_SCOPE) &&
+ !unit_has_name(slice, SPECIAL_ROOT_SLICE))
+ return -EPERM;
+
+ if (UNIT_DEREF(u->slice) == slice)
+ return 0;
+
+ /* Disallow slice changes if @u is already bound to cgroups */
+ if (UNIT_ISSET(u->slice) && u->cgroup_realized)
+ return -EBUSY;
+
+ unit_ref_unset(&u->slice);
+ unit_ref_set(&u->slice, slice);
+ return 1;
}
-int unit_add_default_slice(Unit *u, CGroupContext *c) {
+int unit_set_default_slice(Unit *u) {
_cleanup_free_ char *b = NULL;
const char *slice_name;
Unit *slice;
int r;
assert(u);
- assert(c);
if (UNIT_ISSET(u->slice))
return 0;
@@ -2483,7 +2474,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
if (!escaped)
return -ENOMEM;
- if (u->manager->running_as == MANAGER_SYSTEM)
+ if (MANAGER_IS_SYSTEM(u->manager))
b = strjoin("system-", escaped, ".slice", NULL);
else
b = strappend(escaped, ".slice");
@@ -2493,7 +2484,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
slice_name = b;
} else
slice_name =
- u->manager->running_as == MANAGER_SYSTEM
+ MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE)
? SPECIAL_SYSTEM_SLICE
: SPECIAL_ROOT_SLICE;
@@ -2501,8 +2492,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) {
if (r < 0)
return r;
- unit_ref_set(&u->slice, slice);
- return 0;
+ return unit_set_slice(u, slice);
}
const char *unit_slice_name(Unit *u) {
@@ -2533,14 +2523,70 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
return r;
}
+static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ const char *name, *old_owner, *new_owner;
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
+ if (r < 0) {
+ bus_log_parse_error(r);
+ return 0;
+ }
+
+ if (UNIT_VTABLE(u)->bus_name_owner_change)
+ UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner);
+
+ return 0;
+}
+
+int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
+ const char *match;
+
+ assert(u);
+ assert(bus);
+ assert(name);
+
+ if (u->match_bus_slot)
+ return -EBUSY;
+
+ match = strjoina("type='signal',"
+ "sender='org.freedesktop.DBus',"
+ "path='/org/freedesktop/DBus',"
+ "interface='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "arg0='", name, "'");
+
+ return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u);
+}
+
int unit_watch_bus_name(Unit *u, const char *name) {
+ int r;
+
assert(u);
assert(name);
/* Watch a specific name on the bus. We only support one unit
* watching each name for now. */
- return hashmap_put(u->manager->watch_bus, name, u);
+ if (u->manager->api_bus) {
+ /* If the bus is already available, install the match directly.
+ * Otherwise, just put the name in the list. bus_setup_api() will take care later. */
+ r = unit_install_bus_match(u, u->manager->api_bus, name);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name);
+ }
+
+ r = hashmap_put(u->manager->watch_bus, name, u);
+ if (r < 0) {
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
+ return log_warning_errno(r, "Failed to put bus name to hashmap: %m");
+ }
+
+ return 0;
}
void unit_unwatch_bus_name(Unit *u, const char *name) {
@@ -2548,6 +2594,7 @@ void unit_unwatch_bus_name(Unit *u, const char *name) {
assert(name);
hashmap_remove_value(u->manager->watch_bus, name, u);
+ u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
}
bool unit_can_serialize(Unit *u) {
@@ -2578,10 +2625,13 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
}
}
+ dual_timestamp_serialize(f, "state-change-timestamp", &u->state_change_timestamp);
+
dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
dual_timestamp_serialize(f, "active-enter-timestamp", &u->active_enter_timestamp);
dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp);
dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
+
dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp);
dual_timestamp_serialize(f, "assert-timestamp", &u->assert_timestamp);
@@ -2592,21 +2642,34 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result));
unit_serialize_item(u, f, "transient", yes_no(u->transient));
- unit_serialize_item_format(u, f, "cpuacct-usage-base", "%" PRIu64, u->cpuacct_usage_base);
+
+ unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
+ if (u->cpu_usage_last != NSEC_INFINITY)
+ unit_serialize_item_format(u, f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->cgroup_path)
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized));
+ if (uid_is_valid(u->ref_uid))
+ unit_serialize_item_format(u, f, "ref-uid", UID_FMT, u->ref_uid);
+ if (gid_is_valid(u->ref_gid))
+ unit_serialize_item_format(u, f, "ref-gid", GID_FMT, u->ref_gid);
+
+ if (!sd_id128_is_null(u->invocation_id))
+ unit_serialize_item_format(u, f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
+
+ bus_track_serialize(u->bus_track, f, "ref");
+
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
- job_serialize(u->job, f, fds);
+ job_serialize(u->job, f);
}
if (u->nop_job) {
fprintf(f, "job\n");
- job_serialize(u->nop_job, f, fds);
+ job_serialize(u->nop_job, f);
}
}
@@ -2615,65 +2678,78 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
return 0;
}
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
- va_list ap;
-
+int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
assert(u);
assert(f);
assert(key);
- assert(format);
+
+ if (!value)
+ return 0;
fputs(key, f);
fputc('=', f);
-
- va_start(ap, format);
- vfprintf(f, format, ap);
- va_end(ap);
-
+ fputs(value, f);
fputc('\n', f);
+
+ return 1;
}
-void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
+int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value) {
+ _cleanup_free_ char *c = NULL;
+
assert(u);
assert(f);
assert(key);
- assert(value);
- fprintf(f, "%s=%s\n", key, value);
+ if (!value)
+ return 0;
+
+ c = cescape(value);
+ if (!c)
+ return -ENOMEM;
+
+ fputs(key, f);
+ fputc('=', f);
+ fputs(c, f);
+ fputc('\n', f);
+
+ return 1;
}
-static int unit_set_cgroup_path(Unit *u, const char *path) {
- _cleanup_free_ char *p = NULL;
- int r;
+int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd) {
+ int copy;
assert(u);
+ assert(f);
+ assert(key);
- if (path) {
- p = strdup(path);
- if (!p)
- return -ENOMEM;
- } else
- p = NULL;
-
- if (streq_ptr(u->cgroup_path, p))
+ if (fd < 0)
return 0;
- if (p) {
- r = hashmap_put(u->manager->cgroup_unit, p, u);
- if (r < 0)
- return r;
- }
+ copy = fdset_put_dup(fds, fd);
+ if (copy < 0)
+ return copy;
- if (u->cgroup_path) {
- log_unit_debug(u, "Changing cgroup path from %s to %s.", u->cgroup_path, strna(p));
- hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- free(u->cgroup_path);
- }
+ fprintf(f, "%s=%i\n", key, copy);
+ return 1;
+}
+
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *format, ...) {
+ va_list ap;
- u->cgroup_path = p;
- p = NULL;
+ assert(u);
+ assert(f);
+ assert(key);
+ assert(format);
- return 0;
+ fputs(key, f);
+ fputc('=', f);
+
+ va_start(ap, format);
+ vfprintf(f, format, ap);
+ va_end(ap);
+
+ fputc('\n', f);
}
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
@@ -2704,7 +2780,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
/* End marker */
if (isempty(l))
- return 0;
+ break;
k = strcspn(l, "=");
@@ -2723,7 +2799,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (!j)
return log_oom();
- r = job_deserialize(j, f, fds);
+ r = job_deserialize(j, f);
if (r < 0) {
job_free(j);
return r;
@@ -2744,6 +2820,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
} else /* legacy for pre-44 */
log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
continue;
+ } else if (streq(l, "state-change-timestamp")) {
+ dual_timestamp_deserialize(v, &u->state_change_timestamp);
+ continue;
} else if (streq(l, "inactive-exit-timestamp")) {
dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
continue;
@@ -2792,11 +2871,19 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
continue;
- } else if (streq(l, "cpuacct-usage-base")) {
+ } else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
- r = safe_atou64(v, &u->cpuacct_usage_base);
+ r = safe_atou64(v, &u->cpu_usage_base);
if (r < 0)
- log_unit_debug(u, "Failed to parse CPU usage %s, ignoring.", v);
+ log_unit_debug(u, "Failed to parse CPU usage base %s, ignoring.", v);
+
+ continue;
+
+ } else if (streq(l, "cpu-usage-last")) {
+
+ r = safe_atou64(v, &u->cpu_usage_last);
+ if (r < 0)
+ log_unit_debug(u, "Failed to read CPU usage last %s, ignoring.", v);
continue;
@@ -2806,6 +2893,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
+ (void) unit_watch_cgroup(u);
+
continue;
} else if (streq(l, "cgroup-realized")) {
int b;
@@ -2817,6 +2906,47 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
u->cgroup_realized = b;
continue;
+
+ } else if (streq(l, "ref-uid")) {
+ uid_t uid;
+
+ r = parse_uid(v, &uid);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse referenced UID %s, ignoring.", v);
+ else
+ unit_ref_uid_gid(u, uid, GID_INVALID);
+
+ continue;
+
+ } else if (streq(l, "ref-gid")) {
+ gid_t gid;
+
+ r = parse_gid(v, &gid);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse referenced GID %s, ignoring.", v);
+ else
+ unit_ref_uid_gid(u, UID_INVALID, gid);
+
+ } else if (streq(l, "ref")) {
+
+ r = strv_extend(&u->deserialized_refs, v);
+ if (r < 0)
+ log_oom();
+
+ continue;
+ } else if (streq(l, "invocation-id")) {
+ sd_id128_t id;
+
+ r = sd_id128_from_string(v, &id);
+ if (r < 0)
+ log_unit_debug(u, "Failed to parse invocation id %s, ignoring.", v);
+ else {
+ r = unit_set_invocation_id(u, id);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
+ }
+
+ continue;
}
if (unit_can_serialize(u)) {
@@ -2837,9 +2967,18 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
}
}
+
+ /* Versions before 228 did not carry a state change timestamp. In this case, take the current time. This is
+ * useful, so that timeouts based on this timestamp don't trigger too early, and is in-line with the logic from
+ * before 228 where the base for timeouts was not persistent across reboots. */
+
+ if (!dual_timestamp_is_set(&u->state_change_timestamp))
+ dual_timestamp_get(&u->state_change_timestamp);
+
+ return 0;
}
-int unit_add_node_link(Unit *u, const char *what, bool wants) {
+int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep) {
Unit *device;
_cleanup_free_ char *e = NULL;
int r;
@@ -2866,7 +3005,9 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
if (r < 0)
return r;
- r = unit_add_two_dependencies(u, UNIT_AFTER, u->manager->running_as == MANAGER_SYSTEM ? UNIT_BINDS_TO : UNIT_WANTS, device, true);
+ r = unit_add_two_dependencies(u, UNIT_AFTER,
+ MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS,
+ device, true);
if (r < 0)
return r;
@@ -2880,7 +3021,8 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
}
int unit_coldplug(Unit *u) {
- int r;
+ int r = 0, q;
+ char **i;
assert(u);
@@ -2891,81 +3033,69 @@ int unit_coldplug(Unit *u) {
u->coldplugged = true;
+ STRV_FOREACH(i, u->deserialized_refs) {
+ q = bus_unit_track_add_name(u, *i);
+ if (q < 0 && r >= 0)
+ r = q;
+ }
+ u->deserialized_refs = strv_free(u->deserialized_refs);
+
if (UNIT_VTABLE(u)->coldplug) {
- r = UNIT_VTABLE(u)->coldplug(u);
- if (r < 0)
- return r;
+ q = UNIT_VTABLE(u)->coldplug(u);
+ if (q < 0 && r >= 0)
+ r = q;
}
if (u->job) {
- r = job_coldplug(u->job);
- if (r < 0)
- return r;
+ q = job_coldplug(u->job);
+ if (q < 0 && r >= 0)
+ r = q;
}
- return 0;
+ return r;
}
-void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
- DISABLE_WARNING_FORMAT_NONLITERAL;
- manager_status_printf(u->manager, STATUS_TYPE_NORMAL,
- status, unit_status_msg_format, unit_description(u));
- REENABLE_WARNING;
+static bool fragment_mtime_newer(const char *path, usec_t mtime) {
+ struct stat st;
+
+ if (!path)
+ return false;
+
+ if (stat(path, &st) < 0)
+ /* What, cannot access this anymore? */
+ return true;
+
+ if (mtime > 0)
+ /* For non-empty files check the mtime */
+ return timespec_load(&st.st_mtim) > mtime;
+ else if (!null_or_empty(&st))
+ /* For masked files check if they are still so */
+ return true;
+
+ return false;
}
bool unit_need_daemon_reload(Unit *u) {
_cleanup_strv_free_ char **t = NULL;
char **path;
- struct stat st;
- unsigned loaded_cnt, current_cnt;
assert(u);
- if (u->fragment_path) {
- zero(st);
- if (stat(u->fragment_path, &st) < 0)
- /* What, cannot access this anymore? */
- return true;
-
- if (u->fragment_mtime > 0 &&
- timespec_load(&st.st_mtim) != u->fragment_mtime)
- return true;
- }
-
- if (u->source_path) {
- zero(st);
- if (stat(u->source_path, &st) < 0)
- return true;
+ if (fragment_mtime_newer(u->fragment_path, u->fragment_mtime))
+ return true;
- if (u->source_mtime > 0 &&
- timespec_load(&st.st_mtim) != u->source_mtime)
- return true;
- }
+ if (fragment_mtime_newer(u->source_path, u->source_mtime))
+ return true;
(void) unit_find_dropin_paths(u, &t);
- loaded_cnt = strv_length(t);
- current_cnt = strv_length(u->dropin_paths);
-
- if (loaded_cnt == current_cnt) {
- if (loaded_cnt == 0)
- return false;
-
- if (strv_overlap(u->dropin_paths, t)) {
- STRV_FOREACH(path, u->dropin_paths) {
- zero(st);
- if (stat(*path, &st) < 0)
- return true;
-
- if (u->dropin_mtime > 0 &&
- timespec_load(&st.st_mtim) > u->dropin_mtime)
- return true;
- }
+ if (!strv_equal(u->dropin_paths, t))
+ return true;
- return false;
- } else
+ STRV_FOREACH(path, u->dropin_paths)
+ if (fragment_mtime_newer(*path, u->dropin_mtime))
return true;
- } else
- return true;
+
+ return false;
}
void unit_reset_failed(Unit *u) {
@@ -2973,6 +3103,9 @@ void unit_reset_failed(Unit *u) {
if (UNIT_VTABLE(u)->reset_failed)
UNIT_VTABLE(u)->reset_failed(u);
+
+ RATELIMIT_RESET(u->start_limit);
+ u->start_limit_hit = false;
}
Unit *unit_following(Unit *u) {
@@ -3030,8 +3163,7 @@ bool unit_active_or_pending(Unit *u) {
int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
assert(u);
assert(w >= 0 && w < _KILL_WHO_MAX);
- assert(signo > 0);
- assert(signo < _NSIG);
+ assert(SIGNAL_VALID(signo));
if (!UNIT_VTABLE(u)->kill)
return -EOPNOTSUPP;
@@ -3049,13 +3181,13 @@ static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) {
/* Exclude the main/control pids from being killed via the cgroup */
if (main_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(main_pid));
+ r = set_put(pid_set, PID_TO_PTR(main_pid));
if (r < 0)
goto fail;
}
if (control_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(control_pid));
+ r = set_put(pid_set, PID_TO_PTR(control_pid));
if (r < 0)
goto fail;
}
@@ -3076,32 +3208,39 @@ int unit_kill_common(
sd_bus_error *error) {
int r = 0;
+ bool killed = false;
- if (who == KILL_MAIN && main_pid <= 0) {
+ if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) {
if (main_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
- else
+ else if (main_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
}
- if (who == KILL_CONTROL && control_pid <= 0) {
+ if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) {
if (control_pid < 0)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type));
- else
+ else if (control_pid == 0)
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
}
- if (who == KILL_CONTROL || who == KILL_ALL)
- if (control_pid > 0)
+ if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL))
+ if (control_pid > 0) {
if (kill(control_pid, signo) < 0)
r = -errno;
+ else
+ killed = true;
+ }
- if (who == KILL_MAIN || who == KILL_ALL)
- if (main_pid > 0)
+ if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL))
+ if (main_pid > 0) {
if (kill(main_pid, signo) < 0)
r = -errno;
+ else
+ killed = true;
+ }
- if (who == KILL_ALL && u->cgroup_path) {
+ if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) {
_cleanup_set_free_ Set *pid_set = NULL;
int q;
@@ -3110,11 +3249,16 @@ int unit_kill_common(
if (!pid_set)
return -ENOMEM;
- q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
+ q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, NULL, NULL);
if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
+ else
+ killed = true;
}
+ if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL))
+ return -ESRCH;
+
return r;
}
@@ -3130,12 +3274,19 @@ int unit_following_set(Unit *u, Set **s) {
}
UnitFileState unit_get_unit_file_state(Unit *u) {
+ int r;
+
assert(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,
- NULL, basename(u->fragment_path));
+ if (u->unit_file_state < 0 && u->fragment_path) {
+ r = unit_file_get_state(
+ u->manager->unit_file_scope,
+ NULL,
+ basename(u->fragment_path),
+ &u->unit_file_state);
+ if (r < 0)
+ u->unit_file_state = UNIT_FILE_BAD;
+ }
return u->unit_file_state;
}
@@ -3145,8 +3296,9 @@ int unit_get_unit_file_preset(Unit *u) {
if (u->unit_file_preset < 0 && u->fragment_path)
u->unit_file_preset = unit_file_query_preset(
- u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
- NULL, basename(u->fragment_path));
+ u->manager->unit_file_scope,
+ NULL,
+ basename(u->fragment_path));
return u->unit_file_preset;
}
@@ -3169,10 +3321,41 @@ void unit_ref_unset(UnitRef *ref) {
if (!ref->unit)
return;
+ /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might
+ * be unreferenced now. */
+ unit_add_to_gc_queue(ref->unit);
+
LIST_REMOVE(refs, ref->unit->refs, ref);
ref->unit = NULL;
}
+static int user_from_unit_name(Unit *u, char **ret) {
+
+ static const uint8_t hash_key[] = {
+ 0x58, 0x1a, 0xaf, 0xe6, 0x28, 0x58, 0x4e, 0x96,
+ 0xb4, 0x4e, 0xf5, 0x3b, 0x8c, 0x92, 0x07, 0xec
+ };
+
+ _cleanup_free_ char *n = NULL;
+ int r;
+
+ r = unit_name_to_prefix(u->id, &n);
+ if (r < 0)
+ return r;
+
+ if (valid_user_group_name(n)) {
+ *ret = n;
+ n = NULL;
+ return 0;
+ }
+
+ /* If we can't use the unit name as a user name, then let's hash it and use that */
+ if (asprintf(ret, "_du%016" PRIx64, siphash24(n, strlen(n), hash_key)) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
int unit_patch_contexts(Unit *u) {
CGroupContext *cc;
ExecContext *ec;
@@ -3195,7 +3378,7 @@ int unit_patch_contexts(Unit *u) {
return -ENOMEM;
}
- if (u->manager->running_as == MANAGER_USER &&
+ if (MANAGER_IS_USER(u->manager) &&
!ec->working_directory) {
r = get_home_dir(&ec->working_directory);
@@ -3207,7 +3390,7 @@ int unit_patch_contexts(Unit *u) {
ec->working_directory_missing_ok = true;
}
- if (u->manager->running_as == MANAGER_USER &&
+ if (MANAGER_IS_USER(u->manager) &&
(ec->syscall_whitelist ||
!set_isempty(ec->syscall_filter) ||
!set_isempty(ec->syscall_archs) ||
@@ -3216,7 +3399,30 @@ int unit_patch_contexts(Unit *u) {
ec->no_new_privileges = true;
if (ec->private_devices)
- ec->capability_bounding_set_drop |= (uint64_t) 1ULL << (uint64_t) CAP_MKNOD;
+ ec->capability_bounding_set &= ~(UINT64_C(1) << CAP_MKNOD);
+
+ if (ec->dynamic_user) {
+ if (!ec->user) {
+ r = user_from_unit_name(u, &ec->user);
+ if (r < 0)
+ return r;
+ }
+
+ if (!ec->group) {
+ ec->group = strdup(ec->user);
+ if (!ec->group)
+ return -ENOMEM;
+ }
+
+ /* If the dynamic user option is on, let's make sure that the unit can't leave its UID/GID
+ * around in the file system or on IPC objects. Hence enforce a strict sandbox. */
+
+ ec->private_tmp = true;
+ ec->remove_ipc = true;
+ ec->protect_system = PROTECT_SYSTEM_STRICT;
+ if (ec->protect_home == PROTECT_HOME_NO)
+ ec->protect_home = PROTECT_HOME_READ_ONLY;
+ }
}
cc = unit_get_cgroup_context(u);
@@ -3285,71 +3491,65 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
return *(ExecRuntime**) ((uint8_t*) u + offset);
}
-static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) {
- if (u->manager->running_as == MANAGER_USER) {
- int r;
-
- if (mode == UNIT_PERSISTENT && !transient)
- r = user_config_home(dir);
- else
- r = user_runtime_dir(dir);
+static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) {
+ assert(u);
- if (r == 0)
- return -ENOENT;
- return r;
- }
+ if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT))
+ return NULL;
- if (mode == UNIT_PERSISTENT && !transient)
- *dir = strdup("/etc/systemd/system");
- else
- *dir = strdup("/run/systemd/system");
- if (!*dir)
- return -ENOMEM;
+ if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */
+ return u->manager->lookup_paths.transient;
- return 0;
-}
+ if (mode == UNIT_RUNTIME)
+ return u->manager->lookup_paths.runtime_control;
-static int unit_drop_in_file(Unit *u,
- UnitSetPropertiesMode mode, const char *name, char **p, char **q) {
- _cleanup_free_ char *dir = NULL;
- int r;
+ if (mode == UNIT_PERSISTENT)
+ return u->manager->lookup_paths.persistent_control;
- assert(u);
-
- r = unit_drop_in_dir(u, mode, u->transient, &dir);
- if (r < 0)
- return r;
-
- return drop_in_file(dir, u->id, 50, name, p, q);
+ return NULL;
}
int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
-
- _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL;
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ const char *dir, *wrapped;
int r;
assert(u);
+ if (u->transient_file) {
+ /* When this is a transient unit file in creation, then let's not create a new drop-in but instead
+ * write to the transient unit file. */
+ fputs(data, u->transient_file);
+ fputc('\n', u->transient_file);
+ return 0;
+ }
+
if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
return 0;
- r = unit_drop_in_dir(u, mode, u->transient, &dir);
- if (r < 0)
- return r;
+ dir = unit_drop_in_dir(u, mode);
+ if (!dir)
+ return -EINVAL;
+
+ wrapped = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\"\n"
+ "# or an equivalent operation. Do not edit.\n",
+ data,
+ "\n");
- r = write_drop_in(dir, u->id, 50, name, data);
+ r = drop_in_file(dir, u->id, 50, name, &p, &q);
if (r < 0)
return r;
- r = drop_in_file(dir, u->id, 50, name, &p, &q);
+ (void) mkdir_p(p, 0755);
+ r = write_string_file_atomic_label(q, wrapped);
if (r < 0)
return r;
- r = strv_extend(&u->dropin_paths, q);
+ r = strv_push(&u->dropin_paths, q);
if (r < 0)
return r;
+ q = NULL;
- strv_sort(u->dropin_paths);
strv_uniq(u->dropin_paths);
u->dropin_mtime = now(CLOCK_REALTIME);
@@ -3380,7 +3580,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n
}
int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
- _cleanup_free_ char *ndata = NULL;
+ const char *ndata;
assert(u);
assert(name);
@@ -3392,9 +3592,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *
if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
return 0;
- ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL);
- if (!ndata)
- return -ENOMEM;
+ ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data);
return unit_write_drop_in(u, mode, name, ndata);
}
@@ -3421,63 +3619,89 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const
return unit_write_drop_in_private(u, mode, name, p);
}
-int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) {
- _cleanup_free_ char *p = NULL, *q = NULL;
- int r;
+int unit_make_transient(Unit *u) {
+ FILE *f;
+ char *path;
assert(u);
- if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME))
- return 0;
+ if (!UNIT_VTABLE(u)->can_transient)
+ return -EOPNOTSUPP;
- r = unit_drop_in_file(u, mode, name, &p, &q);
- if (r < 0)
- return r;
+ path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL);
+ if (!path)
+ return -ENOMEM;
- if (unlink(q) < 0)
- r = errno == ENOENT ? 0 : -errno;
- else
- r = 1;
+ /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are
+ * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */
- rmdir(p);
- return r;
-}
+ RUN_WITH_UMASK(0022) {
+ f = fopen(path, "we");
+ if (!f) {
+ free(path);
+ return -errno;
+ }
+ }
-int unit_make_transient(Unit *u) {
- int r;
+ if (u->transient_file)
+ fclose(u->transient_file);
+ u->transient_file = f;
- assert(u);
+ free(u->fragment_path);
+ u->fragment_path = path;
+
+ u->source_path = mfree(u->source_path);
+ u->dropin_paths = strv_free(u->dropin_paths);
+ u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0;
u->load_state = UNIT_STUB;
u->load_error = 0;
u->transient = true;
- free(u->fragment_path);
- u->fragment_path = NULL;
+ unit_add_to_dbus_queue(u);
+ unit_add_to_gc_queue(u);
- if (u->manager->running_as == MANAGER_USER) {
- _cleanup_free_ char *c = NULL;
+ fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n",
+ u->transient_file);
- r = user_runtime_dir(&c);
- if (r < 0)
- return r;
- if (r == 0)
- return -ENOENT;
+ return 0;
+}
- u->fragment_path = strjoin(c, "/", u->id, NULL);
- if (!u->fragment_path)
- return -ENOMEM;
+static void log_kill(pid_t pid, int sig, void *userdata) {
+ _cleanup_free_ char *comm = NULL;
- mkdir_p(c, 0755);
- } else {
- u->fragment_path = strappend("/run/systemd/system/", u->id);
- if (!u->fragment_path)
- return -ENOMEM;
+ (void) get_process_comm(pid, &comm);
- mkdir_p("/run/systemd/system", 0755);
- }
+ /* Don't log about processes marked with brackets, under the assumption that these are temporary processes
+ only, like for example systemd's own PAM stub process. */
+ if (comm && comm[0] == '(')
+ return;
+
+ log_unit_notice(userdata,
+ "Killing process " PID_FMT " (%s) with signal SIG%s.",
+ pid,
+ strna(comm),
+ signal_to_string(sig));
+}
+
+static int operation_to_signal(KillContext *c, KillOperation k) {
+ assert(c);
- return write_string_file_atomic_label(u->fragment_path, "# Transient stub");
+ switch (k) {
+
+ case KILL_TERMINATE:
+ case KILL_TERMINATE_AND_LOG:
+ return c->kill_signal;
+
+ case KILL_KILL:
+ return SIGKILL;
+
+ case KILL_ABORT:
+ return SIGABRT;
+
+ default:
+ assert_not_reached("KillOperation unknown");
+ }
}
int unit_kill_context(
@@ -3488,62 +3712,69 @@ int unit_kill_context(
pid_t control_pid,
bool main_pid_alien) {
- int sig, wait_for_exit = false, r;
+ bool wait_for_exit = false, send_sighup;
+ cg_kill_log_func_t log_func;
+ int sig, r;
assert(u);
assert(c);
+ /* Kill the processes belonging to this unit, in preparation for shutting the unit down. Returns > 0 if we
+ * killed something worth waiting for, 0 otherwise. */
+
if (c->kill_mode == KILL_NONE)
return 0;
- switch (k) {
- case KILL_KILL:
- sig = SIGKILL;
- break;
- case KILL_ABORT:
- sig = SIGABRT;
- break;
- case KILL_TERMINATE:
- sig = c->kill_signal;
- break;
- default:
- assert_not_reached("KillOperation unknown");
- }
+ sig = operation_to_signal(c, k);
+
+ send_sighup =
+ c->send_sighup &&
+ IN_SET(k, KILL_TERMINATE, KILL_TERMINATE_AND_LOG) &&
+ sig != SIGHUP;
+
+ log_func =
+ k != KILL_TERMINATE ||
+ IN_SET(sig, SIGKILL, SIGABRT) ? log_kill : NULL;
if (main_pid > 0) {
- r = kill_and_sigcont(main_pid, sig);
+ if (log_func)
+ log_func(main_pid, sig, u);
+ r = kill_and_sigcont(main_pid, sig);
if (r < 0 && r != -ESRCH) {
_cleanup_free_ char *comm = NULL;
- get_process_comm(main_pid, &comm);
+ (void) get_process_comm(main_pid, &comm);
- log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm));
} else {
if (!main_pid_alien)
wait_for_exit = true;
- if (c->send_sighup && k != KILL_KILL)
- kill(main_pid, SIGHUP);
+ if (r != -ESRCH && send_sighup)
+ (void) kill(main_pid, SIGHUP);
}
}
if (control_pid > 0) {
- r = kill_and_sigcont(control_pid, sig);
+ if (log_func)
+ log_func(control_pid, sig, u);
+ r = kill_and_sigcont(control_pid, sig);
if (r < 0 && r != -ESRCH) {
_cleanup_free_ char *comm = NULL;
- get_process_comm(control_pid, &comm);
+ (void) get_process_comm(control_pid, &comm);
- log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm));
+ log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm));
} else {
wait_for_exit = true;
- if (c->send_sighup && k != KILL_KILL)
- kill(control_pid, SIGHUP);
+ if (r != -ESRCH && send_sighup)
+ (void) kill(control_pid, SIGHUP);
}
}
- if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL)) && u->cgroup_path) {
+ if (u->cgroup_path &&
+ (c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) {
_cleanup_set_free_ Set *pid_set = NULL;
/* Exclude the main/control pids from being killed via the cgroup */
@@ -3551,30 +3782,47 @@ int unit_kill_context(
if (!pid_set)
return -ENOMEM;
- r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
+ r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+ sig,
+ CGROUP_SIGCONT|CGROUP_IGNORE_SELF,
+ pid_set,
+ log_func, u);
if (r < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
- log_unit_warning_errno(u, r, "Failed to kill control group: %m");
- } else if (r > 0) {
+ log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path);
- /* FIXME: For now, we will not wait for the
- * cgroup members to die, simply because
- * cgroup notification is unreliable. It
- * doesn't work at all in containers, and
- * outside of containers it can be confused
- * easily by leaving directories in the
- * cgroup. */
+ } else if (r > 0) {
- /* wait_for_exit = true; */
+ /* FIXME: For now, on the legacy hierarchy, we
+ * will not wait for the cgroup members to die
+ * if we are running in a container or if this
+ * is a delegation unit, simply because cgroup
+ * notification is unreliable in these
+ * cases. It doesn't work at all in
+ * containers, and outside of containers it
+ * can be confused easily by left-over
+ * directories in the cgroup — which however
+ * should not exist in non-delegated units. On
+ * the unified hierarchy that's different,
+ * there we get proper events. Hence rely on
+ * them.*/
+
+ if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0 ||
+ (detect_container() == 0 && !unit_cgroup_delegate(u)))
+ wait_for_exit = true;
- if (c->send_sighup && k != KILL_KILL) {
+ if (send_sighup) {
set_free(pid_set);
pid_set = unit_pid_set(main_pid, control_pid);
if (!pid_set)
return -ENOMEM;
- cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, SIGHUP, false, true, false, pid_set);
+ cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+ SIGHUP,
+ CGROUP_IGNORE_SELF,
+ pid_set,
+ NULL, NULL);
}
}
}
@@ -3683,6 +3931,26 @@ int unit_setup_exec_runtime(Unit *u) {
return exec_runtime_make(rt, unit_get_exec_context(u), u->id);
}
+int unit_setup_dynamic_creds(Unit *u) {
+ ExecContext *ec;
+ DynamicCreds *dcreds;
+ size_t offset;
+
+ assert(u);
+
+ offset = UNIT_VTABLE(u)->dynamic_creds_offset;
+ assert(offset > 0);
+ dcreds = (DynamicCreds*) ((uint8_t*) u + offset);
+
+ ec = unit_get_exec_context(u);
+ assert(ec);
+
+ if (!ec->dynamic_user)
+ return 0;
+
+ return dynamic_creds_acquire(dcreds, u->manager, ec->user, ec->group);
+}
+
bool unit_type_supported(UnitType t) {
if (_unlikely_(t < 0))
return false;
@@ -3741,13 +4009,233 @@ int unit_fail_if_symlink(Unit *u, const char* where) {
return -ELOOP;
}
-static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
- [UNIT_ACTIVE] = "active",
- [UNIT_RELOADING] = "reloading",
- [UNIT_INACTIVE] = "inactive",
- [UNIT_FAILED] = "failed",
- [UNIT_ACTIVATING] = "activating",
- [UNIT_DEACTIVATING] = "deactivating"
-};
+bool unit_is_pristine(Unit *u) {
+ assert(u);
+
+ /* Check if the unit already exists or is already around,
+ * in a number of different ways. Note that to cater for unit
+ * types such as slice, we are generally fine with units that
+ * are marked UNIT_LOADED even though nothing was
+ * actually loaded, as those unit types don't require a file
+ * on disk to validly load. */
+
+ return !(!IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_LOADED) ||
+ u->fragment_path ||
+ u->source_path ||
+ !strv_isempty(u->dropin_paths) ||
+ u->job ||
+ u->merged_into);
+}
+
+pid_t unit_control_pid(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->control_pid)
+ return UNIT_VTABLE(u)->control_pid(u);
+
+ return 0;
+}
-DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
+pid_t unit_main_pid(Unit *u) {
+ assert(u);
+
+ if (UNIT_VTABLE(u)->main_pid)
+ return UNIT_VTABLE(u)->main_pid(u);
+
+ return 0;
+}
+
+static void unit_unref_uid_internal(
+ Unit *u,
+ uid_t *ref_uid,
+ bool destroy_now,
+ void (*_manager_unref_uid)(Manager *m, uid_t uid, bool destroy_now)) {
+
+ assert(u);
+ assert(ref_uid);
+ assert(_manager_unref_uid);
+
+ /* Generic implementation of both unit_unref_uid() and unit_unref_gid(), under the assumption that uid_t and
+ * gid_t are actually the same time, with the same validity rules.
+ *
+ * Drops a reference to UID/GID from a unit. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (!uid_is_valid(*ref_uid))
+ return;
+
+ _manager_unref_uid(u->manager, *ref_uid, destroy_now);
+ *ref_uid = UID_INVALID;
+}
+
+void unit_unref_uid(Unit *u, bool destroy_now) {
+ unit_unref_uid_internal(u, &u->ref_uid, destroy_now, manager_unref_uid);
+}
+
+void unit_unref_gid(Unit *u, bool destroy_now) {
+ unit_unref_uid_internal(u, (uid_t*) &u->ref_gid, destroy_now, manager_unref_gid);
+}
+
+static int unit_ref_uid_internal(
+ Unit *u,
+ uid_t *ref_uid,
+ uid_t uid,
+ bool clean_ipc,
+ int (*_manager_ref_uid)(Manager *m, uid_t uid, bool clean_ipc)) {
+
+ int r;
+
+ assert(u);
+ assert(ref_uid);
+ assert(uid_is_valid(uid));
+ assert(_manager_ref_uid);
+
+ /* Generic implementation of both unit_ref_uid() and unit_ref_guid(), under the assumption that uid_t and gid_t
+ * are actually the same type, and have the same validity rules.
+ *
+ * Adds a reference on a specific UID/GID to this unit. Each unit referencing the same UID/GID maintains a
+ * reference so that we can destroy the UID/GID's IPC resources as soon as this is requested and the counter
+ * drops to zero. */
+
+ assert_cc(sizeof(uid_t) == sizeof(gid_t));
+ assert_cc(UID_INVALID == (uid_t) GID_INVALID);
+
+ if (*ref_uid == uid)
+ return 0;
+
+ if (uid_is_valid(*ref_uid)) /* Already set? */
+ return -EBUSY;
+
+ r = _manager_ref_uid(u->manager, uid, clean_ipc);
+ if (r < 0)
+ return r;
+
+ *ref_uid = uid;
+ return 1;
+}
+
+int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc) {
+ return unit_ref_uid_internal(u, &u->ref_uid, uid, clean_ipc, manager_ref_uid);
+}
+
+int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc) {
+ return unit_ref_uid_internal(u, (uid_t*) &u->ref_gid, (uid_t) gid, clean_ipc, manager_ref_gid);
+}
+
+static int unit_ref_uid_gid_internal(Unit *u, uid_t uid, gid_t gid, bool clean_ipc) {
+ int r = 0, q = 0;
+
+ assert(u);
+
+ /* Reference both a UID and a GID in one go. Either references both, or neither. */
+
+ if (uid_is_valid(uid)) {
+ r = unit_ref_uid(u, uid, clean_ipc);
+ if (r < 0)
+ return r;
+ }
+
+ if (gid_is_valid(gid)) {
+ q = unit_ref_gid(u, gid, clean_ipc);
+ if (q < 0) {
+ if (r > 0)
+ unit_unref_uid(u, false);
+
+ return q;
+ }
+ }
+
+ return r > 0 || q > 0;
+}
+
+int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid) {
+ ExecContext *c;
+ int r;
+
+ assert(u);
+
+ c = unit_get_exec_context(u);
+
+ r = unit_ref_uid_gid_internal(u, uid, gid, c ? c->remove_ipc : false);
+ if (r < 0)
+ return log_unit_warning_errno(u, r, "Couldn't add UID/GID reference to unit, proceeding without: %m");
+
+ return r;
+}
+
+void unit_unref_uid_gid(Unit *u, bool destroy_now) {
+ assert(u);
+
+ unit_unref_uid(u, destroy_now);
+ unit_unref_gid(u, destroy_now);
+}
+
+void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
+ int r;
+
+ assert(u);
+
+ /* This is invoked whenever one of the forked off processes let's us know the UID/GID its user name/group names
+ * resolved to. We keep track of which UID/GID is currently assigned in order to be able to destroy its IPC
+ * objects when no service references the UID/GID anymore. */
+
+ r = unit_ref_uid_gid(u, uid, gid);
+ if (r > 0)
+ bus_unit_send_change_signal(u);
+}
+
+int unit_set_invocation_id(Unit *u, sd_id128_t id) {
+ int r;
+
+ assert(u);
+
+ /* Set the invocation ID for this unit. If we cannot, this will not roll back, but reset the whole thing. */
+
+ if (sd_id128_equal(u->invocation_id, id))
+ return 0;
+
+ if (!sd_id128_is_null(u->invocation_id))
+ (void) hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u);
+
+ if (sd_id128_is_null(id)) {
+ r = 0;
+ goto reset;
+ }
+
+ r = hashmap_ensure_allocated(&u->manager->units_by_invocation_id, &id128_hash_ops);
+ if (r < 0)
+ goto reset;
+
+ u->invocation_id = id;
+ sd_id128_to_string(id, u->invocation_id_string);
+
+ r = hashmap_put(u->manager->units_by_invocation_id, &u->invocation_id, u);
+ if (r < 0)
+ goto reset;
+
+ return 0;
+
+reset:
+ u->invocation_id = SD_ID128_NULL;
+ u->invocation_id_string[0] = 0;
+ return r;
+}
+
+int unit_acquire_invocation_id(Unit *u) {
+ sd_id128_t id;
+ int r;
+
+ assert(u);
+
+ r = sd_id128_randomize(&id);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to generate invocation ID for unit: %m");
+
+ r = unit_set_invocation_id(u, id);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to set invocation ID for unit: %m");
+
+ return 0;
+}
diff --git a/src/core/unit.h b/src/core/unit.h
index 9491ef64f9..a8dd3e602c 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
#pragma once
/***
@@ -27,29 +25,18 @@
typedef struct Unit Unit;
typedef struct UnitVTable UnitVTable;
-typedef enum UnitActiveState UnitActiveState;
typedef struct UnitRef UnitRef;
typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
-#include "list.h"
#include "condition.h"
+#include "failure-action.h"
#include "install.h"
+#include "list.h"
#include "unit-name.h"
-#include "failure-action.h"
-
-enum UnitActiveState {
- UNIT_ACTIVE,
- UNIT_RELOADING,
- UNIT_INACTIVE,
- UNIT_FAILED,
- UNIT_ACTIVATING,
- UNIT_DEACTIVATING,
- _UNIT_ACTIVE_STATE_MAX,
- _UNIT_ACTIVE_STATE_INVALID = -1
-};
typedef enum KillOperation {
KILL_TERMINATE,
+ KILL_TERMINATE_AND_LOG,
KILL_KILL,
KILL_ABORT,
_KILL_OPERATION_MAX,
@@ -109,12 +96,22 @@ struct Unit {
usec_t source_mtime;
usec_t dropin_mtime;
+ /* If this is a transient unit we are currently writing, this is where we are writing it to */
+ FILE *transient_file;
+
/* If there is something to do with this unit, then this is the installed job for it */
Job *job;
/* JOB_NOP jobs are special and can be installed without disturbing the real job. */
Job *nop_job;
+ /* The slot used for watching NameOwnerChanged signals */
+ sd_bus_slot *match_bus_slot;
+
+ /* References to this unit from clients */
+ sd_bus_track *bus_track;
+ char **deserialized_refs;
+
/* Job timeout and action to take */
usec_t job_timeout;
FailureAction job_timeout_action;
@@ -130,6 +127,10 @@ struct Unit {
dual_timestamp condition_timestamp;
dual_timestamp assert_timestamp;
+ /* Updated whenever the low-level state changes */
+ dual_timestamp state_change_timestamp;
+
+ /* Updated whenever the (high-level) active state enters or leaves the active or inactive states */
dual_timestamp inactive_exit_timestamp;
dual_timestamp active_enter_timestamp;
dual_timestamp active_exit_timestamp;
@@ -158,36 +159,58 @@ struct Unit {
/* CGroup realize members queue */
LIST_FIELDS(Unit, cgroup_queue);
+ /* Units with the same CGroup netclass */
+ LIST_FIELDS(Unit, cgroup_netclass);
+
/* PIDs we keep an eye on. Note that a unit might have many
* more, but these are the ones we care enough about to
* process SIGCHLD for */
Set *pids;
+ /* Used in sigchld event invocation to avoid repeat events being invoked */
+ uint64_t sigchldgen;
+
/* Used during GC sweeps */
unsigned gc_marker;
/* Error code when we didn't manage to load the unit (negative) */
int load_error;
+ /* Put a ratelimit on unit starting */
+ RateLimit start_limit;
+ FailureAction start_limit_action;
+ char *reboot_arg;
+
/* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
RateLimit auto_stop_ratelimit;
+ /* Reference to a specific UID/GID */
+ uid_t ref_uid;
+ gid_t ref_gid;
+
/* Cached unit file state and preset */
UnitFileState unit_file_state;
int unit_file_preset;
- /* Where the cpuacct.usage cgroup counter was at the time the unit was started */
- nsec_t cpuacct_usage_base;
+ /* Where the cpu.stat or cpuacct.usage was at the time the unit was started */
+ nsec_t cpu_usage_base;
+ nsec_t cpu_usage_last; /* the most recently read value */
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
- CGroupControllerMask cgroup_realized_mask;
- CGroupControllerMask cgroup_subtree_mask;
- CGroupControllerMask cgroup_members_mask;
+ CGroupMask cgroup_realized_mask;
+ CGroupMask cgroup_enabled_mask;
+ CGroupMask cgroup_subtree_mask;
+ CGroupMask cgroup_members_mask;
+ int cgroup_inotify_wd;
/* How to start OnFailure units */
JobMode on_failure_job_mode;
+ /* The current invocation ID */
+ sd_id128_t invocation_id;
+ char invocation_id_string[SD_ID128_STRING_MAX]; /* useful when logging */
+
/* Garbage collect us we nobody wants or requires us anymore */
bool stop_when_unneeded;
@@ -206,9 +229,6 @@ struct Unit {
/* Ignore this unit when isolating */
bool ignore_on_isolate;
- /* Ignore this unit when snapshotting */
- bool ignore_on_snapshot;
-
/* Did the last condition check succeed? */
bool condition_result;
bool assert_result;
@@ -232,8 +252,13 @@ struct Unit {
bool cgroup_members_mask_valid:1;
bool cgroup_subtree_mask_valid:1;
+ bool start_limit_hit:1;
+
/* Did we already invoke unit_coldplug() for this unit? */
bool coldplugged:1;
+
+ /* For transient units: whether to add a bus track reference after creating the unit */
+ bool bus_track_add:1;
};
struct UnitStatusMessageFormats {
@@ -248,17 +273,16 @@ typedef enum UnitSetPropertiesMode {
UNIT_PERSISTENT = 2,
} UnitSetPropertiesMode;
-#include "socket.h"
+#include "automount.h"
#include "busname.h"
-#include "target.h"
-#include "snapshot.h"
#include "device.h"
-#include "automount.h"
-#include "swap.h"
-#include "timer.h"
-#include "slice.h"
#include "path.h"
#include "scope.h"
+#include "slice.h"
+#include "socket.h"
+#include "swap.h"
+#include "target.h"
+#include "timer.h"
struct UnitVTable {
/* How much memory does an object of this unit type need */
@@ -281,6 +305,10 @@ struct UnitVTable {
* that */
size_t exec_runtime_offset;
+ /* If greater than 0, the offset into the object where the pointer to DynamicCreds is found, if the unit type
+ * has that. */
+ size_t dynamic_creds_offset;
+
/* The name of the configuration file section with the private settings of this unit */
const char *private_section;
@@ -325,7 +353,7 @@ struct UnitVTable {
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);
+ void (*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 */
@@ -346,9 +374,6 @@ struct UnitVTable {
* shall release its runtime resources */
void (*release_resources)(Unit *u);
- /* Return true when this unit is suitable for snapshotting */
- bool (*check_snapshot)(Unit *u);
-
/* Invoked on every child that died */
void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
@@ -362,8 +387,7 @@ struct UnitVTable {
/* Called whenever a process of this unit sends us a message */
void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds);
- /* Called whenever a name this Unit registered for comes or
- * goes away. */
+ /* Called whenever a name this Unit registered for comes or goes away. */
void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner);
/* Called for each property that is being set */
@@ -385,14 +409,21 @@ struct UnitVTable {
/* Called whenever CLOCK_REALTIME made a jump */
void (*time_change)(Unit *u);
- int (*get_timeout)(Unit *u, uint64_t *timeout);
+ /* Returns the next timeout of a unit */
+ int (*get_timeout)(Unit *u, usec_t *timeout);
+
+ /* Returns the main PID if there is any defined, or 0. */
+ pid_t (*main_pid)(Unit *u);
+
+ /* Returns the main PID if there is any defined, or 0. */
+ pid_t (*control_pid)(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
* inactive state. It is the job of the coldplug() call above
* to put the units into the initial state. */
- int (*enumerate)(Manager *m);
+ void (*enumerate)(Manager *m);
/* Type specific cleanups. */
void (*shutdown)(Manager *m);
@@ -401,24 +432,12 @@ struct UnitVTable {
* of this type will immediately fail. */
bool (*supported)(void);
- /* The interface name */
- const char *bus_interface;
-
/* The bus vtable */
const sd_bus_vtable *bus_vtable;
/* The strings to print in status messages */
UnitStatusMessageFormats status_message_formats;
- /* Can units of this type have multiple names? */
- bool no_alias:1;
-
- /* Instances make no sense for this type */
- bool no_instances:1;
-
- /* Exclude from automatic gc */
- bool no_gc:1;
-
/* True if transient units of this type are OK */
bool can_transient:1;
};
@@ -439,13 +458,16 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
/* For casting the various unit types into a unit */
#define UNIT(u) (&(u)->meta)
+#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0)
+#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0)
+#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0)
+
#define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS]))
DEFINE_CAST(SERVICE, Service);
DEFINE_CAST(SOCKET, Socket);
DEFINE_CAST(BUSNAME, BusName);
DEFINE_CAST(TARGET, Target);
-DEFINE_CAST(SNAPSHOT, Snapshot);
DEFINE_CAST(DEVICE, Device);
DEFINE_CAST(MOUNT, Mount);
DEFINE_CAST(AUTOMOUNT, Automount);
@@ -466,9 +488,6 @@ int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
-int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
-int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
-
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
int unit_choose_id(Unit *u, const char *name);
@@ -490,7 +509,8 @@ int unit_load_fragment_and_dropin(Unit *u);
int unit_load_fragment_and_dropin_optional(Unit *u);
int unit_load(Unit *unit);
-int unit_add_default_slice(Unit *u, CGroupContext *c);
+int unit_set_slice(Unit *u, Unit *slice);
+int unit_set_default_slice(Unit *u);
const char *unit_description(Unit *u) _pure_;
@@ -517,11 +537,11 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
int unit_watch_pid(Unit *u, pid_t pid);
void unit_unwatch_pid(Unit *u, pid_t pid);
-int unit_watch_all_pids(Unit *u);
void unit_unwatch_all_pids(Unit *u);
void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2);
+int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name);
int unit_watch_bus_name(Unit *u, const char *name);
void unit_unwatch_bus_name(Unit *u, const char *name);
@@ -530,20 +550,26 @@ bool unit_job_is_applicable(Unit *u, JobType j);
int set_unit_path(const char *p);
char *unit_dbus_path(Unit *u);
+char *unit_dbus_path_invocation_id(Unit *u);
int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
bool unit_can_serialize(Unit *u) _pure_;
+
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
-void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5);
-void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
-int unit_add_node_link(Unit *u, const char *what, bool wants);
+int unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value);
+int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *value);
+int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd);
+void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5);
+
+int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency d);
int unit_coldplug(Unit *u);
void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0);
+void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t);
bool unit_need_daemon_reload(Unit *u);
@@ -560,8 +586,6 @@ bool unit_active_or_pending(Unit *u);
int unit_add_default_target_dependency(Unit *u, Unit *target);
-char *unit_default_cgroup_path(Unit *u);
-
void unit_start_on_failure(Unit *u);
void unit_trigger_notify(Unit *u);
@@ -583,6 +607,7 @@ CGroupContext *unit_get_cgroup_context(Unit *u) _pure_;
ExecRuntime *unit_get_exec_runtime(Unit *u) _pure_;
int unit_setup_exec_runtime(Unit *u);
+int unit_setup_dynamic_creds(Unit *u);
int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data);
int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5);
@@ -590,8 +615,6 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n
int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data);
int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5);
-int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name);
-
int unit_kill_context(Unit *u, KillContext *c, KillOperation k, pid_t main_pid, pid_t control_pid, bool main_pid_alien);
int unit_make_transient(Unit *u);
@@ -600,6 +623,11 @@ int unit_require_mounts_for(Unit *u, const char *path);
bool unit_type_supported(UnitType t);
+bool unit_is_pristine(Unit *u);
+
+pid_t unit_control_pid(Unit *u);
+pid_t unit_main_pid(Unit *u);
+
static inline bool unit_supported(Unit *u) {
return unit_type_supported(u->type);
}
@@ -607,15 +635,28 @@ static inline bool unit_supported(Unit *u) {
void unit_warn_if_dir_nonempty(Unit *u, const char* where);
int unit_fail_if_symlink(Unit *u, const char* where);
-const char *unit_active_state_to_string(UnitActiveState i) _const_;
-UnitActiveState unit_active_state_from_string(const char *s) _pure_;
+int unit_start_limit_test(Unit *u);
+
+void unit_unref_uid(Unit *u, bool destroy_now);
+int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc);
+
+void unit_unref_gid(Unit *u, bool destroy_now);
+int unit_ref_gid(Unit *u, gid_t gid, bool clean_ipc);
+
+int unit_ref_uid_gid(Unit *u, uid_t uid, gid_t gid);
+void unit_unref_uid_gid(Unit *u, bool destroy_now);
+
+void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
+
+int unit_set_invocation_id(Unit *u, sd_id128_t id);
+int unit_acquire_invocation_id(Unit *u);
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
({ \
- Unit *_u = (unit); \
- _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, ##__VA_ARGS__) : \
+ const Unit *_u = (unit); \
+ _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, _u->manager->invocation_log_field, _u->invocation_id_string, ##__VA_ARGS__) : \
log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
})
diff --git a/src/core/user.conf b/src/core/user.conf
index 87c8164378..b427f1ef6d 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -23,7 +23,7 @@
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultRestartSec=100ms
-#DefaultStartLimitInterval=10s
+#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
#DefaultEnvironment=
#DefaultLimitCPU=