summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorPaul Sherwood <paul.sherwood@codethink.co.uk>2013-11-17 18:32:22 +0000
committerPaul Sherwood <paul.sherwood@codethink.co.uk>2013-11-17 18:32:22 +0000
commitaf04dc70741e58c16a3b2c003ef9779d7863827c (patch)
tree23b608397f1ccb73a34d5fa7cc8d6377554f952f /src/core
parentdc8ee9a30e2df2568f2b37e3fb61e4b0bb601b13 (diff)
parent1434ae6fd49f8377b0ddbd4c675736e0d3226ea6 (diff)
downloadsystemd-af04dc70741e58c16a3b2c003ef9779d7863827c.tar.gz
systemd 208
Diffstat (limited to 'src/core')
-rw-r--r--src/core/async.c (renamed from src/core/sync.c)23
-rw-r--r--src/core/async.h (renamed from src/core/sync.h)1
-rw-r--r--src/core/automount.c39
-rw-r--r--src/core/automount.h2
-rw-r--r--src/core/bus-errors.h1
-rw-r--r--src/core/cgroup-attr.c132
-rw-r--r--src/core/cgroup-attr.h50
-rw-r--r--src/core/cgroup-semantics.c333
-rw-r--r--src/core/cgroup-semantics.h43
-rw-r--r--src/core/cgroup.c917
-rw-r--r--src/core/cgroup.h115
-rw-r--r--src/core/condition.c43
-rw-r--r--src/core/condition.h8
-rw-r--r--src/core/dbus-cgroup.c554
-rw-r--r--src/core/dbus-cgroup.h45
-rw-r--r--src/core/dbus-execute.c35
-rw-r--r--src/core/dbus-execute.h18
-rw-r--r--src/core/dbus-job.c53
-rw-r--r--src/core/dbus-kill.c76
-rw-r--r--src/core/dbus-kill.h8
-rw-r--r--src/core/dbus-manager.c359
-rw-r--r--src/core/dbus-mount.c47
-rw-r--r--src/core/dbus-mount.h3
-rw-r--r--src/core/dbus-scope.c189
-rw-r--r--src/core/dbus-scope.h33
-rw-r--r--src/core/dbus-service.c193
-rw-r--r--src/core/dbus-service.h3
-rw-r--r--src/core/dbus-slice.c93
-rw-r--r--src/core/dbus-slice.h33
-rw-r--r--src/core/dbus-socket.c51
-rw-r--r--src/core/dbus-socket.h3
-rw-r--r--src/core/dbus-swap.c46
-rw-r--r--src/core/dbus-swap.h3
-rw-r--r--src/core/dbus-unit.c816
-rw-r--r--src/core/dbus-unit.h48
-rw-r--r--src/core/dbus.c141
-rw-r--r--src/core/dbus.h6
-rw-r--r--src/core/execute.c191
-rw-r--r--src/core/execute.h17
-rw-r--r--src/core/job.c9
-rw-r--r--src/core/kill.c7
-rw-r--r--src/core/kill.h1
-rw-r--r--src/core/killall.c5
-rw-r--r--src/core/load-fragment-gperf.gperf.m441
-rw-r--r--src/core/load-fragment.c751
-rw-r--r--src/core/load-fragment.h11
-rw-r--r--src/core/locale-setup.c44
-rw-r--r--src/core/locale-setup.h2
-rw-r--r--src/core/macros.systemd.in5
-rw-r--r--src/core/main.c197
-rw-r--r--src/core/manager.c294
-rw-r--r--src/core/manager.h41
-rw-r--r--src/core/mount-setup.c10
-rw-r--r--src/core/mount.c350
-rw-r--r--src/core/mount.h4
-rw-r--r--src/core/namespace.c12
-rw-r--r--src/core/org.freedesktop.systemd1.conf4
-rw-r--r--src/core/path.c32
-rw-r--r--src/core/path.h4
-rw-r--r--src/core/scope.c482
-rw-r--r--src/core/scope.h69
-rw-r--r--src/core/selinux-access.c8
-rw-r--r--src/core/selinux-access.h8
-rw-r--r--src/core/service.c180
-rw-r--r--src/core/service.h2
-rw-r--r--src/core/shutdown.c18
-rw-r--r--src/core/slice.c322
-rw-r--r--src/core/slice.h46
-rw-r--r--src/core/smack-setup.c12
-rw-r--r--src/core/snapshot.c34
-rw-r--r--src/core/snapshot.h1
-rw-r--r--src/core/socket.c131
-rw-r--r--src/core/socket.h9
-rw-r--r--src/core/special.h6
-rw-r--r--src/core/swap.c86
-rw-r--r--src/core/swap.h3
-rw-r--r--src/core/system.conf4
-rw-r--r--src/core/systemd.pc.in3
-rw-r--r--src/core/transaction.c18
-rw-r--r--src/core/unit-printf.c216
-rw-r--r--src/core/unit-printf.h6
-rw-r--r--src/core/unit.c1013
-rw-r--r--src/core/unit.h89
-rw-r--r--src/core/user.conf1
84 files changed, 5753 insertions, 3609 deletions
diff --git a/src/core/sync.c b/src/core/async.c
index 7e74b63071..af527bea4e 100644
--- a/src/core/sync.c
+++ b/src/core/async.c
@@ -22,14 +22,10 @@
#include <pthread.h>
#include <unistd.h>
-#include "sync.h"
+#include "async.h"
+#include "log.h"
-static void *sync_thread(void *p) {
- sync();
- return NULL;
-}
-
-int asynchronous_sync(void) {
+int asynchronous_job(void* (*func)(void *p), void *arg) {
pthread_attr_t a;
pthread_t t;
int r;
@@ -53,7 +49,7 @@ int asynchronous_sync(void) {
goto finish;
}
- r = pthread_create(&t, &a, sync_thread, NULL);
+ r = pthread_create(&t, &a, func, arg);
if (r != 0) {
r = -r;
goto finish;
@@ -63,3 +59,14 @@ finish:
pthread_attr_destroy(&a);
return r;
}
+
+static void *sync_thread(void *p) {
+ sync();
+ return NULL;
+}
+
+int asynchronous_sync(void) {
+ log_debug("Spawning new thread for sync");
+
+ return asynchronous_job(sync_thread, NULL);
+}
diff --git a/src/core/sync.h b/src/core/async.h
index eb26c88deb..6601b4dc4b 100644
--- a/src/core/sync.h
+++ b/src/core/async.h
@@ -21,4 +21,5 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+int asynchronous_job(void* (*func)(void *p), void *arg);
int asynchronous_sync(void);
diff --git a/src/core/automount.c b/src/core/automount.c
index a20d5340f2..d1379e0913 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -66,7 +66,7 @@ static void automount_init(Unit *u) {
UNIT(a)->ignore_on_isolate = true;
}
-static void repeat_unmout(const char *path) {
+static void repeat_unmount(const char *path) {
assert(path);
for (;;) {
@@ -100,7 +100,7 @@ static void unmount_autofs(Automount *a) {
if (a->where &&
(UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
UNIT(a)->manager->exit_code != MANAGER_REEXECUTE))
- repeat_unmout(a->where);
+ repeat_unmount(a->where);
}
static void automount_done(Unit *u) {
@@ -117,42 +117,17 @@ static void automount_done(Unit *u) {
a->tokens = NULL;
}
-int automount_add_one_mount_link(Automount *a, Mount *m) {
+static int automount_add_mount_links(Automount *a) {
+ _cleanup_free_ char *parent = NULL;
int r;
assert(a);
- assert(m);
-
- if (UNIT(a)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- if (!path_startswith(a->where, m->where))
- return 0;
- if (path_equal(a->where, m->where))
- return 0;
-
- r = unit_add_two_dependencies(UNIT(a), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
+ r = path_get_parent(a->where, &parent);
if (r < 0)
return r;
- return 0;
-}
-
-static int automount_add_mount_links(Automount *a) {
- Unit *other;
- int r;
-
- assert(a);
-
- LIST_FOREACH(units_by_type, other, UNIT(a)->manager->units_by_type[UNIT_MOUNT]) {
- r = automount_add_one_mount_link(a, MOUNT(other));
- if (r < 0)
- return r;
- }
-
- return 0;
+ return unit_require_mounts_for(UNIT(a), parent);
}
static int automount_add_default_dependencies(Automount *a) {
@@ -575,7 +550,7 @@ fail:
close_nointr_nofail(ioctl_fd);
if (mounted)
- repeat_unmout(a->where);
+ repeat_unmount(a->where);
log_error_unit(UNIT(a)->id,
"Failed to initialize automounter: %s", strerror(-r));
diff --git a/src/core/automount.h b/src/core/automount.h
index 0c6b8a72e9..a7a25d34e0 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -62,8 +62,6 @@ extern const UnitVTable automount_vtable;
int automount_send_ready(Automount *a, int status);
-int automount_add_one_mount_link(Automount *a, Mount *m);
-
const char* automount_state_to_string(AutomountState i) _const_;
AutomountState automount_state_from_string(const char *s) _pure_;
diff --git a/src/core/bus-errors.h b/src/core/bus-errors.h
index 7a4084ea15..9368d68e80 100644
--- a/src/core/bus-errors.h
+++ b/src/core/bus-errors.h
@@ -40,3 +40,4 @@
#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess"
+#define BUS_ERROR_JOB_FAILED "org.freedesktop.systemd1.JobFailed"
diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c
deleted file mode 100644
index 7e3e08eabb..0000000000
--- a/src/core/cgroup-attr.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "cgroup-attr.h"
-#include "cgroup-util.h"
-#include "list.h"
-#include "fileio.h"
-
-int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
- _cleanup_free_ char *path = NULL, *v = NULL;
- int r;
-
- assert(a);
-
- b = cgroup_bonding_find_list(b, a->controller);
- if (!b)
- return 0;
-
- if (a->semantics && a->semantics->map_write) {
- r = a->semantics->map_write(a->semantics, a->value, &v);
- if (r < 0)
- return r;
- }
-
- r = cg_get_path(a->controller, b->path, a->name, &path);
- if (r < 0)
- return r;
-
- r = write_string_file(path, v ? v : a->value);
- if (r < 0)
- log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
-
- return r;
-}
-
-int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
- CGroupAttribute *a;
- int r = 0;
-
- LIST_FOREACH(by_unit, a, first) {
- int k;
-
- k = cgroup_attribute_apply(a, b);
- if (r == 0)
- r = k;
- }
-
- return r;
-}
-
-bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) {
- assert(a);
-
- if (controller) {
- if (streq(a->controller, controller) && (!name || streq(a->name, name)))
- return true;
-
- } else if (!name)
- return true;
- else if (streq(a->name, name)) {
- size_t x, y;
- x = strlen(a->controller);
- y = strlen(name);
-
- if (y > x &&
- memcmp(a->controller, name, x) == 0 &&
- name[x] == '.')
- return true;
- }
-
- return false;
-}
-
-CGroupAttribute *cgroup_attribute_find_list(
- CGroupAttribute *first,
- const char *controller,
- const char *name) {
- CGroupAttribute *a;
-
- assert(name);
-
- LIST_FOREACH(by_unit, a, first)
- if (cgroup_attribute_matches(a, controller, name))
- return a;
-
- return NULL;
-}
-
-void cgroup_attribute_free(CGroupAttribute *a) {
- assert(a);
-
- if (a->unit)
- LIST_REMOVE(CGroupAttribute, by_unit, a->unit->cgroup_attributes, a);
-
- free(a->controller);
- free(a->name);
- free(a->value);
- free(a);
-}
-
-void cgroup_attribute_free_list(CGroupAttribute *first) {
- CGroupAttribute *a, *n;
-
- LIST_FOREACH_SAFE(by_unit, a, n, first)
- cgroup_attribute_free(a);
-}
-
-void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name) {
- CGroupAttribute *a, *n;
-
- LIST_FOREACH_SAFE(by_unit, a, n, first)
- if (cgroup_attribute_matches(a, controller, name))
- cgroup_attribute_free(a);
-}
diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h
deleted file mode 100644
index 3a13b7c92d..0000000000
--- a/src/core/cgroup-attr.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct CGroupAttribute CGroupAttribute;
-
-#include "unit.h"
-#include "cgroup.h"
-#include "cgroup-semantics.h"
-
-struct CGroupAttribute {
- char *controller;
- char *name;
- char *value;
-
- Unit *unit;
-
- const CGroupSemantics *semantics;
-
- LIST_FIELDS(CGroupAttribute, by_unit);
-};
-
-int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
-int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
-
-bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) _pure_;
-CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) _pure_;
-
-void cgroup_attribute_free(CGroupAttribute *a);
-void cgroup_attribute_free_list(CGroupAttribute *first);
-void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name);
diff --git a/src/core/cgroup-semantics.c b/src/core/cgroup-semantics.c
deleted file mode 100644
index 82b02bbd78..0000000000
--- a/src/core/cgroup-semantics.c
+++ /dev/null
@@ -1,333 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
- This file is part of systemd.
-
- Copyright 2013 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-#include "strv.h"
-#include "path-util.h"
-#include "cgroup-util.h"
-
-#include "cgroup-semantics.h"
-
-static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
- unsigned long ul;
-
- assert(s);
- assert(value);
- assert(ret);
-
- if (safe_atolu(value, &ul) < 0 || ul < 1)
- return -EINVAL;
-
- if (asprintf(ret, "%lu", ul) < 0)
- return -ENOMEM;
-
- return 1;
-}
-
-static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
- off_t sz;
-
- assert(s);
- assert(value);
- assert(ret);
-
- if (parse_bytes(value, &sz) < 0 || sz <= 0)
- return -EINVAL;
-
- if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
- return -ENOMEM;
-
- return 1;
-}
-
-static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
- _cleanup_strv_free_ char **l = NULL;
- char *x;
- unsigned k;
-
- assert(s);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2)
- return -EINVAL;
-
- if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
- return -EINVAL;
-
- if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
- return -EINVAL;
-
- x = strdup(value);
- if (!x)
- return -ENOMEM;
-
- *ret = x;
- return 1;
-}
-
-static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
- _cleanup_strv_free_ char **l = NULL;
- unsigned long ul;
-
- assert(s);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- if (strv_length(l) != 1)
- return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
-
- if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
- return -EINVAL;
-
- if (asprintf(ret, "%lu", ul) < 0)
- return -ENOMEM;
-
- return 1;
-}
-
-static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
- _cleanup_strv_free_ char **l = NULL;
- unsigned long ul;
-
- assert(s);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- if (strv_length(l) != 2)
- return -EINVAL;
-
- if (!path_startswith(l[0], "/dev"))
- return -EINVAL;
-
- if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
- return -EINVAL;
-
- if (asprintf(ret, "%s %lu", l[0], ul) < 0)
- return -ENOMEM;
-
- return 1;
-}
-
-static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
- _cleanup_strv_free_ char **l = NULL;
- off_t bytes;
-
- assert(s);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- if (strv_length(l) != 2)
- return -EINVAL;
-
- if (!path_startswith(l[0], "/dev")) {
- return -EINVAL;
- }
-
- if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
- return -EINVAL;
-
- if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
- return -ENOMEM;
-
- return 0;
-}
-
-static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
- _cleanup_strv_free_ char **l = NULL;
- unsigned k;
-
- assert(s);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- k = strv_length(l);
- if (k < 1 || k > 2)
- return -EINVAL;
-
- if (streq(l[0], "*")) {
-
- if (asprintf(ret, "a *:*%s%s",
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
- return -ENOMEM;
- } else {
- struct stat st;
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- return -errno;
- }
-
- if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_warning("%s is not a device.", l[0]);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%c %u:%u%s%s",
- S_ISCHR(st.st_mode) ? 'c' : 'b',
- major(st.st_rdev), minor(st.st_rdev),
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
- _cleanup_strv_free_ char **l = NULL;
- struct stat st;
- dev_t d;
-
- assert(s);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return log_oom();
-
- if (strv_length(l) != 2)
- return -EINVAL;
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- return -errno;
- }
-
- if (S_ISBLK(st.st_mode))
- d = st.st_rdev;
- else if (major(st.st_dev) != 0) {
- /* If this is not a device node then find the block
- * device this file is stored on */
- d = st.st_dev;
-
- /* If this is a partition, try to get the originating
- * block device */
- block_get_whole_disk(d, &d);
- } else {
- log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
- return -ENOMEM;
-
- return 0;
-}
-
-static const CGroupSemantics semantics[] = {
- { "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL },
- { "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL },
- { "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL },
- { "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL },
- { "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL },
- { "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL },
- { "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL },
- { "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL },
- { "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }
-};
-
-int cgroup_semantics_find(
- const char *controller,
- const char *name,
- const char *value,
- char **ret,
- const CGroupSemantics **_s) {
-
- _cleanup_free_ char *c = NULL;
- unsigned i;
- int r;
-
- assert(name);
- assert(_s);
- assert(!value == !ret);
-
- if (!controller) {
- r = cg_controller_from_attr(name, &c);
- if (r < 0)
- return r;
-
- controller = c;
- }
-
- for (i = 0; i < ELEMENTSOF(semantics); i++) {
- const CGroupSemantics *s = semantics + i;
- bool matches_name, matches_pretty;
-
- if (controller && s->controller && !streq(s->controller, controller))
- continue;
-
- matches_name = s->name && streq(s->name, name);
- matches_pretty = s->pretty && streq(s->pretty, name);
-
- if (!matches_name && !matches_pretty)
- continue;
-
- if (value) {
- if (matches_pretty && s->map_pretty) {
-
- r = s->map_pretty(s, value, ret);
- if (r < 0)
- return r;
-
- if (r == 0)
- continue;
-
- } else {
- char *x;
-
- x = strdup(value);
- if (!x)
- return -ENOMEM;
-
- *ret = x;
- }
- }
-
- *_s = s;
- return 1;
- }
-
- *ret = NULL;
- *_s = NULL;
- return 0;
-}
diff --git a/src/core/cgroup-semantics.h b/src/core/cgroup-semantics.h
deleted file mode 100644
index 4f848f4bb7..0000000000
--- a/src/core/cgroup-semantics.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#pragma once
-
-/***
- This file is part of systemd.
-
- Copyright 2011 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-typedef struct CGroupSemantics CGroupSemantics;
-
-struct CGroupSemantics {
- const char *controller;
- const char *name;
- const char *pretty;
-
- bool multiple;
-
- /* This call is used for parsing the pretty value to the actual attribute value */
- int (*map_pretty)(const CGroupSemantics *semantics, const char *value, char **ret);
-
- /* Right before writing this attribute the attribute value is converted to a low-level value */
- int (*map_write)(const CGroupSemantics *semantics, const char *value, char **ret);
-
- /* If this attribute takes a list, this call can be used to reset the list to empty */
- int (*reset)(const CGroupSemantics *semantics, const char *group);
-};
-
-int cgroup_semantics_find(const char *controller, const char *name, const char *value, char **ret, const CGroupSemantics **semantics);
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 83df0f3c9a..8bf4d896de 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright 2010 Lennart Poettering
+ Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -19,310 +19,578 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <assert.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <sys/mount.h>
#include <fcntl.h>
-#include "cgroup.h"
-#include "cgroup-util.h"
-#include "log.h"
-#include "strv.h"
#include "path-util.h"
+#include "special.h"
+#include "cgroup-util.h"
+#include "cgroup.h"
-int cgroup_bonding_realize(CGroupBonding *b) {
- int r;
+void cgroup_context_init(CGroupContext *c) {
+ assert(c);
- assert(b);
- assert(b->path);
- assert(b->controller);
+ /* Initialize everything to the kernel defaults, assuming the
+ * structure is preinitialized to 0 */
- r = cg_create(b->controller, b->path, NULL);
- if (r < 0) {
- log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
- return r;
- }
+ c->cpu_shares = 1024;
+ c->memory_limit = (uint64_t) -1;
+ c->blockio_weight = 1000;
+}
- b->realized = true;
+void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
+ assert(c);
+ assert(a);
- return 0;
+ LIST_REMOVE(CGroupDeviceAllow, device_allow, c->device_allow, a);
+ free(a->path);
+ free(a);
}
-int cgroup_bonding_realize_list(CGroupBonding *first) {
- CGroupBonding *b;
- int r;
-
- LIST_FOREACH(by_unit, b, first)
- if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
- return r;
+void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
+ assert(c);
+ assert(w);
- return 0;
+ LIST_REMOVE(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
+ free(w->path);
+ free(w);
}
-void cgroup_bonding_free(CGroupBonding *b, bool trim) {
+void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
+ assert(c);
assert(b);
- if (b->unit) {
- CGroupBonding *f;
+ LIST_REMOVE(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
+ free(b->path);
+ free(b);
+}
- LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
+void cgroup_context_done(CGroupContext *c) {
+ assert(c);
- if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
- assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
- LIST_REMOVE(CGroupBonding, by_path, f, b);
+ while (c->blockio_device_weights)
+ cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
- if (f)
- hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
- else
- hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
- }
- }
+ while (c->blockio_device_bandwidths)
+ cgroup_context_free_blockio_device_bandwidth(c, c->blockio_device_bandwidths);
- if (b->realized && b->ours && trim)
- cg_trim(b->controller, b->path, false);
+ while (c->device_allow)
+ cgroup_context_free_device_allow(c, c->device_allow);
+}
- free(b->controller);
- free(b->path);
- free(b);
+void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
+ CGroupBlockIODeviceBandwidth *b;
+ CGroupBlockIODeviceWeight *w;
+ CGroupDeviceAllow *a;
+
+ assert(c);
+ assert(f);
+
+ prefix = strempty(prefix);
+
+ fprintf(f,
+ "%sCPUAccounting=%s\n"
+ "%sBlockIOAccounting=%s\n"
+ "%sMemoryAccounting=%s\n"
+ "%sCPUShares=%lu\n"
+ "%sBlockIOWeight=%lu\n"
+ "%sMemoryLimit=%" PRIu64 "\n"
+ "%sDevicePolicy=%s\n",
+ prefix, yes_no(c->cpu_accounting),
+ prefix, yes_no(c->blockio_accounting),
+ prefix, yes_no(c->memory_accounting),
+ prefix, c->cpu_shares,
+ prefix, c->blockio_weight,
+ prefix, c->memory_limit,
+ prefix, cgroup_device_policy_to_string(c->device_policy));
+
+ LIST_FOREACH(device_allow, a, c->device_allow)
+ fprintf(f,
+ "%sDeviceAllow=%s %s%s%s\n",
+ prefix,
+ a->path,
+ a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
+
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights)
+ fprintf(f,
+ "%sBlockIODeviceWeight=%s %lu",
+ prefix,
+ w->path,
+ w->weight);
+
+ 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));
+ }
}
-void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
- CGroupBonding *b, *n;
+static int lookup_blkio_device(const char *p, dev_t *dev) {
+ struct stat st;
+ int r;
- LIST_FOREACH_SAFE(by_unit, b, n, first)
- cgroup_bonding_free(b, remove_or_trim);
-}
+ assert(p);
+ assert(dev);
-void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
- assert(b);
+ r = stat(p, &st);
+ if (r < 0) {
+ log_warning("Couldn't stat device %s: %m", p);
+ return -errno;
+ }
- if (b->realized && b->ours)
- cg_trim(b->controller, b->path, delete_root);
-}
+ if (S_ISBLK(st.st_mode))
+ *dev = st.st_rdev;
+ else if (major(st.st_dev) != 0) {
+ /* If this is not a device node then find the block
+ * device this file is stored on */
+ *dev = st.st_dev;
-void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
- CGroupBonding *b;
+ /* If this is a partition, try to get the originating
+ * block device */
+ block_get_whole_disk(*dev, dev);
+ } else {
+ log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p);
+ return -ENODEV;
+ }
- LIST_FOREACH(by_unit, b, first)
- cgroup_bonding_trim(b, delete_root);
+ return 0;
}
-int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
- _cleanup_free_ char *p = NULL;
- const char *path;
+static int whitelist_device(const char *path, const char *node, const char *acc) {
+ char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
+ struct stat st;
int r;
- assert(b);
- assert(pid >= 0);
+ assert(path);
+ assert(acc);
- if (cgroup_suffix) {
- p = strjoin(b->path, "/", cgroup_suffix, NULL);
- if (!p)
- return -ENOMEM;
+ if (stat(node, &st) < 0) {
+ log_warning("Couldn't stat device %s", node);
+ return -errno;
+ }
- path = p;
- } else
- path = b->path;
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", node);
+ return -ENODEV;
+ }
+
+ sprintf(buf,
+ "%c %u:%u %s",
+ S_ISCHR(st.st_mode) ? 'c' : 'b',
+ major(st.st_rdev), minor(st.st_rdev),
+ acc);
- r = cg_create_and_attach(b->controller, path, pid);
+ r = cg_set_attribute("devices", path, "devices.allow", buf);
if (r < 0)
- return r;
+ log_warning("Failed to set devices.allow on %s: %s", path, strerror(-r));
- b->realized = true;
- return 0;
+ return r;
}
-int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) {
- CGroupBonding *b;
+void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path) {
int r;
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_install(b, pid, cgroup_suffix);
- if (r < 0 && b->essential)
- return r;
+ assert(c);
+ assert(path);
+
+ if (mask == 0)
+ return;
+
+ if (mask & CGROUP_CPU) {
+ char buf[DECIMAL_STR_MAX(unsigned long) + 1];
+
+ sprintf(buf, "%lu\n", c->cpu_shares);
+ r = cg_set_attribute("cpu", path, "cpu.shares", buf);
+ if (r < 0)
+ log_warning("Failed to set cpu.shares on %s: %s", path, strerror(-r));
}
- return 0;
-}
+ 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;
-int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) {
- CGroupBonding *q;
- int ret = 0;
+ sprintf(buf, "%lu\n", c->blockio_weight);
+ r = cg_set_attribute("blkio", path, "blkio.weight", buf);
+ if (r < 0)
+ log_warning("Failed to set blkio.weight on %s: %s", path, strerror(-r));
- LIST_FOREACH(by_unit, q, list) {
- int r;
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
+ dev_t dev;
- if (q == b)
- continue;
+ r = lookup_blkio_device(w->path, &dev);
+ if (r < 0)
+ continue;
- if (!q->ours)
- 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_error("Failed to set blkio.weight_device on %s: %s", path, strerror(-r));
+ }
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+ const char *a;
+ dev_t dev;
+
+ r = lookup_blkio_device(b->path, &dev);
+ if (r < 0)
+ continue;
+
+ a = b->read ? "blkio.throttle.read_bps_device" : "blkio.throttle.write_bps_device";
+
+ sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth);
+ r = cg_set_attribute("blkio", path, a, buf);
+ if (r < 0)
+ log_error("Failed to set %s on %s: %s", a, path, strerror(-r));
+ }
+ }
+
+ if (mask & CGROUP_MEMORY) {
+ if (c->memory_limit != (uint64_t) -1) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 1];
+
+ 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");
- r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false);
- if (r < 0 && ret == 0)
- ret = r;
+ if (r < 0)
+ log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
}
- return ret;
+ if (mask & CGROUP_DEVICE) {
+ CGroupDeviceAllow *a;
+
+ if (c->device_allow || c->device_policy != CGROUP_AUTO)
+ r = cg_set_attribute("devices", path, "devices.deny", "a");
+ else
+ r = cg_set_attribute("devices", path, "devices.allow", "a");
+ if (r < 0)
+ log_error("Failed to reset devices.list on %s: %s", path, strerror(-r));
+
+ if (c->device_policy == CGROUP_CLOSED ||
+ (c->device_policy == CGROUP_AUTO && c->device_allow)) {
+ static const char auto_devices[] =
+ "/dev/null\0" "rw\0"
+ "/dev/zero\0" "rw\0"
+ "/dev/full\0" "rw\0"
+ "/dev/random\0" "rw\0"
+ "/dev/urandom\0" "rw\0";
+
+ const char *x, *y;
+
+ NULSTR_FOREACH_PAIR(x, y, auto_devices)
+ whitelist_device(path, x, y);
+ }
+
+ LIST_FOREACH(device_allow, a, c->device_allow) {
+ char acc[4];
+ unsigned k = 0;
+
+ if (a->r)
+ acc[k++] = 'r';
+ if (a->w)
+ acc[k++] = 'w';
+ if (a->m)
+ acc[k++] = 'm';
+
+ if (k == 0)
+ continue;
+
+ acc[k++] = 0;
+ whitelist_device(path, a->path, acc);
+ }
+ }
}
-int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) {
- assert(b);
- assert(target);
+CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
+ CGroupControllerMask mask = 0;
+
+ /* Figure out which controllers we need */
+
+ if (c->cpu_accounting || c->cpu_shares != 1024)
+ mask |= CGROUP_CPUACCT | CGROUP_CPU;
+
+ if (c->blockio_accounting ||
+ c->blockio_weight != 1000 ||
+ c->blockio_device_weights ||
+ c->blockio_device_bandwidths)
+ mask |= CGROUP_BLKIO;
+
+ if (c->memory_accounting ||
+ c->memory_limit != (uint64_t) -1)
+ mask |= CGROUP_MEMORY;
+
+ if (c->device_allow || c->device_policy != CGROUP_AUTO)
+ mask |= CGROUP_DEVICE;
- return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem);
+ return mask;
}
-int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
- assert(b);
+static CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
+ CGroupContext *c;
- if (!b->realized)
- return -EINVAL;
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return 0;
- return cg_set_group_access(b->controller, b->path, mode, uid, gid);
+ return cgroup_context_get_mask(c);
}
-int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
- CGroupBonding *b;
- int r;
+static CGroupControllerMask unit_get_members_mask(Unit *u) {
+ CGroupControllerMask mask = 0;
+ Unit *m;
+ Iterator i;
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_set_group_access(b, mode, uid, gid);
- if (r < 0)
- return r;
+ assert(u);
+
+ SET_FOREACH(m, u->dependencies[UNIT_BEFORE], i) {
+
+ if (UNIT_DEREF(m->slice) != u)
+ continue;
+
+ mask |= unit_get_cgroup_mask(m) | unit_get_members_mask(m);
}
- return 0;
+ return mask;
}
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
- assert(b);
+static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
+ assert(u);
- if (!b->realized)
- return -EINVAL;
+ if (!UNIT_ISSET(u->slice))
+ return 0;
- return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
+ /* Sibling propagation is only relevant for weight-based
+ * controllers, so let's mask out everything else */
+ return unit_get_members_mask(UNIT_DEREF(u->slice)) &
+ (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT);
}
-int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
- CGroupBonding *b;
+static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
+ char *path = NULL;
int r;
+ bool is_in_hash = false;
+
+ assert(u);
+
+ path = unit_default_cgroup_path(u);
+ if (!path)
+ return -ENOMEM;
+
+ r = hashmap_put(u->manager->cgroup_unit, path, u);
+ if (r == 0)
+ is_in_hash = true;
+
+ if (r < 0) {
+ log_error("cgroup %s exists already: %s", path, strerror(-r));
+ free(path);
+ return r;
+ }
+
+ /* First, create our own group */
+ r = cg_create_everywhere(u->manager->cgroup_supported, mask, path);
+ if (r < 0)
+ log_error("Failed to create cgroup %s: %s", path, strerror(-r));
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
+ /* Then, possibly move things over */
+ if (u->cgroup_path) {
+ r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, path);
if (r < 0)
- return r;
+ log_error("Failed to migrate cgroup %s: %s", path, strerror(-r));
}
+ if (!is_in_hash) {
+ /* And remember the new data */
+ free(u->cgroup_path);
+ u->cgroup_path = path;
+ }
+
+ u->cgroup_realized = true;
+ u->cgroup_mask = mask;
+
return 0;
}
-int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
- char *p = NULL;
- const char *path;
- int r;
+static int unit_realize_cgroup_now(Unit *u) {
+ CGroupControllerMask mask;
- assert(b);
- assert(sig >= 0);
+ assert(u);
+
+ if (u->in_cgroup_queue) {
+ LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u);
+ u->in_cgroup_queue = false;
+ }
+
+ mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
+ mask &= u->manager->cgroup_supported;
- /* Don't kill cgroups that aren't ours */
- if (!b->ours)
+ if (u->cgroup_realized &&
+ u->cgroup_mask == mask)
return 0;
- if (cgroup_suffix) {
- p = strjoin(b->path, "/", cgroup_suffix, NULL);
- if (!p)
- return -ENOMEM;
+ /* First, realize parents */
+ if (UNIT_ISSET(u->slice))
+ unit_realize_cgroup_now(UNIT_DEREF(u->slice));
- path = p;
- } else
- path = b->path;
+ /* And then do the real work */
+ return unit_create_cgroups(u, mask);
+}
- r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
- free(p);
+static void unit_add_to_cgroup_queue(Unit *u) {
- return r;
+ if (u->in_cgroup_queue)
+ return;
+
+ LIST_PREPEND(Unit, cgroup_queue, u->manager->cgroup_queue, u);
+ u->in_cgroup_queue = true;
}
-int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
- CGroupBonding *b;
- Set *allocated_set = NULL;
- int ret = -EAGAIN, r;
+unsigned manager_dispatch_cgroup_queue(Manager *m) {
+ Unit *i;
+ unsigned n = 0;
- if (!first)
- return 0;
+ while ((i = m->cgroup_queue)) {
+ assert(i->in_cgroup_queue);
+
+ if (unit_realize_cgroup_now(i) >= 0)
+ cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
+
+ n++;
+ }
+
+ return n;
+}
+
+static void unit_queue_siblings(Unit *u) {
+ Unit *slice;
+
+ /* This adds the siblings of the specified unit and the
+ * siblings of all parent units to the cgroup queue. (But
+ * neither the specified unit itself nor the parents.) */
- if (!s)
- if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
+ while ((slice = UNIT_DEREF(u->slice))) {
+ Iterator i;
+ Unit *m;
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
- if (r < 0) {
- if (r == -EAGAIN || r == -ESRCH)
+ SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
+ if (m == u)
continue;
- ret = r;
- goto finish;
+ if (UNIT_DEREF(m->slice) != slice)
+ continue;
+
+ unit_add_to_cgroup_queue(m);
}
- if (ret < 0 || r > 0)
- ret = r;
+ u = slice;
}
+}
+
+int unit_realize_cgroup(Unit *u) {
+ CGroupContext *c;
+ int r;
+
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return 0;
-finish:
- if (allocated_set)
- set_free(allocated_set);
+ /* So, here's the deal: when realizing the cgroups for this
+ * unit, we need to first create all parents, but there's more
+ * actually: for the weight-based controllers we also need to
+ * make sure that all our siblings (i.e. units that are in the
+ * same slice as we are) have cgroup too. Otherwise things
+ * would become very uneven as each of their processes would
+ * get as much resources as all our group together. This call
+ * will synchronously create the parent cgroups, but will
+ * defer work on the siblings to the next event loop
+ * iteration. */
- return ret;
+ /* Add all sibling slices to the cgroup queue. */
+ unit_queue_siblings(u);
+
+ /* And realize this one now */
+ r = unit_realize_cgroup_now(u);
+
+ /* And apply the values */
+ if (r >= 0)
+ cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
+
+ return r;
}
-/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
- * cannot know */
-int cgroup_bonding_is_empty(CGroupBonding *b) {
+void unit_destroy_cgroup(Unit *u) {
int r;
- assert(b);
+ assert(u);
- if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
- return r;
+ if (!u->cgroup_path)
+ return;
+
+ r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
+ if (r < 0)
+ log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
+
+ hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
- /* If it is empty it is empty */
- if (r > 0)
- return 1;
+ free(u->cgroup_path);
+ u->cgroup_path = NULL;
+ u->cgroup_realized = false;
+ u->cgroup_mask = 0;
- /* It's not only us using this cgroup, so we just don't know */
- return b->ours ? 0 : -EAGAIN;
}
-int cgroup_bonding_is_empty_list(CGroupBonding *first) {
- CGroupBonding *b;
+pid_t unit_search_main_pid(Unit *u) {
+ _cleanup_fclose_ FILE *f = NULL;
+ pid_t pid = 0, npid, mypid;
- LIST_FOREACH(by_unit, b, first) {
- int r;
+ assert(u);
- if ((r = cgroup_bonding_is_empty(b)) < 0) {
- /* If this returned -EAGAIN, then we don't know if the
- * group is empty, so let's see if another group can
- * tell us */
+ if (!u->cgroup_path)
+ return 0;
- if (r != -EAGAIN)
- return r;
- } else
- return r;
+ if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
+ return 0;
+
+ mypid = getpid();
+ while (cg_read_pid(f, &npid) > 0) {
+ pid_t ppid;
+
+ if (npid == pid)
+ continue;
+
+ /* Ignore processes that aren't our kids */
+ if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
+ continue;
+
+ 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;
+ }
+
+ pid = npid;
}
- return -EAGAIN;
+ return pid;
}
int manager_setup_cgroup(Manager *m) {
- _cleanup_free_ char *current = NULL, *path = NULL;
- char suffix_buffer[sizeof("/systemd-") + DECIMAL_STR_MAX(pid_t)];
- const char *suffix;
+ _cleanup_free_ char *path = NULL;
int r;
+ char *e, *a;
assert(m);
@@ -333,37 +601,30 @@ int manager_setup_cgroup(Manager *m) {
}
/* 1. Determine hierarchy */
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &current);
+ free(m->cgroup_root);
+ m->cgroup_root = NULL;
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
if (r < 0) {
log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
return r;
}
- if (m->running_as == SYSTEMD_SYSTEM)
- suffix = "/system";
- else {
- sprintf(suffix_buffer, "/systemd-%lu", (unsigned long) getpid());
- suffix = suffix_buffer;
+ /* Already in /system.slice? If so, let's cut this off again */
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
+ if (e)
+ *e = 0;
}
- free(m->cgroup_hierarchy);
- if (endswith(current, suffix)) {
- /* We probably got reexecuted and can continue to use our root cgroup */
- m->cgroup_hierarchy = current;
- current = NULL;
- } else {
- /* We need a new root cgroup */
- if (streq(current, "/"))
- m->cgroup_hierarchy = strdup(suffix);
- else
- m->cgroup_hierarchy = strappend(current, suffix);
-
- if (!m->cgroup_hierarchy)
- return log_oom();
- }
+ /* 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;
/* 2. Show data */
- r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
if (r < 0) {
log_error("Cannot find cgroup mount point: %s", strerror(-r));
return r;
@@ -382,8 +643,12 @@ int manager_setup_cgroup(Manager *m) {
log_debug("Release agent already installed.");
}
- /* 4. Realize the group */
- r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0);
+ /* 4. Realize the system slice and put us in there */
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ a = strappenda(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 0);
+ } else
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
if (r < 0) {
log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
return r;
@@ -399,16 +664,11 @@ int manager_setup_cgroup(Manager *m) {
return -errno;
}
- /* 6. Remove non-existing controllers from the default controllers list */
- cg_shorten_controllers(m->default_controllers);
+ /* 6. Figure out which controllers are supported */
+ m->cgroup_supported = cg_mask_supported();
- /* 7. Let's create the user and machine hierarchies
- * right-away, so that people can inotify on them, if they
- * wish, without this being racy. */
- if (m->running_as == SYSTEMD_SYSTEM) {
- cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../user");
- cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../machine");
- }
+ /* 7. Always enable hierarchial support if it exists... */
+ cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
return 0;
}
@@ -416,213 +676,88 @@ int manager_setup_cgroup(Manager *m) {
void manager_shutdown_cgroup(Manager *m, bool delete) {
assert(m);
- if (delete && m->cgroup_hierarchy)
- cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy);
+ /* 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);
if (m->pin_cgroupfs_fd >= 0) {
close_nointr_nofail(m->pin_cgroupfs_fd);
m->pin_cgroupfs_fd = -1;
}
- free(m->cgroup_hierarchy);
- m->cgroup_hierarchy = NULL;
+ free(m->cgroup_root);
+ m->cgroup_root = NULL;
}
-int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) {
- CGroupBonding *b;
+Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
char *p;
+ Unit *u;
assert(m);
assert(cgroup);
- assert(bonding);
- b = hashmap_get(m->cgroup_bondings, cgroup);
- if (b) {
- *bonding = b;
- return 1;
- }
+ u = hashmap_get(m->cgroup_unit, cgroup);
+ if (u)
+ return u;
p = strdupa(cgroup);
- if (!p)
- return -ENOMEM;
-
for (;;) {
char *e;
e = strrchr(p, '/');
- if (e == p || !e) {
- *bonding = NULL;
- return 0;
- }
+ if (e == p || !e)
+ return NULL;
*e = 0;
- b = hashmap_get(m->cgroup_bondings, p);
- if (b) {
- *bonding = b;
- return 1;
- }
+ u = hashmap_get(m->cgroup_unit, p);
+ if (u)
+ return u;
}
}
-int cgroup_notify_empty(Manager *m, const char *group) {
- CGroupBonding *l, *b;
+Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
+ _cleanup_free_ char *cgroup = NULL;
int r;
assert(m);
- assert(group);
-
- r = cgroup_bonding_get(m, group, &l);
- if (r <= 0)
- return r;
-
- LIST_FOREACH(by_path, b, l) {
- int t;
-
- if (!b->unit)
- continue;
-
- t = cgroup_bonding_is_empty_list(b);
- if (t < 0) {
-
- /* If we don't know, we don't know */
- if (t != -EAGAIN)
- log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
-
- continue;
- }
-
- if (t > 0) {
- /* If it is empty, let's delete it */
- cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
-
- if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
- UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
- }
- }
-
- return 0;
-}
-
-Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
- CGroupBonding *l, *b;
- char *group = NULL;
-
- assert(m);
if (pid <= 1)
return NULL;
- if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
- return NULL;
-
- l = hashmap_get(m->cgroup_bondings, group);
-
- if (!l) {
- char *slash;
-
- while ((slash = strrchr(group, '/'))) {
- if (slash == group)
- break;
-
- *slash = 0;
-
- if ((l = hashmap_get(m->cgroup_bondings, group)))
- break;
- }
- }
-
- free(group);
-
- LIST_FOREACH(by_path, b, l) {
-
- if (!b->unit)
- continue;
-
- if (b->ours)
- return b->unit;
- }
-
- return NULL;
-}
-
-CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
- CGroupBonding *b;
-
- if (!controller)
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
- LIST_FOREACH(by_unit, b, first)
- if (streq(b->controller, controller))
- return b;
-
- return NULL;
-}
-
-char *cgroup_bonding_to_string(CGroupBonding *b) {
- char *r;
-
- assert(b);
-
- if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
+ if (r < 0)
return NULL;
- return r;
+ return manager_get_unit_by_cgroup(m, cgroup);
}
-pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
- FILE *f;
- pid_t pid = 0, npid, mypid;
-
- assert(b);
-
- if (!b->ours)
- return 0;
-
- if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
- return 0;
-
- mypid = getpid();
-
- while (cg_read_pid(f, &npid) > 0) {
- pid_t ppid;
+int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
+ Unit *u;
+ int r;
- if (npid == pid)
- continue;
+ assert(m);
+ assert(cgroup);
- /* Ignore processes that aren't our kids */
- if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
- continue;
+ u = manager_get_unit_by_cgroup(m, cgroup);
+ if (u) {
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
+ if (r > 0) {
+ if (UNIT_VTABLE(u)->notify_cgroup_empty)
+ UNIT_VTABLE(u)->notify_cgroup_empty(u);
- 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;
+ unit_add_to_gc_queue(u);
}
-
- pid = npid;
}
- fclose(f);
-
- return pid;
+ return 0;
}
-pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
- CGroupBonding *b;
- pid_t pid;
-
- /* Try to find a main pid from this cgroup, but checking if
- * there's only one PID in the cgroup and returning it. Later
- * on we might want to add additional, smarter heuristics
- * here. */
+static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
+ [CGROUP_AUTO] = "auto",
+ [CGROUP_CLOSED] = "closed",
+ [CGROUP_STRICT] = "strict",
+};
- LIST_FOREACH(by_unit, b, first)
- if ((pid = cgroup_bonding_search_main_pid(b)) != 0)
- return pid;
-
- return 0;
-
-}
+DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 6555d89e37..0a079e909d 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -5,7 +5,7 @@
/***
This file is part of systemd.
- Copyright 2010 Lennart Poettering
+ Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -21,74 +21,95 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-typedef struct CGroupBonding CGroupBonding;
+#include "list.h"
-#include "unit.h"
+typedef struct CGroupContext CGroupContext;
+typedef struct CGroupDeviceAllow CGroupDeviceAllow;
+typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
+typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
-/* Binds a cgroup to a name */
-struct CGroupBonding {
- char *controller;
- char *path;
+typedef enum CGroupDevicePolicy {
- Unit *unit;
+ /* When devices listed, will allow those, plus built-in ones,
+ if none are listed will allow everything. */
+ CGROUP_AUTO,
- /* For the Unit::cgroup_bondings list */
- LIST_FIELDS(CGroupBonding, by_unit);
+ /* Everything forbidden, except built-in ones and listed ones. */
+ CGROUP_CLOSED,
- /* For the Manager::cgroup_bondings hashmap */
- LIST_FIELDS(CGroupBonding, by_path);
+ /* Everythings forbidden, except for the listed devices */
+ CGROUP_STRICT,
- /* When shutting down, remove cgroup? Are our own tasks the
- * only ones in this group?*/
- bool ours:1;
+ _CGROUP_DEVICE_POLICY_MAX,
+ _CGROUP_DEVICE_POLICY_INVALID = -1
+} CGroupDevicePolicy;
- /* If we cannot create this group, or add a process to it, is this fatal? */
- bool essential:1;
+struct CGroupDeviceAllow {
+ LIST_FIELDS(CGroupDeviceAllow, device_allow);
+ char *path;
+ bool r:1;
+ bool w:1;
+ bool m:1;
+};
- /* This cgroup is realized */
- bool realized:1;
+struct CGroupBlockIODeviceWeight {
+ LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights);
+ char *path;
+ unsigned long weight;
};
-int cgroup_bonding_realize(CGroupBonding *b);
-int cgroup_bonding_realize_list(CGroupBonding *first);
+struct CGroupBlockIODeviceBandwidth {
+ LIST_FIELDS(CGroupBlockIODeviceBandwidth, device_bandwidths);
+ char *path;
+ uint64_t bandwidth;
+ bool read;
+};
-void cgroup_bonding_free(CGroupBonding *b, bool trim);
-void cgroup_bonding_free_list(CGroupBonding *first, bool trim);
+struct CGroupContext {
+ bool cpu_accounting;
+ bool blockio_accounting;
+ bool memory_accounting;
-int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *suffix);
-int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *suffix);
+ unsigned long cpu_shares;
-int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list);
-int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem);
+ unsigned long blockio_weight;
+ LIST_HEAD(CGroupBlockIODeviceWeight, blockio_device_weights);
+ LIST_HEAD(CGroupBlockIODeviceBandwidth, blockio_device_bandwidths);
-int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
-int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
+ uint64_t memory_limit;
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
-int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
+ CGroupDevicePolicy device_policy;
+ LIST_HEAD(CGroupDeviceAllow, device_allow);
+};
-int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *suffix);
-int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *suffix);
+#include "unit.h"
+#include "manager.h"
+#include "cgroup-util.h"
-void cgroup_bonding_trim(CGroupBonding *first, bool delete_root);
-void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root);
+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);
+CGroupControllerMask cgroup_context_get_mask(CGroupContext *c);
-int cgroup_bonding_is_empty(CGroupBonding *b);
-int cgroup_bonding_is_empty_list(CGroupBonding *first);
+void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
+void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
+void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
-CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) _pure_;
+int unit_realize_cgroup(Unit *u);
+void unit_destroy_cgroup(Unit *u);
-char *cgroup_bonding_to_string(CGroupBonding *b);
+int manager_setup_cgroup(Manager *m);
+void manager_shutdown_cgroup(Manager *m, bool delete);
-pid_t cgroup_bonding_search_main_pid(CGroupBonding *b);
-pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *b);
+unsigned manager_dispatch_cgroup_queue(Manager *m);
-#include "manager.h"
+Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup);
+Unit* manager_get_unit_by_pid(Manager *m, pid_t pid);
-int manager_setup_cgroup(Manager *m);
-void manager_shutdown_cgroup(Manager *m, bool delete);
+pid_t unit_search_main_pid(Unit *u);
-int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding);
-int cgroup_notify_empty(Manager *m, const char *group);
+int manager_notify_cgroup_empty(Manager *m, const char *group);
-Unit* cgroup_unit_by_pid(Manager *m, pid_t pid);
+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/condition.c b/src/core/condition.c
index 16cae6d23b..6c387450af 100644
--- a/src/core/condition.c
+++ b/src/core/condition.c
@@ -37,6 +37,7 @@
#include "virt.h"
#include "path-util.h"
#include "fileio.h"
+#include "unit.h"
Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
Condition *c;
@@ -157,15 +158,28 @@ static bool test_virtualization(const char *parameter) {
return v > 0 && streq(parameter, id);
}
+static bool test_apparmor_enabled(void) {
+ int r;
+ _cleanup_free_ char *p = NULL;
+
+ r = read_one_line_file("/sys/module/apparmor/parameters/enabled", &p);
+ if (r < 0)
+ return false;
+
+ return parse_boolean(p) > 0;
+}
+
static bool test_security(const char *parameter) {
#ifdef HAVE_SELINUX
if (streq(parameter, "selinux"))
return is_selinux_enabled() > 0;
#endif
- if (streq(parameter, "apparmor"))
- return access("/sys/kernel/security/apparmor/", F_OK) == 0;
- if (streq(parameter, "smack"))
- return access("/sys/fs/smackfs", F_OK) == 0;
+ if (streq(parameter, "apparmor"))
+ return test_apparmor_enabled();
+ if (streq(parameter, "ima"))
+ return access("/sys/kernel/security/ima/", F_OK) == 0;
+ if (streq(parameter, "smack"))
+ return access("/sys/fs/smackfs", F_OK) == 0;
return false;
}
@@ -236,7 +250,7 @@ static bool test_ac_power(const char *parameter) {
return (on_ac_power() != 0) == !!r;
}
-bool condition_test(Condition *c) {
+static bool condition_test(Condition *c) {
assert(c);
switch(c->type) {
@@ -320,7 +334,7 @@ bool condition_test(Condition *c) {
}
}
-bool condition_test_list(Condition *first) {
+bool condition_test_list(const char *unit, Condition *first) {
Condition *c;
int triggered = -1;
@@ -335,6 +349,16 @@ bool condition_test_list(Condition *first) {
bool b;
b = condition_test(c);
+ if (unit)
+ log_debug_unit(unit,
+ "%s=%s%s%s %s for %s.",
+ condition_type_to_string(c->type),
+ c->trigger ? "|" : "",
+ c->negate ? "!" : "",
+ c->parameter,
+ b ? "succeeded" : "failed",
+ unit);
+ c->state = b ? 1 : -1;
if (!c->trigger && !b)
return false;
@@ -354,12 +378,13 @@ void condition_dump(Condition *c, FILE *f, const char *prefix) {
prefix = "";
fprintf(f,
- "%s\t%s: %s%s%s\n",
+ "%s\t%s: %s%s%s %s\n",
prefix,
condition_type_to_string(c->type),
c->trigger ? "|" : "",
c->negate ? "!" : "",
- c->parameter);
+ c->parameter,
+ c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested");
}
void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
@@ -378,9 +403,11 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
[CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
[CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
+ [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
[CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
[CONDITION_VIRTUALIZATION] = "ConditionVirtualization",
[CONDITION_SECURITY] = "ConditionSecurity",
+ [CONDITION_CAPABILITY] = "ConditionCapability",
[CONDITION_HOST] = "ConditionHost",
[CONDITION_AC_POWER] = "ConditionACPower",
[CONDITION_NULL] = "ConditionNull"
diff --git a/src/core/condition.h b/src/core/condition.h
index 50ed955af9..1813b735a5 100644
--- a/src/core/condition.h
+++ b/src/core/condition.h
@@ -48,11 +48,14 @@ typedef enum ConditionType {
typedef struct Condition {
ConditionType type;
- char *parameter;
bool trigger:1;
bool negate:1;
+ char *parameter;
+
+ int state;
+
LIST_FIELDS(struct Condition, conditions);
} Condition;
@@ -60,8 +63,7 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
void condition_free(Condition *c);
void condition_free_list(Condition *c);
-bool condition_test(Condition *c);
-bool condition_test_list(Condition *c);
+bool condition_test_list(const char *unit, Condition *c);
void condition_dump(Condition *c, FILE *f, const char *prefix);
void condition_dump_list(Condition *c, FILE *f, const char *prefix);
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
new file mode 100644
index 0000000000..9ebcad9da6
--- /dev/null
+++ b/src/core/dbus-cgroup.c
@@ -0,0 +1,554 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "path-util.h"
+#include "dbus-cgroup.h"
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_cgroup_append_device_policy, cgroup_device_policy, CGroupDevicePolicy);
+
+static int bus_cgroup_append_device_weights(DBusMessageIter *i, const char *property, void *data) {
+ DBusMessageIter sub, sub2;
+ CGroupContext *c = data;
+ CGroupBlockIODeviceWeight *w;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &w->path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &w->weight) ||
+ !dbus_message_iter_close_container(&sub, &sub2))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_cgroup_append_device_bandwidths(DBusMessageIter *i, const char *property, void *data) {
+ DBusMessageIter sub, sub2;
+ CGroupContext *c = data;
+ CGroupBlockIODeviceBandwidth *b;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(st)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+
+ if (streq(property, "BlockIOReadBandwidth") != b->read)
+ continue;
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &b->path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT64, &b->bandwidth) ||
+ !dbus_message_iter_close_container(&sub, &sub2))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int bus_cgroup_append_device_allow(DBusMessageIter *i, const char *property, void *data) {
+ DBusMessageIter sub, sub2;
+ CGroupContext *c = data;
+ CGroupDeviceAllow *a;
+
+ assert(i);
+ assert(property);
+ assert(c);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(ss)", &sub))
+ return -ENOMEM;
+
+ LIST_FOREACH(device_allow, a, c->device_allow) {
+ const char *rwm;
+ char buf[4];
+ unsigned k = 0;
+
+ if (a->r)
+ buf[k++] = 'r';
+ if (a->w)
+ buf[k++] = 'w';
+ if (a->m)
+ buf[k++] = 'm';
+
+ buf[k] = 0;
+ rwm = buf;
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &rwm) ||
+ !dbus_message_iter_close_container(&sub, &sub2))
+ return -ENOMEM;
+ }
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+const BusProperty bus_cgroup_context_properties[] = {
+ { "CPUAccounting", bus_property_append_bool, "b", offsetof(CGroupContext, cpu_accounting) },
+ { "CPUShares", bus_property_append_ul, "t", offsetof(CGroupContext, cpu_shares) },
+ { "BlockIOAccounting", bus_property_append_bool, "b", offsetof(CGroupContext, blockio_accounting) },
+ { "BlockIOWeight", bus_property_append_ul, "t", offsetof(CGroupContext, blockio_weight) },
+ { "BlockIODeviceWeight", bus_cgroup_append_device_weights, "a(st)", 0 },
+ { "BlockIOReadBandwidth", bus_cgroup_append_device_bandwidths, "a(st)", 0 },
+ { "BlockIOWriteBandwidth", bus_cgroup_append_device_bandwidths, "a(st)", 0 },
+ { "MemoryAccounting", bus_property_append_bool, "b", offsetof(CGroupContext, memory_accounting) },
+ { "MemoryLimit", bus_property_append_uint64, "t", offsetof(CGroupContext, memory_limit) },
+ { "DevicePolicy", bus_cgroup_append_device_policy, "s", offsetof(CGroupContext, device_policy) },
+ { "DeviceAllow", bus_cgroup_append_device_allow, "a(ss)", 0 },
+ {}
+};
+
+int bus_cgroup_set_property(
+ Unit *u,
+ CGroupContext *c,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ assert(name);
+ assert(u);
+ assert(c);
+ assert(i);
+
+ if (streq(name, "CPUAccounting")) {
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ dbus_bool_t b;
+ dbus_message_iter_get_basic(i, &b);
+
+ c->cpu_accounting = b;
+ 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;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(i, &u64);
+ ul = (unsigned long) u64;
+
+ if (u64 <= 0 || u64 != (uint64_t) ul)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ c->cpu_shares = ul;
+ unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul);
+ }
+
+ return 1;
+
+ } else if (streq(name, "BlockIOAccounting")) {
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ dbus_bool_t b;
+ dbus_message_iter_get_basic(i, &b);
+
+ c->blockio_accounting = b;
+ 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;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(i, &u64);
+ ul = (unsigned long) u64;
+
+ if (u64 < 10 || u64 > 1000)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ c->blockio_weight = ul;
+ unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul);
+ }
+
+ return 1;
+
+ } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
+ DBusMessageIter sub;
+ unsigned n = 0;
+ bool read = true;
+
+ if (streq(name, "BlockIOWriteBandwidth"))
+ read = false;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(i, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter sub2;
+ const char *path;
+ uint64_t u64;
+ CGroupBlockIODeviceBandwidth *a;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ CGroupBlockIODeviceBandwidth *b;
+ bool exist = false;
+
+ LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+ if (path_equal(path, b->path) && read == b->read) {
+ a = b;
+ exist = true;
+ break;
+ }
+ }
+
+ if (!exist) {
+ a = new0(CGroupBlockIODeviceBandwidth, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->read = read;
+ a->path = strdup(path);
+ if (!a->path) {
+ free(a);
+ return -ENOMEM;
+ }
+ }
+
+ a->bandwidth = u64;
+
+ if (!exist)
+ LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths,
+ c->blockio_device_bandwidths, a);
+ }
+
+ n++;
+ dbus_message_iter_next(&sub);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ CGroupBlockIODeviceBandwidth *a;
+ CGroupBlockIODeviceBandwidth *next;
+ 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);
+ }
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ 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);
+ } 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);
+ }
+
+ fflush(f);
+ unit_write_drop_in_private(u, mode, name, buf);
+ }
+
+ return 1;
+
+ } else if (streq(name, "BlockIODeviceWeight")) {
+ DBusMessageIter sub;
+ unsigned n = 0;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(i, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter sub2;
+ const char *path;
+ uint64_t u64;
+ unsigned long ul;
+ CGroupBlockIODeviceWeight *a;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &u64, false) < 0)
+ return -EINVAL;
+
+ ul = (unsigned long) u64;
+ if (ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ CGroupBlockIODeviceWeight *b;
+ bool exist = false;
+
+ LIST_FOREACH(device_weights, b, c->blockio_device_weights) {
+ if (path_equal(b->path, path)) {
+ a = b;
+ exist = true;
+ break;
+ }
+ }
+
+ if (!exist) {
+ a = new0(CGroupBlockIODeviceWeight, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->path = strdup(path);
+ if (!a->path) {
+ free(a);
+ return -ENOMEM;
+ }
+ }
+
+ a->weight = ul;
+
+ if (!exist)
+ LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights,
+ c->blockio_device_weights, a);
+ }
+
+ n++;
+ dbus_message_iter_next(&sub);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ CGroupBlockIODeviceWeight *a;
+ size_t size = 0;
+
+ if (n == 0) {
+ while (c->blockio_device_weights)
+ cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
+ }
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs("BlockIODeviceWeight=\n", f);
+ LIST_FOREACH(device_weights, a, c->blockio_device_weights)
+ fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight);
+
+ fflush(f);
+ unit_write_drop_in_private(u, mode, name, buf);
+ }
+
+ return 1;
+
+ } else if (streq(name, "MemoryAccounting")) {
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ dbus_bool_t b;
+ dbus_message_iter_get_basic(i, &b);
+
+ c->memory_accounting = b;
+ unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no");
+ }
+
+ return 1;
+
+ } else if (streq(name, "MemoryLimit")) {
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ uint64_t limit;
+ dbus_message_iter_get_basic(i, &limit);
+
+ c->memory_limit = limit;
+ unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit);
+ }
+
+ return 1;
+
+ } else if (streq(name, "DevicePolicy")) {
+ const char *policy;
+ CGroupDevicePolicy p;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(i, &policy);
+ p = cgroup_device_policy_from_string(policy);
+ if (p < 0)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ char *buf;
+
+ c->device_policy = p;
+
+ buf = strappenda("DevicePolicy=", policy);
+ unit_write_drop_in_private(u, mode, name, buf);
+ }
+
+ return 1;
+
+ } else if (streq(name, "DeviceAllow")) {
+ DBusMessageIter sub;
+ unsigned n = 0;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(i, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ DBusMessageIter sub2;
+ const char *path, *rwm;
+ CGroupDeviceAllow *a;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) < 0)
+ return -EINVAL;
+
+ if (!path_startswith(path, "/dev")) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node");
+ return -EINVAL;
+ }
+
+ if (isempty(rwm))
+ rwm = "rwm";
+
+ if (!in_charset(rwm, "rwm")) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags");
+ return -EINVAL;
+ }
+
+ if (mode != UNIT_CHECK) {
+ CGroupDeviceAllow *b;
+ bool exist = false;
+
+ LIST_FOREACH(device_allow, b, c->device_allow) {
+ if (path_equal(b->path, path)) {
+ a = b;
+ exist = true;
+ break;
+ }
+ }
+
+ if (!exist) {
+ a = new0(CGroupDeviceAllow, 1);
+ if (!a)
+ return -ENOMEM;
+
+ a->path = strdup(path);
+ if (!a->path) {
+ free(a);
+ return -ENOMEM;
+ }
+ }
+
+ a->r = !!strchr(rwm, 'r');
+ a->w = !!strchr(rwm, 'w');
+ a->m = !!strchr(rwm, 'm');
+
+ if (!exist)
+ LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
+ }
+
+ n++;
+ dbus_message_iter_next(&sub);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ CGroupDeviceAllow *a;
+ size_t size = 0;
+
+ if (n == 0) {
+ while (c->device_allow)
+ cgroup_context_free_device_allow(c, c->device_allow);
+ }
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs("DeviceAllow=\n", f);
+ 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);
+ unit_write_drop_in_private(u, mode, name, buf);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/core/dbus-cgroup.h b/src/core/dbus-cgroup.h
new file mode 100644
index 0000000000..e5ac4c3af7
--- /dev/null
+++ b/src/core/dbus-cgroup.h
@@ -0,0 +1,45 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "manager.h"
+#include "dbus-common.h"
+#include "cgroup.h"
+
+#define BUS_CGROUP_CONTEXT_INTERFACE \
+ " <property name=\"CPUAccounting\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"CPUShares\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"BlockIOAccounting\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"BlockIOWeight\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"BlockIODeviceWeight\" type=\"a(st)\" access=\"read\"/>\n" \
+ " <property name=\"BlockIOReadBandwidth=\" type=\"a(st)\" access=\"read\"/>\n" \
+ " <property name=\"BlockIOWriteBandwidth=\" type=\"a(st)\" access=\"read\"/>\n" \
+ " <property name=\"MemoryAccounting\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"MemoryLimit\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"DevicePolicy\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"DeviceAllow\" type=\"a(ss)\" access=\"read\"/>\n"
+
+extern const BusProperty bus_cgroup_context_properties[];
+
+int bus_cgroup_set_property(Unit *u, CGroupContext *c, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 2a8a0e1ac5..2402e8c34d 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -31,10 +31,10 @@
#include "syscall-list.h"
#include "fileio.h"
-DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput);
-DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_input, exec_input, ExecInput);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_execute_append_output, exec_output, ExecOutput);
-int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data) {
char **env_files = data, **j;
DBusMessageIter sub, sub2;
@@ -66,7 +66,7 @@ int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void
return 0;
}
-int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
int32_t n;
@@ -77,12 +77,11 @@ int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property
if (c->oom_score_adjust_set)
n = c->oom_score_adjust;
else {
- char *t;
+ _cleanup_free_ char *t = NULL;
n = 0;
if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0) {
safe_atoi(t, &n);
- free(t);
}
}
@@ -92,7 +91,7 @@ int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property
return 0;
}
-int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
int32_t n;
@@ -111,7 +110,7 @@ int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data
return 0;
}
-int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
int32_t n;
@@ -130,7 +129,7 @@ int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *da
return 0;
}
-int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
int32_t n;
@@ -149,7 +148,7 @@ int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property
return 0;
}
-int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
int32_t n;
@@ -174,7 +173,7 @@ int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *proper
return 0;
}
-int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
dbus_bool_t b;
DBusMessageIter sub;
@@ -200,7 +199,7 @@ int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *
return 0;
}
-int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
uint64_t u;
@@ -219,7 +218,7 @@ int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property
return 0;
}
-int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
uint64_t normal, inverted;
@@ -236,7 +235,7 @@ int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, v
return bus_property_append_uint64(i, property, &inverted);
}
-int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
char *t = NULL;
const char *s;
@@ -265,7 +264,7 @@ int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, vo
return 0;
}
-int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
int r;
uint64_t u;
@@ -347,7 +346,7 @@ int bus_execute_append_command(DBusMessageIter *i, const char *property, void *d
return 0;
}
-int bus_execute_append_syscall_filter(DBusMessageIter *i, const char *property, void *data) {
+static int bus_execute_append_syscall_filter(DBusMessageIter *i, const char *property, void *data) {
ExecContext *c = data;
dbus_bool_t b;
DBusMessageIter sub;
@@ -430,10 +429,8 @@ const BusProperty bus_exec_context_properties[] = {
{ "PrivateNetwork", bus_property_append_bool, "b", offsetof(ExecContext, private_network) },
{ "SameProcessGroup", bus_property_append_bool, "b", offsetof(ExecContext, same_pgrp) },
{ "UtmpIdentifier", bus_property_append_string, "s", offsetof(ExecContext, utmp_id), true },
- { "ControlGroupModify", bus_property_append_bool, "b", offsetof(ExecContext, control_group_modify) },
- { "ControlGroupPersistent", bus_property_append_tristate_false, "b", offsetof(ExecContext, control_group_persistent) },
{ "IgnoreSIGPIPE", bus_property_append_bool, "b", offsetof(ExecContext, ignore_sigpipe) },
{ "NoNewPrivileges", bus_property_append_bool, "b", offsetof(ExecContext, no_new_privileges) },
{ "SystemCallFilter", bus_execute_append_syscall_filter, "au", 0 },
- { NULL, }
+ {}
};
diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h
index 91d70e535f..79bf30838a 100644
--- a/src/core/dbus-execute.h
+++ b/src/core/dbus-execute.h
@@ -63,7 +63,7 @@
" <property name=\"CPUSchedulingPolicy\" type=\"i\" access=\"read\"/>\n" \
" <property name=\"CPUSchedulingPriority\" type=\"i\" access=\"read\"/>\n" \
" <property name=\"CPUAffinity\" type=\"ay\" access=\"read\"/>\n" \
- " <property name=\"TimerSlackNS\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"TimerSlackNSec\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"CPUSchedulingResetOnFork\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"NonBlocking\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"StandardInput\" type=\"s\" access=\"read\"/>\n" \
@@ -92,8 +92,6 @@
" <property name=\"PrivateNetwork\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SameProcessGroup\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"UtmpIdentifier\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupModify\" type=\"b\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupPersistent\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IgnoreSIGPIPE\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"NoNewPrivileges\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SystemCallFilter\" type=\"au\" access=\"read\"/>\n"
@@ -106,18 +104,4 @@ extern const BusProperty bus_exec_context_properties[];
#define BUS_EXEC_COMMAND_PROPERTY(name, command, indirect) \
{ name, bus_execute_append_command, "a(sasbttttuii)", (command), (indirect), NULL }
-int bus_execute_append_output(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_input(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_oom_score_adjust(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_nice(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_ioprio(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_cpu_sched_policy(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_cpu_sched_priority(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_affinity(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_timer_slack_nsec(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_capabilities(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_capability_bs(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_rlimits(DBusMessageIter *i, const char *property, void *data);
int bus_execute_append_command(DBusMessageIter *u, const char *property, void *data);
-int bus_execute_append_env_files(DBusMessageIter *i, const char *property, void *data);
-int bus_execute_append_syscall_filter(DBusMessageIter *i, const char *property, void *data);
diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c
index 98ccfa62ec..4ab88d06c3 100644
--- a/src/core/dbus-job.c
+++ b/src/core/dbus-job.c
@@ -60,7 +60,7 @@ static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_job_append_type, job_type, JobType);
static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *data) {
Job *j = data;
DBusMessageIter sub;
- char *p;
+ _cleanup_free_ char *p = NULL;
assert(i);
assert(property);
@@ -75,12 +75,9 @@ static int bus_job_append_unit(DBusMessageIter *i, const char *property, void *d
if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &j->unit->id) ||
!dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p)) {
- free(p);
return -ENOMEM;
}
- free(p);
-
if (!dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
@@ -136,7 +133,7 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
/* Be nice to gdbus and return introspection data for our mid-level paths */
if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
- char *introspection = NULL;
+ _cleanup_free_ char *introspection = NULL;
FILE *f;
Iterator i;
size_t size;
@@ -169,7 +166,6 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
if (ferror(f)) {
fclose(f);
- free(introspection);
goto oom;
}
@@ -179,12 +175,9 @@ static DBusHandlerResult bus_job_message_handler(DBusConnection *connection, DBu
goto oom;
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
- free(introspection);
goto oom;
}
- free(introspection);
-
if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
@@ -261,55 +254,51 @@ static int job_send_message(Job *j, DBusMessage* (*new_message)(Job *j)) {
}
static DBusMessage* new_change_signal_message(Job *j) {
- DBusMessage *m = NULL;
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
+ DBusMessage *m;
p = job_dbus_path(j);
if (!p)
- goto oom;
+ return NULL;
if (j->sent_dbus_new_signal) {
/* Send a properties changed signal */
m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Job", INVALIDATING_PROPERTIES);
if (!m)
- goto oom;
+ return NULL;
} else {
/* Send a new signal */
m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobNew");
if (!m)
- goto oom;
+ return NULL;
if (!dbus_message_append_args(m,
DBUS_TYPE_UINT32, &j->id,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_STRING, &j->unit->id,
- DBUS_TYPE_INVALID))
- goto oom;
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(m);
+ return NULL;
+ }
}
return m;
-
-oom:
- if (m)
- dbus_message_unref(m);
- free(p);
- return NULL;
}
static DBusMessage* new_removed_signal_message(Job *j) {
- DBusMessage *m = NULL;
- char *p = NULL;
+ _cleanup_free_ char *p = NULL;
+ DBusMessage *m;
const char *r;
p = job_dbus_path(j);
if (!p)
- goto oom;
+ return NULL;
m = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "JobRemoved");
if (!m)
- goto oom;
+ return NULL;
r = job_result_to_string(j->result);
@@ -318,16 +307,12 @@ static DBusMessage* new_removed_signal_message(Job *j) {
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_STRING, &j->unit->id,
DBUS_TYPE_STRING, &r,
- DBUS_TYPE_INVALID))
- goto oom;
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(m);
+ return NULL;
+ }
return m;
-
-oom:
- if (m)
- dbus_message_unref(m);
- free(p);
- return NULL;
}
void bus_job_send_change_signal(Job *j) {
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index 165f63074b..811adb1b5a 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -25,11 +25,83 @@
#include "dbus-kill.h"
#include "dbus-common.h"
-DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_kill_append_mode, kill_mode, KillMode);
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_kill_append_mode, kill_mode, KillMode);
const BusProperty bus_kill_context_properties[] = {
{ "KillMode", bus_kill_append_mode, "s", offsetof(KillContext, kill_mode) },
{ "KillSignal", bus_property_append_int, "i", offsetof(KillContext, kill_signal) },
{ "SendSIGKILL", bus_property_append_bool, "b", offsetof(KillContext, send_sigkill) },
- { NULL, }
+ { "SendSIGHUP", bus_property_append_bool, "b", offsetof(KillContext, send_sighup) },
+ {}
};
+
+int bus_kill_context_set_transient_property(
+ Unit *u,
+ KillContext *c,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ assert(u);
+ assert(c);
+ assert(name);
+ assert(i);
+
+ if (streq(name, "KillMode")) {
+ const char *m;
+ KillMode k;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(i, &m);
+
+ k = kill_mode_from_string(m);
+ if (k < 0)
+ return -EINVAL;
+
+ 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));
+ }
+
+ return 1;
+
+ } else if (streq(name, "SendSIGHUP")) {
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ dbus_bool_t b;
+
+ dbus_message_iter_get_basic(i, &b);
+ c->send_sighup = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "SendSIGHUP=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "SendSIGKILL")) {
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ dbus_bool_t b;
+
+ dbus_message_iter_get_basic(i, &b);
+ c->send_sigkill = b;
+
+ unit_write_drop_in_private_format(u, mode, name, "SendSIGKILL=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ }
+
+ return 0;
+}
diff --git a/src/core/dbus-kill.h b/src/core/dbus-kill.h
index 238fbd36d6..7676d98e91 100644
--- a/src/core/dbus-kill.h
+++ b/src/core/dbus-kill.h
@@ -29,11 +29,9 @@
#define BUS_KILL_CONTEXT_INTERFACE \
" <property name=\"KillMode\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"KillSignal\" type=\"i\" access=\"read\"/>\n" \
- " <property name=\"SendSIGKILL\" type=\"b\" access=\"read\"/>\n"
-
-#define BUS_KILL_COMMAND_INTERFACE(name) \
- " <property name=\"" name "\" type=\"a(sasbttuii)\" access=\"read\"/>\n"
+ " <property name=\"SendSIGKILL\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"SendSIGHUP\" type=\"b\" access=\"read\"/>\n"
extern const BusProperty bus_kill_context_properties[];
-int bus_kill_append_mode(DBusMessageIter *i, const char *property, void *data);
+int bus_kill_context_set_transient_property(Unit *u, KillContext *c, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 56b02a1cf5..676a07ffa5 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -103,32 +103,6 @@
" <method name=\"ResetFailedUnit\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"SetUnitControlGroup\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"UnsetUnitControlGroup\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
- " </method>\n" \
- " <method name=\"GetUnitControlGroupAttribute\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"SetUnitControlGroupAttribute\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
- " </method>\n" \
- " <method name=\"UnsetUnitControlGroupAttributes\">\n" \
- " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " </method>\n" \
" <method name=\"GetJob\">\n" \
" <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
@@ -178,8 +152,8 @@
" <arg name=\"unset\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"set\" type=\"as\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"ListUnitFiles\">\n" \
- " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
+ " <method name=\"ListUnitFiles\">\n" \
+ " <arg name=\"files\" type=\"a(ss)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetUnitFileState\">\n" \
" <arg name=\"file\" type=\"s\" direction=\"in\"/>\n" \
@@ -227,6 +201,25 @@
" <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetDefaultTarget\">\n" \
+ " <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetDefaultTarget\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetUnitProperties\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
+ " </method>\n" \
+ " <method name=\"StartTransientUnit\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
+ " <arg name=\"aux\" type=\"a(sa(sv))\" direction=\"in\"/>\n" \
+ " <arg name=\"job\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n"
#define BUS_MANAGER_INTERFACE_SIGNALS \
@@ -257,7 +250,10 @@
" <arg name=\"userspace\" type=\"t\"/>\n" \
" <arg name=\"total\" type=\"t\"/>\n" \
" </signal>" \
- " <signal name=\"UnitFilesChanged\"/>\n"
+ " <signal name=\"UnitFilesChanged\"/>\n" \
+ " <signal name=\"Reloading\">\n" \
+ " <arg name=\"active\" type=\"b\"/>\n" \
+ " </signal>"
#define BUS_MANAGER_INTERFACE_PROPERTIES_GENERAL \
" <property name=\"Version\" type=\"s\" access=\"read\"/>\n" \
@@ -275,6 +271,14 @@
" <property name=\"UserspaceTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"FinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"FinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"GeneratorsStartTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"GeneratorsStartTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"GeneratorsFinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"GeneratorsFinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"UnitsLoadStartTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"UnitsLoadStartTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"UnitsLoadFinishTimestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"UnitsLoadFinishTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"LogLevel\" type=\"s\" access=\"readwrite\"/>\n" \
" <property name=\"LogTarget\" type=\"s\" access=\"readwrite\"/>\n" \
" <property name=\"NNames\" type=\"u\" access=\"read\"/>\n" \
@@ -286,8 +290,6 @@
" <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"RuntimeWatchdogUSec\" type=\"t\" access=\"readwrite\"/>\n" \
@@ -384,7 +386,7 @@ static int bus_manager_set_log_target(DBusMessageIter *i, const char *property,
}
static int bus_manager_append_log_level(DBusMessageIter *i, const char *property, void *data) {
- char *t;
+ _cleanup_free_ char *t = NULL;
int r;
assert(i);
@@ -397,7 +399,6 @@ static int bus_manager_append_log_level(DBusMessageIter *i, const char *property
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t))
r = -ENOMEM;
- free(t);
return r;
}
@@ -580,6 +581,14 @@ static const BusProperty bus_manager_properties[] = {
{ "UserspaceTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, userspace_timestamp.monotonic) },
{ "FinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.realtime) },
{ "FinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, finish_timestamp.monotonic) },
+ { "GeneratorsStartTimestamp", bus_property_append_uint64, "t", offsetof(Manager, generators_start_timestamp.realtime) },
+ { "GeneratorsStartTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, generators_start_timestamp.monotonic) },
+ { "GeneratorsFinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, generators_finish_timestamp.realtime) },
+ { "GeneratorsFinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, generators_finish_timestamp.monotonic) },
+ { "UnitsLoadStartTimestamp", bus_property_append_uint64, "t", offsetof(Manager, unitsload_start_timestamp.realtime) },
+ { "UnitsLoadStartTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, unitsload_start_timestamp.monotonic) },
+ { "UnitsLoadFinishTimestamp", bus_property_append_uint64, "t", offsetof(Manager, unitsload_finish_timestamp.realtime) },
+ { "UnitsLoadFinishTimestampMonotonic", bus_property_append_uint64, "t", offsetof(Manager, unitsload_finish_timestamp.monotonic) },
{ "LogLevel", bus_manager_append_log_level, "s", 0, false, bus_manager_set_log_level },
{ "LogTarget", bus_manager_append_log_target, "s", 0, false, bus_manager_set_log_target },
{ "NNames", bus_manager_append_n_names, "u", 0 },
@@ -591,8 +600,6 @@ static const BusProperty bus_manager_properties[] = {
{ "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) },
{ "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) },
{ "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true },
- { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true },
- { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true },
{ "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) },
{ "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) },
{ "RuntimeWatchdogUSec", bus_property_append_usec, "t", offsetof(Manager, runtime_watchdog), false, bus_manager_set_runtime_watchdog_usec },
@@ -662,7 +669,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
DBUS_TYPE_INVALID))
return bus_send_error_reply(connection, message, &error, -EINVAL);
- u = cgroup_unit_by_pid(m, (pid_t) pid);
+ u = manager_get_unit_by_pid(m, (pid_t) pid);
if (!u) {
dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "No unit for PID %lu is loaded.", (unsigned long) pid);
return bus_send_error_reply(connection, message, &error, -ENOENT);
@@ -875,151 +882,6 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroup")) {
- const char *name;
- Unit *u;
- DBusMessageIter iter;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- u = manager_get_unit(m, name);
- if (!u) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
-
- r = bus_unit_cgroup_set(u, &iter);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroup")) {
- const char *name;
- Unit *u;
- DBusMessageIter iter;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- u = manager_get_unit(m, name);
- if (!u) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
-
- r = bus_unit_cgroup_unset(u, &iter);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttribute")) {
- const char *name;
- Unit *u;
- DBusMessageIter iter;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- u = manager_get_unit(m, name);
- if (!u) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
-
- r = bus_unit_cgroup_attribute_set(u, &iter);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttribute")) {
- const char *name;
- Unit *u;
- DBusMessageIter iter;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- u = manager_get_unit(m, name);
- if (!u) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
-
- r = bus_unit_cgroup_attribute_unset(u, &iter);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttribute")) {
- const char *name;
- Unit *u;
- DBusMessageIter iter;
- _cleanup_strv_free_ char **list = NULL;
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- u = manager_get_unit(m, name);
- if (!u) {
- dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
- return bus_send_error_reply(connection, message, &error, -ENOENT);
- }
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
-
- r = bus_unit_cgroup_attribute_get(u, &iter, &list);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- dbus_message_iter_init_append(reply, &iter);
- if (bus_append_strv_iter(&iter, list) < 0)
- goto oom;
-
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ListUnits")) {
DBusMessageIter iter, sub;
Iterator i;
@@ -1170,17 +1032,9 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
SELINUX_ACCESS_CHECK(connection, message, "status");
- s = BUS_CONNECTION_SUBSCRIBED(m, connection);
- if (!s) {
- s = set_new(string_hash_func, string_compare_func);
- if (!s)
- goto oom;
-
- if (!dbus_connection_set_data(connection, m->subscribed_data_slot, s, NULL)) {
- set_free(s);
- goto oom;
- }
- }
+ s = bus_acquire_subscribed(m, connection);
+ if (!s)
+ goto oom;
client = strdup(bus_message_get_sender_with_fallback(message));
if (!client)
@@ -1309,7 +1163,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
- char *introspection = NULL;
+ _cleanup_free_ char *introspection = NULL;
FILE *f;
Iterator i;
Unit *u;
@@ -1335,7 +1189,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
fputs(INTROSPECTION_BEGIN, f);
HASHMAP_FOREACH_KEY(u, k, m->units, i) {
- char *p;
+ _cleanup_free_ char *p = NULL;
if (k != u->id)
continue;
@@ -1343,12 +1197,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
p = bus_path_escape(k);
if (!p) {
fclose(f);
- free(introspection);
goto oom;
}
fprintf(f, "<node name=\"unit/%s\"/>", p);
- free(p);
}
HASHMAP_FOREACH(j, m->jobs, i)
@@ -1358,7 +1210,6 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (ferror(f)) {
fclose(f);
- free(introspection);
goto oom;
}
@@ -1368,12 +1219,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
goto oom;
if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
- free(introspection);
goto oom;
}
-
- free(introspection);
-
} else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "Reload")) {
SELINUX_ACCESS_CHECK(connection, message, "reload");
@@ -1728,7 +1575,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReenableUnitFiles") ||
dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "LinkUnitFiles") ||
dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "PresetUnitFiles") ||
- dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles")) {
+ dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "MaskUnitFiles") ||
+ dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetDefaultTarget")) {
char **l = NULL;
DBusMessageIter iter;
@@ -1771,6 +1619,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
carries_install_info = r;
} else if (streq(member, "MaskUnitFiles"))
r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
+ else if (streq(member, "SetDefaultTarget"))
+ r = unit_file_set_default(scope, NULL, l[0], &changes, &n_changes);
else
assert_not_reached("Uh? Wrong method");
@@ -1838,6 +1688,111 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetDefaultTarget")) {
+ UnitFileScope scope = m->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+ _cleanup_free_ char *default_target = NULL;
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ r = unit_file_get_default(scope, NULL, &default_target);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &default_target, DBUS_TYPE_INVALID)) {
+ goto oom;
+ }
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitProperties")) {
+ DBusMessageIter iter;
+ dbus_bool_t runtime;
+ const char *name;
+ Unit *u;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true) < 0 ||
+ bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+ u = manager_get_unit(m, name);
+ if (!u) {
+ dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+ }
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "StartTransientUnit")) {
+ const char *name, *smode;
+ DBusMessageIter iter;
+ JobMode mode;
+ UnitType t;
+ Unit *u;
+
+ if (!dbus_message_iter_init(message, &iter))
+ goto oom;
+
+ if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &name, true) < 0 ||
+ bus_iter_get_basic_and_next(&iter, DBUS_TYPE_STRING, &smode, true) < 0)
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
+
+ t = unit_name_to_type(name);
+ if (t < 0)
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
+ if (!unit_vtable[t]->can_transient) {
+ dbus_set_error(&error, DBUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ mode = job_mode_from_string(smode);
+ if (mode < 0) {
+ dbus_set_error(&error, BUS_ERROR_INVALID_JOB_MODE, "Job mode %s is invalid.", smode);
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ r = manager_load_unit(m, name, NULL, NULL, &u);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
+ if (u->load_state != UNIT_NOT_FOUND || set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0) {
+ dbus_set_error(&error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name);
+ return bus_send_error_reply(connection, message, &error, -EEXIST);
+ }
+
+ /* OK, the unit failed to load and is unreferenced,
+ * now let's fill in the transient data instead */
+ r = unit_make_transient(u);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ /* Set our properties */
+ r = bus_unit_set_properties(u, &iter, UNIT_RUNTIME, false, &error);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ /* And load this stub fully */
+ r = unit_load(u);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, &error, r);
+
+ manager_dispatch_load_queue(m);
+
+ /* Finally, start it */
+ return bus_unit_queue_job(connection, message, u, JOB_START, mode, false);
+
} else {
const BusBoundProperties bps[] = {
{ "org.freedesktop.systemd1.Manager", bus_systemd_properties, systemd_property_string },
diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c
index 0fcceb500d..72e187063c 100644
--- a/src/core/dbus-mount.c
+++ b/src/core/dbus-mount.c
@@ -22,11 +22,12 @@
#include <errno.h>
#include "dbus-unit.h"
-#include "dbus-mount.h"
-#include "dbus-kill.h"
#include "dbus-execute.h"
+#include "dbus-kill.h"
+#include "dbus-cgroup.h"
#include "dbus-common.h"
#include "selinux-access.h"
+#include "dbus-mount.h"
#define BUS_MOUNT_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Mount\">\n" \
@@ -35,12 +36,13 @@
" <property name=\"Options\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ BUS_UNIT_CGROUP_INTERFACE \
BUS_EXEC_COMMAND_INTERFACE("ExecMount") \
BUS_EXEC_COMMAND_INTERFACE("ExecUnmount") \
BUS_EXEC_COMMAND_INTERFACE("ExecRemount") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
- BUS_UNIT_CGROUP_INTERFACE \
+ BUS_CGROUP_CONTEXT_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
@@ -156,11 +158,12 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess
Mount *m = MOUNT(u);
const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Mount", bus_mount_properties, m },
- { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context },
- { "org.freedesktop.systemd1.Mount", bus_kill_context_properties, &m->kill_context },
- { "org.freedesktop.systemd1.Mount", bus_unit_cgroup_properties, u },
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Mount", bus_unit_cgroup_properties, u },
+ { "org.freedesktop.systemd1.Mount", bus_mount_properties, m },
+ { "org.freedesktop.systemd1.Mount", bus_exec_context_properties, &m->exec_context },
+ { "org.freedesktop.systemd1.Mount", bus_kill_context_properties, &m->kill_context },
+ { "org.freedesktop.systemd1.Mount", bus_cgroup_context_properties, &m->cgroup_context },
{ NULL, }
};
@@ -168,3 +171,31 @@ DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMess
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps );
}
+
+int bus_mount_set_property(
+ Unit *u,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ Mount *m = MOUNT(u);
+ int r;
+
+ assert(name);
+ assert(u);
+ assert(i);
+
+ r = bus_cgroup_set_property(u, &m->cgroup_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ return 0;
+}
+
+int bus_mount_commit_properties(Unit *u) {
+ assert(u);
+
+ unit_realize_cgroup(u);
+ return 0;
+}
diff --git a/src/core/dbus-mount.h b/src/core/dbus-mount.h
index 8597394373..f4ec8b1625 100644
--- a/src/core/dbus-mount.h
+++ b/src/core/dbus-mount.h
@@ -27,5 +27,8 @@
DBusHandlerResult bus_mount_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+int bus_mount_set_property(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
+int bus_mount_commit_properties(Unit *u);
+
extern const char bus_mount_interface[];
extern const char bus_mount_invalidating_properties[];
diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c
new file mode 100644
index 0000000000..783a969fb3
--- /dev/null
+++ b/src/core/dbus-scope.c
@@ -0,0 +1,189 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-common.h"
+#include "dbus-cgroup.h"
+#include "dbus-kill.h"
+#include "selinux-access.h"
+#include "dbus-scope.h"
+
+#define BUS_SCOPE_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Scope\">\n" \
+ BUS_UNIT_CGROUP_INTERFACE \
+ " <property name=\"TimeoutStopUSec\" type=\"t\" access=\"read\"/>\n" \
+ BUS_KILL_CONTEXT_INTERFACE \
+ BUS_CGROUP_CONTEXT_INTERFACE \
+ " <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_SCOPE_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Scope\0"
+
+const char bus_scope_interface[] _introspect_("Scope") = BUS_SCOPE_INTERFACE;
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_scope_append_scope_result, scope_result, ScopeResult);
+
+static const BusProperty bus_scope_properties[] = {
+ { "TimeoutStopUSec", bus_property_append_usec, "t", offsetof(Scope, timeout_stop_usec) },
+ { "Result", bus_scope_append_scope_result, "s", offsetof(Scope, result) },
+ {}
+};
+
+DBusHandlerResult bus_scope_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Scope *s = SCOPE(u);
+
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Scope", bus_unit_cgroup_properties, u },
+ { "org.freedesktop.systemd1.Scope", bus_scope_properties, s },
+ { "org.freedesktop.systemd1.Scope", bus_cgroup_context_properties, &s->cgroup_context },
+ { "org.freedesktop.systemd1.Scope", bus_kill_context_properties, &s->kill_context },
+ {}
+ };
+
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
+
+static int bus_scope_set_transient_property(
+ Scope *s,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ int r;
+
+ assert(name);
+ assert(s);
+ assert(i);
+
+ if (streq(name, "PIDs")) {
+ DBusMessageIter sub;
+ unsigned n = 0;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(i) != DBUS_TYPE_UINT32)
+ return -EINVAL;
+
+ r = set_ensure_allocated(&s->pids, trivial_hash_func, trivial_compare_func);
+ if (r < 0)
+ return r;
+
+ dbus_message_iter_recurse(i, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32) {
+ uint32_t pid;
+
+ dbus_message_iter_get_basic(&sub, &pid);
+
+ if (pid <= 1)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ r = set_put(s->pids, LONG_TO_PTR(pid));
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ dbus_message_iter_next(&sub);
+ n++;
+ }
+
+ if (n <= 0)
+ return -EINVAL;
+
+ return 1;
+
+ } else if (streq(name, "TimeoutStopUSec")) {
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_UINT64)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ uint64_t t;
+
+ dbus_message_iter_get_basic(i, &t);
+
+ s->timeout_stop_usec = t;
+
+ unit_write_drop_in_format(UNIT(s), mode, name, "[Scope]\nTimeoutStopSec=%lluus\n", (unsigned long long) t);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int bus_scope_set_property(
+ Unit *u,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ Scope *s = SCOPE(u);
+ int r;
+
+ assert(name);
+ assert(u);
+ assert(i);
+
+ r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ if (u->load_state == UNIT_STUB) {
+ /* While we are created we still accept PIDs */
+
+ r = bus_scope_set_transient_property(s, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ r = bus_kill_context_set_transient_property(u, &s->kill_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_scope_commit_properties(Unit *u) {
+ assert(u);
+
+ unit_realize_cgroup(u);
+ return 0;
+}
diff --git a/src/core/dbus-scope.h b/src/core/dbus-scope.h
new file mode 100644
index 0000000000..e6836f13f0
--- /dev/null
+++ b/src/core/dbus-scope.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_scope_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+int bus_scope_set_property(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
+int bus_scope_commit_properties(Unit *u);
+
+extern const char bus_scope_interface[];
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index e06a5dce97..696c4462fe 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -21,12 +21,15 @@
#include <errno.h>
+#include "strv.h"
+#include "path-util.h"
#include "dbus-unit.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
-#include "dbus-service.h"
+#include "dbus-cgroup.h"
#include "dbus-common.h"
#include "selinux-access.h"
+#include "dbus-service.h"
#define BUS_SERVICE_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Service\">\n" \
@@ -35,13 +38,15 @@
" <property name=\"PIDFile\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"NotifyAccess\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"RestartUSec\" type=\"t\" access=\"read\"/>\n" \
- " <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"TimeoutStartUSec\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"TimeoutStopUSec\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"WatchdogUSec\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"WatchdogTimestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"WatchdogTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"StartLimitInterval\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"StartLimitBurst\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"StartLimitAction\" type=\"s\" access=\"readwrite\"/>\n" \
+ BUS_UNIT_CGROUP_INTERFACE \
BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \
BUS_EXEC_COMMAND_INTERFACE("ExecStart") \
BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \
@@ -50,7 +55,7 @@
BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
- BUS_UNIT_CGROUP_INTERFACE \
+ BUS_CGROUP_CONTEXT_INTERFACE \
" <property name=\"PermissionsStartOnly\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RootDirectoryStartOnly\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RemainAfterExit\" type=\"b\" access=\"read\"/>\n" \
@@ -103,12 +108,12 @@ static DEFINE_BUS_PROPERTY_SET_ENUM(bus_service_set_start_limit_action, start_li
static const BusProperty bus_exec_main_status_properties[] = {
{ "ExecMainStartTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) },
{ "ExecMainStartTimestampMonotonic",bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) },
- { "ExecMainExitTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.realtime) },
- { "ExecMainExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(ExecStatus, start_timestamp.monotonic) },
+ { "ExecMainExitTimestamp", bus_property_append_usec, "t", offsetof(ExecStatus, exit_timestamp.realtime) },
+ { "ExecMainExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(ExecStatus, exit_timestamp.monotonic) },
{ "ExecMainPID", bus_property_append_pid, "u", offsetof(ExecStatus, pid) },
{ "ExecMainCode", bus_property_append_int, "i", offsetof(ExecStatus, code) },
{ "ExecMainStatus", bus_property_append_int, "i", offsetof(ExecStatus, status) },
- { NULL, }
+ {}
};
static const BusProperty bus_service_properties[] = {
@@ -117,7 +122,6 @@ static const BusProperty bus_service_properties[] = {
{ "PIDFile", bus_property_append_string, "s", offsetof(Service, pid_file), true },
{ "NotifyAccess", bus_service_append_notify_access, "s", offsetof(Service, notify_access) },
{ "RestartUSec", bus_property_append_usec, "t", offsetof(Service, restart_usec) },
- { "TimeoutUSec", bus_property_append_usec, "t", offsetof(Service, timeout_start_usec) },
{ "TimeoutStartUSec", bus_property_append_usec, "t", offsetof(Service, timeout_start_usec) },
{ "TimeoutStopUSec", bus_property_append_usec, "t", offsetof(Service, timeout_stop_usec) },
{ "WatchdogUSec", bus_property_append_usec, "t", offsetof(Service, watchdog_usec) },
@@ -141,7 +145,7 @@ static const BusProperty bus_service_properties[] = {
{ "BusName", bus_property_append_string, "s", offsetof(Service, bus_name), true },
{ "StatusText", bus_property_append_string, "s", offsetof(Service, status_text), true },
{ "Result", bus_service_append_service_result,"s", offsetof(Service, result) },
- { NULL, }
+ {}
};
DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connection, DBusMessage *message) {
@@ -149,15 +153,184 @@ DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *connectio
const BusBoundProperties bps[] = {
{ "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Service", bus_unit_cgroup_properties, u },
{ "org.freedesktop.systemd1.Service", bus_service_properties, s },
{ "org.freedesktop.systemd1.Service", bus_exec_context_properties, &s->exec_context },
{ "org.freedesktop.systemd1.Service", bus_kill_context_properties, &s->kill_context },
+ { "org.freedesktop.systemd1.Service", bus_cgroup_context_properties, &s->cgroup_context },
{ "org.freedesktop.systemd1.Service", bus_exec_main_status_properties, &s->main_exec_status },
- { "org.freedesktop.systemd1.Service", bus_unit_cgroup_properties, u },
- { NULL, }
+ {}
};
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
}
+
+static int bus_service_set_transient_property(
+ Service *s,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ int r;
+
+ assert(name);
+ assert(s);
+ assert(i);
+
+ if (streq(name, "RemainAfterExit")) {
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ dbus_bool_t b;
+
+ dbus_message_iter_get_basic(i, &b);
+
+ s->remain_after_exit = b;
+ unit_write_drop_in_private_format(UNIT(s), mode, name, "RemainAfterExit=%s\n", yes_no(b));
+ }
+
+ return 1;
+
+ } else if (streq(name, "ExecStart")) {
+ DBusMessageIter sub;
+ unsigned n = 0;
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(i, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ _cleanup_strv_free_ char **argv = NULL;
+ DBusMessageIter sub2;
+ dbus_bool_t ignore;
+ const char *path;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
+ return -EINVAL;
+
+ if (!path_is_absolute(path)) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
+ return -EINVAL;
+ }
+
+ r = bus_parse_strv_iter(&sub2, &argv);
+ if (r < 0)
+ return r;
+
+ dbus_message_iter_next(&sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) < 0)
+ return -EINVAL;
+
+ if (mode != UNIT_CHECK) {
+ ExecCommand *c;
+
+ c = new0(ExecCommand, 1);
+ if (!c)
+ return -ENOMEM;
+
+ c->path = strdup(path);
+ if (!c->path) {
+ free(c);
+ return -ENOMEM;
+ }
+
+ c->argv = argv;
+ argv = NULL;
+
+ c->ignore = ignore;
+
+ path_kill_slashes(c->path);
+ exec_command_append_list(&s->exec_command[SERVICE_EXEC_START], c);
+ }
+
+ n++;
+ dbus_message_iter_next(&sub);
+ }
+
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ ExecCommand *c;
+ size_t size = 0;
+
+ if (n == 0) {
+ exec_command_free_list(s->exec_command[SERVICE_EXEC_START]);
+ s->exec_command[SERVICE_EXEC_START] = NULL;
+ }
+
+ f = open_memstream(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs("ExecStart=\n", f);
+
+ LIST_FOREACH(command, c, s->exec_command[SERVICE_EXEC_START]) {
+ _cleanup_free_ char *a;
+
+ a = strv_join_quoted(c->argv);
+ if (!a)
+ return -ENOMEM;
+
+ fprintf(f, "ExecStart=%s@%s %s\n",
+ c->ignore ? "-" : "",
+ c->path,
+ a);
+ }
+
+ fflush(f);
+ unit_write_drop_in_private(UNIT(s), mode, name, buf);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int bus_service_set_property(
+ Unit *u,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ Service *s = SERVICE(u);
+ int r;
+
+ assert(name);
+ assert(u);
+ assert(i);
+
+ r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ if (u->transient && u->load_state == UNIT_STUB) {
+ /* This is a transient unit, let's load a little more */
+
+ r = bus_service_set_transient_property(s, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ r = bus_kill_context_set_transient_property(u, &s->kill_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int bus_service_commit_properties(Unit *u) {
+ assert(u);
+
+ unit_realize_cgroup(u);
+ return 0;
+}
diff --git a/src/core/dbus-service.h b/src/core/dbus-service.h
index 143aed7ae5..9b9f13701c 100644
--- a/src/core/dbus-service.h
+++ b/src/core/dbus-service.h
@@ -27,5 +27,8 @@
DBusHandlerResult bus_service_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+int bus_service_set_property(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
+int bus_service_commit_properties(Unit *u);
+
extern const char bus_service_interface[];
extern const char bus_service_invalidating_properties[];
diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c
new file mode 100644
index 0000000000..dac9fbdf5f
--- /dev/null
+++ b/src/core/dbus-slice.c
@@ -0,0 +1,93 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+
+#include "dbus-unit.h"
+#include "dbus-common.h"
+#include "dbus-cgroup.h"
+#include "selinux-access.h"
+#include "dbus-slice.h"
+
+#define BUS_SLICE_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Slice\">\n" \
+ BUS_UNIT_CGROUP_INTERFACE \
+ BUS_CGROUP_CONTEXT_INTERFACE \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_UNIT_INTERFACE \
+ BUS_SLICE_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_UNIT_INTERFACES_LIST \
+ "org.freedesktop.systemd1.Slice\0"
+
+const char bus_slice_interface[] _introspect_("Slice") = BUS_SLICE_INTERFACE;
+
+DBusHandlerResult bus_slice_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
+ Slice *s = SLICE(u);
+
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Slice", bus_unit_cgroup_properties, u },
+ { "org.freedesktop.systemd1.Slice", bus_cgroup_context_properties, &s->cgroup_context },
+ {}
+ };
+
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
+
+int bus_slice_set_property(
+ Unit *u,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ Slice *s = SLICE(u);
+ int r;
+
+ assert(name);
+ assert(u);
+ assert(i);
+
+ r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ return 0;
+}
+
+int bus_slice_commit_properties(Unit *u) {
+ assert(u);
+
+ unit_realize_cgroup(u);
+ return 0;
+}
diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h
new file mode 100644
index 0000000000..c5ac473763
--- /dev/null
+++ b/src/core/dbus-slice.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "unit.h"
+
+DBusHandlerResult bus_slice_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+
+int bus_slice_set_property(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
+int bus_slice_commit_properties(Unit *u);
+
+extern const char bus_slice_interface[];
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index 77d98ea0fd..30c4b6302c 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -22,24 +22,26 @@
#include <errno.h>
#include "dbus-unit.h"
-#include "dbus-socket.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
+#include "dbus-cgroup.h"
#include "dbus-common.h"
#include "selinux-access.h"
+#include "dbus-socket.h"
#define BUS_SOCKET_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Socket\">\n" \
" <property name=\"BindIPv6Only\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"Backlog\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ BUS_UNIT_CGROUP_INTERFACE \
BUS_EXEC_COMMAND_INTERFACE("ExecStartPre") \
BUS_EXEC_COMMAND_INTERFACE("ExecStartPost") \
BUS_EXEC_COMMAND_INTERFACE("ExecStopPre") \
BUS_EXEC_COMMAND_INTERFACE("ExecStopPost") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
- BUS_UNIT_CGROUP_INTERFACE \
+ BUS_CGROUP_CONTEXT_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"BindToDevice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DirectoryMode\" type=\"u\" access=\"read\"/>\n" \
@@ -65,6 +67,7 @@
" <property name=\"MessageQueueMessageSize\" type=\"x\" access=\"read\"/>\n" \
" <property name=\"Listen\" type=\"a(ss)\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ReusePort\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"SmackLabel\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SmackLabelIPIn\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"SmackLabelIPOut\" type=\"s\" access=\"read\"/>\n" \
@@ -192,24 +195,54 @@ static const BusProperty bus_socket_properties[] = {
{ "MessageQueueMaxMessages", bus_property_append_long, "x", offsetof(Socket, mq_maxmsg) },
{ "MessageQueueMessageSize", bus_property_append_long, "x", offsetof(Socket, mq_msgsize) },
{ "Result", bus_socket_append_socket_result, "s", offsetof(Socket, result) },
+ { "ReusePort", bus_property_append_bool, "b", offsetof(Socket, reuseport) },
{ "SmackLabel", bus_property_append_string, "s", offsetof(Socket, smack), true },
{ "SmackLabelIPIn", bus_property_append_string, "s", offsetof(Socket, smack_ip_in), true },
{ "SmackLabelIPOut",bus_property_append_string, "s", offsetof(Socket, smack_ip_out), true },
- { NULL, }
+ {}
};
DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
Socket *s = SOCKET(u);
const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Socket", bus_socket_properties, s },
- { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context },
- { "org.freedesktop.systemd1.Socket", bus_kill_context_properties, &s->kill_context },
- { "org.freedesktop.systemd1.Socket", bus_unit_properties, u },
- { NULL, }
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Socket", bus_unit_cgroup_properties, u },
+ { "org.freedesktop.systemd1.Socket", bus_socket_properties, s },
+ { "org.freedesktop.systemd1.Socket", bus_exec_context_properties, &s->exec_context },
+ { "org.freedesktop.systemd1.Socket", bus_kill_context_properties, &s->kill_context },
+ { "org.freedesktop.systemd1.Socket", bus_cgroup_context_properties, &s->cgroup_context },
+ {}
};
SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
+
+int bus_socket_set_property(
+ Unit *u,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ Socket *s = SOCKET(u);
+ int r;
+
+ assert(name);
+ assert(u);
+ assert(i);
+
+ r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ return 0;
+}
+
+int bus_socket_commit_properties(Unit *u) {
+ assert(u);
+
+ unit_realize_cgroup(u);
+ return 0;
+}
diff --git a/src/core/dbus-socket.h b/src/core/dbus-socket.h
index 5369b22e5e..eb035c1a94 100644
--- a/src/core/dbus-socket.h
+++ b/src/core/dbus-socket.h
@@ -27,5 +27,8 @@
DBusHandlerResult bus_socket_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+int bus_socket_set_property(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
+int bus_socket_commit_properties(Unit *u);
+
extern const char bus_socket_interface[];
extern const char bus_socket_invalidating_properties[];
diff --git a/src/core/dbus-swap.c b/src/core/dbus-swap.c
index 2e99fba7db..06edfdcde4 100644
--- a/src/core/dbus-swap.c
+++ b/src/core/dbus-swap.c
@@ -23,22 +23,24 @@
#include <errno.h>
#include "dbus-unit.h"
-#include "dbus-swap.h"
#include "dbus-execute.h"
#include "dbus-kill.h"
+#include "dbus-cgroup.h"
#include "dbus-common.h"
#include "selinux-access.h"
+#include "dbus-swap.h"
#define BUS_SWAP_INTERFACE \
" <interface name=\"org.freedesktop.systemd1.Swap\">\n" \
" <property name=\"What\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Priority\" type=\"i\" access=\"read\"/>\n" \
" <property name=\"TimeoutUSec\" type=\"t\" access=\"read\"/>\n" \
+ BUS_UNIT_CGROUP_INTERFACE \
BUS_EXEC_COMMAND_INTERFACE("ExecActivate") \
BUS_EXEC_COMMAND_INTERFACE("ExecDeactivate") \
BUS_EXEC_CONTEXT_INTERFACE \
BUS_KILL_CONTEXT_INTERFACE \
- BUS_UNIT_CGROUP_INTERFACE \
+ BUS_CGROUP_CONTEXT_INTERFACE \
" <property name=\"ControlPID\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Result\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
@@ -93,6 +95,7 @@ static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_swap_append_swap_result, swap_result,
static const BusProperty bus_swap_properties[] = {
{ "What", bus_property_append_string, "s", offsetof(Swap, what), true },
{ "Priority", bus_swap_append_priority, "i", 0 },
+ { "TimeoutUSec",bus_property_append_usec, "t", offsetof(Swap, timeout_usec)},
BUS_EXEC_COMMAND_PROPERTY("ExecActivate", offsetof(Swap, exec_command[SWAP_EXEC_ACTIVATE]), false),
BUS_EXEC_COMMAND_PROPERTY("ExecDeactivate", offsetof(Swap, exec_command[SWAP_EXEC_DEACTIVATE]), false),
{ "ControlPID", bus_property_append_pid, "u", offsetof(Swap, control_pid) },
@@ -103,11 +106,12 @@ static const BusProperty bus_swap_properties[] = {
DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message) {
Swap *s = SWAP(u);
const BusBoundProperties bps[] = {
- { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
- { "org.freedesktop.systemd1.Swap", bus_swap_properties, s },
- { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context },
- { "org.freedesktop.systemd1.Swap", bus_kill_context_properties, &s->kill_context },
- { "org.freedesktop.systemd1.Swap", bus_unit_cgroup_properties, u },
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Swap", bus_unit_cgroup_properties, u },
+ { "org.freedesktop.systemd1.Swap", bus_swap_properties, s },
+ { "org.freedesktop.systemd1.Swap", bus_exec_context_properties, &s->exec_context },
+ { "org.freedesktop.systemd1.Swap", bus_kill_context_properties, &s->kill_context },
+ { "org.freedesktop.systemd1.Swap", bus_cgroup_context_properties, &s->cgroup_context },
{ NULL, }
};
@@ -115,3 +119,31 @@ DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessa
return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
}
+
+int bus_swap_set_property(
+ Unit *u,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
+
+ Swap *s = SWAP(u);
+ int r;
+
+ assert(name);
+ assert(u);
+ assert(i);
+
+ r = bus_cgroup_set_property(u, &s->cgroup_context, name, i, mode, error);
+ if (r != 0)
+ return r;
+
+ return 0;
+}
+
+int bus_swap_commit_properties(Unit *u) {
+ assert(u);
+
+ unit_realize_cgroup(u);
+ return 0;
+}
diff --git a/src/core/dbus-swap.h b/src/core/dbus-swap.h
index 41fe4447ff..9b586a1ad2 100644
--- a/src/core/dbus-swap.h
+++ b/src/core/dbus-swap.h
@@ -28,5 +28,8 @@
DBusHandlerResult bus_swap_message_handler(Unit *u, DBusConnection *c, DBusMessage *message);
+int bus_swap_set_property(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
+int bus_swap_commit_properties(Unit *u);
+
extern const char bus_swap_interface[];
extern const char bus_swap_invalidating_properties[];
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 575f8eb36a..2ea59b2913 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -81,6 +81,22 @@ static int bus_unit_append_following(DBusMessageIter *i, const char *property, v
return 0;
}
+static int bus_unit_append_slice(DBusMessageIter *i, const char *property, void *data) {
+ Unit *u = data;
+ const char *d;
+
+ assert(i);
+ assert(property);
+ assert(u);
+
+ d = strempty(unit_slice_name(u));
+
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
+ return -ENOMEM;
+
+ return 0;
+}
+
static int bus_unit_append_dependencies(DBusMessageIter *i, const char *property, void *data) {
Unit *u;
Iterator j;
@@ -279,101 +295,69 @@ static int bus_unit_append_job(DBusMessageIter *i, const char *property, void *d
return 0;
}
-static int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
+static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
Unit *u = data;
- char *t;
- CGroupBonding *cgb;
- bool success;
+ dbus_bool_t b;
assert(i);
assert(property);
assert(u);
- cgb = unit_get_default_cgroup(u);
- if (cgb) {
- t = cgroup_bonding_to_string(cgb);
- if (!t)
- return -ENOMEM;
- } else
- t = (char*) "";
-
- success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
-
- if (cgb)
- free(t);
-
- return success ? 0 : -ENOMEM;
-}
-
-static int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- CGroupBonding *cgb;
- DBusMessageIter sub;
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "s", &sub))
- return -ENOMEM;
-
- LIST_FOREACH(by_unit, cgb, u->cgroup_bondings) {
- _cleanup_free_ char *t = NULL;
- bool success;
-
- t = cgroup_bonding_to_string(cgb);
- if (!t)
- return -ENOMEM;
-
- success = dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &t);
- if (!success)
- return -ENOMEM;
- }
+ b = unit_need_daemon_reload(u);
- if (!dbus_message_iter_close_container(i, &sub))
+ if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
return -ENOMEM;
return 0;
}
-static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- CGroupAttribute *a;
- DBusMessageIter sub, sub2;
-
- if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sss)", &sub))
- return -ENOMEM;
+static int bus_property_append_condition(DBusMessageIter *i, const char *property, void *data) {
+ Condition **cp = data;
+ Condition *c;
+ const char *name, *param;
+ dbus_bool_t trigger, negate;
+ dbus_int32_t state;
+ DBusMessageIter sub;
- LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- _cleanup_free_ char *v = NULL;
- bool success;
+ assert(i);
+ assert(property);
+ assert(cp);
- if (a->semantics && a->semantics->map_write)
- a->semantics->map_write(a->semantics, a->value, &v);
+ c = *cp;
+ assert(c);
- success =
- dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->controller) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &a->name) &&
- dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, v ? &v : &a->value) &&
- dbus_message_iter_close_container(&sub, &sub2);
- if (!success)
- return -ENOMEM;
- }
+ name = condition_type_to_string(c->type);
+ param = c->parameter;
+ trigger = c->trigger;
+ negate = c->negate;
+ state = c->state;
- if (!dbus_message_iter_close_container(i, &sub))
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &trigger) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &negate) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &param) ||
+ !dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &state) ||
+ !dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
return 0;
}
-static int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data) {
- Unit *u = data;
- dbus_bool_t b;
+static int bus_property_append_condition_list(DBusMessageIter *i, const char *property, void *data) {
+ Condition **first = data, *c;
+ DBusMessageIter sub;
assert(i);
- assert(property);
- assert(u);
+ assert(data);
- b = unit_need_daemon_reload(u);
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(sbbsi)", &sub))
+ return -ENOMEM;
- if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
+ LIST_FOREACH(conditions, c, *first)
+ bus_property_append_condition(&sub, property, &c);
+
+ if (!dbus_message_iter_close_container(i, &sub))
return -ENOMEM;
return 0;
@@ -471,86 +455,21 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
reply = dbus_message_new_method_return(message);
if (!reply)
goto oom;
-
- } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "SetProperties")) {
DBusMessageIter iter;
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+ dbus_bool_t runtime;
if (!dbus_message_iter_init(message, &iter))
goto oom;
- r = bus_unit_cgroup_set(u, &iter);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
- DBusMessageIter iter;
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_unit_cgroup_unset(u, &iter);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
- } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
- DBusMessageIter iter;
- _cleanup_strv_free_ char **list = NULL;
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_unit_cgroup_attribute_get(u, &iter, &list);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- dbus_message_iter_init_append(reply, &iter);
- if (bus_append_strv_iter(&iter, list) < 0)
- goto oom;
-
- } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
- DBusMessageIter iter;
+ if (bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &runtime, true) < 0)
+ return bus_send_error_reply(connection, message, NULL, -EINVAL);
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_unit_cgroup_attribute_set(u, &iter);
+ r = bus_unit_set_properties(u, &iter, runtime ? UNIT_RUNTIME : UNIT_PERSISTENT, true, &error);
if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
-
- reply = dbus_message_new_method_return(message);
- if (!reply)
- goto oom;
-
- } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
- DBusMessageIter iter;
-
- SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
-
- if (!dbus_message_iter_init(message, &iter))
- goto oom;
-
- r = bus_unit_cgroup_attribute_unset(u, &iter);
- if (r < 0)
- return bus_send_error_reply(connection, message, NULL, r);
+ return bus_send_error_reply(connection, message, &error, r);
reply = dbus_message_new_method_return(message);
if (!reply)
@@ -701,8 +620,9 @@ const DBusObjectPathVTable bus_unit_vtable = {
};
void bus_unit_send_change_signal(Unit *u) {
- _cleanup_free_ char *p = NULL;
_cleanup_dbus_message_unref_ DBusMessage *m = NULL;
+ _cleanup_free_ char *p = NULL;
+ int r;
assert(u);
@@ -720,8 +640,10 @@ void bus_unit_send_change_signal(Unit *u) {
}
p = unit_dbus_path(u);
- if (!p)
- goto oom;
+ if (!p) {
+ log_oom();
+ return;
+ }
if (u->sent_dbus_new_signal) {
/* Send a properties changed signal. First for the
@@ -734,19 +656,26 @@ void bus_unit_send_change_signal(Unit *u) {
m = bus_properties_changed_new(p,
UNIT_VTABLE(u)->bus_interface,
UNIT_VTABLE(u)->bus_invalidating_properties);
- if (!m)
- goto oom;
+ if (!m) {
+ log_oom();
+ return;
+ }
- if (bus_broadcast(u->manager, m) < 0)
- goto oom;
+ r = bus_broadcast(u->manager, m);
+ if (r < 0) {
+ log_error("Failed to broadcast change message: %s", strerror(-r));
+ return;
+ }
dbus_message_unref(m);
}
m = bus_properties_changed_new(p, "org.freedesktop.systemd1.Unit",
INVALIDATING_PROPERTIES);
- if (!m)
- goto oom;
+ if (!m) {
+ log_oom();
+ return;
+ }
} else {
/* Send a new signal */
@@ -754,25 +683,27 @@ void bus_unit_send_change_signal(Unit *u) {
m = dbus_message_new_signal("/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UnitNew");
- if (!m)
- goto oom;
+ if (!m) {
+ log_oom();
+ return;
+ }
if (!dbus_message_append_args(m,
DBUS_TYPE_STRING, &u->id,
DBUS_TYPE_OBJECT_PATH, &p,
- DBUS_TYPE_INVALID))
- goto oom;
+ DBUS_TYPE_INVALID)) {
+ log_oom();
+ return;
+ }
}
- if (bus_broadcast(u->manager, m) < 0)
- goto oom;
+ r = bus_broadcast(u->manager, m);
+ if (r < 0) {
+ log_error("Failed to broadcast UnitNew/PropertiesChanged message.");
+ return;
+ }
u->sent_dbus_new_signal = true;
-
- return;
-
-oom:
- log_oom();
}
void bus_unit_send_removed_signal(Unit *u) {
@@ -849,7 +780,7 @@ DBusHandlerResult bus_unit_queue_job(
(type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" :
type == JOB_STOP ? "stop" : "reload");
- if (type == JOB_STOP && u->load_state == UNIT_ERROR && unit_active_state(u) == UNIT_INACTIVE) {
+ if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) {
dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id);
return bus_send_error_reply(connection, message, &error, -EPERM);
}
@@ -897,428 +828,273 @@ oom:
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
-static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
- const char *mode;
- int r;
-
- assert(iter);
- assert(runtime);
-
- r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
- if (r < 0)
- return r;
-
- if (streq(mode, "runtime"))
- *runtime = true;
- else if (streq(mode, "persistent"))
- *runtime = false;
- else
- return -EINVAL;
-
- return 0;
-}
+static int bus_unit_set_transient_property(
+ Unit *u,
+ const char *name,
+ DBusMessageIter *i,
+ UnitSetPropertiesMode mode,
+ DBusError *error) {
-int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
- _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
- const char *name;
- CGroupBonding *b;
- bool runtime;
int r;
assert(u);
- assert(iter);
+ assert(name);
+ assert(i);
- if (!unit_get_exec_context(u))
- return -EINVAL;
+ if (streq(name, "Description")) {
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
+ return -EINVAL;
- r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return r;
+ if (mode != UNIT_CHECK) {
+ const char *description;
- r = parse_mode(iter, &runtime, false);
- if (r < 0)
- return r;
+ dbus_message_iter_get_basic(i, &description);
- r = cg_split_spec(name, &controller, &new_path);
- if (r < 0)
- return r;
+ r = unit_set_description(u, description);
+ if (r < 0)
+ return r;
- if (!new_path) {
- new_path = unit_default_cgroup_path(u);
- if (!new_path)
- return -ENOMEM;
- }
+ unit_write_drop_in_format(u, mode, name, "[Unit]\nDescription=%s\n", description);
+ }
- if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
- return -EINVAL;
+ return 1;
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (b) {
- if (streq(b->path, new_path))
- return 0;
+ } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
+ const char *s;
- if (b->essential)
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING)
return -EINVAL;
- old_path = strdup(b->path);
- if (!old_path)
- return -ENOMEM;
- }
-
- r = unit_add_cgroup_from_text(u, name, true, &b);
- if (r < 0)
- return r;
- if (r > 0) {
- CGroupAttribute *a;
-
- /* Try to move things to the new place, and clean up the old place */
- cgroup_bonding_realize(b);
- cgroup_bonding_migrate(b, u->cgroup_bondings);
-
- if (old_path)
- cg_trim(controller, old_path, true);
-
- /* Apply the attributes to the new group */
- LIST_FOREACH(by_unit, a, u->cgroup_attributes)
- if (streq(a->controller, controller))
- cgroup_attribute_apply(a, b);
- }
-
- contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
- "ControlGroup=", name, "\n", NULL);
- if (!contents)
- return -ENOMEM;
-
- return unit_write_drop_in(u, runtime, controller, contents);
-}
+ dbus_message_iter_get_basic(i, &s);
-int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
- _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
- const char *name;
- CGroupAttribute *a, *n;
- CGroupBonding *b;
- bool runtime;
- int r;
+ if (isempty(s)) {
+ if (mode != UNIT_CHECK) {
+ unit_ref_unset(&u->slice);
+ unit_remove_drop_in(u, mode, name);
+ }
+ } else {
+ Unit *slice;
- assert(u);
- assert(iter);
+ r = manager_load_unit(u->manager, s, NULL, error, &slice);
+ if (r < 0)
+ return r;
- if (!unit_get_exec_context(u))
- return -EINVAL;
+ if (slice->type != UNIT_SLICE)
+ return -EINVAL;
- r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return r;
+ if (mode != UNIT_CHECK) {
+ unit_ref_set(&u->slice, slice);
+ unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
+ }
+ }
- r = parse_mode(iter, &runtime, false);
- if (r < 0)
- return r;
+ return 1;
+
+ } else if (streq(name, "Requires") ||
+ streq(name, "RequiresOverridable") ||
+ streq(name, "Requisite") ||
+ streq(name, "RequisiteOverridable") ||
+ streq(name, "Wants") ||
+ streq(name, "BindsTo") ||
+ streq(name, "Conflicts") ||
+ streq(name, "Before") ||
+ streq(name, "After") ||
+ streq(name, "OnFailure") ||
+ streq(name, "PropagatesReloadTo") ||
+ streq(name, "ReloadPropagatedFrom") ||
+ streq(name, "PartOf")) {
+
+ UnitDependency d;
+ DBusMessageIter sub;
+
+ d = unit_dependency_from_string(name);
+ if (d < 0)
+ return -EINVAL;
- r = cg_split_spec(name, &controller, &path);
- if (r < 0)
- return r;
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(i) != DBUS_TYPE_STRING)
+ return -EINVAL;
- if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
- return -EINVAL;
+ dbus_message_iter_recurse(i, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+ const char *other;
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (!b)
- return -ENOENT;
+ dbus_message_iter_get_basic(&sub, &other);
- if (path && !path_equal(path, b->path))
- return -ENOENT;
+ if (!unit_name_is_valid(other, false))
+ return -EINVAL;
- if (b->essential)
- return -EINVAL;
+ if (mode != UNIT_CHECK) {
+ _cleanup_free_ char *label = NULL;
- unit_remove_drop_in(u, runtime, controller);
+ r = unit_add_dependency_by_name(u, d, other, NULL, true);
+ if (r < 0)
+ return r;
- /* Try to migrate the old group away */
- if (cg_pid_get_path(controller, 0, &target) >= 0)
- cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
+ label = strjoin(name, "-", other, NULL);
+ if (!label)
+ return -ENOMEM;
- cgroup_bonding_free(b, true);
+ unit_write_drop_in_format(u, mode, label, "[Unit]\n%s=%s\n", name, other);
+ }
- /* Drop all attributes of this controller */
- LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
- if (!streq(a->controller, controller))
- continue;
+ dbus_message_iter_next(&sub);
+ }
- unit_remove_drop_in(u, runtime, a->name);
- cgroup_attribute_free(a);
+ return 1;
}
return 0;
}
-int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
- _cleanup_free_ char *controller = NULL;
- CGroupAttribute *a;
- CGroupBonding *b;
- const char *name;
- char **l = NULL;
+int bus_unit_set_properties(
+ Unit *u,
+ DBusMessageIter *iter,
+ UnitSetPropertiesMode mode,
+ bool commit,
+ DBusError *error) {
+
+ bool for_real = false;
+ DBusMessageIter sub;
+ unsigned n = 0;
int r;
assert(u);
assert(iter);
- assert(_result);
- if (!unit_get_exec_context(u))
- return -EINVAL;
+ if (u->transient)
+ mode &= UNIT_RUNTIME;
- r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
- if (r < 0)
- return r;
+ /* We iterate through the array twice. First run we just check
+ * if all passed data is valid, second run actually applies
+ * it. This is to implement transaction-like behaviour without
+ * actually providing full transactions. */
- r = cg_controller_from_attr(name, &controller);
- if (r < 0)
- return r;
-
- /* First attempt, read the value from the kernel */
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (b) {
- _cleanup_free_ char *p = NULL, *v = NULL;
-
- r = cg_get_path(b->controller, b->path, name, &p);
- if (r < 0)
- return r;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(iter) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
- r = read_full_file(p, &v, NULL);
- if (r >= 0) {
- /* Split on new lines */
- l = strv_split_newlines(v);
- if (!l)
- return -ENOMEM;
+ dbus_message_iter_recurse(iter, &sub);
+ for (;;) {
+ DBusMessageIter sub2, sub3;
+ const char *name;
- *_result = l;
- return 0;
+ if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_INVALID) {
- }
- }
+ if (for_real || mode == UNIT_CHECK)
+ break;
- /* If that didn't work, read our cached value */
- LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
-
- if (!cgroup_attribute_matches(a, controller, name))
+ /* Reached EOF. Let's try again, and this time for realz... */
+ dbus_message_iter_recurse(iter, &sub);
+ for_real = true;
continue;
-
- r = strv_extend(&l, a->value);
- if (r < 0) {
- strv_free(l);
- return r;
}
- }
-
- if (!l)
- return -ENOENT;
-
- *_result = l;
- return 0;
-}
-
-static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
- _cleanup_free_ char *buf = NULL;
- CGroupAttribute *a;
- assert(u);
- assert(name);
-
- LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- if (!cgroup_attribute_matches(a, NULL, name))
- continue;
-
- if (!buf) {
- buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
- "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
-
- if (!buf)
- return -ENOMEM;
- } else {
- char *b;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
- b = strjoin(buf,
- "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
+ dbus_message_iter_recurse(&sub, &sub2);
- if (!b)
- return -ENOMEM;
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
+ dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)
+ return -EINVAL;
- free(buf);
- buf = b;
+ if (!UNIT_VTABLE(u)->bus_set_property) {
+ dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
+ return -ENOENT;
}
- }
-
- if (buf)
- return unit_write_drop_in(u, runtime, name, buf);
- else
- return unit_remove_drop_in(u, runtime, name);
-}
-
-int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
- _cleanup_strv_free_ char **l = NULL;
- int r;
- bool runtime = false;
- char **value;
- const char *name;
-
- assert(u);
- assert(iter);
-
- if (!unit_get_exec_context(u))
- return -EINVAL;
-
- r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return r;
-
- r = bus_parse_strv_iter(iter, &l);
- if (r < 0)
- return r;
-
- if (!dbus_message_iter_next(iter))
- return -EINVAL;
- r = parse_mode(iter, &runtime, false);
- if (r < 0)
- return r;
-
- STRV_FOREACH(value, l) {
- _cleanup_free_ char *v = NULL;
- CGroupAttribute *a;
- const CGroupSemantics *s;
-
- r = cgroup_semantics_find(NULL, name, *value, &v, &s);
+ dbus_message_iter_recurse(&sub2, &sub3);
+ r = UNIT_VTABLE(u)->bus_set_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
+ if (r == 0 && u->transient && u->load_state == UNIT_STUB)
+ r = bus_unit_set_transient_property(u, name, &sub3, for_real ? mode : UNIT_CHECK, error);
if (r < 0)
return r;
-
- if (s && !s->multiple && l[1])
- return -EINVAL;
-
- r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
- if (r < 0)
- return r;
-
- if (r > 0) {
- CGroupBonding *b;
-
- b = cgroup_bonding_find_list(u->cgroup_bondings, a->controller);
- if (!b) {
- /* Doesn't exist yet? Then let's add it */
- r = unit_add_cgroup_from_text(u, a->controller, false, &b);
- if (r < 0)
- return r;
-
- if (r > 0) {
- cgroup_bonding_realize(b);
- cgroup_bonding_migrate(b, u->cgroup_bondings);
- }
- }
-
- /* Make it count */
- cgroup_attribute_apply(a, u->cgroup_bondings);
+ if (r == 0) {
+ dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
+ return -ENOENT;
}
- }
-
- r = update_attribute_drop_in(u, runtime, name);
- if (r < 0)
- return r;
-
- return 0;
-}
+ dbus_message_iter_next(&sub);
-int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
- const char *name;
- bool runtime;
- int r;
-
- assert(u);
- assert(iter);
-
- if (!unit_get_exec_context(u))
- return -EINVAL;
-
- r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
- if (r < 0)
- return r;
-
- r = parse_mode(iter, &runtime, false);
- if (r < 0)
- return r;
+ n += for_real;
+ }
- cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
- update_attribute_drop_in(u, runtime, name);
+ if (commit && n > 0 && UNIT_VTABLE(u)->bus_commit_properties)
+ UNIT_VTABLE(u)->bus_commit_properties(u);
- return 0;
+ return n;
}
const BusProperty bus_unit_properties[] = {
- { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
- { "Names", bus_unit_append_names, "as", 0 },
- { "Following", bus_unit_append_following, "s", 0 },
- { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
- { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
- { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
- { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
- { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
- { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
- { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
- { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
- { "RequiredByOverridable",bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
- { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
- { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
- { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
- { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
- { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
- { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
- { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
- { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
- { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
- { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
- { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
- { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
- { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
- { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
- { "Description", bus_unit_append_description, "s", 0 },
- { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
- { "ActiveState", bus_unit_append_active_state, "s", 0 },
- { "SubState", bus_unit_append_sub_state, "s", 0 },
- { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
- { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
- { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
- { "UnitFileState", bus_unit_append_file_state, "s", 0 },
- { "InactiveExitTimestamp",bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
- { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
- { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
- { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
- { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
- { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
- { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
- { "InactiveEnterTimestampMonotonic",bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
- { "CanStart", bus_unit_append_can_start, "b", 0 },
- { "CanStop", bus_unit_append_can_stop, "b", 0 },
- { "CanReload", bus_unit_append_can_reload, "b", 0 },
- { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
- { "Job", bus_unit_append_job, "(uo)", 0 },
- { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
- { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
- { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
- { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
- { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
- { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
- { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
- { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
- { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
- { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
- { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
- { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
- { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
- { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
- { NULL, }
+ { "Id", bus_property_append_string, "s", offsetof(Unit, id), true },
+ { "Names", bus_unit_append_names, "as", 0 },
+ { "Following", bus_unit_append_following, "s", 0 },
+ { "Requires", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES]), true },
+ { "RequiresOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRES_OVERRIDABLE]), true },
+ { "Requisite", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE]), true },
+ { "RequisiteOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUISITE_OVERRIDABLE]), true },
+ { "Wants", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTS]), true },
+ { "BindsTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BINDS_TO]), true },
+ { "PartOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PART_OF]), true },
+ { "RequiredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), true },
+ { "RequiredByOverridable", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), true },
+ { "WantedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_WANTED_BY]), true },
+ { "BoundBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BOUND_BY]), true },
+ { "ConsistsOf", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), true },
+ { "Conflicts", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTS]), true },
+ { "ConflictedBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), true },
+ { "Before", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_BEFORE]), true },
+ { "After", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_AFTER]), true },
+ { "OnFailure", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_ON_FAILURE]), true },
+ { "Triggers", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERS]), true },
+ { "TriggeredBy", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), true },
+ { "PropagatesReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), true },
+ { "ReloadPropagatedFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), true },
+ { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true },
+ { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true },
+ { "Description", bus_unit_append_description, "s", 0 },
+ { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) },
+ { "ActiveState", bus_unit_append_active_state, "s", 0 },
+ { "SubState", bus_unit_append_sub_state, "s", 0 },
+ { "FragmentPath", bus_property_append_string, "s", offsetof(Unit, fragment_path), true },
+ { "SourcePath", bus_property_append_string, "s", offsetof(Unit, source_path), true },
+ { "DropInPaths", bus_property_append_strv, "as", offsetof(Unit, dropin_paths), true },
+ { "UnitFileState", bus_unit_append_file_state, "s", 0 },
+ { "InactiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.realtime) },
+ { "InactiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_exit_timestamp.monotonic) },
+ { "ActiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.realtime) },
+ { "ActiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_enter_timestamp.monotonic) },
+ { "ActiveExitTimestamp", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.realtime) },
+ { "ActiveExitTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, active_exit_timestamp.monotonic) },
+ { "InactiveEnterTimestamp", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.realtime) },
+ { "InactiveEnterTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, inactive_enter_timestamp.monotonic) },
+ { "CanStart", bus_unit_append_can_start, "b", 0 },
+ { "CanStop", bus_unit_append_can_stop, "b", 0 },
+ { "CanReload", bus_unit_append_can_reload, "b", 0 },
+ { "CanIsolate", bus_unit_append_can_isolate, "b", 0 },
+ { "Job", bus_unit_append_job, "(uo)", 0 },
+ { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) },
+ { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) },
+ { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) },
+ { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) },
+ { "DefaultDependencies", bus_property_append_bool, "b", offsetof(Unit, default_dependencies) },
+ { "OnFailureIsolate", bus_property_append_bool, "b", offsetof(Unit, on_failure_isolate) },
+ { "IgnoreOnIsolate", bus_property_append_bool, "b", offsetof(Unit, ignore_on_isolate) },
+ { "IgnoreOnSnapshot", bus_property_append_bool, "b", offsetof(Unit, ignore_on_snapshot) },
+ { "NeedDaemonReload", bus_unit_append_need_daemon_reload, "b", 0 },
+ { "JobTimeoutUSec", bus_property_append_usec, "t", offsetof(Unit, job_timeout) },
+ { "ConditionTimestamp", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.realtime) },
+ { "ConditionTimestampMonotonic", bus_property_append_usec, "t", offsetof(Unit, condition_timestamp.monotonic) },
+ { "ConditionResult", bus_property_append_bool, "b", offsetof(Unit, condition_result) },
+ { "Conditions", bus_property_append_condition_list, "a(sbbsi)", offsetof(Unit, conditions) },
+ { "LoadError", bus_unit_append_load_error, "(ss)", 0 },
+ { "Transient", bus_property_append_bool, "b", offsetof(Unit, transient) },
+ {}
};
const BusProperty bus_unit_cgroup_properties[] = {
- { "DefaultControlGroup", bus_unit_append_default_cgroup, "s", 0 },
- { "ControlGroups", bus_unit_append_cgroups, "as", 0 },
- { "ControlGroupAttributes", bus_unit_append_cgroup_attrs, "a(sss)", 0 },
- { NULL, }
+ { "Slice", bus_unit_append_slice, "s", 0 },
+ { "ControlGroup", bus_property_append_string, "s", offsetof(Unit, cgroup_path), true },
+ {}
};
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index acd1ddbe78..3064cd552a 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -61,6 +61,10 @@
" <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"ResetFailed\"/>\n" \
+ " <method name=\"SetProperties\">\n" \
+ " <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <property name=\"Id\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Names\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Following\" type=\"s\" access=\"read\"/>\n" \
@@ -121,34 +125,14 @@
" <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"ConditionTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"ConditionResult\" type=\"b\" access=\"read\"/>\n" \
+ " <property name=\"Conditions\" type=\"a(sbbsi)\" access=\"read\"/>\n" \
" <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
+ " <property name=\"Transient\" type=\"b\" access=\"read\"/>\n" \
" </interface>\n"
#define BUS_UNIT_CGROUP_INTERFACE \
- " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
- " <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>\n" \
- " <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
- " <method name=\"SetControlGroup\">\n" \
- " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"UnsetControlGroup\">\n" \
- " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"GetControlGroupAttribute\">\n" \
- " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"SetControlGroupAttribute\">\n" \
- " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " </method>\n" \
- " <method name=\"UnsetControlGroupAttribute\">\n" \
- " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
- " </method>\n"
+ " <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"ControlGroup\" type=\"s\" access=\"read\"/>\n"
#define BUS_UNIT_INTERFACES_LIST \
BUS_GENERIC_INTERFACES_LIST \
@@ -160,19 +144,9 @@ extern const BusProperty bus_unit_cgroup_properties[];
void bus_unit_send_change_signal(Unit *u);
void bus_unit_send_removed_signal(Unit *u);
-DBusHandlerResult bus_unit_queue_job(
- DBusConnection *connection,
- DBusMessage *message,
- Unit *u,
- JobType type,
- JobMode mode,
- bool reload_if_possible);
-
-int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter);
-int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter);
-int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result);
-int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter);
-int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter);
+DBusHandlerResult bus_unit_queue_job(DBusConnection *connection, DBusMessage *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible);
+
+int bus_unit_set_properties(Unit *u, DBusMessageIter *i, UnitSetPropertiesMode mode, bool commit, DBusError *error);
extern const DBusObjectPathVTable bus_unit_vtable;
diff --git a/src/core/dbus.c b/src/core/dbus.c
index 1272c938cf..aa3d93bf06 100644
--- a/src/core/dbus.c
+++ b/src/core/dbus.c
@@ -28,7 +28,6 @@
#include "dbus.h"
#include "log.h"
#include "strv.h"
-#include "cgroup.h"
#include "mkdir.h"
#include "missing.h"
#include "dbus-unit.h"
@@ -453,7 +452,7 @@ static DBusHandlerResult system_bus_message_filter(DBusConnection *connection, D
DBUS_TYPE_INVALID))
log_error("Failed to parse Released message: %s", bus_error_message(&error));
else
- cgroup_notify_empty(m, cgroup);
+ manager_notify_cgroup_empty(m, cgroup);
}
dbus_error_free(&error);
@@ -489,7 +488,7 @@ static DBusHandlerResult private_bus_message_filter(DBusConnection *connection,
DBUS_TYPE_INVALID))
log_error("Failed to parse Released message: %s", bus_error_message(&error));
else
- cgroup_notify_empty(m, cgroup);
+ manager_notify_cgroup_empty(m, cgroup);
/* Forward the message to the system bus, so that user
* instances are notified as well */
@@ -1136,19 +1135,19 @@ int bus_init(Manager *m, bool try_bus_connect) {
if (set_ensure_allocated(&m->bus_connections, trivial_hash_func, trivial_compare_func) < 0 ||
set_ensure_allocated(&m->bus_connections_for_dispatch, trivial_hash_func, trivial_compare_func) < 0)
- goto oom;
+ return log_oom();
if (m->name_data_slot < 0)
if (!dbus_pending_call_allocate_data_slot(&m->name_data_slot))
- goto oom;
+ return log_oom();
if (m->conn_data_slot < 0)
if (!dbus_pending_call_allocate_data_slot(&m->conn_data_slot))
- goto oom;
+ return log_oom();
if (m->subscribed_data_slot < 0)
if (!dbus_connection_allocate_data_slot(&m->subscribed_data_slot))
- goto oom;
+ return log_oom();
if (try_bus_connect) {
if ((r = bus_init_system(m)) < 0 ||
@@ -1156,16 +1155,14 @@ int bus_init(Manager *m, bool try_bus_connect) {
return r;
}
- if ((r = bus_init_private(m)) < 0)
+ r = bus_init_private(m);
+ if (r < 0)
return r;
return 0;
-oom:
- return log_oom();
}
static void shutdown_connection(Manager *m, DBusConnection *c) {
- Set *s;
Job *j;
Iterator i;
@@ -1181,15 +1178,7 @@ static void shutdown_connection(Manager *m, DBusConnection *c) {
set_remove(m->bus_connections, c);
set_remove(m->bus_connections_for_dispatch, c);
-
- if ((s = BUS_CONNECTION_SUBSCRIBED(m, c))) {
- char *t;
-
- while ((t = set_steal_first(s)))
- free(t);
-
- set_free(s);
- }
+ set_free_free(BUS_CONNECTION_SUBSCRIBED(m, c));
if (m->queued_message_connection == c) {
m->queued_message_connection = NULL;
@@ -1260,10 +1249,10 @@ void bus_done(Manager *m) {
set_free(m->bus_connections_for_dispatch);
if (m->name_data_slot >= 0)
- dbus_pending_call_free_data_slot(&m->name_data_slot);
+ dbus_pending_call_free_data_slot(&m->name_data_slot);
if (m->conn_data_slot >= 0)
- dbus_pending_call_free_data_slot(&m->conn_data_slot);
+ dbus_pending_call_free_data_slot(&m->conn_data_slot);
if (m->subscribed_data_slot >= 0)
dbus_connection_free_data_slot(&m->subscribed_data_slot);
@@ -1390,6 +1379,12 @@ bool bus_has_subscriber(Manager *m) {
assert(m);
+ /* If we are reloading then we might not have deserialized the
+ subscribers yet, hence let's assume that there are some */
+
+ if (m->n_reloading > 0)
+ return true;
+
SET_FOREACH(c, m->bus_connections_for_dispatch, i)
if (bus_connection_has_subscriber(m, c))
return true;
@@ -1456,7 +1451,7 @@ void bus_broadcast_finished(
usec_t userspace_usec,
usec_t total_usec) {
- DBusMessage *message;
+ _cleanup_dbus_message_unref_ DBusMessage *message = NULL;
assert(m);
@@ -1476,16 +1471,106 @@ void bus_broadcast_finished(
DBUS_TYPE_UINT64, &total_usec,
DBUS_TYPE_INVALID)) {
log_oom();
- goto finish;
+ return;
}
if (bus_broadcast(m, message) < 0) {
log_oom();
- goto finish;
+ return;
}
+}
-finish:
- if (message)
- dbus_message_unref(message);
+void bus_broadcast_reloading(Manager *m, bool active) {
+
+ _cleanup_dbus_message_unref_ DBusMessage *message = NULL;
+ dbus_bool_t b = active;
+
+ assert(m);
+
+ message = dbus_message_new_signal("/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Reloading");
+ if (!message) {
+ log_oom();
+ return;
+ }
+
+ assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+ if (!dbus_message_append_args(message,
+ DBUS_TYPE_BOOLEAN, &b,
+ DBUS_TYPE_INVALID)) {
+ log_oom();
+ return;
+ }
+
+
+ if (bus_broadcast(m, message) < 0) {
+ log_oom();
+ return;
+ }
+}
+
+Set *bus_acquire_subscribed(Manager *m, DBusConnection *c) {
+ Set *s;
+
+ assert(m);
+ assert(c);
+
+ s = BUS_CONNECTION_SUBSCRIBED(m, c);
+ if (s)
+ return s;
+
+ s = set_new(string_hash_func, string_compare_func);
+ if (!s)
+ return NULL;
+
+ if (!dbus_connection_set_data(c, m->subscribed_data_slot, s, NULL)) {
+ set_free(s);
+ return NULL;
+ }
+
+ return s;
+}
+
+void bus_serialize(Manager *m, FILE *f) {
+ char *client;
+ Iterator i;
+ Set *s;
+
+ assert(m);
+ assert(f);
+
+ if (!m->api_bus)
+ return;
+
+ s = BUS_CONNECTION_SUBSCRIBED(m, m->api_bus);
+ SET_FOREACH(client, s, i)
+ fprintf(f, "subscribed=%s\n", client);
+}
+
+int bus_deserialize_item(Manager *m, const char *line) {
+ const char *e;
+ char *b;
+ Set *s;
+
+ assert(m);
+ assert(line);
+
+ if (!m->api_bus)
+ return 0;
+
+ e = startswith(line, "subscribed=");
+ if (!e)
+ return 0;
+
+ s = bus_acquire_subscribed(m, m->api_bus);
+ if (!s)
+ return -ENOMEM;
+
+ b = strdup(e);
+ if (!b)
+ return -ENOMEM;
+
+ set_consume(s, b);
+
+ return 1;
}
diff --git a/src/core/dbus.h b/src/core/dbus.h
index c7a058e198..6500cd7455 100644
--- a/src/core/dbus.h
+++ b/src/core/dbus.h
@@ -43,6 +43,12 @@ bool bus_connection_has_subscriber(Manager *m, DBusConnection *c);
int bus_fdset_add_all(Manager *m, FDSet *fds);
void bus_broadcast_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
+void bus_broadcast_reloading(Manager *m, bool active);
+
+Set *bus_acquire_subscribed(Manager *m, DBusConnection *c);
+
+void bus_serialize(Manager *m, FILE *f);
+int bus_deserialize_item(Manager *m, const char *line);
#define BUS_CONNECTION_SUBSCRIBED(m, c) dbus_connection_get_data((c), (m)->subscribed_data_slot)
#define BUS_PENDING_CALL_NAME(m, p) dbus_pending_call_get_data((p), (m)->name_data_slot)
diff --git a/src/core/execute.c b/src/core/execute.c
index 3959ef9623..a53ef48ef8 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -55,7 +55,6 @@
#include "sd-messages.h"
#include "ioprio.h"
#include "securebits.h"
-#include "cgroup.h"
#include "namespace.h"
#include "tcpwrap.h"
#include "exit-status.h"
@@ -67,8 +66,11 @@
#include "syscall-list.h"
#include "env-util.h"
#include "fileio.h"
+#include "unit.h"
+#include "async.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
+#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
@@ -758,24 +760,30 @@ 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. */
- if ((pam_code = pam_start(name, user, &conv, &handle)) != PAM_SUCCESS) {
+ pam_code = pam_start(name, user, &conv, &handle);
+ if (pam_code != PAM_SUCCESS) {
handle = NULL;
goto fail;
}
- if (tty)
- if ((pam_code = pam_set_item(handle, PAM_TTY, tty)) != PAM_SUCCESS)
+ if (tty) {
+ pam_code = pam_set_item(handle, PAM_TTY, tty);
+ if (pam_code != PAM_SUCCESS)
goto fail;
+ }
- if ((pam_code = pam_acct_mgmt(handle, PAM_SILENT)) != PAM_SUCCESS)
+ pam_code = pam_acct_mgmt(handle, PAM_SILENT);
+ if (pam_code != PAM_SUCCESS)
goto fail;
- if ((pam_code = pam_open_session(handle, PAM_SILENT)) != PAM_SUCCESS)
+ pam_code = pam_open_session(handle, PAM_SILENT);
+ if (pam_code != PAM_SUCCESS)
goto fail;
close_session = true;
- if ((!(e = pam_getenvlist(handle)))) {
+ e = pam_getenvlist(handle);
+ if (!e) {
pam_code = PAM_BUF_ERR;
goto fail;
}
@@ -789,7 +797,8 @@ static int setup_pam(
parent_pid = getpid();
- if ((pam_pid = fork()) < 0)
+ pam_pid = fork();
+ if (pam_pid < 0)
goto fail;
if (pam_pid == 0) {
@@ -840,9 +849,11 @@ static int setup_pam(
}
/* If our parent died we'll end the session */
- if (getppid() != parent_pid)
- if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS)
+ if (getppid() != parent_pid) {
+ pam_code = pam_close_session(handle, PAM_DATA_SILENT);
+ if (pam_code != PAM_SUCCESS)
goto child_finish;
+ }
r = 0;
@@ -977,6 +988,35 @@ static int apply_seccomp(uint32_t *syscall_filter) {
return 0;
}
+static void do_idle_pipe_dance(int idle_pipe[4]) {
+ assert(idle_pipe);
+
+ if (idle_pipe[1] >= 0)
+ close_nointr_nofail(idle_pipe[1]);
+ if (idle_pipe[2] >= 0)
+ close_nointr_nofail(idle_pipe[2]);
+
+ if (idle_pipe[0] >= 0) {
+ int r;
+
+ r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
+
+ if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
+ /* Signal systemd that we are bored and want to continue. */
+ write(idle_pipe[3], "x", 1);
+
+ /* Wait for systemd to react to the signal above. */
+ fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
+ }
+
+ close_nointr_nofail(idle_pipe[0]);
+
+ }
+
+ if (idle_pipe[3] >= 0)
+ close_nointr_nofail(idle_pipe[3]);
+}
+
int exec_spawn(ExecCommand *command,
char **argv,
ExecContext *context,
@@ -986,18 +1026,17 @@ int exec_spawn(ExecCommand *command,
bool apply_chroot,
bool apply_tty_stdin,
bool confirm_spawn,
- CGroupBonding *cgroup_bondings,
- CGroupAttribute *cgroup_attributes,
- const char *cgroup_suffix,
+ CGroupControllerMask cgroup_supported,
+ const char *cgroup_path,
const char *unit_id,
- int idle_pipe[2],
+ int idle_pipe[4],
pid_t *ret) {
+ _cleanup_strv_free_ char **files_env = NULL;
+ int socket_fd;
+ char *line;
pid_t pid;
int r;
- char *line;
- int socket_fd;
- _cleanup_strv_free_ char **files_env = NULL;
assert(command);
assert(context);
@@ -1042,17 +1081,6 @@ int exec_spawn(ExecCommand *command,
NULL);
free(line);
- r = cgroup_bonding_realize_list(cgroup_bondings);
- if (r < 0)
- return r;
-
- /* We must initialize the attributes in the parent, before we
- fork, because we really need them initialized before making
- the process a member of the group (which we do in both the
- child and the parent), and we cannot really apply them twice
- (due to 'append' style attributes) */
- cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
-
if (context->private_tmp && !context->tmp_dir && !context->var_tmp_dir) {
r = setup_tmpdirs(&context->tmp_dir, &context->var_tmp_dir);
if (r < 0)
@@ -1072,7 +1100,6 @@ int exec_spawn(ExecCommand *command,
_cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL,
**final_env = NULL, **final_argv = NULL;
unsigned n_env = 0;
- bool set_access = false;
/* child */
@@ -1096,14 +1123,8 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
- if (idle_pipe) {
- if (idle_pipe[1] >= 0)
- close_nointr_nofail(idle_pipe[1]);
- if (idle_pipe[0] >= 0) {
- fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
- close_nointr_nofail(idle_pipe[0]);
- }
- }
+ if (idle_pipe)
+ do_idle_pipe_dance(idle_pipe);
/* Close sockets very early to make sure we don't
* block init reexecution because it cannot bind its
@@ -1185,8 +1206,8 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
- if (cgroup_bondings) {
- err = cgroup_bonding_install_list(cgroup_bondings, 0, cgroup_suffix);
+ if (cgroup_path) {
+ err = cg_attach_everywhere(cgroup_supported, cgroup_path, 0);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
@@ -1269,37 +1290,24 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
}
+ }
- if (cgroup_bondings && context->control_group_modify) {
- err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid);
- if (err >= 0)
- err = cgroup_bonding_set_task_access_list(
- cgroup_bondings,
- 0644,
- uid,
- gid,
- context->control_group_persistent);
- if (err < 0) {
- r = EXIT_CGROUP;
- goto fail_child;
- }
-
- set_access = true;
+#ifdef HAVE_PAM
+ if (cgroup_path && context->user && context->pam_name) {
+ err = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, 0644, uid, gid);
+ if (err < 0) {
+ r = EXIT_CGROUP;
+ goto fail_child;
}
- }
- if (cgroup_bondings && !set_access && context->control_group_persistent >= 0) {
- err = cgroup_bonding_set_task_access_list(
- cgroup_bondings,
- (mode_t) -1,
- (uid_t) -1,
- (uid_t) -1,
- context->control_group_persistent);
+
+ err = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, 0755, uid, gid);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
}
}
+#endif
if (apply_permissions) {
err = enforce_groups(context, username, gid);
@@ -1562,7 +1570,8 @@ int exec_spawn(ExecCommand *command,
* outside of the cgroup) and in the parent (so that we can be
* sure that when we kill the cgroup the process will be
* killed too). */
- cgroup_bonding_install_list(cgroup_bondings, pid, cgroup_suffix);
+ if (cgroup_path)
+ cg_attach(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, pid);
exec_status_start(&command->exec_status, pid);
@@ -1578,11 +1587,32 @@ void exec_context_init(ExecContext *c) {
c->cpu_sched_policy = SCHED_OTHER;
c->syslog_priority = LOG_DAEMON|LOG_INFO;
c->syslog_level_prefix = true;
- c->control_group_persistent = -1;
c->ignore_sigpipe = true;
c->timer_slack_nsec = (nsec_t) -1;
}
+static void *remove_tmpdir_thread(void *p) {
+ int r;
+ _cleanup_free_ char *dirp = p;
+ char *dir;
+
+ assert(dirp);
+
+ r = rm_rf_dangerous(dirp, false, true, false);
+ dir = dirname(dirp);
+ if (r < 0)
+ log_warning("Failed to remove content of temporary directory %s: %s",
+ dir, strerror(-r));
+ else {
+ r = rmdir(dir);
+ if (r < 0)
+ log_warning("Failed to remove temporary directory %s: %s",
+ dir, strerror(-r));
+ }
+
+ return NULL;
+}
+
void exec_context_tmp_dirs_done(ExecContext *c) {
char* dirs[] = {c->tmp_dir ? c->tmp_dir : c->var_tmp_dir,
c->tmp_dir ? c->var_tmp_dir : NULL,
@@ -1590,22 +1620,8 @@ void exec_context_tmp_dirs_done(ExecContext *c) {
char **dirp;
for(dirp = dirs; *dirp; dirp++) {
- char *dir;
- int r;
-
- r = rm_rf_dangerous(*dirp, false, true, false);
- dir = dirname(*dirp);
- if (r < 0)
- log_warning("Failed to remove content of temporary directory %s: %s",
- dir, strerror(-r));
- else {
- r = rmdir(dir);
- if (r < 0)
- log_warning("Failed to remove temporary directory %s: %s",
- dir, strerror(-r));
- }
-
- free(*dirp);
+ log_debug("Spawning thread to nuke %s", *dirp);
+ asynchronous_job(remove_tmpdir_thread, *dirp);
}
c->tmp_dir = c->var_tmp_dir = NULL;
@@ -1770,10 +1786,10 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
strv_free(r);
return k;
- }
+ }
/* Log invalid environment variables with filename */
- if (p)
- p = strv_env_clean_log(p, pglob.gl_pathv[n]);
+ if (p)
+ p = strv_env_clean_log(p, pglob.gl_pathv[n]);
if (r == NULL)
r = p;
@@ -1837,14 +1853,13 @@ static void strv_fprintf(FILE *f, char **l) {
}
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
- char ** e;
+ char **e;
unsigned i;
assert(c);
assert(f);
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
fprintf(f,
"%sUMask: %04o\n"
@@ -1852,8 +1867,6 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sRootDirectory: %s\n"
"%sNonBlocking: %s\n"
"%sPrivateTmp: %s\n"
- "%sControlGroupModify: %s\n"
- "%sControlGroupPersistent: %s\n"
"%sPrivateNetwork: %s\n"
"%sIgnoreSIGPIPE: %s\n",
prefix, c->umask,
@@ -1861,8 +1874,6 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, c->root_directory ? c->root_directory : "/",
prefix, yes_no(c->non_blocking),
prefix, yes_no(c->private_tmp),
- prefix, yes_no(c->control_group_modify),
- prefix, yes_no(c->control_group_persistent),
prefix, yes_no(c->private_network),
prefix, yes_no(c->ignore_sigpipe));
diff --git a/src/core/execute.h b/src/core/execute.h
index 15574dc97e..c1e9717dc8 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -33,14 +33,11 @@ typedef struct ExecContext ExecContext;
#include <stdio.h>
#include <sched.h>
-struct CGroupBonding;
-struct CGroupAttribute;
-
-typedef struct Unit Unit;
-
#include "list.h"
#include "util.h"
+typedef struct Unit Unit;
+
typedef enum ExecInput {
EXEC_INPUT_NULL,
EXEC_INPUT_TTY,
@@ -148,9 +145,6 @@ struct ExecContext {
bool no_new_privileges;
- bool control_group_modify;
- int control_group_persistent;
-
/* This is not exposed to the user but available
* internally. We need it to make sure that whenever we spawn
* /bin/mount it is run in the same process group as us so
@@ -166,6 +160,8 @@ struct ExecContext {
bool cpu_sched_set:1;
};
+#include "cgroup.h"
+
int exec_spawn(ExecCommand *command,
char **argv,
ExecContext *context,
@@ -175,9 +171,8 @@ int exec_spawn(ExecCommand *command,
bool apply_chroot,
bool apply_tty_stdin,
bool confirm_spawn,
- struct CGroupBonding *cgroup_bondings,
- struct CGroupAttribute *cgroup_attributes,
- const char *cgroup_suffix,
+ CGroupControllerMask cgroup_mask,
+ const char *cgroup_path,
const char *unit_id,
int pipe_fd[2],
pid_t *ret);
diff --git a/src/core/job.c b/src/core/job.c
index d304a16d06..bf1d956908 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -35,7 +35,7 @@
#include "log.h"
#include "dbus-job.h"
#include "special.h"
-#include "sync.h"
+#include "async.h"
#include "virt.h"
JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name) {
@@ -1088,10 +1088,13 @@ void job_shutdown_magic(Job *j) {
* asynchronous sync() would cause their exit to be
* delayed. */
- if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
+ if (j->type != JOB_START)
return;
- if (j->type != JOB_START)
+ if (j->unit->manager->running_as != SYSTEMD_SYSTEM)
+ return;
+
+ if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
return;
if (detect_container(NULL) > 0)
diff --git a/src/core/kill.c b/src/core/kill.c
index 0775653f73..ea947c23ae 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -29,6 +29,7 @@ void kill_context_init(KillContext *c) {
c->kill_signal = SIGTERM;
c->send_sigkill = true;
+ c->send_sighup = false;
}
void kill_context_dump(KillContext *c, FILE *f, const char *prefix) {
@@ -40,10 +41,12 @@ void kill_context_dump(KillContext *c, FILE *f, const char *prefix) {
fprintf(f,
"%sKillMode: %s\n"
"%sKillSignal: SIG%s\n"
- "%sSendSIGKILL: %s\n",
+ "%sSendSIGKILL: %s\n"
+ "%sSendSIGHUP: %s\n",
prefix, kill_mode_to_string(c->kill_mode),
prefix, signal_to_string(c->kill_signal),
- prefix, yes_no(c->send_sigkill));
+ prefix, yes_no(c->send_sigkill),
+ prefix, yes_no(c->send_sighup));
}
static const char* const kill_mode_table[_KILL_MODE_MAX] = {
diff --git a/src/core/kill.h b/src/core/kill.h
index 71a0513e84..41773f07ae 100644
--- a/src/core/kill.h
+++ b/src/core/kill.h
@@ -41,6 +41,7 @@ struct KillContext {
KillMode kill_mode;
int kill_signal;
bool send_sigkill;
+ bool send_sighup;
};
typedef enum KillWho {
diff --git a/src/core/killall.c b/src/core/killall.c
index a0f57455fb..e395050107 100644
--- a/src/core/killall.c
+++ b/src/core/killall.c
@@ -33,7 +33,7 @@
static bool ignore_proc(pid_t pid) {
_cleanup_fclose_ FILE *f = NULL;
- char c;
+ char c, *p;
size_t count;
uid_t uid;
int r;
@@ -50,7 +50,8 @@ static bool ignore_proc(pid_t pid) {
if (uid != 0)
return false;
- f = fopen(procfs_file_alloca(pid, "cmdline"), "re");
+ p = procfs_file_alloca(pid, "cmdline");
+ f = fopen(p, "re");
if (!f)
return true; /* not really, but has the desired effect */
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 4e1454ee6c..31fb7bcd3f 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -66,16 +66,6 @@ $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.ControlGroup, config_parse_unit_cgroup, 0, 0
-$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0
-$1.CPUShares, config_parse_unit_cgroup_attr_pretty, 0, 0
-$1.MemoryLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
-$1.MemorySoftLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
-$1.DeviceAllow, config_parse_unit_cgroup_attr_pretty, 0, 0
-$1.DeviceDeny, config_parse_unit_cgroup_attr_pretty, 0, 0
-$1.BlockIOWeight, config_parse_unit_cgroup_attr_pretty, 0, 0
-$1.BlockIOReadBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
-$1.BlockIOWriteBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
@@ -85,15 +75,28 @@ $1.MountFlags, config_parse_exec_mount_flags, 0,
$1.TCPWrapName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.tcpwrap_name)
$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name)
$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.ControlGroupModify, config_parse_bool, 0, offsetof($1, exec_context.control_group_modify)
-$1.ControlGroupPersistent, config_parse_tristate, 0, offsetof($1, exec_context.control_group_persistent)'
+$1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id)'
)m4_dnl
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)'
)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.CPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context)
+$1.MemoryAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.memory_accounting)
+$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.BlockIOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.blockio_accounting)
+$1.BlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context)
+$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)'
+)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)
@@ -113,7 +116,7 @@ Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAG
Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
-Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, offsetof(Unit, requires_mounts_for)
+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)
Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop)
@@ -172,6 +175,7 @@ Service.NotifyAccess, config_parse_notify_access, 0,
Service.Sockets, config_parse_service_sockets, 0, 0
Service.FsckPassNo, config_parse_fsck_passno, 0, offsetof(Service, fsck_passno)
EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
+CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
m4_dnl
Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0
@@ -214,6 +218,7 @@ Socket.SmackLabel, config_parse_string, 0,
Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in)
Socket.SmackLabelIPOut, config_parse_string, 0, offsetof(Socket, smack_ip_out)
EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
+CGROUP_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
m4_dnl
Mount.What, config_parse_string, 0, offsetof(Mount, parameters_fragment.what)
@@ -224,6 +229,7 @@ Mount.FsckPassNo, config_parse_fsck_passno, 0,
Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec)
Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode)
EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
+CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
m4_dnl
Automount.Where, config_parse_path, 0, offsetof(Automount, where)
@@ -233,6 +239,7 @@ Swap.What, config_parse_path, 0,
Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority)
Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec)
EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
+CGROUP_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
m4_dnl
Timer.OnCalendar, config_parse_timer, 0, 0
@@ -251,6 +258,12 @@ Path.DirectoryNotEmpty, config_parse_path_spec, 0,
Path.Unit, config_parse_trigger_unit, 0, 0
Path.MakeDirectory, config_parse_bool, 0, offsetof(Path, make_directory)
Path.DirectoryMode, config_parse_mode, 0, offsetof(Path, directory_mode)
+m4_dnl
+CGROUP_CONTEXT_CONFIG_ITEMS(Slice)m4_dnl
+m4_dnl
+CGROUP_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl
+KILL_CONTEXT_CONFIG_ITEMS(Scope)m4_dnl
+Scope.TimeoutStopSec, config_parse_sec, 0, offsetof(Scope, timeout_stop_usec)
m4_dnl The [Install] section is ignored here.
Install.Alias, NULL, 0, 0
Install.WantedBy, NULL, 0, 0
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e2015ed58f..44920d6449 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -51,6 +51,7 @@
#include "path-util.h"
#include "syscall-list.h"
#include "env-util.h"
+#include "cgroup.h"
#ifndef HAVE_SYSV_COMPAT
int config_parse_warn_compat(const char *unit,
@@ -98,9 +99,12 @@ int config_parse_unit_deps(const char* unit,
if (!t)
return log_oom();
- k = unit_name_printf(u, t);
- if (!k)
- return log_oom();
+ r = unit_name_printf(u, t, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to resolve specifiers, ignoring: %s", strerror(-r));
+ continue;
+ }
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0)
@@ -123,16 +127,17 @@ int config_parse_unit_string_printf(const char *unit,
Unit *u = userdata;
_cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(u);
- k = unit_full_printf(u, rvalue);
- if (!k)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ 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));
return config_parse_string(unit, filename, line, section, lvalue, ltype,
k ? k : rvalue, data, userdata);
@@ -150,16 +155,17 @@ int config_parse_unit_strv_printf(const char *unit,
Unit *u = userdata;
_cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(u);
- k = unit_full_printf(u, rvalue);
- if (!k)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ 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));
return config_parse_strv(unit, filename, line, section, lvalue, ltype,
k ? k : rvalue, data, userdata);
@@ -177,16 +183,17 @@ int config_parse_unit_path_printf(const char *unit,
Unit *u = userdata;
_cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(u);
- k = unit_full_printf(u, rvalue);
- if (!k)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ 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));
return config_parse_path(unit, filename, line, section, lvalue, ltype,
k ? k : rvalue, data, userdata);
@@ -204,6 +211,7 @@ int config_parse_socket_listen(const char *unit,
SocketPort *p, *tail;
Socket *s;
+ int r;
assert(filename);
assert(lvalue);
@@ -225,32 +233,31 @@ int config_parse_socket_listen(const char *unit,
if (ltype != SOCKET_SOCKET) {
p->type = ltype;
- p->path = unit_full_printf(UNIT(s), rvalue);
- if (!p->path) {
+ r = unit_full_printf(UNIT(s), rvalue, &p->path);
+ if (r < 0) {
p->path = strdup(rvalue);
if (!p->path) {
free(p);
return log_oom();
} else
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
}
path_kill_slashes(p->path);
} else if (streq(lvalue, "ListenNetlink")) {
_cleanup_free_ char *k = NULL;
- int r;
p->type = SOCKET_SOCKET;
- k = unit_full_printf(UNIT(s), rvalue);
- if (!k)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ 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));
r = socket_address_parse_netlink(&p->address, k ? k : rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to parse address value, ignoring: %s", rvalue);
free(p);
return 0;
@@ -258,17 +265,16 @@ int config_parse_socket_listen(const char *unit,
} else {
_cleanup_free_ char *k = NULL;
- int r;
p->type = SOCKET_SOCKET;
- k = unit_full_printf(UNIT(s), rvalue);
- if (!k)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+ 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));
r = socket_address_parse(&p->address, k ? k : rvalue);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to parse address value, ignoring: %s", rvalue);
free(p);
return 0;
@@ -996,58 +1002,6 @@ int config_parse_limit(const char *unit,
return 0;
}
-int config_parse_unit_cgroup(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = userdata;
- char *w;
- size_t l;
- char *state;
-
- if (isempty(rvalue)) {
- /* An empty assignment resets the list */
- cgroup_bonding_free_list(u->cgroup_bondings, false);
- u->cgroup_bondings = NULL;
- return 0;
- }
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- _cleanup_free_ char *t = NULL, *k = NULL, *ku = NULL;
- int r;
-
- t = strndup(w, l);
- if (!t)
- return log_oom();
-
- k = unit_full_printf(u, t);
- if (!k)
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to resolve unit specifiers on %s. Ignoring.",
- t);
-
- ku = cunescape(k ? k : t);
- if (!ku)
- return log_oom();
-
- r = unit_add_cgroup_from_text(u, ku, true, NULL);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse cgroup value %s, ignoring: %s",
- k, rvalue);
- return 0;
- }
- }
-
- return 0;
-}
-
#ifdef HAVE_SYSV_COMPAT
int config_parse_sysv_priority(const char *unit,
const char *filename,
@@ -1281,11 +1235,12 @@ int config_parse_trigger_unit(
return 0;
}
- p = unit_name_printf(u, rvalue);
- if (!p)
- return log_oom();
+ 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));
- type = unit_name_to_type(p);
+ type = unit_name_to_type(p ?: rvalue);
if (type < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Unit type not valid, ignoring: %s", rvalue);
@@ -1298,10 +1253,10 @@ int config_parse_trigger_unit(
return 0;
}
- r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
+ r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p ?: rvalue, NULL, true);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add trigger on %s, ignoring: %s", p, strerror(-r));
+ "Failed to add trigger on %s, ignoring: %s", p ?: rvalue, strerror(-r));
return 0;
}
@@ -1322,6 +1277,7 @@ int config_parse_path_spec(const char *unit,
PathSpec *s;
PathType b;
_cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1341,13 +1297,13 @@ int config_parse_path_spec(const char *unit,
return 0;
}
- k = unit_full_printf(UNIT(p), rvalue);
- if (!k) {
+ 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, EINVAL,
+ log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to resolve unit specifiers on %s. Ignoring.",
rvalue);
}
@@ -1395,19 +1351,20 @@ int config_parse_socket_service(const char *unit,
dbus_error_init(&error);
- p = unit_name_printf(UNIT(s), rvalue);
- if (!p)
- return log_oom();
+ 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);
- if (!endswith(p, ".service")) {
+ if (!endswith(p ?: rvalue, ".service")) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
- r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
+ r = manager_load_unit(UNIT(s)->manager, p ?: rvalue, NULL, &error, &x);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to load unit %s, ignoring: %s",
rvalue, bus_error(&error, r));
dbus_error_free(&error);
@@ -1446,23 +1403,24 @@ int config_parse_service_sockets(const char *unit,
if (!t)
return log_oom();
- k = unit_name_printf(UNIT(s), t);
- if (!k)
- return log_oom();
+ r = unit_name_printf(UNIT(s), t, &k);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to resolve specifiers, ignoring: %s", strerror(-r));
- if (!endswith(k, ".socket")) {
+ if (!endswith(k ?: t, ".socket")) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Unit must be of type socket, ignoring: %s", k);
+ "Unit must be of type socket, ignoring: %s", k ?: t);
continue;
}
- r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
+ r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k ?: t, NULL, true);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, -r,
"Failed to add dependency on %s, ignoring: %s",
- k, strerror(-r));
+ k ?: t, strerror(-r));
- r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
+ r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k ?: t, NULL, true);
if (r < 0)
return r;
}
@@ -1514,7 +1472,8 @@ int config_parse_unit_env_file(const char *unit,
char ***env = data;
Unit *u = userdata;
- _cleanup_free_ char *s = NULL;
+ _cleanup_free_ char *n = NULL;
+ const char *s;
int r;
assert(filename);
@@ -1529,10 +1488,12 @@ int config_parse_unit_env_file(const char *unit,
return 0;
}
- s = unit_full_printf(u, rvalue);
- if (!s)
- return log_oom();
+ 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);
+ 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);
@@ -1560,11 +1521,12 @@ int config_parse_environ(const char *unit,
char*** env = data, *w, *state;
size_t l;
_cleanup_free_ char *k = NULL;
+ int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- assert(u);
+ assert(data);
if (isempty(rvalue)) {
/* Empty assignment resets the list */
@@ -1573,7 +1535,15 @@ int config_parse_environ(const char *unit,
return 0;
}
- k = unit_full_printf(u, rvalue);
+ 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 (!k)
+ k = strdup(rvalue);
if (!k)
return log_oom();
@@ -1645,6 +1615,7 @@ int config_parse_unit_condition_path(const char *unit,
bool trigger, negate;
Condition *c;
_cleanup_free_ char *p = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1666,9 +1637,15 @@ int config_parse_unit_condition_path(const char *unit,
if (negate)
rvalue++;
- p = unit_full_printf(u, rvalue);
- if (!p)
- return log_oom();
+ 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);
+ if (!p) {
+ p = strdup(rvalue);
+ if (!p)
+ return log_oom();
+ }
if (!path_is_absolute(p)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
@@ -1699,6 +1676,7 @@ int config_parse_unit_condition_string(const char *unit,
bool trigger, negate;
Condition *c;
_cleanup_free_ char *s = NULL;
+ int r;
assert(filename);
assert(lvalue);
@@ -1720,9 +1698,15 @@ int config_parse_unit_condition_string(const char *unit,
if (negate)
rvalue++;
- s = unit_full_printf(u, rvalue);
- if (!s)
- return log_oom();
+ 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);
+ if (!s) {
+ s = strdup(rvalue);
+ if (!s)
+ return log_oom();
+ }
c = condition_new(cond, s, trigger, negate);
if (!c)
@@ -1789,139 +1773,52 @@ int config_parse_unit_condition_null(const char *unit,
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_start_limit_action, start_limit_action, StartLimitAction, "Failed to parse start limit action specifier");
-int config_parse_unit_cgroup_attr(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_unit_requires_mounts_for(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- Unit *u = data;
- size_t a, b;
- _cleanup_free_ char *n = NULL, *v = NULL;
- const CGroupSemantics *s;
- int r;
+ Unit *u = userdata;
+ char *state;
+ size_t l;
+ char *w;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- if (isempty(rvalue)) {
- /* Empty assignment clears the list */
- cgroup_attribute_free_list(u->cgroup_attributes);
- u->cgroup_attributes = NULL;
- return 0;
- }
-
- a = strcspn(rvalue, WHITESPACE);
- b = strspn(rvalue + a, WHITESPACE);
- if (a <= 0 || b <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, EINVAL,
- "Failed to parse cgroup attribute value, ignoring: %s",
- rvalue);
- return 0;
- }
-
- n = strndup(rvalue, a);
- if (!n)
- return log_oom();
-
- r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse cgroup attribute value, ignoring: %s",
- rvalue);
- return 0;
- }
-
- r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add cgroup attribute value, ignoring: %s", rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_cgroup_attr_pretty(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = data;
- _cleanup_free_ char *v = NULL;
- const CGroupSemantics *s;
- int r;
+ FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+ int r;
+ _cleanup_free_ char *n;
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
+ n = strndup(w, l);
+ if (!n)
+ return log_oom();
- r = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to parse cgroup attribute value, ignoring: %s",
- rvalue);
- return 0;
- } else if (r == 0) {
- log_syntax(unit, LOG_ERR, filename, line, ENOTSUP,
- "Unknown or unsupported cgroup attribute %s, ignoring: %s",
- lvalue, rvalue);
- return 0;
- }
+ if (!utf8_is_valid(n)) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Path is not UTF-8 clean, ignoring assignment: %s", rvalue);
+ continue;
+ }
- r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -r,
- "Failed to add cgroup attribute value, ignoring: %s", rvalue);
- return 0;
+ 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);
+ continue;
+ }
}
return 0;
}
-int config_parse_unit_requires_mounts_for(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Unit *u = userdata;
- int r;
- bool empty_before;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- empty_before = !u->requires_mounts_for;
-
- r = config_parse_path_strv(unit, filename, line, section, lvalue, ltype,
- rvalue, data, userdata);
-
- /* Make it easy to find units with requires_mounts set */
- if (empty_before && u->requires_mounts_for)
- LIST_PREPEND(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
-
- return r;
-}
-
int config_parse_documentation(const char *unit,
const char *filename,
unsigned line,
@@ -2058,6 +1955,365 @@ int config_parse_syscall_filter(const char *unit,
return 0;
}
+int config_parse_unit_slice(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *k = NULL;
+ Unit *u = userdata, *slice;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ 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();
+ }
+
+ 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);
+ return 0;
+ }
+
+ if (slice->type != UNIT_SLICE) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Slice unit %s is not a slice. Ignoring.", k);
+ 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(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ CGroupContext *c = data;
+ unsigned long lu;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ c->cpu_shares = 1024;
+ 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;
+ }
+
+ c->cpu_shares = lu;
+ return 0;
+}
+
+int config_parse_memory_limit(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ CGroupContext *c = data;
+ off_t bytes;
+ int r;
+
+ if (isempty(rvalue)) {
+ c->memory_limit = (uint64_t) -1;
+ return 0;
+ }
+
+ assert_cc(sizeof(uint64_t) == sizeof(off_t));
+
+ r = parse_bytes(rvalue, &bytes);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Memory limit '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+
+ c->memory_limit = (uint64_t) bytes;
+ return 0;
+}
+
+int config_parse_device_allow(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *path = NULL;
+ CGroupContext *c = data;
+ CGroupDeviceAllow *a;
+ const char *m;
+ size_t n;
+
+ if (isempty(rvalue)) {
+ while (c->device_allow)
+ cgroup_context_free_device_allow(c, c->device_allow);
+
+ return 0;
+ }
+
+ n = strcspn(rvalue, WHITESPACE);
+ path = strndup(rvalue, n);
+ if (!path)
+ return log_oom();
+
+ if (!path_startswith(path, "/dev")) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Invalid device node path '%s'. Ignoring.", path);
+ return 0;
+ }
+
+ m = rvalue + n + strspn(rvalue + 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);
+ return 0;
+ }
+
+ a = new0(CGroupDeviceAllow, 1);
+ if (!a)
+ return log_oom();
+
+ a->path = path;
+ path = NULL;
+ a->r = !!strchr(m, 'r');
+ a->w = !!strchr(m, 'w');
+ a->m = !!strchr(m, 'm');
+
+ LIST_PREPEND(CGroupDeviceAllow, device_allow, c->device_allow, a);
+ return 0;
+}
+
+int config_parse_blockio_weight(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ CGroupContext *c = data;
+ unsigned long lu;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ c->blockio_weight = 1000;
+ 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);
+ return 0;
+ }
+
+ c->blockio_weight = lu;
+
+ return 0;
+}
+
+int config_parse_blockio_device_weight(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *path = NULL;
+ CGroupBlockIODeviceWeight *w;
+ CGroupContext *c = data;
+ unsigned long lu;
+ const char *weight;
+ size_t n;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ 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);
+ weight = rvalue + n;
+ if (!*weight) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Expected block device and device weight. Ignoring.");
+ return 0;
+ }
+
+ path = strndup(rvalue, n);
+ if (!path)
+ return log_oom();
+
+ if (!path_startswith(path, "/dev")) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "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);
+ return 0;
+ }
+
+
+ w = new0(CGroupBlockIODeviceWeight, 1);
+ if (!w)
+ return log_oom();
+
+ w->path = path;
+ path = NULL;
+
+ w->weight = lu;
+
+ LIST_PREPEND(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
+ return 0;
+}
+
+int config_parse_blockio_bandwidth(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *path = NULL;
+ CGroupBlockIODeviceBandwidth *b;
+ CGroupContext *c = data;
+ const char *bandwidth;
+ off_t bytes;
+ bool read;
+ size_t n;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ read = streq("BlockIOReadBandwidth", lvalue);
+
+ if (isempty(rvalue)) {
+ CGroupBlockIODeviceBandwidth *next;
+
+ LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths)
+ if (b->read == read)
+ cgroup_context_free_blockio_device_bandwidth(c, b);
+
+ return 0;
+ }
+
+ n = strcspn(rvalue, WHITESPACE);
+ bandwidth = rvalue + n;
+ bandwidth += strspn(bandwidth, WHITESPACE);
+
+ if (!*bandwidth) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "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, EINVAL,
+ "Invalid device node path '%s'. Ignoring.", path);
+ return 0;
+ }
+
+ r = parse_bytes(bandwidth, &bytes);
+ if (r < 0 || bytes <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Block IO Bandwidth '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+
+ b = new0(CGroupBlockIODeviceBandwidth, 1);
+ if (!b)
+ return log_oom();
+
+ b->path = path;
+ path = NULL;
+ b->bandwidth = (uint64_t) bytes;
+ b->read = read;
+
+ LIST_PREPEND(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
+
+ return 0;
+}
+
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
@@ -2267,14 +2523,14 @@ static int load_from_path(Unit *u, const char *path) {
if (null_or_empty(&st))
u->load_state = UNIT_MASKED;
else {
+ u->load_state = UNIT_LOADED;
+
/* Now, parse the file contents */
r = config_parse(u->id, filename, f, UNIT_VTABLE(u)->sections,
config_item_perf_lookup,
(void*) load_fragment_gperf_lookup, false, true, u);
if (r < 0)
goto finish;
-
- u->load_state = UNIT_LOADED;
}
free(u->fragment_path);
@@ -2417,7 +2673,6 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_exec_secure_bits, "SECUREBITS" },
{ config_parse_bounding_set, "BOUNDINGSET" },
{ config_parse_limit, "LIMIT" },
- { config_parse_unit_cgroup, "CGROUP [...]" },
{ config_parse_unit_deps, "UNIT [...]" },
{ config_parse_exec, "PATH [ARGUMENT [...]]" },
{ config_parse_service_type, "SERVICETYPE" },
@@ -2446,6 +2701,24 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_unit_condition_path, "CONDITION" },
{ config_parse_unit_condition_string, "CONDITION" },
{ config_parse_unit_condition_null, "CONDITION" },
+ { config_parse_unit_slice, "SLICE" },
+ { config_parse_documentation, "URL" },
+ { config_parse_service_timeout, "SECONDS" },
+ { config_parse_start_limit_action, "ACTION" },
+ { config_parse_set_status, "STATUS" },
+ { config_parse_service_sockets, "SOCKETS" },
+ { config_parse_fsck_passno, "PASSNO" },
+ { config_parse_environ, "ENVIRON" },
+ { config_parse_syscall_filter, "SYSCALL" },
+ { config_parse_cpu_shares, "SHARES" },
+ { config_parse_memory_limit, "LIMIT" },
+ { config_parse_device_allow, "DEVICE" },
+ { config_parse_device_policy, "POLICY" },
+ { config_parse_blockio_bandwidth, "BANDWIDTH" },
+ { config_parse_blockio_weight, "WEIGHT" },
+ { config_parse_blockio_device_weight, "DEVICEWEIGHT" },
+ { config_parse_long, "LONG" },
+ { config_parse_socket_service, "SERVICE" },
};
const char *prev = NULL;
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index ff7f22a6f0..90e5e3a5c9 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -55,7 +55,6 @@ int config_parse_exec_capabilities(const char *unit, const char *filename, unsig
int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_bounding_set(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_limit(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_sysv_priority(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_fsck_passno(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_kill_signal(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -73,11 +72,17 @@ int config_parse_unit_condition_null(const char *unit, const char *filename, uns
int config_parse_kill_mode(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_notify_access(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_start_limit_action(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup_attr(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cgroup_attr_pretty(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_requires_mounts_for(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_syscall_filter(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_device_policy(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_device_allow(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_blockio_device_weight(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c
index d7113b9795..276deb9dc1 100644
--- a/src/core/locale-setup.c
+++ b/src/core/locale-setup.c
@@ -28,6 +28,8 @@
#include "macro.h"
#include "virt.h"
#include "fileio.h"
+#include "strv.h"
+#include "env-util.h"
enum {
/* We don't list LC_ALL here on purpose. People should be
@@ -67,7 +69,8 @@ static const char * const variable_names[_VARIABLE_MAX] = {
[VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION"
};
-int locale_setup(void) {
+int locale_setup(char ***environment) {
+ char **add;
char *variables[_VARIABLE_MAX] = {};
int r = 0, i;
@@ -117,27 +120,44 @@ int locale_setup(void) {
log_warning("Failed to read /etc/locale.conf: %s", strerror(-r));
}
- if (!variables[VARIABLE_LANG]) {
- variables[VARIABLE_LANG] = strdup("C");
- if (!variables[VARIABLE_LANG]) {
+ add = NULL;
+ for (i = 0; i < _VARIABLE_MAX; i++) {
+ char *s;
+
+ if (!variables[i])
+ continue;
+
+ s = strjoin(variable_names[i], "=", variables[i], NULL);
+ if (!s) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (strv_push(&add, s) < 0) {
+ free(s);
r = -ENOMEM;
goto finish;
}
}
- for (i = 0; i < _VARIABLE_MAX; i++) {
- if (variables[i]) {
- if (setenv(variable_names[i], variables[i], 1) < 0) {
- r = -errno;
- goto finish;
- }
- } else
- unsetenv(variable_names[i]);
+ if (!strv_isempty(add)) {
+ char **e;
+
+ e = strv_env_merge(2, *environment, add);
+ if (!e) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ strv_free(*environment);
+ *environment = e;
}
r = 0;
finish:
+ strv_free(add);
+
for (i = 0; i < _VARIABLE_MAX; i++)
free(variables[i]);
diff --git a/src/core/locale-setup.h b/src/core/locale-setup.h
index 5a0f2f7888..62c654c37c 100644
--- a/src/core/locale-setup.h
+++ b/src/core/locale-setup.h
@@ -21,4 +21,4 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int locale_setup(void);
+int locale_setup(char ***environment);
diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in
index f77082c2db..89b48259ad 100644
--- a/src/core/macros.systemd.in
+++ b/src/core/macros.systemd.in
@@ -20,6 +20,7 @@
# RPM macros for packages installing systemd unit files
%_unitdir @systemunitdir@
+%_userunitdir @userunitdir@
%_presetdir @systempresetdir@
%_udevhwdbdir @udevhwdbdir@
%_udevrulesdir @udevrulesdir@
@@ -71,3 +72,7 @@ fi \
%journal_catalog_update() \
@rootbindir@/journalctl --update-catalog >/dev/null 2>&1 || : \
%{nil}
+
+%tmpfiles_create() \
+@rootbindir@/systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || : \
+%{nil}
diff --git a/src/core/main.c b/src/core/main.c
index 7fc06bea05..fe291f8410 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -64,12 +64,10 @@
#endif
#include "hostname-setup.h"
#include "machine-id-setup.h"
-#include "locale-setup.h"
#include "selinux-setup.h"
#include "ima-setup.h"
#include "fileio.h"
#include "smack-setup.h"
-#include "efivars.h"
static enum {
ACTION_RUN,
@@ -89,12 +87,12 @@ static int arg_crash_chvt = -1;
static bool arg_confirm_spawn = false;
static bool arg_show_status = true;
static bool arg_switched_root = false;
-static char **arg_default_controllers = NULL;
static char ***arg_join_controllers = NULL;
static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
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_NLIMITS] = {};
static uint64_t arg_capability_bounding_set_drop = 0;
static nsec_t arg_timer_slack_nsec = (nsec_t) -1;
@@ -106,7 +104,10 @@ static void nop_handler(int sig) {
_noreturn_ static void crash(int sig) {
- if (!arg_dump_core)
+ if (getpid() != 1)
+ /* Pass this on immediately, if this is not PID 1 */
+ raise(sig);
+ else if (!arg_dump_core)
log_error("Caught <%s>, not dumping core.", signal_to_string(sig));
else {
struct sigaction sa = {
@@ -116,7 +117,7 @@ _noreturn_ static void crash(int sig) {
pid_t pid;
/* We want to wait for the core process, hence let's enable SIGCHLD */
- assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
+ sigaction(SIGCHLD, &sa, NULL);
pid = fork();
if (pid < 0)
@@ -128,7 +129,7 @@ _noreturn_ static void crash(int sig) {
/* Enable default signal handler for core dump */
zero(sa);
sa.sa_handler = SIG_DFL;
- assert_se(sigaction(sig, &sa, NULL) == 0);
+ sigaction(sig, &sa, NULL);
/* Don't limit the core dump size */
rl.rlim_cur = RLIM_INFINITY;
@@ -136,7 +137,7 @@ _noreturn_ static void crash(int sig) {
setrlimit(RLIMIT_CORE, &rl);
/* Just to be sure... */
- assert_se(chdir("/") == 0);
+ chdir("/");
/* Raise the signal again */
raise(sig);
@@ -347,32 +348,21 @@ static int parse_proc_cmdline_word(const char *word) {
arg_default_std_error = r;
} else if (startswith(word, "systemd.setenv=")) {
_cleanup_free_ char *cenv = NULL;
- char *eq;
- int r;
cenv = strdup(word + 15);
if (!cenv)
return -ENOMEM;
- eq = strchr(cenv, '=');
- if (!eq) {
- if (!env_name_is_valid(cenv))
- log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv);
- else {
- r = unsetenv(cenv);
- if (r < 0)
- log_warning("Unsetting environment variable '%s' failed, ignoring: %m", cenv);
- }
- } else {
- if (!env_assignment_is_valid(cenv))
- log_warning("Environment variable assignment '%s' is not valid. Ignoring.", cenv);
- else {
- *eq = 0;
- r = setenv(cenv, eq + 1, 1);
- if (r < 0)
- log_warning("Setting environment variable '%s=%s' failed, ignoring: %m", cenv, eq + 1);
- }
- }
+ if (env_assignment_is_valid(cenv)) {
+ char **env;
+
+ env = strv_env_set(arg_default_environment, cenv);
+ if (env)
+ arg_default_environment = env;
+ else
+ log_warning("Setting environment variable '%s' failed, ignoring: %m", cenv);
+ } else
+ log_warning("Environment variable name '%s' is not valid. Ignoring.", cenv);
} else if (startswith(word, "systemd.") ||
(in_initrd() && startswith(word, "rd.systemd."))) {
@@ -411,7 +401,14 @@ static int parse_proc_cmdline_word(const char *word) {
} else if (streq(word, "quiet"))
arg_show_status = false;
- else if (!in_initrd()) {
+ else if (streq(word, "debug")) {
+ /* Log to kmsg, the journal socket will fill up before the
+ * journal is started and tools running during that time
+ * will block with every log message for for 60 seconds,
+ * before they give up. */
+ log_set_max_level(LOG_DEBUG);
+ log_set_target(LOG_TARGET_KMSG);
+ } else if (!in_initrd()) {
unsigned i;
/* SysV compatibility */
@@ -637,7 +634,6 @@ static int parse_config_file(void) {
{ "Manager", "ShowStatus", config_parse_bool, 0, &arg_show_status },
{ "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt },
{ "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL },
- { "Manager", "DefaultControllers", config_parse_strv, 0, &arg_default_controllers },
{ "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output },
{ "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error },
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
@@ -645,6 +641,7 @@ static int parse_config_file(void) {
{ "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
{ "Manager", "CapabilityBoundingSet", config_parse_bounding_set, 0, &arg_capability_bounding_set_drop },
{ "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
+ { "Manager", "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]},
@@ -1051,15 +1048,16 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching
assert(_f);
assert(_fds);
- /* Make sure nothing is really destructed when we shut down */
- m->n_reloading ++;
-
r = manager_open_serialization(m, &f);
if (r < 0) {
log_error("Failed to create serialization file: %s", strerror(-r));
goto fail;
}
+ /* Make sure nothing is really destructed when we shut down */
+ m->n_reloading ++;
+ bus_broadcast_reloading(m, true);
+
fds = fdset_new();
if (!fds) {
r = -ENOMEM;
@@ -1140,25 +1138,6 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) {
return 0;
}
-static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) {
- const char *e;
- unsigned long long a, b;
-
- assert(t);
-
- e = getenv("RD_TIMESTAMP");
- if (!e)
- return NULL;
-
- if (sscanf(e, "%llu %llu", &a, &b) != 2)
- return NULL;
-
- t->realtime = (usec_t) a;
- t->monotonic = (usec_t) b;
-
- return t;
-}
-
static void test_mtab(void) {
char *p;
@@ -1239,8 +1218,6 @@ int main(int argc, char *argv[]) {
dual_timestamp initrd_timestamp = { 0ULL, 0ULL };
dual_timestamp userspace_timestamp = { 0ULL, 0ULL };
dual_timestamp kernel_timestamp = { 0ULL, 0ULL };
- dual_timestamp firmware_timestamp = { 0ULL, 0ULL };
- dual_timestamp loader_timestamp = { 0ULL, 0ULL };
static char systemd[] = "systemd";
bool skip_setup = false;
int j;
@@ -1288,28 +1265,20 @@ int main(int argc, char *argv[]) {
log_show_color(isatty(STDERR_FILENO) > 0);
+ /* Disable the umask logic */
+ if (getpid() == 1)
+ umask(0);
+
if (getpid() == 1 && detect_container(NULL) <= 0) {
-#ifdef ENABLE_EFI
- efi_get_boot_timestamps(&userspace_timestamp, &firmware_timestamp, &loader_timestamp);
-#endif
+
/* Running outside of a container as PID 1 */
arg_running_as = SYSTEMD_SYSTEM;
make_null_stdio();
log_set_target(LOG_TARGET_KMSG);
log_open();
- if (in_initrd()) {
- char *rd_timestamp = NULL;
-
+ if (in_initrd())
initrd_timestamp = userspace_timestamp;
- asprintf(&rd_timestamp, "%llu %llu",
- (unsigned long long) initrd_timestamp.realtime,
- (unsigned long long) initrd_timestamp.monotonic);
- if (rd_timestamp) {
- setenv("RD_TIMESTAMP", rd_timestamp, 1);
- free(rd_timestamp);
- }
- }
if (!skip_setup) {
mount_setup_early();
@@ -1345,10 +1314,10 @@ int main(int argc, char *argv[]) {
*/
hwclock_reset_timezone();
- /* Tell the kernel our time zone */
+ /* Tell the kernel our timezone */
r = hwclock_set_timezone(NULL);
if (r < 0)
- log_error("Failed to set the kernel's time zone, ignoring: %s", strerror(-r));
+ log_error("Failed to set the kernel's timezone, ignoring: %s", strerror(-r));
}
}
@@ -1408,7 +1377,6 @@ int main(int argc, char *argv[]) {
/* Reset all signal handlers. */
assert_se(reset_all_signal_handlers() == 0);
- /* If we are init, we can block sigkill. Yay. */
ignore_signals(SIGNALS_IGNORE, -1);
if (parse_config_file() < 0)
@@ -1474,59 +1442,12 @@ int main(int argc, char *argv[]) {
if (serialization)
assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
- /* Set up PATH unless it is already set */
- setenv("PATH",
-#ifdef HAVE_SPLIT_USR
- "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
-#else
- "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
-#endif
- arg_running_as == SYSTEMD_SYSTEM);
-
- if (arg_running_as == SYSTEMD_SYSTEM) {
- /* Parse the data passed to us. We leave this
- * variables set, but the manager later on will not
- * pass them on to our children. */
- if (!in_initrd())
- parse_initrd_timestamp(&initrd_timestamp);
-
- /* Unset some environment variables passed in from the
- * kernel that don't really make sense for us. */
- unsetenv("HOME");
- unsetenv("TERM");
-
- /* When we are invoked by a shell, these might be set,
- * but make little sense to pass on */
- unsetenv("PWD");
- unsetenv("SHLVL");
- unsetenv("_");
-
- /* When we are invoked by a chroot-like tool such as
- * nspawn, these might be set, but make little sense
- * to pass on */
- unsetenv("USER");
- unsetenv("LOGNAME");
-
- /* We suppress the socket activation env vars, as
- * we'll try to match *any* open fd to units if
- * possible. */
- unsetenv("LISTEN_FDS");
- unsetenv("LISTEN_PID");
-
- /* All other variables are left as is, so that clients
- * can still read them via /proc/1/environ */
- }
-
- /* Move out of the way, so that we won't block unmounts */
- assert_se(chdir("/") == 0);
-
- if (arg_running_as == SYSTEMD_SYSTEM) {
+ if (arg_running_as == SYSTEMD_SYSTEM)
/* Become a session leader if we aren't one yet. */
setsid();
- /* Disable the umask logic */
- umask(0);
- }
+ /* Move out of the way, so that we won't block unmounts */
+ assert_se(chdir("/") == 0);
/* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */
dbus_connection_set_change_sigpipe(FALSE);
@@ -1565,8 +1486,6 @@ int main(int argc, char *argv[]) {
log_debug(PACKAGE_STRING " running in user mode. (" SYSTEMD_FEATURES ")");
if (arg_running_as == SYSTEMD_SYSTEM && !skip_setup) {
- locale_setup();
-
if (arg_show_status || plymouth_running())
status_welcome();
@@ -1590,14 +1509,14 @@ int main(int argc, char *argv[]) {
log_error("Failed to adjust timer slack: %m");
if (arg_capability_bounding_set_drop) {
- r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
+ r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
if (r < 0) {
- log_error("Failed to drop capability bounding set: %s", strerror(-r));
+ log_error("Failed to drop capability bounding set of usermode helpers: %s", strerror(-r));
goto finish;
}
- r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop);
+ r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true);
if (r < 0) {
- log_error("Failed to drop capability bounding set of usermode helpers: %s", strerror(-r));
+ log_error("Failed to drop capability bounding set: %s", strerror(-r));
goto finish;
}
}
@@ -1614,7 +1533,7 @@ int main(int argc, char *argv[]) {
if (arg_running_as == SYSTEMD_SYSTEM)
bump_rlimit_nofile(&saved_rlimit_nofile);
- r = manager_new(arg_running_as, &m);
+ r = manager_new(arg_running_as, !!serialization, &m);
if (r < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r));
goto finish;
@@ -1627,14 +1546,12 @@ int main(int argc, char *argv[]) {
m->shutdown_watchdog = arg_shutdown_watchdog;
m->userspace_timestamp = userspace_timestamp;
m->kernel_timestamp = kernel_timestamp;
- m->firmware_timestamp = firmware_timestamp;
- m->loader_timestamp = loader_timestamp;
m->initrd_timestamp = initrd_timestamp;
manager_set_default_rlimits(m, arg_default_rlimit);
- if (arg_default_controllers)
- manager_set_default_controllers(m, arg_default_controllers);
+ if (arg_default_environment)
+ manager_environment_add(m, arg_default_environment);
manager_set_show_status(m, arg_show_status);
@@ -1650,6 +1567,7 @@ 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;
if (serialization) {
fclose(serialization);
@@ -1669,7 +1587,7 @@ int main(int argc, char *argv[]) {
if (r < 0) {
log_error("Failed to load default target: %s", bus_error(&error, r));
dbus_error_free(&error);
- } else if (target->load_state == UNIT_ERROR)
+ } else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND)
log_error("Failed to load default target: %s", strerror(-target->load_error));
else if (target->load_state == UNIT_MASKED)
log_error("Default target masked.");
@@ -1682,7 +1600,7 @@ int main(int argc, char *argv[]) {
log_error("Failed to load rescue target: %s", bus_error(&error, r));
dbus_error_free(&error);
goto finish;
- } else if (target->load_state == UNIT_ERROR) {
+ } else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) {
log_error("Failed to load rescue target: %s", strerror(-target->load_error));
goto finish;
} else if (target->load_state == UNIT_MASKED) {
@@ -1805,7 +1723,6 @@ finish:
free(arg_default_rlimit[j]);
free(arg_default_unit);
- strv_free(arg_default_controllers);
free_join_controllers();
dbus_shutdown();
@@ -1865,6 +1782,10 @@ finish:
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);
}
@@ -1946,6 +1867,12 @@ finish:
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);
+
execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block);
free(env_block);
log_error("Failed to execute shutdown binary, freezing: %m");
diff --git a/src/core/manager.c b/src/core/manager.c
index c7f8f20a70..58dacdc8b5 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -55,7 +55,7 @@
#include "util.h"
#include "mkdir.h"
#include "ratelimit.h"
-#include "cgroup.h"
+#include "locale-setup.h"
#include "mount-setup.h"
#include "unit-name.h"
#include "dbus-unit.h"
@@ -70,11 +70,9 @@
#include "cgroup-util.h"
#include "path-util.h"
#include "audit-fd.h"
+#include "boot-timestamps.h"
#include "env-util.h"
-/* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
-#define GC_QUEUE_ENTRIES_MAX 16
-
/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
@@ -276,6 +274,54 @@ static void manager_print_jobs_in_progress(Manager *m) {
m->jobs_in_progress_iteration++;
}
+static int manager_watch_idle_pipe(Manager *m) {
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.ptr = &m->idle_pipe_watch,
+ };
+ int r;
+
+ if (m->idle_pipe_watch.type != WATCH_INVALID)
+ return 0;
+
+ if (m->idle_pipe[2] < 0)
+ return 0;
+
+ m->idle_pipe_watch.type = WATCH_IDLE_PIPE;
+ m->idle_pipe_watch.fd = m->idle_pipe[2];
+ if (m->idle_pipe_watch.fd < 0) {
+ log_error("Failed to create timerfd: %m");
+ r = -errno;
+ goto err;
+ }
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_pipe_watch.fd, &ev) < 0) {
+ log_error("Failed to add idle_pipe fd to epoll: %m");
+ r = -errno;
+ goto err;
+ }
+
+ log_debug("Set up idle_pipe watch.");
+
+ return 0;
+
+err:
+ if (m->idle_pipe_watch.fd >= 0)
+ close_nointr_nofail(m->idle_pipe_watch.fd);
+ watch_init(&m->idle_pipe_watch);
+ return r;
+}
+
+static void manager_unwatch_idle_pipe(Manager *m) {
+ if (m->idle_pipe_watch.type != WATCH_IDLE_PIPE)
+ return;
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->idle_pipe_watch.fd, NULL) >= 0);
+ watch_init(&m->idle_pipe_watch);
+
+ log_debug("Closed idle_pipe watch.");
+}
+
static int manager_setup_time_change(Manager *m) {
struct epoll_event ev = {
.events = EPOLLIN,
@@ -409,25 +455,34 @@ static int manager_setup_signals(Manager *m) {
return 0;
}
-static void manager_strip_environment(Manager *m) {
+static int manager_default_environment(Manager *m) {
assert(m);
- /* Remove variables from the inherited set that are part of
- * the container interface:
- * http://www.freedesktop.org/wiki/Software/systemd/ContainerInterface */
- strv_remove_prefix(m->environment, "container=");
- strv_remove_prefix(m->environment, "container_");
+ if (m->running_as == SYSTEMD_SYSTEM) {
+ /* The system manager always starts with a clean
+ * environment for its children. It does not import
+ * the kernel or the parents exported variables.
+ *
+ * The initial passed environ is untouched to keep
+ * /proc/self/environ valid; it is used for tagging
+ * the init process inside containers. */
+ m->environment = strv_new("PATH=" DEFAULT_PATH,
+ NULL);
+
+ /* Import locale variables LC_*= from configuration */
+ locale_setup(&m->environment);
+ } else
+ /* The user manager passes its own environment
+ * along to its children. */
+ m->environment = strv_copy(environ);
- /* Remove variables from the inherited set that are part of
- * the initrd interface:
- * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
- strv_remove_prefix(m->environment, "RD_");
+ if (!m->environment)
+ return -ENOMEM;
- /* Drop invalid entries */
- strv_env_clean(m->environment);
+ return 0;
}
-int manager_new(SystemdRunningAs running_as, Manager **_m) {
+int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
Manager *m;
int r = -ENOMEM;
@@ -439,11 +494,16 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
if (!m)
return -ENOMEM;
+#ifdef ENABLE_EFI
+ if (detect_container(NULL) <= 0)
+ boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp);
+#endif
+
m->running_as = running_as;
m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->pin_cgroupfs_fd = -1;
- m->idle_pipe[0] = m->idle_pipe[1] = -1;
+ m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
watch_init(&m->signal_watch);
watch_init(&m->mount_watch);
@@ -455,18 +515,10 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
m->epoll_fd = m->dev_autofs_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
- m->environment = strv_copy(environ);
- if (!m->environment)
+ r = manager_default_environment(m);
+ if (r < 0)
goto fail;
- manager_strip_environment(m);
-
- if (running_as == SYSTEMD_SYSTEM) {
- m->default_controllers = strv_new("cpu", NULL);
- if (!m->default_controllers)
- goto fail;
- }
-
if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
goto fail;
@@ -476,10 +528,12 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
goto fail;
- if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
+ m->cgroup_unit = hashmap_new(string_hash_func, string_compare_func);
+ if (!m->cgroup_unit)
goto fail;
- if (!(m->watch_bus = hashmap_new(string_hash_func, string_compare_func)))
+ m->watch_bus = hashmap_new(string_hash_func, string_compare_func);
+ if (!m->watch_bus)
goto fail;
m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
@@ -503,9 +557,13 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
goto fail;
/* Try to connect to the busses, if possible. */
- r = bus_init(m, running_as != SYSTEMD_SYSTEM);
- if (r < 0)
- goto fail;
+ if ((running_as == SYSTEMD_USER && getenv("DBUS_SESSION_BUS_ADDRESS")) ||
+ running_as == SYSTEMD_SYSTEM) {
+ r = bus_init(m, reexecuting || running_as != SYSTEMD_SYSTEM);
+ if (r < 0)
+ goto fail;
+ } else
+ log_debug("Skipping DBus session bus connection attempt - no DBUS_SESSION_BUS_ADDRESS set...");
m->taint_usr = dir_is_empty("/usr") > 0;
@@ -600,12 +658,7 @@ static unsigned manager_dispatch_gc_queue(Manager *m) {
assert(m);
- if ((m->n_in_gc_queue < GC_QUEUE_ENTRIES_MAX) &&
- (m->gc_queue_timestamp <= 0 ||
- (m->gc_queue_timestamp + GC_QUEUE_USEC_MAX) > now(CLOCK_MONOTONIC)))
- return 0;
-
- log_debug("Running GC...");
+ /* log_debug("Running GC..."); */
m->gc_marker += _GC_OFFSET_MAX;
if (m->gc_marker + _GC_OFFSET_MAX <= _GC_OFFSET_MAX)
@@ -632,7 +685,6 @@ static unsigned manager_dispatch_gc_queue(Manager *m) {
}
m->n_in_gc_queue = 0;
- m->gc_queue_timestamp = 0;
return n;
}
@@ -661,6 +713,11 @@ static void manager_clear_jobs_and_units(Manager *m) {
m->n_running_jobs = 0;
}
+static void close_idle_pipe(Manager *m) {
+ close_pipe(m->idle_pipe);
+ close_pipe(m->idle_pipe + 2);
+}
+
void manager_free(Manager *m) {
UnitType c;
int i;
@@ -702,12 +759,10 @@ void manager_free(Manager *m) {
lookup_paths_free(&m->lookup_paths);
strv_free(m->environment);
- strv_free(m->default_controllers);
-
- hashmap_free(m->cgroup_bondings);
+ hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache);
- close_pipe(m->idle_pipe);
+ close_idle_pipe(m);
free(m->switch_root);
free(m->switch_root_init);
@@ -715,6 +770,9 @@ void manager_free(Manager *m) {
for (i = 0; i < RLIMIT_NLIMITS; i++)
free(m->rlimit[i]);
+ assert(hashmap_isempty(m->units_requiring_mounts_for));
+ hashmap_free(m->units_requiring_mounts_for);
+
free(m);
}
@@ -727,9 +785,11 @@ 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++)
- if (unit_vtable[c]->enumerate)
- if ((q = unit_vtable[c]->enumerate(m)) < 0)
+ if (unit_vtable[c]->enumerate) {
+ q = unit_vtable[c]->enumerate(m);
+ if (q < 0)
r = q;
+ }
manager_dispatch_load_queue(m);
return r;
@@ -820,7 +880,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
assert(m);
+ dual_timestamp_get(&m->generators_start_timestamp);
manager_run_generators(m);
+ dual_timestamp_get(&m->generators_finish_timestamp);
r = lookup_paths_init(
&m->lookup_paths, m->running_as, true,
@@ -839,7 +901,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
m->n_reloading ++;
/* First, enumerate what we can from all config files */
+ dual_timestamp_get(&m->unitsload_start_timestamp);
r = manager_enumerate(m);
+ dual_timestamp_get(&m->unitsload_finish_timestamp);
/* Second, deserialize if there is something to deserialize */
if (serialization) {
@@ -866,6 +930,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (serialization) {
assert(m->n_reloading > 0);
m->n_reloading --;
+
+ /* Let's wait for the UnitNew/JobNew messages being
+ * sent, before we notify that the reload is
+ * finished */
+ m->send_reloading_done = true;
}
return r;
@@ -987,7 +1056,13 @@ unsigned manager_dispatch_load_queue(Manager *m) {
return n;
}
-int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) {
+int manager_load_unit_prepare(
+ Manager *m,
+ const char *name,
+ const char *path,
+ DBusError *e,
+ Unit **_ret) {
+
Unit *ret;
UnitType t;
int r;
@@ -1031,7 +1106,8 @@ int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DB
}
}
- if ((r = unit_add_name(ret, name)) < 0) {
+ r = unit_add_name(ret, name);
+ if (r < 0) {
unit_free(ret);
return r;
}
@@ -1046,7 +1122,13 @@ int manager_load_unit_prepare(Manager *m, const char *name, const char *path, DB
return 0;
}
-int manager_load_unit(Manager *m, const char *name, const char *path, DBusError *e, Unit **_ret) {
+int manager_load_unit(
+ Manager *m,
+ const char *name,
+ const char *path,
+ DBusError *e,
+ Unit **_ret) {
+
int r;
assert(m);
@@ -1122,6 +1204,9 @@ unsigned manager_dispatch_run_queue(Manager *m) {
if (m->n_running_jobs > 0)
manager_watch_jobs_in_progress(m);
+ if (m->n_on_console > 0)
+ manager_watch_idle_pipe(m);
+
return n;
}
@@ -1152,6 +1237,13 @@ unsigned manager_dispatch_dbus_queue(Manager *m) {
}
m->dispatching_dbus_queue = false;
+
+ if (m->send_reloading_done) {
+ m->send_reloading_done = false;
+
+ bus_broadcast_reloading(m, false);
+ }
+
return n;
}
@@ -1205,7 +1297,7 @@ static int manager_process_notify_fd(Manager *m) {
u = hashmap_get(m->watch_pids, LONG_TO_PTR(ucred->pid));
if (!u) {
- u = cgroup_unit_by_pid(m, ucred->pid);
+ u = manager_get_unit_by_pid(m, ucred->pid);
if (!u) {
log_warning("Cannot find unit for notify message of PID %lu.", (unsigned long) ucred->pid);
continue;
@@ -1270,7 +1362,7 @@ static int manager_dispatch_sigchld(Manager *m) {
/* And now figure out the unit this belongs to */
u = hashmap_get(m->watch_pids, LONG_TO_PTR(si.si_pid));
if (!u)
- u = cgroup_unit_by_pid(m, si.si_pid);
+ u = manager_get_unit_by_pid(m, si.si_pid);
/* And now, we actually reap the zombie. */
if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) {
@@ -1372,7 +1464,7 @@ static int manager_process_signal_fd(Manager *m) {
case SIGINT:
if (m->running_as == SYSTEMD_SYSTEM) {
- manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE);
+ manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
break;
}
@@ -1668,6 +1760,14 @@ static int process_event(Manager *m, struct epoll_event *ev) {
break;
}
+ case WATCH_IDLE_PIPE: {
+ m->no_console_output = true;
+
+ manager_unwatch_idle_pipe(m);
+ close_idle_pipe(m);
+ break;
+ }
+
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
@@ -1714,16 +1814,19 @@ int manager_loop(Manager *m) {
if (manager_dispatch_load_queue(m) > 0)
continue;
- if (manager_dispatch_run_queue(m) > 0)
+ if (manager_dispatch_gc_queue(m) > 0)
continue;
- if (bus_dispatch(m) > 0)
+ if (manager_dispatch_cleanup_queue(m) > 0)
continue;
- if (manager_dispatch_cleanup_queue(m) > 0)
+ if (manager_dispatch_cgroup_queue(m) > 0)
continue;
- if (manager_dispatch_gc_queue(m) > 0)
+ if (manager_dispatch_run_queue(m) > 0)
+ continue;
+
+ if (bus_dispatch(m) > 0)
continue;
if (manager_dispatch_dbus_queue(m) > 0)
@@ -1761,7 +1864,7 @@ int manager_loop(Manager *m) {
}
int manager_load_unit_from_dbus_path(Manager *m, const char *s, DBusError *e, Unit **_u) {
- char *n;
+ _cleanup_free_ char *n = NULL;
Unit *u;
int r;
@@ -1769,16 +1872,11 @@ int manager_load_unit_from_dbus_path(Manager *m, const char *s, DBusError *e, Un
assert(s);
assert(_u);
- if (!startswith(s, "/org/freedesktop/systemd1/unit/"))
- return -EINVAL;
-
- n = bus_path_unescape(s+31);
- if (!n)
- return -ENOMEM;
+ r = unit_name_from_dbus_path(s, &n);
+ if (r < 0)
+ return r;
r = manager_load_unit(m, n, NULL, e, &u);
- free(n);
-
if (r < 0)
return r;
@@ -2033,6 +2131,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
}
}
+ bus_serialize(m, f);
+
fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units, i) {
@@ -2046,7 +2146,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) {
fputs(u->id, f);
fputc('\n', f);
- if ((r = unit_serialize(u, f, fds, !switching_root)) < 0) {
+ r = unit_serialize(u, f, fds, !switching_root);
+ if (r < 0) {
m->n_reloading --;
return r;
}
@@ -2151,7 +2252,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
strv_free(m->environment);
m->environment = e;
- } else
+ } else if (bus_deserialize_item(m, l) == 0)
log_debug("Unknown serialization item '%s'", l);
}
@@ -2226,6 +2327,7 @@ int manager_reload(Manager *m) {
return r;
m->n_reloading ++;
+ bus_broadcast_reloading(m, true);
fds = fdset_new();
if (!fds) {
@@ -2285,6 +2387,8 @@ int manager_reload(Manager *m) {
assert(m->n_reloading > 0);
m->n_reloading--;
+ m->send_reloading_done = true;
+
finish:
if (f)
fclose(f);
@@ -2357,7 +2461,8 @@ void manager_check_finished(Manager *m) {
}
/* Notify Type=idle units that we are done now */
- close_pipe(m->idle_pipe);
+ manager_unwatch_idle_pipe(m);
+ close_idle_pipe(m);
/* Turn off confirm spawn now */
m->confirm_spawn = false;
@@ -2559,19 +2664,16 @@ void manager_undo_generators(Manager *m) {
remove_generator_dir(m, &m->generator_unit_path_late);
}
-int manager_set_default_controllers(Manager *m, char **controllers) {
- char **l;
-
+int manager_environment_add(Manager *m, char **environment) {
+ char **e = NULL;
assert(m);
- l = strv_copy(controllers);
- if (!l)
+ e = strv_env_merge(2, m->environment, environment);
+ if (!e)
return -ENOMEM;
- strv_free(m->default_controllers);
- m->default_controllers = l;
-
- cg_shorten_controllers(m->default_controllers);
+ strv_free(m->environment);
+ m->environment = e;
return 0;
}
@@ -2638,6 +2740,9 @@ static bool manager_get_show_status(Manager *m) {
if (m->running_as != SYSTEMD_SYSTEM)
return false;
+ if (m->no_console_output)
+ return false;
+
if (m->show_status)
return true;
@@ -2666,6 +2771,41 @@ void manager_status_printf(Manager *m, bool ephemeral, const char *status, const
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;
+
+ assert(m);
+ assert(path);
+ assert(suffix);
+ assert(_found);
+
+ p = unit_name_from_path(path, suffix);
+ if (!p)
+ return -ENOMEM;
+
+ 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];
+
+ assert(m);
+ assert(path);
+
+ strcpy(p, path);
+ path_kill_slashes(p);
+
+ return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
+}
+
void watch_init(Watch *w) {
assert(w);
diff --git a/src/core/manager.h b/src/core/manager.h
index bf833540ae..a3049b5e5b 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -27,6 +27,7 @@
#include <dbus/dbus.h>
#include "fdset.h"
+#include "cgroup-util.h"
/* Enforce upper limit how many names we allow */
#define MANAGER_MAX_NAMES 131072 /* 128K */
@@ -62,7 +63,8 @@ enum WatchType {
WATCH_DBUS_WATCH,
WATCH_DBUS_TIMEOUT,
WATCH_TIME_CHANGE,
- WATCH_JOBS_IN_PROGRESS
+ WATCH_JOBS_IN_PROGRESS,
+ WATCH_IDLE_PIPE,
};
struct Watch {
@@ -86,6 +88,7 @@ struct Watch {
#include "dbus.h"
#include "path-lookup.h"
#include "execute.h"
+#include "unit-name.h"
struct Manager {
/* Note that the set of units we know of is allowed to be
@@ -100,9 +103,6 @@ struct Manager {
* type we maintain a per type linked list */
LIST_HEAD(Unit, units_by_type[_UNIT_TYPE_MAX]);
- /* To optimize iteration of units that have requires_mounts_for set */
- LIST_HEAD(Unit, has_requires_mounts_for);
-
/* Units that need to be loaded */
LIST_HEAD(Unit, load_queue); /* this is actually more a stack than a queue, but uh. */
@@ -122,6 +122,9 @@ struct Manager {
/* Units to check when doing GC */
LIST_HEAD(Unit, gc_queue);
+ /* Units that should be realized */
+ LIST_HEAD(Unit, cgroup_queue);
+
Hashmap *watch_pids; /* pid => Unit object n:1 */
char *notify_socket;
@@ -130,6 +133,7 @@ struct Manager {
Watch signal_watch;
Watch time_change_watch;
Watch jobs_in_progress_watch;
+ Watch idle_pipe_watch;
int epoll_fd;
@@ -139,7 +143,6 @@ struct Manager {
Set *unit_path_cache;
char **environment;
- char **default_controllers;
usec_t runtime_watchdog;
usec_t shutdown_watchdog;
@@ -150,6 +153,10 @@ struct Manager {
dual_timestamp initrd_timestamp;
dual_timestamp userspace_timestamp;
dual_timestamp finish_timestamp;
+ dual_timestamp generators_start_timestamp;
+ dual_timestamp generators_finish_timestamp;
+ dual_timestamp unitsload_start_timestamp;
+ dual_timestamp unitsload_finish_timestamp;
char *generator_unit_path;
char *generator_unit_path_early;
@@ -187,6 +194,8 @@ struct Manager {
int32_t conn_data_slot;
int32_t subscribed_data_slot;
+ bool send_reloading_done;
+
uint32_t current_job_id;
uint32_t default_unit_job_id;
@@ -194,10 +203,10 @@ struct Manager {
int dev_autofs_fd;
/* Data specific to the cgroup subsystem */
- Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
- char *cgroup_hierarchy;
+ Hashmap *cgroup_unit;
+ CGroupControllerMask cgroup_supported;
+ char *cgroup_root;
- usec_t gc_queue_timestamp;
int gc_marker;
unsigned n_in_gc_queue;
@@ -217,6 +226,7 @@ struct Manager {
bool show_status;
bool confirm_spawn;
+ bool no_console_output;
ExecOutput default_std_output, default_std_error;
@@ -234,13 +244,18 @@ struct Manager {
unsigned jobs_in_progress_iteration;
/* Type=idle pipes */
- int idle_pipe[2];
+ int idle_pipe[4];
char *switch_root;
char *switch_root_init;
+
+ /* This maps all possible path prefixes to the units needing
+ * them. It's a hashmap with a path string as key and a Set as
+ * value where Unit objects are contained. */
+ Hashmap *units_requiring_mounts_for;
};
-int manager_new(SystemdRunningAs running_as, Manager **m);
+int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **m);
void manager_free(Manager *m);
int manager_enumerate(Manager *m);
@@ -250,6 +265,8 @@ 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, DBusError *e, Unit **_ret);
@@ -268,7 +285,7 @@ unsigned manager_dispatch_load_queue(Manager *m);
unsigned manager_dispatch_run_queue(Manager *m);
unsigned manager_dispatch_dbus_queue(Manager *m);
-int manager_set_default_controllers(Manager *m, char **controllers);
+int manager_environment_add(Manager *m, char **environment);
int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
int manager_loop(Manager *m);
@@ -303,4 +320,6 @@ void manager_recheck_journal(Manager *m);
void manager_set_show_status(Manager *m, bool b);
void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) _printf_attr_(4,5);
+Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
+
void watch_init(Watch *w);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 4629808a7a..4359f59908 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -350,14 +350,8 @@ static int nftw_cb(
};
int mount_setup(bool loaded_policy) {
-
- static const char relabel[] =
- "/run/initramfs/root-fsck\0"
- "/run/initramfs/shutdown\0";
-
int r;
unsigned i;
- const char *j;
for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
r = mount_one(mount_table + i, true);
@@ -379,10 +373,6 @@ int mount_setup(bool loaded_policy) {
nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
- /* Explicitly relabel these */
- NULSTR_FOREACH(j, relabel)
- label_fix(j, true, false);
-
after_relabel = now(CLOCK_MONOTONIC);
log_info("Relabelled /dev and /run in %s.",
diff --git a/src/core/mount.c b/src/core/mount.c
index 10073b50be..3d46557fb1 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -82,6 +82,7 @@ static void mount_init(Unit *u) {
}
kill_context_init(&m->kill_context);
+ cgroup_context_init(&m->cgroup_context);
/* We need to make sure that /bin/mount is always called in
* the same process group as us, so that the autofs kernel
@@ -127,6 +128,7 @@ static void mount_done(Unit *u) {
mount_parameters_done(&m->parameters_proc_self_mountinfo);
mount_parameters_done(&m->parameters_fragment);
+ cgroup_context_done(&m->cgroup_context);
exec_context_done(&m->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
@@ -155,138 +157,58 @@ _pure_ static MountParameters* get_mount_parameters(Mount *m) {
}
static int mount_add_mount_links(Mount *m) {
- Unit *other;
- int r;
+ _cleanup_free_ char *parent = NULL;
MountParameters *pm;
-
- assert(m);
-
- pm = get_mount_parameters_fragment(m);
-
- /* Adds in links to other mount points that might lie below or
- * above us in the hierarchy */
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_MOUNT]) {
- Mount *n = MOUNT(other);
- MountParameters *pn;
-
- if (n == m)
- continue;
-
- if (UNIT(n)->load_state != UNIT_LOADED)
- continue;
-
- pn = get_mount_parameters_fragment(n);
-
- if (path_startswith(m->where, n->where)) {
-
- if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
- return r;
-
- if (pn)
- if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
- return r;
-
- } else if (path_startswith(n->where, m->where)) {
-
- if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
- return r;
-
- if (pm)
- if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
-
- } else if (pm && pm->what && path_startswith(pm->what, n->where)) {
-
- if ((r = unit_add_dependency(UNIT(m), UNIT_AFTER, UNIT(n), true)) < 0)
- return r;
-
- if ((r = unit_add_dependency(UNIT(m), UNIT_REQUIRES, UNIT(n), true)) < 0)
- return r;
-
- } else if (pn && pn->what && path_startswith(pn->what, m->where)) {
-
- if ((r = unit_add_dependency(UNIT(n), UNIT_AFTER, UNIT(m), true)) < 0)
- return r;
-
- if ((r = unit_add_dependency(UNIT(n), UNIT_REQUIRES, UNIT(m), true)) < 0)
- return r;
- }
- }
-
- return 0;
-}
-
-static int mount_add_swap_links(Mount *m) {
Unit *other;
+ Iterator i;
+ Set *s;
int r;
assert(m);
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SWAP]) {
- r = swap_add_one_mount_link(SWAP(other), 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;
- }
- return 0;
-}
-
-static int mount_add_path_links(Mount *m) {
- Unit *other;
- int r;
-
- assert(m);
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_PATH]) {
- r = path_add_one_mount_link(PATH(other), m);
+ r = unit_require_mounts_for(UNIT(m), parent);
if (r < 0)
return r;
}
- return 0;
-}
-
-static int mount_add_automount_links(Mount *m) {
- Unit *other;
- int r;
-
- assert(m);
-
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_AUTOMOUNT]) {
- r = automount_add_one_mount_link(AUTOMOUNT(other), m);
+ /* Adds in links to other mount points that might be needed
+ * for the source path (if this is a bind mount) to be
+ * available. */
+ pm = get_mount_parameters_fragment(m);
+ if (pm && path_is_absolute(pm->what)) {
+ r = unit_require_mounts_for(UNIT(m), pm->what);
if (r < 0)
return r;
}
- return 0;
-}
+ /* Adds in links to other units that use this path or paths
+ * further down in the hierarchy */
+ s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where);
+ SET_FOREACH(other, s, i) {
-static int mount_add_socket_links(Mount *m) {
- Unit *other;
- int r;
+ if (other->load_state != UNIT_LOADED)
+ continue;
- assert(m);
+ if (other == UNIT(m))
+ continue;
- LIST_FOREACH(units_by_type, other, UNIT(m)->manager->units_by_type[UNIT_SOCKET]) {
- r = socket_add_one_mount_link(SOCKET(other), m);
+ r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true);
if (r < 0)
return r;
- }
-
- return 0;
-}
-static int mount_add_requires_mounts_links(Mount *m) {
- Unit *other;
- int r;
-
- assert(m);
-
- LIST_FOREACH(has_requires_mounts_for, other, UNIT(m)->manager->has_requires_mounts_for) {
- r = unit_add_one_mount_link(other, m);
- if (r < 0)
- return r;
+ if (UNIT(m)->fragment_path) {
+ /* If we have fragment configuration, then make this dependency required */
+ r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true);
+ if (r < 0)
+ return r;
+ }
}
return 0;
@@ -336,6 +258,12 @@ static bool mount_is_bind(MountParameters *p) {
return false;
}
+static bool mount_is_auto(MountParameters *p) {
+ assert(p);
+
+ return !mount_test_option(p->options, "noauto");
+}
+
static bool needs_quota(MountParameters *p) {
assert(p);
@@ -354,6 +282,7 @@ static bool needs_quota(MountParameters *p) {
static int mount_add_device_links(Mount *m) {
MountParameters *p;
+ bool device_wants_mount = false;
int r;
assert(m);
@@ -374,7 +303,10 @@ static int mount_add_device_links(Mount *m) {
if (path_equal(m->where, "/"))
return 0;
- r = unit_add_node_link(UNIT(m), p->what, false);
+ if (mount_is_auto(p) && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ device_wants_mount = true;
+
+ r = unit_add_node_link(UNIT(m), p->what, device_wants_mount);
if (r < 0)
return r;
@@ -435,6 +367,21 @@ static int mount_add_quota_links(Mount *m) {
return 0;
}
+static bool should_umount(Mount *m) {
+ MountParameters *p;
+
+ if (path_equal(m->where, "/") ||
+ path_equal(m->where, "/usr"))
+ return false;
+
+ p = get_mount_parameters(m);
+ if (p && mount_test_option(p->options, "x-initrd.mount") &&
+ !in_initrd())
+ return false;
+
+ return true;
+}
+
static int mount_add_default_dependencies(Mount *m) {
const char *after, *after2, *online;
MountParameters *p;
@@ -479,9 +426,11 @@ static int mount_add_default_dependencies(Mount *m) {
return r;
}
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
- if (r < 0)
- return r;
+ if (should_umount(m)) {
+ r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ }
return 0;
}
@@ -538,8 +487,9 @@ static int mount_fix_timeouts(Mount *m) {
}
static int mount_verify(Mount *m) {
+ _cleanup_free_ char *e = NULL;
bool b;
- char *e;
+
assert(m);
if (UNIT(m)->load_state != UNIT_LOADED)
@@ -548,12 +498,11 @@ static int mount_verify(Mount *m) {
if (!m->from_fragment && !m->from_proc_self_mountinfo)
return -ENOENT;
- if (!(e = unit_name_from_path(m->where, ".mount")))
+ e = unit_name_from_path(m->where, ".mount");
+ if (!e)
return -ENOMEM;
b = unit_has_name(UNIT(m), e);
- free(e);
-
if (!b) {
log_error_unit(UNIT(m)->id,
"%s's Where setting doesn't match unit name. Refusing.",
@@ -617,26 +566,6 @@ static int mount_add_extras(Mount *m) {
if (r < 0)
return r;
- r = mount_add_socket_links(m);
- if (r < 0)
- return r;
-
- r = mount_add_swap_links(m);
- if (r < 0)
- return r;
-
- r = mount_add_path_links(m);
- if (r < 0)
- return r;
-
- r = mount_add_requires_mounts_links(m);
- if (r < 0)
- return r;
-
- r = mount_add_automount_links(m);
- if (r < 0)
- return r;
-
r = mount_add_quota_links(m);
if (r < 0)
return r;
@@ -647,7 +576,7 @@ static int mount_add_extras(Mount *m) {
return r;
}
- r = unit_add_default_cgroups(u);
+ r = unit_add_default_slice(u);
if (r < 0)
return r;
@@ -820,9 +749,9 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
prefix, mount_state_to_string(m->state),
prefix, mount_result_to_string(m->result),
prefix, m->where,
- prefix, strna(p->what),
- prefix, strna(p->fstype),
- prefix, strna(p->options),
+ prefix, p ? strna(p->what) : "n/a",
+ prefix, p ? strna(p->fstype) : "n/a",
+ 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);
@@ -844,28 +773,31 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
assert(c);
assert(_pid);
+ unit_realize_cgroup(UNIT(m));
+
r = unit_watch_timer(UNIT(m), CLOCK_MONOTONIC, true, m->timeout_usec, &m->timer_watch);
if (r < 0)
goto fail;
- if ((r = exec_spawn(c,
- NULL,
- &m->exec_context,
- NULL, 0,
- UNIT(m)->manager->environment,
- true,
- true,
- true,
- UNIT(m)->manager->confirm_spawn,
- UNIT(m)->cgroup_bondings,
- UNIT(m)->cgroup_attributes,
- NULL,
- UNIT(m)->id,
- NULL,
- &pid)) < 0)
+ r = exec_spawn(c,
+ NULL,
+ &m->exec_context,
+ NULL, 0,
+ UNIT(m)->manager->environment,
+ true,
+ true,
+ true,
+ UNIT(m)->manager->confirm_spawn,
+ UNIT(m)->manager->cgroup_supported,
+ UNIT(m)->cgroup_path,
+ UNIT(m)->id,
+ NULL,
+ &pid);
+ if (r < 0)
goto fail;
- if ((r = unit_watch_pid(UNIT(m), pid)) < 0)
+ r = unit_watch_pid(UNIT(m), pid);
+ if (r < 0)
/* FIXME: we need to do something here */
goto fail;
@@ -1538,9 +1470,11 @@ static int mount_add_one(
if (r < 0)
goto fail;
- r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
- if (r < 0)
- goto fail;
+ if (should_umount(MOUNT(u))) {
+ r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
+ if (r < 0)
+ goto fail;
+ }
unit_add_to_load_queue(u);
} else {
@@ -1555,7 +1489,7 @@ static int mount_add_one(
}
}
- if (u->load_state == UNIT_ERROR) {
+ if (u->load_state == UNIT_NOT_FOUND) {
u->load_state = UNIT_LOADED;
u->load_error = 0;
@@ -1616,79 +1550,56 @@ fail:
static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
int r = 0;
unsigned i;
- char *device, *path, *options, *options2, *fstype, *d, *p, *o;
assert(m);
rewind(m->proc_self_mountinfo);
for (i = 1;; i++) {
+ _cleanup_free_ char *device = NULL, *path = NULL, *options = NULL, *options2 = NULL, *fstype = NULL, *d = NULL, *p = NULL, *o = NULL;
int k;
- device = path = options = options2 = fstype = d = p = o = NULL;
-
- if ((k = fscanf(m->proc_self_mountinfo,
- "%*s " /* (1) mount id */
- "%*s " /* (2) parent id */
- "%*s " /* (3) major:minor */
- "%*s " /* (4) root */
- "%ms " /* (5) mount point */
- "%ms" /* (6) mount options */
- "%*[^-]" /* (7) optional fields */
- "- " /* (8) separator */
- "%ms " /* (9) file system type */
- "%ms" /* (10) mount source */
- "%ms" /* (11) mount options 2 */
- "%*[^\n]", /* some rubbish at the end */
- &path,
- &options,
- &fstype,
- &device,
- &options2)) != 5) {
-
- if (k == EOF)
- break;
-
+ k = fscanf(m->proc_self_mountinfo,
+ "%*s " /* (1) mount id */
+ "%*s " /* (2) parent id */
+ "%*s " /* (3) major:minor */
+ "%*s " /* (4) root */
+ "%ms " /* (5) mount point */
+ "%ms" /* (6) mount options */
+ "%*[^-]" /* (7) optional fields */
+ "- " /* (8) separator */
+ "%ms " /* (9) file system type */
+ "%ms" /* (10) mount source */
+ "%ms" /* (11) mount options 2 */
+ "%*[^\n]", /* some rubbish at the end */
+ &path,
+ &options,
+ &fstype,
+ &device,
+ &options2);
+
+ if (k == EOF)
+ break;
+
+ if (k != 5) {
log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
- goto clean_up;
+ continue;
}
o = strjoin(options, ",", options2, NULL);
- if (!o) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!o)
+ return log_oom();
- if (!(d = cunescape(device)) ||
- !(p = cunescape(path))) {
- r = -ENOMEM;
- goto finish;
- }
+ d = cunescape(device);
+ p = cunescape(path);
+ if (!d || !p)
+ return log_oom();
- if ((k = mount_add_one(m, d, p, o, fstype, 0, set_flags)) < 0)
+ k = mount_add_one(m, d, p, o, fstype, 0, set_flags);
+ if (k < 0)
r = k;
-
-clean_up:
- free(device);
- free(path);
- free(options);
- free(options2);
- free(fstype);
- free(d);
- free(p);
- free(o);
}
-finish:
- free(device);
- free(path);
- free(options);
- free(options2);
- free(fstype);
- free(d);
- free(p);
- free(o);
-
return r;
}
@@ -1872,8 +1783,9 @@ const UnitVTable mount_vtable = {
"Mount\0"
"Install\0",
+ .private_section = "Mount",
.exec_context_offset = offsetof(Mount, exec_context),
- .exec_section = "Mount",
+ .cgroup_context_offset = offsetof(Mount, cgroup_context),
.no_alias = true,
.no_instances = true,
@@ -1908,6 +1820,8 @@ const UnitVTable mount_vtable = {
.bus_interface = "org.freedesktop.systemd1.Mount",
.bus_message_handler = bus_mount_message_handler,
.bus_invalidating_properties = bus_mount_invalidating_properties,
+ .bus_set_property = bus_mount_set_property,
+ .bus_commit_properties = bus_mount_commit_properties,
.enumerate = mount_enumerate,
.shutdown = mount_shutdown,
diff --git a/src/core/mount.h b/src/core/mount.h
index bcc10ee0d4..7cd4320d94 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -25,6 +25,8 @@ typedef struct Mount Mount;
#include "unit.h"
#include "kill.h"
+#include "execute.h"
+#include "cgroup.h"
typedef enum MountState {
MOUNT_DEAD,
@@ -95,8 +97,10 @@ struct Mount {
usec_t timeout_usec;
ExecCommand exec_command[_MOUNT_EXEC_COMMAND_MAX];
+
ExecContext exec_context;
KillContext kill_context;
+ CGroupContext cgroup_context;
MountState state, deserialized_state;
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 7e33d84156..16b132ba56 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -51,6 +51,7 @@ typedef struct BindMount {
const char *path;
MountMode mode;
bool done;
+ bool ignore;
} BindMount;
static int append_mounts(BindMount **p, char **strv, MountMode mode) {
@@ -58,6 +59,13 @@ static int append_mounts(BindMount **p, char **strv, MountMode mode) {
STRV_FOREACH(i, strv) {
+ (*p)->ignore = false;
+
+ if ((mode == INACCESSIBLE || mode == READONLY) && (*i)[0] == '-') {
+ (*p)->ignore = true;
+ (*i)++;
+ }
+
if (!path_is_absolute(*i))
return -EINVAL;
@@ -155,6 +163,8 @@ static int apply_mount(
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)
+ r = 0;
return r;
}
@@ -168,7 +178,7 @@ static int make_read_only(BindMount *m) {
return 0;
r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
- if (r < 0)
+ if (r < 0 && !(m->ignore && errno == ENOENT))
return -errno;
return 0;
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index a07a8e1ce3..a375dce0b0 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -86,6 +86,10 @@
send_interface="org.freedesktop.systemd1.Manager"
send_member="Dump"/>
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetDefaultTarget"/>
+
<allow receive_sender="org.freedesktop.systemd1"/>
</policy>
diff --git a/src/core/path.c b/src/core/path.c
index 8a09deb4ff..99e2fedf29 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -241,10 +241,6 @@ static bool path_spec_check_good(PathSpec *s, bool initial) {
return good;
}
-static bool path_spec_startswith(PathSpec *s, const char *what) {
- return path_startswith(s->path, what);
-}
-
static void path_spec_mkdir(PathSpec *s, mode_t mode) {
int r;
@@ -301,38 +297,14 @@ static void path_done(Unit *u) {
path_free_specs(p);
}
-int path_add_one_mount_link(Path *p, Mount *m) {
+static int path_add_mount_links(Path *p) {
PathSpec *s;
int r;
assert(p);
- assert(m);
-
- if (UNIT(p)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
LIST_FOREACH(spec, s, p->specs) {
- if (!path_spec_startswith(s, m->where))
- continue;
-
- r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
- UNIT(m), true);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int path_add_mount_links(Path *p) {
- Unit *other;
- int r;
-
- assert(p);
-
- LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) {
- r = path_add_one_mount_link(p, MOUNT(other));
+ r = unit_require_mounts_for(UNIT(p), s->path);
if (r < 0)
return r;
}
diff --git a/src/core/path.h b/src/core/path.h
index 6adab5897d..dec3df7035 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -90,10 +90,6 @@ struct Path {
PathResult result;
};
-/* Called from the mount code figure out if a mount is a dependency of
- * any of the paths of this path object */
-int path_add_one_mount_link(Path *p, Mount *m);
-
void path_free_specs(Path *p);
extern const UnitVTable path_vtable;
diff --git a/src/core/scope.c b/src/core/scope.c
new file mode 100644
index 0000000000..50e5dbacb4
--- /dev/null
+++ b/src/core/scope.c
@@ -0,0 +1,482 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "unit.h"
+#include "scope.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "dbus-scope.h"
+#include "special.h"
+#include "unit-name.h"
+#include "load-dropin.h"
+
+static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = {
+ [SCOPE_DEAD] = UNIT_INACTIVE,
+ [SCOPE_RUNNING] = UNIT_ACTIVE,
+ [SCOPE_STOP_SIGTERM] = UNIT_DEACTIVATING,
+ [SCOPE_STOP_SIGKILL] = UNIT_DEACTIVATING,
+ [SCOPE_FAILED] = UNIT_FAILED
+};
+
+static void scope_init(Unit *u) {
+ Scope *s = SCOPE(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ s->timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
+
+ watch_init(&s->timer_watch);
+
+ cgroup_context_init(&s->cgroup_context);
+ kill_context_init(&s->kill_context);
+
+ UNIT(s)->ignore_on_isolate = true;
+ UNIT(s)->ignore_on_snapshot = true;
+}
+
+static void scope_done(Unit *u) {
+ Scope *s = SCOPE(u);
+
+ assert(u);
+
+ cgroup_context_done(&s->cgroup_context);
+
+ set_free(s->pids);
+ s->pids = NULL;
+
+ unit_unwatch_timer(u, &s->timer_watch);
+}
+
+static void scope_set_state(Scope *s, ScopeState state) {
+ ScopeState old_state;
+ assert(s);
+
+ old_state = s->state;
+ s->state = state;
+
+ if (state != SCOPE_STOP_SIGTERM &&
+ state != SCOPE_STOP_SIGKILL)
+ unit_unwatch_timer(UNIT(s), &s->timer_watch);
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(s)->id,
+ scope_state_to_string(old_state),
+ scope_state_to_string(state));
+
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int scope_add_default_dependencies(Scope *s) {
+ int r;
+
+ assert(s);
+
+ /* Make sure scopes are unloaded on shutdown */
+ r = unit_add_two_dependencies_by_name(
+ UNIT(s),
+ UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int scope_verify(Scope *s) {
+ assert(s);
+
+ if (UNIT(s)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (set_size(s->pids) <= 0 && UNIT(s)->manager->n_reloading <= 0) {
+ log_error_unit(UNIT(s)->id, "Scope %s has no PIDs. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int scope_load(Unit *u) {
+ Scope *s = SCOPE(u);
+ int r;
+
+ assert(s);
+ assert(u->load_state == UNIT_STUB);
+
+ if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
+ return -ENOENT;
+
+ u->load_state = UNIT_LOADED;
+
+ r = unit_load_dropin(u);
+ if (r < 0)
+ return r;
+
+ r = unit_add_default_slice(u);
+ if (r < 0)
+ return r;
+
+ if (u->default_dependencies) {
+ r = scope_add_default_dependencies(s);
+ if (r < 0)
+ return r;
+ }
+
+ return scope_verify(s);
+}
+
+static int scope_coldplug(Unit *u) {
+ Scope *s = SCOPE(u);
+ int r;
+
+ assert(s);
+ assert(s->state == SCOPE_DEAD);
+
+ if (s->deserialized_state != s->state) {
+
+ if ((s->deserialized_state == SCOPE_STOP_SIGKILL || s->deserialized_state == SCOPE_STOP_SIGTERM)
+ && s->timeout_stop_usec > 0) {
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch);
+ if (r < 0)
+
+ return r;
+ }
+
+ scope_set_state(s, s->deserialized_state);
+ }
+
+ return 0;
+}
+
+static void scope_dump(Unit *u, FILE *f, const char *prefix) {
+ Scope *s = SCOPE(u);
+
+ assert(s);
+ assert(f);
+
+ fprintf(f,
+ "%sScope State: %s\n"
+ "%sResult: %s\n",
+ prefix, scope_state_to_string(s->state),
+ prefix, scope_result_to_string(s->result));
+
+ cgroup_context_dump(&s->cgroup_context, f, prefix);
+ kill_context_dump(&s->kill_context, f, prefix);
+}
+
+static void scope_enter_dead(Scope *s, ScopeResult f) {
+ assert(s);
+
+ if (f != SCOPE_SUCCESS)
+ s->result = f;
+
+ scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
+}
+
+static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
+ int r;
+
+ assert(s);
+
+ if (f != SCOPE_SUCCESS)
+ s->result = f;
+
+ r = unit_kill_context(
+ UNIT(s),
+ &s->kill_context,
+ state != SCOPE_STOP_SIGTERM,
+ -1, -1, false);
+ if (r < 0)
+ goto fail;
+
+ if (r > 0) {
+ if (s->timeout_stop_usec > 0) {
+ r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_stop_usec, &s->timer_watch);
+ if (r < 0)
+ goto fail;
+ }
+
+ scope_set_state(s, state);
+ } else
+ scope_enter_dead(s, SCOPE_SUCCESS);
+
+ return;
+
+fail:
+ log_warning_unit(UNIT(s)->id,
+ "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r));
+
+ scope_enter_dead(s, SCOPE_FAILURE_RESOURCES);
+}
+
+static int scope_start(Unit *u) {
+ Scope *s = SCOPE(u);
+ int r;
+
+ assert(s);
+
+ if (s->state == SCOPE_FAILED)
+ return -EPERM;
+
+ if (s->state == SCOPE_STOP_SIGTERM ||
+ s->state == SCOPE_STOP_SIGKILL)
+ return -EAGAIN;
+
+ assert(s->state == SCOPE_DEAD);
+
+ if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
+ return -ENOENT;
+
+ r = unit_realize_cgroup(u);
+ if (r < 0) {
+ log_error("Failed to realize cgroup: %s", strerror(-r));
+ return r;
+ }
+
+ r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, s->pids);
+ if (r < 0)
+ return r;
+
+ set_free(s->pids);
+ s->pids = NULL;
+
+ s->result = SCOPE_SUCCESS;
+
+ scope_set_state(s, SCOPE_RUNNING);
+ return 0;
+}
+
+static int scope_stop(Unit *u) {
+ Scope *s = SCOPE(u);
+
+ assert(s);
+ assert(s->state == SCOPE_RUNNING);
+
+ if (s->state == SCOPE_STOP_SIGTERM ||
+ s->state == SCOPE_STOP_SIGKILL)
+ return 0;
+
+ assert(s->state == SCOPE_RUNNING);
+
+ scope_enter_signal(s, SCOPE_STOP_SIGTERM, SCOPE_SUCCESS);
+ return 0;
+}
+
+static void scope_reset_failed(Unit *u) {
+ Scope *s = SCOPE(u);
+
+ assert(s);
+
+ if (s->state == SCOPE_FAILED)
+ scope_set_state(s, SCOPE_DEAD);
+
+ s->result = SCOPE_SUCCESS;
+}
+
+static int scope_kill(Unit *u, KillWho who, int signo, DBusError *error) {
+ return unit_kill_common(u, who, signo, -1, -1, error);
+}
+
+static int scope_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Scope *s = SCOPE(u);
+
+ assert(s);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", scope_state_to_string(s->state));
+ return 0;
+}
+
+static int scope_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Scope *s = SCOPE(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ ScopeState state;
+
+ state = scope_state_from_string(value);
+ if (state < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+static bool scope_check_gc(Unit *u) {
+ Scope *s = SCOPE(u);
+ int r;
+
+ assert(s);
+
+ /* Never clean up scopes that still have a process around,
+ * even if the scope is formally dead. */
+
+ if (UNIT(s)->cgroup_path) {
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
+ if (r <= 0)
+ return true;
+ }
+
+ return false;
+}
+
+static void scope_timer_event(Unit *u, uint64_t elapsed, Watch*w) {
+ Scope *s = SCOPE(u);
+
+ assert(s);
+ assert(elapsed == 1);
+ assert(w == &s->timer_watch);
+
+ switch (s->state) {
+
+ case SCOPE_STOP_SIGTERM:
+ if (s->kill_context.send_sigkill) {
+ log_warning_unit(u->id, "%s stopping timed out. Killing.", u->id);
+ scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT);
+ } else {
+ log_warning_unit(u->id, "%s stopping timed out. Skipping SIGKILL.", u->id);
+ scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
+ }
+
+ break;
+
+ case SCOPE_STOP_SIGKILL:
+ log_warning_unit(u->id, "%s still around after SIGKILL. Ignoring.", u->id);
+ scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT);
+ break;
+
+ default:
+ assert_not_reached("Timeout at wrong time.");
+ }
+}
+
+static void scope_notify_cgroup_empty_event(Unit *u) {
+ Scope *s = SCOPE(u);
+ assert(u);
+
+ log_debug_unit(u->id, "%s: cgroup is empty", u->id);
+
+ switch (s->state) {
+
+ case SCOPE_RUNNING:
+ case SCOPE_STOP_SIGTERM:
+ case SCOPE_STOP_SIGKILL:
+ scope_enter_dead(s, SCOPE_SUCCESS);
+
+ break;
+
+ default:
+ ;
+ }
+}
+
+_pure_ static UnitActiveState scope_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[SCOPE(u)->state];
+}
+
+_pure_ static const char *scope_sub_state_to_string(Unit *u) {
+ assert(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_STOP_SIGTERM] = "stop-sigterm",
+ [SCOPE_STOP_SIGKILL] = "stop-sigkill",
+ [SCOPE_FAILED] = "failed",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
+
+static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
+ [SCOPE_SUCCESS] = "success",
+ [SCOPE_FAILURE_RESOURCES] = "resources",
+ [SCOPE_FAILURE_TIMEOUT] = "timeout",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);
+
+const UnitVTable scope_vtable = {
+ .object_size = sizeof(Scope),
+ .sections =
+ "Unit\0"
+ "Scope\0"
+ "Install\0",
+
+ .private_section = "Scope",
+ .cgroup_context_offset = offsetof(Scope, cgroup_context),
+
+ .no_alias = true,
+ .no_instances = true,
+
+ .init = scope_init,
+ .load = scope_load,
+ .done = scope_done,
+
+ .coldplug = scope_coldplug,
+
+ .dump = scope_dump,
+
+ .start = scope_start,
+ .stop = scope_stop,
+
+ .kill = scope_kill,
+
+ .serialize = scope_serialize,
+ .deserialize_item = scope_deserialize_item,
+
+ .active_state = scope_active_state,
+ .sub_state_to_string = scope_sub_state_to_string,
+
+ .check_gc = scope_check_gc,
+
+ .timer_event = scope_timer_event,
+
+ .reset_failed = scope_reset_failed,
+
+ .notify_cgroup_empty = scope_notify_cgroup_empty_event,
+
+ .bus_interface = "org.freedesktop.systemd1.Scope",
+ .bus_message_handler = bus_scope_message_handler,
+ .bus_set_property = bus_scope_set_property,
+ .bus_commit_properties = bus_scope_commit_properties,
+
+ .can_transient = true
+};
diff --git a/src/core/scope.h b/src/core/scope.h
new file mode 100644
index 0000000000..2a3dcb73d7
--- /dev/null
+++ b/src/core/scope.h
@@ -0,0 +1,69 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Scope Scope;
+
+#include "unit.h"
+#include "kill.h"
+
+typedef enum ScopeState {
+ SCOPE_DEAD,
+ SCOPE_RUNNING,
+ SCOPE_STOP_SIGTERM,
+ SCOPE_STOP_SIGKILL,
+ SCOPE_FAILED,
+ _SCOPE_STATE_MAX,
+ _SCOPE_STATE_INVALID = -1
+} ScopeState;
+
+typedef enum ScopeResult {
+ SCOPE_SUCCESS,
+ SCOPE_FAILURE_RESOURCES,
+ SCOPE_FAILURE_TIMEOUT,
+ _SCOPE_RESULT_MAX,
+ _SCOPE_RESULT_INVALID = -1
+} ScopeResult;
+
+struct Scope {
+ Unit meta;
+
+ CGroupContext cgroup_context;
+ KillContext kill_context;
+
+ ScopeState state, deserialized_state;
+ ScopeResult result;
+
+ usec_t timeout_stop_usec;
+
+ Set *pids;
+
+ Watch timer_watch;
+};
+
+extern const UnitVTable scope_vtable;
+
+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 426aed07d2..0a3ee18bb9 100644
--- a/src/core/selinux-access.c
+++ b/src/core/selinux-access.c
@@ -6,16 +6,16 @@
Copyright 2012 Dan Walsh
systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ 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
- General Public License for more details.
+ Lesser General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h
index 9183cbc9a6..2d7ac64c8f 100644
--- a/src/core/selinux-access.h
+++ b/src/core/selinux-access.h
@@ -8,16 +8,16 @@
Copyright 2012 Dan Walsh
systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ 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
- General Public License for more details.
+ Lesser General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
diff --git a/src/core/service.c b/src/core/service.c
index 3617c24711..67920248d3 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -141,6 +141,7 @@ static void service_init(Unit *u) {
exec_context_init(&s->exec_context);
kill_context_init(&s->kill_context);
+ cgroup_context_init(&s->cgroup_context);
RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5);
@@ -190,6 +191,14 @@ static int service_set_main_pid(Service *s, pid_t pid) {
if (pid == getpid())
return -EINVAL;
+ if (s->main_pid == pid && s->main_pid_known)
+ return 0;
+
+ if (s->main_pid != pid) {
+ service_unwatch_main_pid(s);
+ exec_status_start(&s->main_exec_status, pid);
+ }
+
s->main_pid = pid;
s->main_pid_known = true;
@@ -202,8 +211,6 @@ static int service_set_main_pid(Service *s, pid_t pid) {
} else
s->main_pid_alien = false;
- exec_status_start(&s->main_exec_status, pid);
-
return 0;
}
@@ -220,7 +227,7 @@ static void service_close_socket_fd(Service *s) {
static void service_connection_unref(Service *s) {
assert(s);
- if (!UNIT_DEREF(s->accept_socket))
+ if (!UNIT_ISSET(s->accept_socket))
return;
socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
@@ -235,7 +242,7 @@ static void service_stop_watchdog(Service *s) {
s->watchdog_timestamp.monotonic = 0;
}
-static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart);
+static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
static void service_handle_watchdog(Service *s) {
usec_t offset;
@@ -249,7 +256,7 @@ static void service_handle_watchdog(Service *s) {
offset = now(CLOCK_MONOTONIC) - s->watchdog_timestamp.monotonic;
if (offset >= s->watchdog_usec) {
log_error_unit(UNIT(s)->id, "%s watchdog timeout!", UNIT(s)->id);
- service_enter_dead(s, SERVICE_FAILURE_WATCHDOG, true);
+ service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_WATCHDOG);
return;
}
@@ -283,6 +290,7 @@ static void service_done(Unit *u) {
free(s->status_text);
s->status_text = NULL;
+ cgroup_context_done(&s->cgroup_context);
exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -981,7 +989,7 @@ static int service_load_sysv_name(Service *s, const char *name) {
assert(s);
assert(name);
- /* For SysV services we strip the *.sh suffixes. */
+ /* For SysV services we strip the *.sh suffixes. */
if (endswith(name, ".sh.service"))
return -ENOENT;
@@ -1109,6 +1117,12 @@ static int service_verify(Service *s) {
return -EINVAL;
}
+ if (s->type == SERVICE_ONESHOT && s->restart != SERVICE_RESTART_NO) {
+ log_error_unit(UNIT(s)->id,
+ "%s has Restart setting other than no, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+
if (s->type == SERVICE_DBUS && !s->bus_name) {
log_error_unit(UNIT(s)->id,
"%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id);
@@ -1191,27 +1205,32 @@ static int service_load(Unit *u) {
assert(s);
/* Load a .service file */
- if ((r = unit_load_fragment(u)) < 0)
+ r = unit_load_fragment(u);
+ if (r < 0)
return r;
#ifdef HAVE_SYSV_COMPAT
/* Load a classic init script as a fallback, if we couldn't find anything */
- if (u->load_state == UNIT_STUB)
- if ((r = service_load_sysv(s)) < 0)
+ if (u->load_state == UNIT_STUB) {
+ r = service_load_sysv(s);
+ if (r < 0)
return r;
+ }
#endif
/* Still nothing found? Then let's give up */
if (u->load_state == UNIT_STUB)
return -ENOENT;
- /* We were able to load something, then let's add in the
- * dropin directories. */
- if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
- return r;
-
/* This is a new unit? Then let's add in some extras */
if (u->load_state == UNIT_LOADED) {
+
+ /* We were able to load something, then let's add in
+ * the dropin directories. */
+ r = unit_load_dropin(u);
+ if (r < 0)
+ return r;
+
if (s->type == _SERVICE_TYPE_INVALID)
s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE;
@@ -1225,7 +1244,7 @@ static int service_load(Unit *u) {
if (r < 0)
return r;
- r = unit_add_default_cgroups(u);
+ r = unit_add_default_slice(u);
if (r < 0)
return r;
@@ -1453,7 +1472,7 @@ static int service_search_main_pid(Service *s) {
assert(s->main_pid <= 0);
- pid = cgroup_bonding_search_main_pid_list(UNIT(s)->cgroup_bondings);
+ pid = unit_search_main_pid(UNIT(s));
if (pid <= 0)
return -ENOENT;
@@ -1474,24 +1493,6 @@ static int service_search_main_pid(Service *s) {
return 0;
}
-static void service_notify_sockets_dead(Service *s, bool failed_permanent) {
- Iterator i;
- Unit *u;
-
- assert(s);
-
- /* Notifies all our sockets when we die */
-
- if (s->socket_fd >= 0)
- return;
-
- SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i)
- if (u->type == UNIT_SOCKET)
- socket_notify_service_dead(SOCKET(u), failed_permanent);
-
- return;
-}
-
static void service_set_state(Service *s, ServiceState state) {
ServiceState old_state;
const UnitActiveState *table;
@@ -1543,19 +1544,6 @@ static void service_set_state(Service *s, ServiceState state) {
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
- if (state == SERVICE_FAILED)
- service_notify_sockets_dead(s, s->result == SERVICE_FAILURE_START_LIMIT);
-
- if (state == SERVICE_DEAD ||
- state == SERVICE_STOP ||
- state == SERVICE_STOP_SIGTERM ||
- state == SERVICE_STOP_SIGKILL ||
- state == SERVICE_STOP_POST ||
- state == SERVICE_FINAL_SIGTERM ||
- state == SERVICE_FINAL_SIGKILL ||
- state == SERVICE_AUTO_RESTART)
- service_notify_sockets_dead(s, false);
-
if (state != SERVICE_START_PRE &&
state != SERVICE_START &&
state != SERVICE_START_POST &&
@@ -1578,7 +1566,7 @@ static void service_set_state(Service *s, ServiceState state) {
/* 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)
- cgroup_bonding_trim_list(UNIT(s)->cgroup_bondings, true);
+ unit_destroy_cgroup(UNIT(s));
if (old_state != state)
log_debug_unit(UNIT(s)->id,
@@ -1610,6 +1598,7 @@ static int service_coldplug(Unit *u) {
s->deserialized_state == SERVICE_FINAL_SIGTERM ||
s->deserialized_state == SERVICE_FINAL_SIGKILL ||
s->deserialized_state == SERVICE_AUTO_RESTART) {
+
if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_start_usec > 0) {
usec_t k;
@@ -1747,11 +1736,14 @@ static int service_spawn(
unsigned n_fds = 0, n_env = 0;
_cleanup_strv_free_ char
**argv = NULL, **final_env = NULL, **our_env = NULL;
+ const char *path;
assert(s);
assert(c);
assert(_pid);
+ unit_realize_cgroup(UNIT(s));
+
if (pass_fds ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
@@ -1777,11 +1769,9 @@ static int service_spawn(
} else
unit_unwatch_timer(UNIT(s), &s->timer_watch);
- argv = unit_full_printf_strv(UNIT(s), c->argv);
- if (!argv) {
- r = -ENOMEM;
+ r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
+ if (r < 0)
goto fail;
- }
our_env = new0(char*, 5);
if (!our_env) {
@@ -1807,7 +1797,7 @@ static int service_spawn(
goto fail;
}
- if (s->meta.manager->running_as != SYSTEMD_SYSTEM)
+ if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM)
if (asprintf(our_env + n_env++, "MANAGERPID=%lu", (unsigned long) getpid()) < 0) {
r = -ENOMEM;
goto fail;
@@ -1819,6 +1809,12 @@ static int service_spawn(
goto fail;
}
+ if (is_control && UNIT(s)->cgroup_path) {
+ path = strappenda(UNIT(s)->cgroup_path, "/control");
+ cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+ } else
+ path = UNIT(s)->cgroup_path;
+
r = exec_spawn(c,
argv,
&s->exec_context,
@@ -1828,9 +1824,8 @@ static int service_spawn(
apply_chroot,
apply_tty_stdin,
UNIT(s)->manager->confirm_spawn,
- UNIT(s)->cgroup_bondings,
- UNIT(s)->cgroup_attributes,
- is_control ? "control" : NULL,
+ UNIT(s)->manager->cgroup_supported,
+ path,
UNIT(s)->id,
s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
&pid);
@@ -1865,7 +1860,7 @@ static int main_pid_good(Service *s) {
/* If it's an alien child let's check if it is still
* alive ... */
- if (s->main_pid_alien)
+ if (s->main_pid_alien && s->main_pid > 0)
return kill(s->main_pid, 0) >= 0 || errno != ESRCH;
/* .. otherwise assume we'll get a SIGCHLD for it,
@@ -1889,7 +1884,10 @@ static int cgroup_good(Service *s) {
assert(s);
- r = cgroup_bonding_is_empty_list(UNIT(s)->cgroup_bondings);
+ if (!UNIT(s)->cgroup_path)
+ return 0;
+
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
if (r < 0)
return r;
@@ -1910,6 +1908,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
(s->restart == SERVICE_RESTART_ALWAYS ||
(s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
+ (s->restart == SERVICE_RESTART_ON_WATCHDOG && s->result == SERVICE_FAILURE_WATCHDOG) ||
(s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
s->result == SERVICE_FAILURE_CORE_DUMP))) &&
(s->result != SERVICE_FAILURE_EXIT_CODE ||
@@ -1930,6 +1929,12 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
/* we want fresh tmpdirs in case service is started again immediately */
exec_context_tmp_dirs_done(&s->exec_context);
+ /* 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);
+
return;
fail:
@@ -1939,8 +1944,6 @@ fail:
service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false);
}
-static void service_enter_signal(Service *s, ServiceState state, ServiceResult f);
-
static void service_enter_stop_post(Service *s, ServiceResult f) {
int r;
assert(s);
@@ -1970,7 +1973,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
service_set_state(s, SERVICE_STOP_POST);
} else
- service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_SUCCESS);
+ service_enter_dead(s, SERVICE_SUCCESS, true);
return;
@@ -2121,25 +2124,33 @@ fail:
service_enter_stop(s, SERVICE_FAILURE_RESOURCES);
}
+static void service_kill_control_processes(Service *s) {
+ char *p;
+
+ if (!UNIT(s)->cgroup_path)
+ return;
+
+ p = strappenda(UNIT(s)->cgroup_path, "/control");
+ cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, p, SIGKILL, true, true, true, NULL);
+}
+
static void service_enter_start(Service *s) {
+ ExecCommand *c;
pid_t pid;
int r;
- ExecCommand *c;
assert(s);
assert(s->exec_command[SERVICE_EXEC_START]);
assert(!s->exec_command[SERVICE_EXEC_START]->command_next || s->type == SERVICE_ONESHOT);
- if (s->type == SERVICE_FORKING)
- service_unwatch_control_pid(s);
- else
- service_unwatch_main_pid(s);
+ service_unwatch_control_pid(s);
+ service_unwatch_main_pid(s);
/* We want to ensure that nobody leaks processes from
* START_PRE here, so let's go on a killing spree, People
* should not spawn long running processes from START_PRE. */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL, true, true, NULL, "control");
+ service_kill_control_processes(s);
if (s->type == SERVICE_FORKING) {
s->control_command_id = SERVICE_EXEC_START;
@@ -2215,11 +2226,9 @@ static void service_enter_start_pre(Service *s) {
s->control_command = s->exec_command[SERVICE_EXEC_START_PRE];
if (s->control_command) {
-
/* Before we start anything, let's clear up what might
* be left from previous runs. */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
- true,true, NULL, "control");
+ service_kill_control_processes(s);
s->control_command_id = SERVICE_EXEC_START_PRE;
@@ -2691,8 +2700,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
if (parse_pid(value, &pid) < 0)
log_debug_unit(u->id, "Failed to parse main-pid value %s", value);
- else
- service_set_main_pid(s, (pid_t) pid);
+ else {
+ service_set_main_pid(s, pid);
+ unit_watch_pid(UNIT(s), pid);
+ }
} else if (streq(key, "main-pid-known")) {
int b;
@@ -3043,7 +3054,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
}
} else if (s->control_pid == pid) {
-
s->control_pid = 0;
if (s->control_command) {
@@ -3064,8 +3074,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
/* Immediately get rid of the cgroup, so that the
* kernel doesn't delay the cgroup empty messages for
* the service cgroup any longer than necessary */
- cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, SIGKILL,
- true, true, NULL, "control");
+ service_kill_control_processes(s);
if (s->control_command &&
s->control_command->command_next &&
@@ -3294,13 +3303,12 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
}
}
-static void service_cgroup_notify_event(Unit *u) {
+static void service_notify_cgroup_empty_event(Unit *u) {
Service *s = SERVICE(u);
assert(u);
- log_debug_unit(u->id,
- "%s: cgroup is empty", u->id);
+ log_debug_unit(u->id, "%s: cgroup is empty", u->id);
switch (s->state) {
@@ -3387,6 +3395,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) {
log_debug_unit(u->id,
"%s: got %s", u->id, e);
service_set_main_pid(s, pid);
+ unit_watch_pid(UNIT(s), pid);
}
}
@@ -3683,8 +3692,10 @@ static void service_bus_query_pid_done(
(s->state == SERVICE_START ||
s->state == SERVICE_START_POST ||
s->state == SERVICE_RUNNING ||
- s->state == SERVICE_RELOAD))
+ s->state == SERVICE_RELOAD)){
service_set_main_pid(s, pid);
+ unit_watch_pid(UNIT(s), pid);
+ }
}
int service_set_socket_fd(Service *s, int fd, Socket *sock) {
@@ -3729,6 +3740,7 @@ static void service_reset_failed(Unit *u) {
static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) {
Service *s = SERVICE(u);
+
return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
}
@@ -3756,6 +3768,7 @@ static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
[SERVICE_RESTART_ON_FAILURE] = "on-failure",
+ [SERVICE_RESTART_ON_WATCHDOG] = "on-watchdog",
[SERVICE_RESTART_ON_ABORT] = "on-abort",
[SERVICE_RESTART_ALWAYS] = "always"
};
@@ -3821,8 +3834,9 @@ const UnitVTable service_vtable = {
"Service\0"
"Install\0",
+ .private_section = "Service",
.exec_context_offset = offsetof(Service, exec_context),
- .exec_section = "Service",
+ .cgroup_context_offset = offsetof(Service, cgroup_context),
.init = service_init,
.done = service_done,
@@ -3855,7 +3869,7 @@ const UnitVTable service_vtable = {
.reset_failed = service_reset_failed,
- .cgroup_notify_empty = service_cgroup_notify_event,
+ .notify_cgroup_empty = service_notify_cgroup_empty_event,
.notify_message = service_notify_message,
.bus_name_owner_change = service_bus_name_owner_change,
@@ -3864,6 +3878,10 @@ const UnitVTable service_vtable = {
.bus_interface = "org.freedesktop.systemd1.Service",
.bus_message_handler = bus_service_message_handler,
.bus_invalidating_properties = bus_service_invalidating_properties,
+ .bus_set_property = bus_service_set_property,
+ .bus_commit_properties = bus_service_commit_properties,
+
+ .can_transient = true,
#ifdef HAVE_SYSV_COMPAT
.enumerate = service_enumerate,
diff --git a/src/core/service.h b/src/core/service.h
index 703d3faa45..ce5b5e04ab 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -54,6 +54,7 @@ typedef enum ServiceRestart {
SERVICE_RESTART_NO,
SERVICE_RESTART_ON_SUCCESS,
SERVICE_RESTART_ON_FAILURE,
+ SERVICE_RESTART_ON_WATCHDOG,
SERVICE_RESTART_ON_ABORT,
SERVICE_RESTART_ALWAYS,
_SERVICE_RESTART_MAX,
@@ -135,6 +136,7 @@ struct Service {
ExecContext exec_context;
KillContext kill_context;
+ CGroupContext cgroup_context;
ServiceState state, deserialized_state;
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
index 2db761de36..4709746de4 100644
--- a/src/core/shutdown.c
+++ b/src/core/shutdown.c
@@ -39,6 +39,7 @@
#include "missing.h"
#include "log.h"
+#include "fileio.h"
#include "umount.h"
#include "util.h"
#include "mkdir.h"
@@ -130,12 +131,27 @@ static int pivot_to_new_root(void) {
}
int main(int argc, char *argv[]) {
+ _cleanup_free_ char *line = NULL;
int cmd, r;
unsigned retries;
bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true;
bool in_container, use_watchdog = false;
char *arguments[3];
+ /* suppress shutdown status output if 'quiet' is used */
+ r = read_one_line_file("/proc/cmdline", &line);
+ if (r >= 0) {
+ char *w, *state;
+ size_t l;
+
+ FOREACH_WORD_QUOTED(w, l, line, state) {
+ if (l == 5 && memcmp(w, "quiet", 5) == 0) {
+ log_set_max_level(LOG_WARNING);
+ break;
+ }
+ }
+ }
+
log_parse_environment();
log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
log_open();
@@ -302,7 +318,7 @@ int main(int argc, char *argv[]) {
log_warning("kexec failed. Falling back to normal reboot.");
} else {
/* Child */
- const char *args[3] = { "/sbin/kexec", "-e", NULL };
+ const char *args[3] = { KEXEC, "-e", NULL };
execv(args[0], (char * const *) args);
return EXIT_FAILURE;
}
diff --git a/src/core/slice.c b/src/core/slice.c
new file mode 100644
index 0000000000..40d416e35e
--- /dev/null
+++ b/src/core/slice.c
@@ -0,0 +1,322 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "unit.h"
+#include "slice.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "dbus-slice.h"
+#include "special.h"
+#include "unit-name.h"
+
+static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = {
+ [SLICE_DEAD] = UNIT_INACTIVE,
+ [SLICE_ACTIVE] = UNIT_ACTIVE
+};
+
+static void slice_init(Unit *u) {
+ Slice *s = SLICE(u);
+
+ assert(u);
+ assert(u->load_state == UNIT_STUB);
+
+ cgroup_context_init(&s->cgroup_context);
+}
+
+static void slice_done(Unit *u) {
+ Slice *s = SLICE(u);
+
+ assert(u);
+
+ cgroup_context_done(&s->cgroup_context);
+}
+
+static void slice_set_state(Slice *t, SliceState state) {
+ SliceState old_state;
+ assert(t);
+
+ old_state = t->state;
+ t->state = state;
+
+ if (state != old_state)
+ log_debug("%s changed %s -> %s",
+ UNIT(t)->id,
+ slice_state_to_string(old_state),
+ slice_state_to_string(state));
+
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], true);
+}
+
+static int slice_add_parent_slice(Slice *s) {
+ char *a, *dash;
+ Unit *parent;
+ int r;
+
+ assert(s);
+
+ if (UNIT_ISSET(UNIT(s)->slice))
+ return 0;
+
+ if (unit_has_name(UNIT(s), SPECIAL_ROOT_SLICE))
+ return 0;
+
+ a = strdupa(UNIT(s)->id);
+ dash = strrchr(a, '-');
+ if (dash)
+ strcpy(dash, ".slice");
+ else
+ a = (char*) SPECIAL_ROOT_SLICE;
+
+ r = manager_load_unit(UNIT(s)->manager, a, NULL, NULL, &parent);
+ if (r < 0)
+ return r;
+
+ unit_ref_set(&UNIT(s)->slice, parent);
+ return 0;
+}
+
+static int slice_add_default_dependencies(Slice *s) {
+ int r;
+
+ assert(s);
+
+ /* Make sure slices are unloaded on shutdown */
+ r = unit_add_two_dependencies_by_name(
+ UNIT(s),
+ UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int slice_verify(Slice *s) {
+ assert(s);
+
+ if (UNIT(s)->load_state != UNIT_LOADED)
+ return 0;
+
+ if (UNIT_DEREF(UNIT(s)->slice)) {
+ char *a, *dash;
+
+ a = strdupa(UNIT(s)->id);
+ dash = strrchr(a, '-');
+ if (dash)
+ strcpy(dash, ".slice");
+ else
+ a = (char*) SPECIAL_ROOT_SLICE;
+
+ if (!unit_has_name(UNIT_DEREF(UNIT(s)->slice), a)) {
+ log_error_unit(UNIT(s)->id,
+ "%s located outside its parent slice. Refusing.", UNIT(s)->id);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int slice_load(Unit *u) {
+ Slice *s = SLICE(u);
+ int r;
+
+ assert(s);
+
+ r = unit_load_fragment_and_dropin_optional(u);
+ if (r < 0)
+ return r;
+
+ /* This is a new unit? Then let's add in some extras */
+ if (u->load_state == UNIT_LOADED) {
+
+ r = slice_add_parent_slice(s);
+ if (r < 0)
+ return r;
+
+ if (u->default_dependencies) {
+ r = slice_add_default_dependencies(s);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return slice_verify(s);
+}
+
+static int slice_coldplug(Unit *u) {
+ Slice *t = SLICE(u);
+
+ assert(t);
+ assert(t->state == SLICE_DEAD);
+
+ if (t->deserialized_state != t->state)
+ slice_set_state(t, t->deserialized_state);
+
+ return 0;
+}
+
+static void slice_dump(Unit *u, FILE *f, const char *prefix) {
+ Slice *t = SLICE(u);
+
+ assert(t);
+ assert(f);
+
+ fprintf(f,
+ "%sSlice State: %s\n",
+ prefix, slice_state_to_string(t->state));
+
+ cgroup_context_dump(&t->cgroup_context, f, prefix);
+}
+
+static int slice_start(Unit *u) {
+ Slice *t = SLICE(u);
+
+ assert(t);
+ assert(t->state == SLICE_DEAD);
+
+ unit_realize_cgroup(u);
+
+ slice_set_state(t, SLICE_ACTIVE);
+ return 0;
+}
+
+static int slice_stop(Unit *u) {
+ Slice *t = SLICE(u);
+
+ assert(t);
+ assert(t->state == SLICE_ACTIVE);
+
+ /* We do not need to destroy the cgroup explicitly,
+ * unit_notify() will do that for us anyway. */
+
+ slice_set_state(t, SLICE_DEAD);
+ return 0;
+}
+
+static int slice_kill(Unit *u, KillWho who, int signo, DBusError *error) {
+ return unit_kill_common(u, who, signo, -1, -1, error);
+}
+
+static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
+ Slice *s = SLICE(u);
+
+ assert(s);
+ assert(f);
+ assert(fds);
+
+ unit_serialize_item(u, f, "state", slice_state_to_string(s->state));
+ return 0;
+}
+
+static int slice_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
+ Slice *s = SLICE(u);
+
+ assert(u);
+ assert(key);
+ assert(value);
+ assert(fds);
+
+ if (streq(key, "state")) {
+ SliceState state;
+
+ state = slice_state_from_string(value);
+ if (state < 0)
+ log_debug("Failed to parse state value %s", value);
+ else
+ s->deserialized_state = state;
+
+ } else
+ log_debug("Unknown serialization key '%s'", key);
+
+ return 0;
+}
+
+_pure_ static UnitActiveState slice_active_state(Unit *u) {
+ assert(u);
+
+ return state_translation_table[SLICE(u)->state];
+}
+
+_pure_ static const char *slice_sub_state_to_string(Unit *u) {
+ assert(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"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
+
+const UnitVTable slice_vtable = {
+ .object_size = sizeof(Slice),
+ .sections =
+ "Unit\0"
+ "Slice\0"
+ "Install\0",
+
+ .private_section = "Slice",
+ .cgroup_context_offset = offsetof(Slice, cgroup_context),
+
+ .no_alias = true,
+ .no_instances = true,
+
+ .init = slice_init,
+ .load = slice_load,
+ .done = slice_done,
+
+ .coldplug = slice_coldplug,
+
+ .dump = slice_dump,
+
+ .start = slice_start,
+ .stop = slice_stop,
+
+ .kill = slice_kill,
+
+ .serialize = slice_serialize,
+ .deserialize_item = slice_deserialize_item,
+
+ .active_state = slice_active_state,
+ .sub_state_to_string = slice_sub_state_to_string,
+
+ .bus_interface = "org.freedesktop.systemd1.Slice",
+ .bus_message_handler = bus_slice_message_handler,
+ .bus_set_property = bus_slice_set_property,
+ .bus_commit_properties = bus_slice_commit_properties,
+
+ .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
new file mode 100644
index 0000000000..ad0c63902b
--- /dev/null
+++ b/src/core/slice.h
@@ -0,0 +1,46 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct Slice Slice;
+
+#include "unit.h"
+
+typedef enum SliceState {
+ SLICE_DEAD,
+ SLICE_ACTIVE,
+ _SLICE_STATE_MAX,
+ _SLICE_STATE_INVALID = -1
+} SliceState;
+
+struct Slice {
+ Unit meta;
+
+ SliceState state, deserialized_state;
+
+ CGroupContext cgroup_context;
+};
+
+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 73eeb04190..1434dea7c1 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -40,7 +40,9 @@
#include "label.h"
#define SMACK_CONFIG "/etc/smack/accesses.d/"
-#define CIPSO_CONFIG "/etc/smack/cipso/"
+#define CIPSO_CONFIG "/etc/smack/cipso.d/"
+
+#ifdef HAVE_SMACK
static int write_rules(const char* dstpath, const char* srcdir) {
_cleanup_fclose_ FILE *dst = NULL;
@@ -111,8 +113,12 @@ static int write_rules(const char* dstpath, const char* srcdir) {
return r;
}
+#endif
int smack_setup(void) {
+
+#ifdef HAVE_SMACK
+
int r;
r = write_rules("/sys/fs/smackfs/load2", SMACK_CONFIG);
@@ -148,4 +154,8 @@ int smack_setup(void) {
strerror(abs(r)));
return 0;
}
+
+#endif
+
+ return 0;
}
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
index a63eccd8de..d11239dff3 100644
--- a/src/core/snapshot.c
+++ b/src/core/snapshot.c
@@ -40,6 +40,7 @@ static void snapshot_init(Unit *u) {
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) {
@@ -66,7 +67,7 @@ static int snapshot_load(Unit *u) {
/* Make sure that only snapshots created via snapshot_create()
* can be loaded */
- if (!s->by_snapshot_create && UNIT(s)->manager->n_reloading <= 0)
+ if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
return -ENOENT;
u->load_state = UNIT_LOADED;
@@ -151,21 +152,24 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value
if (streq(key, "state")) {
SnapshotState state;
- if ((state = snapshot_state_from_string(value)) < 0)
+ state = snapshot_state_from_string(value);
+ if (state < 0)
log_debug("Failed to parse state value %s", value);
else
s->deserialized_state = state;
} else if (streq(key, "cleanup")) {
- if ((r = parse_boolean(value)) < 0)
+ r = parse_boolean(value);
+ if (r < 0)
log_debug("Failed to parse cleanup value %s", value);
else
s->cleanup = r;
} else if (streq(key, "wants")) {
- if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true)) < 0)
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true);
+ if (r < 0)
return r;
} else
log_debug("Unknown serialization key '%s'", key);
@@ -186,9 +190,9 @@ _pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
}
int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **_s) {
- Iterator i;
+ _cleanup_free_ char *n = NULL;
Unit *other, *u = NULL;
- char *n = NULL;
+ Iterator i;
int r;
const char *k;
@@ -217,28 +221,28 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Sn
if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
return -ENOMEM;
- if (!manager_get_unit(m, n))
+ if (!manager_get_unit(m, n)) {
+ name = n;
break;
+ }
free(n);
+ n = NULL;
}
-
- name = n;
}
r = manager_load_unit_prepare(m, name, NULL, e, &u);
- free(n);
-
if (r < 0)
goto fail;
- SNAPSHOT(u)->by_snapshot_create = true;
+ 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)
+ if (other->ignore_on_snapshot ||
+ other->transient)
continue;
if (k != other->id)
@@ -251,12 +255,12 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Sn
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
continue;
- if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true)) < 0)
+ r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
+ if (r < 0)
goto fail;
}
SNAPSHOT(u)->cleanup = cleanup;
- u->allow_isolate = true;
*_s = SNAPSHOT(u);
return 0;
diff --git a/src/core/snapshot.h b/src/core/snapshot.h
index 56f87cff4d..2675b1b242 100644
--- a/src/core/snapshot.h
+++ b/src/core/snapshot.h
@@ -38,7 +38,6 @@ struct Snapshot {
SnapshotState state, deserialized_state;
bool cleanup;
- bool by_snapshot_create:1;
};
extern const UnitVTable snapshot_vtable;
diff --git a/src/core/socket.c b/src/core/socket.c
index 1b08f0a5fd..6c0ac1a898 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -88,6 +88,7 @@ static void socket_init(Unit *u) {
s->exec_context.std_output = u->manager->default_std_output;
s->exec_context.std_error = u->manager->default_std_error;
kill_context_init(&s->kill_context);
+ cgroup_context_init(&s->cgroup_context);
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
}
@@ -128,6 +129,8 @@ static void socket_done(Unit *u) {
socket_free_ports(s);
exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
+ cgroup_context_init(&s->cgroup_context);
+
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -255,53 +258,24 @@ static int socket_verify(Socket *s) {
return 0;
}
-static bool socket_needs_mount(Socket *s, const char *prefix) {
+static int socket_add_mount_links(Socket *s) {
SocketPort *p;
-
- assert(s);
-
- LIST_FOREACH(port, p, s->ports) {
-
- if (p->type == SOCKET_SOCKET) {
- if (socket_address_needs_mount(&p->address, prefix))
- return true;
- } else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL) {
- if (path_startswith(p->path, prefix))
- return true;
- }
- }
-
- return false;
-}
-
-int socket_add_one_mount_link(Socket *s, Mount *m) {
int r;
assert(s);
- assert(m);
- if (UNIT(s)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- if (!socket_needs_mount(s, m->where))
- return 0;
-
- r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
- if (r < 0)
- return r;
-
- return 0;
-}
+ LIST_FOREACH(port, p, s->ports) {
+ const char *path = NULL;
-static int socket_add_mount_links(Socket *s) {
- Unit *other;
- int r;
+ if (p->type == SOCKET_SOCKET)
+ path = socket_address_get_path(&p->address);
+ else if (p->type == SOCKET_FIFO || p->type == SOCKET_SPECIAL)
+ path = p->path;
- assert(s);
+ if (!path)
+ continue;
- LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT]) {
- r = socket_add_one_mount_link(s, MOUNT(other));
+ r = unit_require_mounts_for(UNIT(s), path);
if (r < 0)
return r;
}
@@ -395,7 +369,8 @@ static int socket_load(Unit *u) {
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
return r;
- if ((r = unit_add_default_cgroups(u)) < 0)
+ r = unit_add_default_slice(u);
+ if (r < 0)
return r;
if (UNIT(s)->default_dependencies)
@@ -532,6 +507,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sMessageQueueMessageSize: %li\n",
prefix, s->mq_msgsize);
+ if (s->reuseport)
+ fprintf(f,
+ "%sReusePort: %s\n",
+ prefix, yes_no(s->reuseport));
+
if (s->smack)
fprintf(f,
"%sSmackLabel: %s\n",
@@ -788,7 +768,13 @@ static void socket_apply_socket_options(Socket *s, int fd) {
if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0)
log_warning_unit(UNIT(s)->id, "TCP_CONGESTION failed: %m");
-#ifdef HAVE_XATTR
+ if (s->reuseport) {
+ int b = s->reuseport;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &b, sizeof(b)))
+ log_warning_unit(UNIT(s)->id, "SO_REUSEPORT failed: %m");
+ }
+
+#ifdef HAVE_SMACK
if (s->smack_ip_in)
if (fsetxattr(fd, "security.SMACK64IPIN", s->smack_ip_in, strlen(s->smack_ip_in), 0) < 0)
log_error_unit(UNIT(s)->id,
@@ -810,7 +796,7 @@ static void socket_apply_fifo_options(Socket *s, int fd) {
log_warning_unit(UNIT(s)->id,
"F_SETPIPE_SZ: %m");
-#ifdef HAVE_XATTR
+#ifdef HAVE_SMACK
if (s->smack)
if (fsetxattr(fd, "security.SMACK64", s->smack, strlen(s->smack), 0) < 0)
log_error_unit(UNIT(s)->id,
@@ -1000,7 +986,7 @@ static int socket_open_fds(Socket *s) {
if ((r = socket_instantiate_service(s)) < 0)
return r;
- if (UNIT_DEREF(s->service) &&
+ if (UNIT_ISSET(s->service) &&
SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) {
r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label);
@@ -1205,15 +1191,15 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
assert(c);
assert(_pid);
+ unit_realize_cgroup(UNIT(s));
+
r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
if (r < 0)
goto fail;
- argv = unit_full_printf_strv(UNIT(s), c->argv);
- if (!argv) {
- r = -ENOMEM;
+ r = unit_full_printf_strv(UNIT(s), c->argv, &argv);
+ if (r < 0)
goto fail;
- }
r = exec_spawn(c,
argv,
@@ -1224,9 +1210,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
true,
true,
UNIT(s)->manager->confirm_spawn,
- UNIT(s)->cgroup_bondings,
- UNIT(s)->cgroup_attributes,
- NULL,
+ UNIT(s)->manager->cgroup_supported,
+ UNIT(s)->cgroup_path,
UNIT(s)->id,
NULL,
&pid);
@@ -1628,7 +1613,7 @@ static int socket_start(Unit *u) {
return 0;
/* Cannot run this without the service being around */
- if (UNIT_DEREF(s->service)) {
+ if (UNIT_ISSET(s->service)) {
Service *service;
service = SERVICE(UNIT_DEREF(s->service));
@@ -2261,7 +2246,7 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
return 0;
}
-void socket_notify_service_dead(Socket *s, bool failed_permanent) {
+static void socket_notify_service_dead(Socket *s, bool failed_permanent) {
assert(s);
/* The service is dead. Dang!
@@ -2306,6 +2291,41 @@ static void socket_reset_failed(Unit *u) {
s->result = SOCKET_SUCCESS;
}
+static void socket_trigger_notify(Unit *u, Unit *other) {
+ Socket *s = SOCKET(u);
+ Service *se = SERVICE(other);
+
+ 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)
+ return;
+
+ if (other->load_state != UNIT_LOADED ||
+ other->type != UNIT_SERVICE)
+ return;
+
+ if (se->state == SERVICE_FAILED)
+ socket_notify_service_dead(s, se->result == SERVICE_FAILURE_START_LIMIT);
+
+ if (se->state == SERVICE_DEAD ||
+ se->state == SERVICE_STOP ||
+ se->state == SERVICE_STOP_SIGTERM ||
+ se->state == SERVICE_STOP_SIGKILL ||
+ se->state == SERVICE_STOP_POST ||
+ se->state == SERVICE_FINAL_SIGTERM ||
+ se->state == SERVICE_FINAL_SIGKILL ||
+ se->state == SERVICE_AUTO_RESTART)
+ socket_notify_service_dead(s, false);
+
+ if (se->state == SERVICE_RUNNING)
+ socket_set_state(s, SOCKET_RUNNING);
+}
+
static int socket_kill(Unit *u, KillWho who, int signo, DBusError *error) {
return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error);
}
@@ -2356,8 +2376,9 @@ const UnitVTable socket_vtable = {
"Socket\0"
"Install\0",
+ .private_section = "Socket",
.exec_context_offset = offsetof(Socket, exec_context),
- .exec_section = "Socket",
+ .cgroup_context_offset = offsetof(Socket, cgroup_context),
.init = socket_init,
.done = socket_done,
@@ -2385,11 +2406,15 @@ const UnitVTable socket_vtable = {
.sigchld_event = socket_sigchld_event,
.timer_event = socket_timer_event,
+ .trigger_notify = socket_trigger_notify,
+
.reset_failed = socket_reset_failed,
.bus_interface = "org.freedesktop.systemd1.Socket",
.bus_message_handler = bus_socket_message_handler,
.bus_invalidating_properties = bus_socket_invalidating_properties,
+ .bus_set_property = bus_socket_set_property,
+ .bus_commit_properties = bus_socket_commit_properties,
.status_message_formats = {
/*.starting_stopping = {
diff --git a/src/core/socket.h b/src/core/socket.h
index 9d48cde0a6..3d7eadc9fe 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -102,6 +102,7 @@ struct Socket {
ExecCommand* exec_command[_SOCKET_EXEC_COMMAND_MAX];
ExecContext exec_context;
KillContext kill_context;
+ CGroupContext cgroup_context;
/* For Accept=no sockets refers to the one service we'll
activate. For Accept=yes sockets is either NULL, or filled
@@ -143,6 +144,7 @@ struct Socket {
size_t pipe_size;
char *bind_to_device;
char *tcp_congestion;
+ bool reuseport;
long mq_maxmsg;
long mq_msgsize;
@@ -154,13 +156,6 @@ struct Socket {
/* Called from the service code when collecting fds */
int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
-/* Called from the service when it shut down */
-void socket_notify_service_dead(Socket *s, bool failed_permanent);
-
-/* Called from the mount code figure out if a mount is a dependency of
- * any of the sockets of this socket */
-int socket_add_one_mount_link(Socket *s, Mount *m);
-
/* Called from the service code when a per-connection service ended */
void socket_connection_unref(Socket *s);
diff --git a/src/core/special.h b/src/core/special.h
index a9b50bce05..6d252e7baa 100644
--- a/src/core/special.h
+++ b/src/core/special.h
@@ -113,3 +113,9 @@
#define SPECIAL_RUNLEVEL3_TARGET "runlevel3.target"
#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target"
#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target"
+
+/* Where we add all our system units, users and machines by default */
+#define SPECIAL_SYSTEM_SLICE "system.slice"
+#define SPECIAL_USER_SLICE "user.slice"
+#define SPECIAL_MACHINE_SLICE "machine.slice"
+#define SPECIAL_ROOT_SLICE "-.slice"
diff --git a/src/core/swap.c b/src/core/swap.c
index d503fe20df..a68ab7cdf8 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -92,6 +92,7 @@ static void swap_init(Unit *u) {
s->exec_context.std_output = u->manager->default_std_output;
s->exec_context.std_error = u->manager->default_std_error;
kill_context_init(&s->kill_context);
+ cgroup_context_init(&s->cgroup_context);
s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1;
@@ -129,47 +130,13 @@ static void swap_done(Unit *u) {
exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
s->control_command = NULL;
+ cgroup_context_done(&s->cgroup_context);
+
swap_unwatch_control_pid(s);
unit_unwatch_timer(u, &s->timer_watch);
}
-int swap_add_one_mount_link(Swap *s, Mount *m) {
- int r;
-
- assert(s);
- assert(m);
-
- if (UNIT(s)->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
-
- if (is_device_path(s->what))
- return 0;
-
- if (!path_startswith(s->what, m->where))
- return 0;
-
- r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int swap_add_mount_links(Swap *s) {
- Unit *other;
- int r;
-
- assert(s);
-
- LIST_FOREACH(units_by_type, other, UNIT(s)->manager->units_by_type[UNIT_MOUNT])
- if ((r = swap_add_one_mount_link(s, MOUNT(other))) < 0)
- return r;
-
- return 0;
-}
-
static int swap_add_device_links(Swap *s) {
SwapParameters *p;
@@ -184,8 +151,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,
- !p->noauto && p->nofail &&
+ return unit_add_node_link(UNIT(s), s->what, !p->noauto &&
UNIT(s)->manager->running_as == SYSTEMD_SYSTEM);
else
/* File based swap devices need to be ordered after
@@ -195,6 +161,7 @@ static int swap_add_device_links(Swap *s) {
}
static int swap_add_default_dependencies(Swap *s) {
+ bool nofail = false, noauto = false;
int r;
assert(s);
@@ -209,6 +176,24 @@ static int swap_add_default_dependencies(Swap *s) {
if (r < 0)
return r;
+ if (s->from_fragment) {
+ SwapParameters *p = &s->parameters_fragment;
+
+ nofail = p->nofail;
+ noauto = p->noauto;
+ }
+
+ if (!noauto) {
+ if (nofail)
+ r = unit_add_dependency_by_name_inverse(UNIT(s),
+ UNIT_WANTS, SPECIAL_SWAP_TARGET, NULL, true);
+ else
+ r = unit_add_two_dependencies_by_name_inverse(UNIT(s),
+ UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SWAP_TARGET, NULL, true);
+ if (r < 0)
+ return r;
+ }
+
return 0;
}
@@ -279,15 +264,15 @@ static int swap_load(Unit *u) {
if ((r = unit_set_description(u, s->what)) < 0)
return r;
- r = swap_add_device_links(s);
+ r = unit_require_mounts_for(UNIT(s), s->what);
if (r < 0)
return r;
- r = swap_add_mount_links(s);
+ r = swap_add_device_links(s);
if (r < 0)
return r;
- r = unit_add_default_cgroups(u);
+ r = unit_add_default_slice(u);
if (r < 0)
return r;
@@ -589,6 +574,8 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
assert(c);
assert(_pid);
+ unit_realize_cgroup(UNIT(s));
+
r = unit_watch_timer(UNIT(s), CLOCK_MONOTONIC, true, s->timeout_usec, &s->timer_watch);
if (r < 0)
goto fail;
@@ -602,9 +589,8 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
true,
true,
UNIT(s)->manager->confirm_spawn,
- UNIT(s)->cgroup_bondings,
- UNIT(s)->cgroup_attributes,
- NULL,
+ UNIT(s)->manager->cgroup_supported,
+ UNIT(s)->cgroup_path,
UNIT(s)->id,
NULL,
&pid);
@@ -1052,7 +1038,7 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
(void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n");
for (i = 1;; i++) {
- char *dev = NULL, *d;
+ _cleanup_free_ char *dev = NULL, *d = NULL;
int prio = 0, k;
k = fscanf(m->proc_swaps,
@@ -1067,19 +1053,14 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) {
break;
log_warning("Failed to parse /proc/swaps:%u", i);
- free(dev);
continue;
}
d = cunescape(dev);
- free(dev);
-
if (!d)
return -ENOMEM;
k = swap_process_new_swap(m, d, prio, set_flags);
- free(d);
-
if (k < 0)
r = k;
}
@@ -1323,8 +1304,9 @@ const UnitVTable swap_vtable = {
"Swap\0"
"Install\0",
+ .private_section = "Swap",
.exec_context_offset = offsetof(Swap, exec_context),
- .exec_section = "Swap",
+ .cgroup_context_offset = offsetof(Swap, cgroup_context),
.no_alias = true,
.no_instances = true,
@@ -1358,6 +1340,8 @@ const UnitVTable swap_vtable = {
.bus_interface = "org.freedesktop.systemd1.Swap",
.bus_message_handler = bus_swap_message_handler,
.bus_invalidating_properties = bus_swap_invalidating_properties,
+ .bus_set_property = bus_swap_set_property,
+ .bus_commit_properties = bus_swap_commit_properties,
.following = swap_following,
.following_set = swap_following_set,
diff --git a/src/core/swap.h b/src/core/swap.h
index 121889d1d5..dd89535895 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -88,6 +88,7 @@ struct Swap {
ExecCommand exec_command[_SWAP_EXEC_COMMAND_MAX];
ExecContext exec_context;
KillContext kill_context;
+ CGroupContext cgroup_context;
SwapState state, deserialized_state;
@@ -106,8 +107,6 @@ struct Swap {
extern const UnitVTable swap_vtable;
-int swap_add_one_mount_link(Swap *s, Mount *m);
-
int swap_dispatch_reload(Manager *m);
int swap_fd_event(Manager *m, int events);
diff --git a/src/core/system.conf b/src/core/system.conf
index 508e0f5fa4..7b03c8782b 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -17,14 +17,14 @@
#ShowStatus=yes
#CrashChVT=1
#CPUAffinity=1 2
-#DefaultControllers=cpu
#DefaultStandardOutput=journal
#DefaultStandardError=inherit
-#JoinControllers=cpu,cpuacct,cpuset net_cls,net_prio
+#JoinControllers=cpu,cpuacct net_cls,net_prio
#RuntimeWatchdogSec=0
#ShutdownWatchdogSec=10min
#CapabilityBoundingSet=
#TimerSlackNSec=
+#DefaultEnvironment=
#DefaultLimitCPU=
#DefaultLimitFSIZE=
#DefaultLimitDATA=
diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in
index 2f49d5df52..de0f6494e9 100644
--- a/src/core/systemd.pc.in
+++ b/src/core/systemd.pc.in
@@ -16,6 +16,9 @@ systemdsystemconfdir=@pkgsysconfdir@/system
systemduserconfdir=@pkgsysconfdir@/user
systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system
systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user
+systemdsystemgeneratordir=@systemgeneratordir@
+systemdusergeneratordir=@usergeneratordir@
+catalogdir=@catalogdir@
Name: systemd
Description: systemd System and Service Manager
diff --git a/src/core/transaction.c b/src/core/transaction.c
index fa97b69755..203070fa26 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -344,7 +344,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
assert(!j->transaction_prev);
/* Does a recursive sweep through the ordering graph, looking
- * for a cycle. If we find cycle we try to break it. */
+ * for a cycle. If we find a cycle we try to break it. */
/* Have we seen this before? */
if (j->generation == generation) {
@@ -371,7 +371,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
/* logging for j not k here here to provide consistent narrative */
log_info_unit(j->unit->id,
- "Walked on cycle path to %s/%s",
+ "Found dependency on %s/%s",
k->unit->id, job_type_to_string(k->type));
if (!delete &&
@@ -733,8 +733,11 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e
* feature for cosmetics, not actually useful for
* anything beyond that. */
- if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0)
+ 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);
+ }
}
return 0;
@@ -851,6 +854,7 @@ int transaction_add_job_and_dependencies(
if (unit->load_state != UNIT_LOADED &&
unit->load_state != UNIT_ERROR &&
+ unit->load_state != UNIT_NOT_FOUND &&
unit->load_state != UNIT_MASKED) {
dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
return -EINVAL;
@@ -866,6 +870,14 @@ int transaction_add_job_and_dependencies(
return -EINVAL;
}
+ if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND) {
+ dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
+ "Unit %s failed to load: %s.",
+ unit->id,
+ strerror(-unit->load_error));
+ return -EINVAL;
+ }
+
if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
return -EADDRNOTAVAIL;
diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c
index 85a05b872a..1a29a986e9 100644
--- a/src/core/unit-printf.c
+++ b/src/core/unit-printf.c
@@ -27,99 +27,161 @@
#include "unit-name.h"
#include "unit-printf.h"
#include "macro.h"
+#include "cgroup-util.h"
+#include "special.h"
-static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) {
+static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
+ char *n;
+
assert(u);
- return unit_name_to_prefix_and_instance(u->id);
+ n = unit_name_to_prefix_and_instance(u->id);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 0;
}
-static char *specifier_prefix(char specifier, void *data, void *userdata) {
+static int specifier_prefix(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
+ char *n;
+
assert(u);
- return unit_name_to_prefix(u->id);
+ n = unit_name_to_prefix(u->id);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 0;
}
-static char *specifier_prefix_unescaped(char specifier, void *data, void *userdata) {
+static int specifier_prefix_unescaped(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
- char *p, *r;
+ _cleanup_free_ char *p = NULL;
+ char *n;
assert(u);
p = unit_name_to_prefix(u->id);
if (!p)
- return NULL;
+ return -ENOMEM;
- r = unit_name_unescape(p);
- free(p);
+ n = unit_name_unescape(p);
+ if (!n)
+ return -ENOMEM;
- return r;
+ *ret = n;
+ return 0;
}
-static char *specifier_instance_unescaped(char specifier, void *data, void *userdata) {
+static int specifier_instance_unescaped(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
+ char *n;
+
assert(u);
if (u->instance)
- return unit_name_unescape(u->instance);
+ n = unit_name_unescape(u->instance);
+ else
+ n = strdup("");
+
+ if (!n)
+ return -ENOMEM;
- return strdup("");
+ *ret = n;
+ return 0;
}
-static char *specifier_filename(char specifier, void *data, void *userdata) {
+static int specifier_filename(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
+ char *n;
+
assert(u);
if (u->instance)
- return unit_name_path_unescape(u->instance);
+ n = unit_name_path_unescape(u->instance);
+ else
+ n = unit_name_to_path(u->id);
- return unit_name_to_path(u->id);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 0;
}
-static char *specifier_cgroup(char specifier, void *data, void *userdata) {
+static int specifier_cgroup(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
+ char *n;
+
assert(u);
- return unit_default_cgroup_path(u);
+ n = unit_default_cgroup_path(u);
+ if (!n)
+ return -ENOMEM;
+
+ *ret = n;
+ return 0;
}
-static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
+static int specifier_cgroup_root(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
- char *p;
+ const char *slice;
+ char *n;
+ int r;
+
assert(u);
- if (specifier == 'r')
- return strdup(u->manager->cgroup_hierarchy);
+ slice = unit_slice_name(u);
+ if (specifier == 'R' || !slice)
+ n = strdup(u->manager->cgroup_root);
+ else {
+ _cleanup_free_ char *p = NULL;
- if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0)
- return strdup("");
+ r = cg_slice_to_path(slice, &p);
+ if (r < 0)
+ return r;
- if (streq(p, "/")) {
- free(p);
- return strdup("");
+ n = strjoin(u->manager->cgroup_root, "/", p, NULL);
+ if (!n)
+ return -ENOMEM;
}
- return p;
+ *ret = n;
+ return 0;
}
-static char *specifier_runtime(char specifier, void *data, void *userdata) {
+static int specifier_runtime(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
+ char *n = NULL;
+
assert(u);
if (u->manager->running_as == SYSTEMD_USER) {
const char *e;
e = getenv("XDG_RUNTIME_DIR");
- if (e)
- return strdup(e);
+ if (e) {
+ n = strdup(e);
+ if (!n)
+ return -ENOMEM;
+ }
+ }
+
+ if (!n) {
+ n = strdup("/run");
+ if (!n)
+ return -ENOMEM;
}
- return strdup("/run");
+ *ret = n;
+ return 0;
}
-static char *specifier_user_name(char specifier, void *data, void *userdata) {
+static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
ExecContext *c;
int r;
@@ -141,26 +203,31 @@ static char *specifier_user_name(char specifier, void *data, void *userdata) {
/* fish username from passwd */
r = get_user_creds(&username, &uid, NULL, NULL, NULL);
if (r < 0)
- return NULL;
+ return r;
switch (specifier) {
case 'U':
if (asprintf(&printed, "%d", uid) < 0)
- return NULL;
+ return -ENOMEM;
break;
case 'u':
printed = strdup(username);
break;
}
- return printed;
+ if (!printed)
+ return -ENOMEM;
+
+ *ret = printed;
+ return 0;
}
-static char *specifier_user_home(char specifier, void *data, void *userdata) {
+static int specifier_user_home(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
ExecContext *c;
int r;
const char *username, *home;
+ char *n;
assert(u);
@@ -172,25 +239,31 @@ static char *specifier_user_home(char specifier, void *data, void *userdata) {
r = get_home_dir(&h);
if (r < 0)
- return NULL;
+ return r;
- return h;
+ *ret = h;
+ return 0;
}
username = c->user;
r = get_user_creds(&username, NULL, NULL, &home, NULL);
if (r < 0)
- return NULL;
+ return r;
+
+ n = strdup(home);
+ if (!n)
+ return -ENOMEM;
- return strdup(home);
+ *ret = n;
+ return 0;
}
-static char *specifier_user_shell(char specifier, void *data, void *userdata) {
+static int specifier_user_shell(char specifier, void *data, void *userdata, char **ret) {
Unit *u = userdata;
ExecContext *c;
int r;
const char *username, *shell;
- char *ret;
+ char *n;
assert(u);
@@ -203,27 +276,18 @@ static char *specifier_user_shell(char specifier, void *data, void *userdata) {
/* return /bin/sh for root, otherwise the value from passwd */
r = get_user_creds(&username, NULL, NULL, NULL, &shell);
- if (r < 0) {
- log_warning_unit(u->id,
- "Failed to determine shell: %s",
- strerror(-r));
- return NULL;
- }
-
- if (!path_is_absolute(shell)) {
- log_warning_unit(u->id,
- "Shell %s is not absolute, ignoring.",
- shell);
- }
+ if (r < 0)
+ return r;
- ret = strdup(shell);
- if (!ret)
- log_oom();
+ n = strdup(shell);
+ if (!n)
+ return -ENOMEM;
- return ret;
+ *ret = n;
+ return 0;
}
-char *unit_name_printf(Unit *u, const char* format) {
+int unit_name_printf(Unit *u, const char* format, char **ret) {
/*
* This will use the passed string as format string and
@@ -245,19 +309,20 @@ char *unit_name_printf(Unit *u, const char* format) {
assert(u);
assert(format);
+ assert(ret);
- return specifier_printf(format, table, u);
+ return specifier_printf(format, table, u, ret);
}
-char *unit_full_printf(Unit *u, const char *format) {
+int unit_full_printf(Unit *u, const char *format, char **ret) {
/* This is similar to unit_name_printf() but also supports
* unescaping. Also, adds a couple of additional codes:
*
* %f the the instance if set, otherwise the id
* %c cgroup path of unit
- * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711")
- * %R parent of root cgroup path (e.g. "/usr/lennart/shared")
+ * %r where units in this slice are place 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
@@ -266,6 +331,7 @@ char *unit_full_printf(Unit *u, const char *format) {
* %m the machine ID of the running system
* %H the host name of the running system
* %b the boot ID of the running system
+ * %v `uname -r` of the running system
*/
const Specifier table[] = {
@@ -289,17 +355,21 @@ char *unit_full_printf(Unit *u, const char *format) {
{ 'm', specifier_machine_id, NULL },
{ 'H', specifier_host_name, NULL },
{ 'b', specifier_boot_id, NULL },
- { 0, NULL, NULL }
+ { 'v', specifier_kernel_release, NULL },
+ {}
};
+ assert(u);
assert(format);
+ assert(ret);
- return specifier_printf(format, table, u);
+ return specifier_printf(format, table, u, ret);
}
-char **unit_full_printf_strv(Unit *u, char **l) {
+int unit_full_printf_strv(Unit *u, char **l, char ***ret) {
size_t n;
char **r, **i, **j;
+ int q;
/* Applies unit_full_printf to every entry in l */
@@ -308,22 +378,22 @@ char **unit_full_printf_strv(Unit *u, char **l) {
n = strv_length(l);
r = new(char*, n+1);
if (!r)
- return NULL;
+ return -ENOMEM;
for (i = l, j = r; *i; i++, j++) {
- *j = unit_full_printf(u, *i);
- if (!*j)
+ q = unit_full_printf(u, *i, j);
+ if (q < 0)
goto fail;
}
*j = NULL;
- return r;
+ *ret = r;
+ return 0;
fail:
for (j--; j >= r; j--)
free(*j);
free(r);
-
- return NULL;
+ return q;
}
diff --git a/src/core/unit-printf.h b/src/core/unit-printf.h
index d2f4ccd178..51acad63e9 100644
--- a/src/core/unit-printf.h
+++ b/src/core/unit-printf.h
@@ -23,6 +23,6 @@
#include "unit.h"
-char *unit_name_printf(Unit *u, const char* text);
-char *unit_full_printf(Unit *u, const char *text);
-char **unit_full_printf_strv(Unit *u, char **l);
+int unit_name_printf(Unit *u, const char* text, char **ret);
+int unit_full_printf(Unit *u, const char *text, char **ret);
+int unit_full_printf_strv(Unit *u, char **l, char ***ret);
diff --git a/src/core/unit.c b/src/core/unit.c
index 9b36b225fa..4b9771076a 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -44,7 +44,6 @@
#include "special.h"
#include "cgroup-util.h"
#include "missing.h"
-#include "cgroup-attr.h"
#include "mkdir.h"
#include "label.h"
#include "fileio-label.h"
@@ -60,7 +59,9 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_AUTOMOUNT] = &automount_vtable,
[UNIT_SNAPSHOT] = &snapshot_vtable,
[UNIT_SWAP] = &swap_vtable,
- [UNIT_PATH] = &path_vtable
+ [UNIT_PATH] = &path_vtable,
+ [UNIT_SLICE] = &slice_vtable,
+ [UNIT_SCOPE] = &scope_vtable
};
Unit *unit_new(Manager *m, size_t size) {
@@ -188,7 +189,8 @@ fail:
}
int unit_choose_id(Unit *u, const char *name) {
- char *s, *t = NULL, *i;
+ char *s, *i;
+ _cleanup_free_ char *t = NULL;
int r;
assert(u);
@@ -207,7 +209,6 @@ int unit_choose_id(Unit *u, const char *name) {
/* Selects one of the names of this unit as the id */
s = set_get(u->names, (char*) name);
- free(t);
if (!s)
return -ENOENT;
@@ -230,8 +231,13 @@ int unit_set_description(Unit *u, const char *description) {
assert(u);
- if (!(s = strdup(description)))
- return -ENOMEM;
+ if (isempty(description))
+ s = NULL;
+ else {
+ s = strdup(description);
+ if (!s)
+ return -ENOMEM;
+ }
free(u->description);
u->description = s;
@@ -305,9 +311,6 @@ void unit_add_to_gc_queue(Unit *u) {
u->in_gc_queue = true;
u->manager->n_in_gc_queue ++;
-
- if (u->manager->gc_queue_timestamp <= 0)
- u->manager->gc_queue_timestamp = now(CLOCK_MONOTONIC);
}
void unit_add_to_dbus_queue(Unit *u) {
@@ -348,6 +351,57 @@ static void bidi_set_free(Unit *u, Set *s) {
set_free(s);
}
+static void unit_remove_transient(Unit *u) {
+ char **i;
+
+ assert(u);
+
+ if (!u->transient)
+ return;
+
+ if (u->fragment_path)
+ unlink(u->fragment_path);
+
+ STRV_FOREACH(i, u->dropin_paths) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ unlink(*i);
+
+ r = path_get_parent(*i, &p);
+ if (r >= 0)
+ rmdir(p);
+ }
+}
+
+static void unit_free_requires_mounts_for(Unit *u) {
+ char **j;
+
+ STRV_FOREACH(j, u->requires_mounts_for) {
+ char s[strlen(*j) + 1];
+
+ PATH_FOREACH_PREFIX_MORE(s, *j) {
+ char *y;
+ Set *x;
+
+ x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y);
+ if (!x)
+ continue;
+
+ set_remove(x, u);
+
+ if (set_isempty(x)) {
+ hashmap_remove(u->manager->units_requiring_mounts_for, y);
+ free(y);
+ set_free(x);
+ }
+ }
+ }
+
+ strv_free(u->requires_mounts_for);
+ u->requires_mounts_for = NULL;
+}
+
void unit_free(Unit *u) {
UnitDependency d;
Iterator i;
@@ -355,12 +409,17 @@ void unit_free(Unit *u) {
assert(u);
+ if (u->manager->n_reloading <= 0)
+ unit_remove_transient(u);
+
bus_unit_send_removed_signal(u);
if (u->load_state != UNIT_STUB)
if (UNIT_VTABLE(u)->done)
UNIT_VTABLE(u)->done(u);
+ unit_free_requires_mounts_for(u);
+
SET_FOREACH(t, u->names, i)
hashmap_remove_value(u->manager->units, t, u);
@@ -379,11 +438,6 @@ void unit_free(Unit *u) {
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
bidi_set_free(u, u->dependencies[d]);
- if (u->requires_mounts_for) {
- LIST_REMOVE(Unit, has_requires_mounts_for, u->manager->has_requires_mounts_for, u);
- strv_free(u->requires_mounts_for);
- }
-
if (u->type != _UNIT_TYPE_INVALID)
LIST_REMOVE(Unit, units_by_type, u->manager->units_by_type[u->type], u);
@@ -401,8 +455,13 @@ void unit_free(Unit *u) {
u->manager->n_in_gc_queue--;
}
- cgroup_bonding_free_list(u->cgroup_bondings, u->manager->n_reloading <= 0);
- cgroup_attribute_free_list(u->cgroup_attributes);
+ if (u->in_cgroup_queue)
+ LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u);
+
+ if (u->cgroup_path) {
+ hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
+ free(u->cgroup_path);
+ }
free(u->description);
strv_free(u->documentation);
@@ -415,6 +474,8 @@ void unit_free(Unit *u) {
condition_free_list(u->conditions);
+ unit_ref_unset(&u->slice);
+
while (u->refs)
unit_ref_unset(u->refs);
@@ -521,7 +582,7 @@ int unit_merge(Unit *u, Unit *other) {
return -EINVAL;
if (other->load_state != UNIT_STUB &&
- other->load_state != UNIT_ERROR)
+ other->load_state != UNIT_NOT_FOUND)
return -EEXIST;
if (other->job)
@@ -562,7 +623,7 @@ int unit_merge(Unit *u, Unit *other) {
int unit_merge_by_name(Unit *u, const char *name) {
Unit *other;
int r;
- char *s = NULL;
+ _cleanup_free_ char *s = NULL;
assert(u);
assert(name);
@@ -577,12 +638,12 @@ int unit_merge_by_name(Unit *u, const char *name) {
name = s;
}
- if (!(other = manager_get_unit(u->manager, name)))
+ other = manager_get_unit(u->manager, name);
+ if (!other)
r = unit_add_name(u, name);
else
r = unit_merge(u, other);
- free(s);
return r;
}
@@ -640,7 +701,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
char *t, **j;
UnitDependency d;
Iterator i;
- char *p2;
+ _cleanup_free_ char *p2 = NULL;
const char *prefix2;
char
timestamp1[FORMAT_TIMESTAMP_MAX],
@@ -669,7 +730,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tActive Exit Timestamp: %s\n"
"%s\tInactive Enter Timestamp: %s\n"
"%s\tGC Check Good: %s\n"
- "%s\tNeed Daemon Reload: %s\n",
+ "%s\tNeed Daemon Reload: %s\n"
+ "%s\tTransient: %s\n"
+ "%s\tSlice: %s\n"
+ "%s\tCGroup: %s\n"
+ "%s\tCGroup realized: %s\n"
+ "%s\tCGroup mask: 0x%x\n",
prefix, u->id,
prefix, unit_description(u),
prefix, strna(u->instance),
@@ -680,7 +746,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)),
prefix, yes_no(unit_check_gc(u)),
- prefix, yes_no(unit_need_daemon_reload(u)));
+ prefix, yes_no(unit_need_daemon_reload(u)),
+ prefix, yes_no(u->transient),
+ prefix, strna(unit_slice_name(u)),
+ prefix, strna(u->cgroup_path),
+ prefix, yes_no(u->cgroup_realized),
+ prefix, u->cgroup_mask);
SET_FOREACH(t, u->names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);
@@ -730,8 +801,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
}
if (u->load_state == UNIT_LOADED) {
- CGroupBonding *b;
- CGroupAttribute *a;
fprintf(f,
"%s\tStopWhenUnneeded: %s\n"
@@ -749,20 +818,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(u->ignore_on_isolate),
prefix, yes_no(u->ignore_on_snapshot));
- LIST_FOREACH(by_unit, b, u->cgroup_bondings)
- fprintf(f, "%s\tControlGroup: %s:%s\n",
- prefix, b->controller, b->path);
-
- LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- _cleanup_free_ char *v = NULL;
-
- if (a->semantics && a->semantics->map_write)
- a->semantics->map_write(a->semantics, a->value, &v);
-
- fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
- prefix, a->controller, a->name, v ? v : a->value);
- }
-
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
@@ -780,7 +835,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->nop_job)
job_dump(u->nop_job, f, prefix2);
- free(p2);
}
/* Common implementation for multiple backends */
@@ -790,14 +844,16 @@ int unit_load_fragment_and_dropin(Unit *u) {
assert(u);
/* Load a .service file */
- if ((r = unit_load_fragment(u)) < 0)
+ r = unit_load_fragment(u);
+ if (r < 0)
return r;
if (u->load_state == UNIT_STUB)
return -ENOENT;
/* Load drop-in directory data */
- if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
+ r = unit_load_dropin(unit_follow_merge(u));
+ if (r < 0)
return r;
return 0;
@@ -813,14 +869,16 @@ int unit_load_fragment_and_dropin_optional(Unit *u) {
* something can be loaded or not doesn't matter. */
/* Load a .service file */
- if ((r = unit_load_fragment(u)) < 0)
+ r = unit_load_fragment(u);
+ if (r < 0)
return r;
if (u->load_state == UNIT_STUB)
u->load_state = UNIT_LOADED;
/* Load drop-in directory data */
- if ((r = unit_load_dropin(unit_follow_merge(u))) < 0)
+ r = unit_load_dropin(unit_follow_merge(u));
+ if (r < 0)
return r;
return 0;
@@ -853,6 +911,7 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) {
}
static int unit_add_default_dependencies(Unit *u) {
+
static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUIRED_BY_OVERRIDABLE,
@@ -868,9 +927,21 @@ static int unit_add_default_dependencies(Unit *u) {
assert(u);
for (k = 0; k < ELEMENTSOF(deps); k++)
- SET_FOREACH(target, u->dependencies[deps[k]], i)
- if ((r = unit_add_default_target_dependency(u, target)) < 0)
+ SET_FOREACH(target, u->dependencies[deps[k]], i) {
+ r = unit_add_default_target_dependency(u, target);
+ if (r < 0)
return r;
+ }
+
+ if (u->default_dependencies && unit_get_cgroup_context(u)) {
+ if (UNIT_ISSET(u->slice))
+ r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true);
+ else
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, SPECIAL_ROOT_SLICE, NULL, true);
+
+ if (r < 0)
+ return r;
+ }
return 0;
}
@@ -891,34 +962,38 @@ int unit_load(Unit *u) {
if (u->load_state != UNIT_STUB)
return 0;
- if (UNIT_VTABLE(u)->load)
- if ((r = UNIT_VTABLE(u)->load(u)) < 0)
+ if (UNIT_VTABLE(u)->load) {
+ r = UNIT_VTABLE(u)->load(u);
+ if (r < 0)
goto fail;
+ }
if (u->load_state == UNIT_STUB) {
r = -ENOENT;
goto fail;
}
- if (u->load_state == UNIT_LOADED &&
- u->default_dependencies)
- if ((r = unit_add_default_dependencies(u)) < 0)
- goto fail;
-
if (u->load_state == UNIT_LOADED) {
+
+ if (u->default_dependencies) {
+ r = unit_add_default_dependencies(u);
+ if (r < 0)
+ goto fail;
+ }
+
r = unit_add_mount_links(u);
if (r < 0)
- return r;
- }
+ goto fail;
- if (u->on_failure_isolate &&
- set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
+ if (u->on_failure_isolate &&
+ set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
- log_error_unit(u->id,
- "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id);
+ log_error_unit(u->id,
+ "More than one OnFailure= dependencies specified for %s but OnFailureIsolate= enabled. Refusing.", u->id);
- r = -EINVAL;
- goto fail;
+ r = -EINVAL;
+ goto fail;
+ }
}
assert((u->load_state != UNIT_MERGED) == !u->merged_into);
@@ -929,7 +1004,7 @@ int unit_load(Unit *u) {
return 0;
fail:
- u->load_state = UNIT_ERROR;
+ u->load_state = u->load_state == UNIT_STUB ? UNIT_NOT_FOUND : UNIT_ERROR;
u->load_error = r;
unit_add_to_dbus_queue(u);
unit_add_to_gc_queue(u);
@@ -944,7 +1019,7 @@ bool unit_condition_test(Unit *u) {
assert(u);
dual_timestamp_get(&u->condition_timestamp);
- u->condition_result = condition_test_list(u->conditions);
+ u->condition_result = condition_test_list(u->id, u->conditions);
return u->condition_result;
}
@@ -1073,7 +1148,8 @@ int unit_start(Unit *u) {
}
/* Forward to the main object, if we aren't it. */
- if ((following = unit_following(u))) {
+ following = unit_following(u);
+ if (following) {
log_debug_unit(u->id, "Redirecting start request from %s to %s.",
u->id, following->id);
return unit_start(following);
@@ -1368,15 +1444,19 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
}
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- cgroup_bonding_trim_list(u->cgroup_bondings, true);
+ unit_destroy_cgroup(u);
if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
ExecContext *ec = unit_get_exec_context(u);
if (ec && exec_context_may_touch_console(ec)) {
- if (UNIT_IS_INACTIVE_OR_FAILED(ns))
- m->n_on_console--;
- else
- m->n_on_console++;
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
+ m->n_on_console --;
+
+ if (m->n_on_console == 0)
+ /* unset no_console_output flag, since the console is free */
+ m->no_console_output = 0;
+ } else
+ m->n_on_console ++;
}
}
@@ -1857,7 +1937,7 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
- char *s;
+ _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
@@ -1866,19 +1946,17 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
return -ENOMEM;
if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
+ return r;
r = unit_add_two_dependencies(u, d, e, other, add_reference);
-finish:
- free(s);
return r;
}
int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
- char *s;
+ _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
@@ -1887,19 +1965,17 @@ int unit_add_dependency_by_name_inverse(Unit *u, UnitDependency d, const char *n
return -ENOMEM;
if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
+ return r;
r = unit_add_dependency(other, d, u, add_reference);
-finish:
- free(s);
return r;
}
int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
Unit *other;
int r;
- char *s;
+ _cleanup_free_ char *s = NULL;
assert(u);
assert(name || path);
@@ -1908,13 +1984,11 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep
return -ENOMEM;
if ((r = manager_load_unit(u->manager, name, path, NULL, &other)) < 0)
- goto finish;
+ return r;
if ((r = unit_add_two_dependencies(other, d, e, u, add_reference)) < 0)
- goto finish;
+ return r;
-finish:
- free(s);
return r;
}
@@ -1938,351 +2012,91 @@ char *unit_dbus_path(Unit *u) {
return unit_dbus_path_from_name(u->id);
}
-static int unit_add_cgroup(Unit *u, CGroupBonding *b) {
+char *unit_default_cgroup_path(Unit *u) {
+ _cleanup_free_ char *escaped = NULL, *slice = NULL;
int r;
assert(u);
- assert(b);
-
- assert(b->path);
- if (!b->controller) {
- b->controller = strdup(SYSTEMD_CGROUP_CONTROLLER);
- if (!b->controller)
- return log_oom();
-
- b->ours = true;
- }
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE))
+ return strdup(u->manager->cgroup_root);
- /* Ensure this hasn't been added yet */
- assert(!b->unit);
-
- if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
- CGroupBonding *l;
-
- l = hashmap_get(u->manager->cgroup_bondings, b->path);
- LIST_PREPEND(CGroupBonding, by_path, l, b);
-
- r = hashmap_replace(u->manager->cgroup_bondings, b->path, l);
- if (r < 0) {
- LIST_REMOVE(CGroupBonding, by_path, l, b);
- return r;
- }
+ 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;
}
- LIST_PREPEND(CGroupBonding, by_unit, u->cgroup_bondings, b);
- b->unit = u;
-
- return 0;
-}
-
-char *unit_default_cgroup_path(Unit *u) {
- _cleanup_free_ char *escaped_instance = NULL;
-
- assert(u);
-
- escaped_instance = cg_escape(u->id);
- if (!escaped_instance)
+ escaped = cg_escape(u->id);
+ if (!escaped)
return NULL;
- if (u->instance) {
- _cleanup_free_ char *t = NULL, *escaped_template = NULL;
-
- t = unit_name_template(u->id);
- if (!t)
- return NULL;
-
- escaped_template = cg_escape(t);
- if (!escaped_template)
- return NULL;
-
- return strjoin(u->manager->cgroup_hierarchy, "/", escaped_template, "/", escaped_instance, NULL);
- } else
- return strjoin(u->manager->cgroup_hierarchy, "/", escaped_instance, NULL);
+ if (slice)
+ return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
+ else
+ return strjoin(u->manager->cgroup_root, "/", escaped, NULL);
}
-int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) {
- char *controller = NULL, *path = NULL;
- CGroupBonding *b = NULL;
- bool ours = false;
+int unit_add_default_slice(Unit *u) {
+ _cleanup_free_ char *b = NULL;
+ const char *slice_name;
+ Unit *slice;
int r;
assert(u);
- assert(name);
-
- r = cg_split_spec(name, &controller, &path);
- if (r < 0)
- return r;
-
- if (!path) {
- path = unit_default_cgroup_path(u);
- ours = true;
- }
-
- if (!controller) {
- controller = strdup("systemd");
- ours = true;
- }
-
- if (!path || !controller) {
- free(path);
- free(controller);
- return log_oom();
- }
- if (streq(controller, "systemd")) {
- /* Within the systemd unit hierarchy we do not allow changes. */
- if (path_startswith(path, "/system")) {
- log_warning_unit(u->id, "Manipulating the systemd:/system cgroup hierarchy is not permitted.");
- free(path);
- free(controller);
- return -EPERM;
- }
- }
-
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (b) {
- if (streq(path, b->path)) {
- free(path);
- free(controller);
-
- if (ret)
- *ret = b;
- return 0;
- }
-
- if (overwrite && !b->essential) {
- free(controller);
-
- free(b->path);
- b->path = path;
-
- b->ours = ours;
- b->realized = false;
-
- if (ret)
- *ret = b;
-
- return 1;
- }
-
- r = -EEXIST;
- b = NULL;
- goto fail;
- }
-
- b = new0(CGroupBonding, 1);
- if (!b) {
- r = -ENOMEM;
- goto fail;
- }
-
- b->controller = controller;
- b->path = path;
- b->ours = ours;
- b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
-
- r = unit_add_cgroup(u, b);
- if (r < 0)
- goto fail;
-
- if (ret)
- *ret = b;
-
- return 1;
-
-fail:
- free(path);
- free(controller);
- free(b);
-
- return r;
-}
-
-static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
- CGroupBonding *b = NULL;
- int r = -ENOMEM;
-
- assert(u);
-
- if (controller && !cg_controller_is_valid(controller, true))
- return -EINVAL;
-
- if (!controller)
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
- if (cgroup_bonding_find_list(u->cgroup_bondings, controller))
+ if (UNIT_ISSET(u->slice))
return 0;
- b = new0(CGroupBonding, 1);
- if (!b)
- return -ENOMEM;
-
- b->controller = strdup(controller);
- if (!b->controller)
- goto fail;
-
- b->path = unit_default_cgroup_path(u);
- if (!b->path)
- goto fail;
-
- b->ours = true;
- b->essential = streq(controller, SYSTEMD_CGROUP_CONTROLLER);
-
- r = unit_add_cgroup(u, b);
- if (r < 0)
- goto fail;
+ if (!unit_get_cgroup_context(u))
+ return 0;
- return 1;
+ if (u->instance) {
+ _cleanup_free_ char *prefix = NULL, *escaped = NULL;
-fail:
- free(b->path);
- free(b->controller);
- free(b);
+ /* Implicitly place all instantiated units in their
+ * own per-template slice */
- return r;
-}
-
-int unit_add_default_cgroups(Unit *u) {
- CGroupAttribute *a;
- char **c;
- int r;
+ prefix = unit_name_to_prefix(u->id);
+ if (!prefix)
+ return -ENOMEM;
- assert(u);
+ /* The prefix is already escaped, but it might include
+ * "-" which has a special meaning for slice units,
+ * hence escape it here extra. */
+ escaped = strreplace(prefix, "-", "\\x2d");
+ if (!escaped)
+ return -ENOMEM;
- /* Adds in the default cgroups, if they weren't specified
- * otherwise. */
+ if (u->manager->running_as == SYSTEMD_SYSTEM)
+ b = strjoin("system-", escaped, ".slice", NULL);
+ else
+ b = strappend(escaped, ".slice");
+ if (!b)
+ return -ENOMEM;
- if (!u->manager->cgroup_hierarchy)
- return 0;
+ slice_name = b;
+ } else
+ slice_name =
+ u->manager->running_as == SYSTEMD_SYSTEM
+ ? SPECIAL_SYSTEM_SLICE
+ : SPECIAL_ROOT_SLICE;
- r = unit_add_one_default_cgroup(u, NULL);
+ r = manager_load_unit(u->manager, slice_name, NULL, NULL, &slice);
if (r < 0)
return r;
- STRV_FOREACH(c, u->manager->default_controllers)
- unit_add_one_default_cgroup(u, *c);
-
- LIST_FOREACH(by_unit, a, u->cgroup_attributes)
- unit_add_one_default_cgroup(u, a->controller);
-
+ unit_ref_set(&u->slice, slice);
return 0;
}
-CGroupBonding* unit_get_default_cgroup(Unit *u) {
- assert(u);
-
- return cgroup_bonding_find_list(u->cgroup_bondings, NULL);
-}
-
-int unit_add_cgroup_attribute(
- Unit *u,
- const CGroupSemantics *semantics,
- const char *controller,
- const char *name,
- const char *value,
- CGroupAttribute **ret) {
-
- _cleanup_free_ char *c = NULL;
- CGroupAttribute *a;
- int r;
-
+const char *unit_slice_name(Unit *u) {
assert(u);
- assert(value);
-
- if (semantics) {
- /* Semantics always take precedence */
- if (semantics->name)
- name = semantics->name;
-
- if (semantics->controller)
- controller = semantics->controller;
- }
-
- if (!name)
- return -EINVAL;
-
- if (!controller) {
- r = cg_controller_from_attr(name, &c);
- if (r < 0)
- return -EINVAL;
-
- controller = c;
- }
-
- if (!controller ||
- streq(controller, SYSTEMD_CGROUP_CONTROLLER) ||
- streq(controller, "systemd"))
- return -EINVAL;
-
- if (!filename_is_safe(name))
- return -EINVAL;
-
- if (!cg_controller_is_valid(controller, false))
- return -EINVAL;
-
- /* Check if this attribute already exists. Note that we will
- * explicitly check for the value here too, as there are
- * attributes which accept multiple values. */
- a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name);
- if (a) {
- if (streq(value, a->value)) {
- /* Exactly the same value is always OK, let's ignore this */
- if (ret)
- *ret = a;
-
- return 0;
- }
-
- if (semantics && !semantics->multiple) {
- char *v;
-
- /* If this is a single-item entry, we can
- * simply patch the existing attribute */
-
- v = strdup(value);
- if (!v)
- return -ENOMEM;
-
- free(a->value);
- a->value = v;
-
- if (ret)
- *ret = a;
- return 1;
- }
- }
- a = new0(CGroupAttribute, 1);
- if (!a)
- return -ENOMEM;
-
- if (c) {
- a->controller = c;
- c = NULL;
- } else
- a->controller = strdup(controller);
-
- a->name = strdup(name);
- a->value = strdup(value);
-
- if (!a->controller || !a->name || !a->value) {
- free(a->controller);
- free(a->name);
- free(a->value);
- free(a);
- return -ENOMEM;
- }
-
- a->semantics = semantics;
- a->unit = u;
-
- LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
-
- if (ret)
- *ret = a;
+ if (!UNIT_ISSET(u->slice))
+ return NULL;
- return 1;
+ return UNIT_DEREF(u->slice)->id;
}
int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
@@ -2359,7 +2173,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (!unit_can_serialize(u))
return 0;
- if ((r = UNIT_VTABLE(u)->serialize(u, f, fds)) < 0)
+ r = UNIT_VTABLE(u)->serialize(u, f, fds);
+ if (r < 0)
return r;
@@ -2384,6 +2199,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (dual_timestamp_is_set(&u->condition_timestamp))
unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result));
+ unit_serialize_item(u, f, "transient", yes_no(u->transient));
+
+ if (u->cgroup_path)
+ unit_serialize_item(u, f, "cgroup", u->cgroup_path);
+
/* End marker */
fputc('\n', f);
return 0;
@@ -2506,22 +2326,47 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
} else if (streq(l, "condition-result")) {
int b;
- if ((b = parse_boolean(v)) < 0)
+ b = parse_boolean(v);
+ if (b < 0)
log_debug("Failed to parse condition result value %s", v);
else
u->condition_result = b;
continue;
+
+ } else if (streq(l, "transient")) {
+ int b;
+
+ b = parse_boolean(v);
+ if (b < 0)
+ log_debug("Failed to parse transient bool %s", v);
+ else
+ u->transient = b;
+
+ continue;
+ } else if (streq(l, "cgroup")) {
+ char *s;
+
+ s = strdup(v);
+ if (!s)
+ return -ENOMEM;
+
+ free(u->cgroup_path);
+ u->cgroup_path = s;
+
+ assert(hashmap_put(u->manager->cgroup_unit, s, u) == 1);
+ continue;
}
- if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0)
+ r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
+ if (r < 0)
return r;
}
}
int unit_add_node_link(Unit *u, const char *what, bool wants) {
Unit *device;
- char *e;
+ _cleanup_free_ char *e = NULL;
int r;
assert(u);
@@ -2539,7 +2384,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
return -ENOMEM;
r = manager_load_unit(u->manager, e, NULL, NULL, &device);
- free(e);
+
if (r < 0)
return r;
@@ -2714,6 +2559,34 @@ int unit_kill(Unit *u, KillWho w, int signo, DBusError *error) {
return UNIT_VTABLE(u)->kill(u, w, signo, error);
}
+static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) {
+ Set *pid_set;
+ int r;
+
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set)
+ return NULL;
+
+ /* Exclude the main/control pids from being killed via the cgroup */
+ if (main_pid > 0) {
+ r = set_put(pid_set, LONG_TO_PTR(main_pid));
+ if (r < 0)
+ goto fail;
+ }
+
+ if (control_pid > 0) {
+ r = set_put(pid_set, LONG_TO_PTR(control_pid));
+ if (r < 0)
+ goto fail;
+ }
+
+ return pid_set;
+
+fail:
+ set_free(pid_set);
+ return NULL;
+}
+
int unit_kill_common(
Unit *u,
KillWho who,
@@ -2750,28 +2623,16 @@ int unit_kill_common(
if (kill(main_pid, signo) < 0)
r = -errno;
- if (who == KILL_ALL) {
+ if (who == KILL_ALL && u->cgroup_path) {
_cleanup_set_free_ Set *pid_set = NULL;
int q;
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ /* Exclude the main/control pids from being killed via the cgroup */
+ pid_set = unit_pid_set(main_pid, control_pid);
if (!pid_set)
return -ENOMEM;
- /* Exclude the control/main pid from being killed via the cgroup */
- if (control_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(control_pid));
- if (q < 0)
- return q;
- }
-
- if (main_pid > 0) {
- q = set_put(pid_set, LONG_TO_PTR(main_pid));
- if (q < 0)
- return q;
- }
-
- q = cgroup_bonding_kill_list(u->cgroup_bondings, signo, false, false, pid_set, NULL);
+ q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
}
@@ -2823,40 +2684,39 @@ void unit_ref_unset(UnitRef *ref) {
ref->unit = NULL;
}
-int unit_add_one_mount_link(Unit *u, Mount *m) {
+int unit_add_mount_links(Unit *u) {
char **i;
+ int r;
assert(u);
- assert(m);
-
- if (u->load_state != UNIT_LOADED ||
- UNIT(m)->load_state != UNIT_LOADED)
- return 0;
STRV_FOREACH(i, u->requires_mounts_for) {
+ char prefix[strlen(*i) + 1];
- if (UNIT(m) == u)
- continue;
+ PATH_FOREACH_PREFIX_MORE(prefix, *i) {
+ Unit *m;
- if (!path_startswith(*i, m->where))
- continue;
-
- return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true);
- }
+ r = manager_get_unit_by_path(u->manager, prefix, ".mount", &m);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+ if (m == u)
+ continue;
- return 0;
-}
+ if (m->load_state != UNIT_LOADED)
+ continue;
-int unit_add_mount_links(Unit *u) {
- Unit *other;
- int r;
-
- assert(u);
+ r = unit_add_dependency(u, UNIT_AFTER, m, true);
+ if (r < 0)
+ return r;
- LIST_FOREACH(units_by_type, other, u->manager->units_by_type[UNIT_MOUNT]) {
- r = unit_add_one_mount_link(u, MOUNT(other));
- if (r < 0)
- return r;
+ if (m->fragment_path) {
+ r = unit_add_dependency(u, UNIT_REQUIRES, m, true);
+ if (r < 0)
+ return r;
+ }
+ }
}
return 0;
@@ -2870,7 +2730,6 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c) {
assert(c);
/* This only copies in the ones that need memory */
-
for (i = 0; i < RLIMIT_NLIMITS; i++)
if (u->manager->rlimit[i] && !c->rlimit[i]) {
c->rlimit[i] = newdup(struct rlimit, u->manager->rlimit[i], 1);
@@ -2900,7 +2759,18 @@ ExecContext *unit_get_exec_context(Unit *u) {
return (ExecContext*) ((uint8_t*) u + offset);
}
-static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) {
+CGroupContext *unit_get_cgroup_context(Unit *u) {
+ size_t offset;
+
+ offset = UNIT_VTABLE(u)->cgroup_context_offset;
+ if (offset <= 0)
+ return NULL;
+
+ return (CGroupContext*) ((uint8_t*) u + offset);
+}
+
+static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **_p, char **_q) {
+ _cleanup_free_ char *b = NULL;
char *p, *q;
int r;
@@ -2908,11 +2778,13 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char
assert(name);
assert(_p);
assert(_q);
+ assert(mode & (UNIT_PERSISTENT|UNIT_RUNTIME));
- if (u->manager->running_as == SYSTEMD_USER && runtime)
- return -ENOTSUP;
+ b = xescape(name, "/.");
+ if (!b)
+ return -ENOMEM;
- if (!filename_is_safe(name))
+ if (!filename_is_safe(b))
return -EINVAL;
if (u->manager->running_as == SYSTEMD_USER) {
@@ -2925,14 +2797,14 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char
return -ENOENT;
p = strjoin(c, "/", u->id, ".d", NULL);
- } else if (runtime)
- p = strjoin("/run/systemd/system/", u->id, ".d", NULL);
- else
+ } else if (mode & UNIT_PERSISTENT)
p = strjoin("/etc/systemd/system/", u->id, ".d", NULL);
+ else
+ p = strjoin("/run/systemd/system/", u->id, ".d", NULL);
if (!p)
return -ENOMEM;
- q = strjoin(p, "/50-", name, ".conf", NULL);
+ q = strjoin(p, "/90-", b, ".conf", NULL);
if (!q) {
free(p);
return -ENOMEM;
@@ -2943,13 +2815,18 @@ static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char
return 0;
}
-int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
+int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
_cleanup_free_ char *p = NULL, *q = NULL;
int r;
assert(u);
+ assert(name);
+ assert(data);
+
+ if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)))
+ return 0;
- r = drop_in_file(u, runtime, name, &p, &q);
+ r = drop_in_file(u, mode, name, &p, &q);
if (r < 0)
return r;
@@ -2957,22 +2834,126 @@ int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data
return write_string_file_atomic_label(q, data);
}
-int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {
+ _cleanup_free_ char *p = NULL;
+ va_list ap;
+ int r;
+
+ assert(u);
+ assert(name);
+ assert(format);
+
+ if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)))
+ return 0;
+
+ va_start(ap, format);
+ r = vasprintf(&p, format, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ return unit_write_drop_in(u, mode, name, p);
+}
+
+int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) {
+ _cleanup_free_ char *ndata = NULL;
+
+ assert(u);
+ assert(name);
+ assert(data);
+
+ if (!UNIT_VTABLE(u)->private_section)
+ return -EINVAL;
+
+ if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)))
+ return 0;
+
+ ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL);
+ if (!ndata)
+ return -ENOMEM;
+
+ return unit_write_drop_in(u, mode, name, ndata);
+}
+
+int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) {
+ _cleanup_free_ char *p = NULL;
+ va_list ap;
+ int r;
+
+ assert(u);
+ assert(name);
+ assert(format);
+
+ if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)))
+ return 0;
+
+ va_start(ap, format);
+ r = vasprintf(&p, format, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+
+ 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;
assert(u);
- r = drop_in_file(u, runtime, name, &p, &q);
+ if (!(mode & (UNIT_PERSISTENT|UNIT_RUNTIME)))
+ return 0;
+
+ r = drop_in_file(u, mode, name, &p, &q);
if (unlink(q) < 0)
- r = -errno;
+ r = errno == ENOENT ? 0 : -errno;
else
- r = 0;
+ r = 1;
rmdir(p);
return r;
}
+int unit_make_transient(Unit *u) {
+ int r;
+
+ assert(u);
+
+ u->load_state = UNIT_STUB;
+ u->load_error = 0;
+ u->transient = true;
+
+ free(u->fragment_path);
+ u->fragment_path = NULL;
+
+ if (u->manager->running_as == SYSTEMD_USER) {
+ _cleanup_free_ char *c = NULL;
+
+ r = user_config_home(&c);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ u->fragment_path = strjoin(c, "/", u->id, NULL);
+ if (!u->fragment_path)
+ return -ENOMEM;
+
+ mkdir_p(c, 0755);
+ } else {
+ u->fragment_path = strappend("/run/systemd/system/", u->id);
+ if (!u->fragment_path)
+ return -ENOMEM;
+
+ mkdir_p("/run/systemd/system", 0755);
+ }
+
+ return write_string_file_atomic_label(u->fragment_path, "# Transient stub");
+}
+
int unit_kill_context(
Unit *u,
KillContext *c,
@@ -3000,8 +2981,12 @@ int unit_kill_context(
log_warning_unit(u->id, "Failed to kill main process %li (%s): %s",
(long) main_pid, strna(comm), strerror(-r));
- } else
+ } else {
wait_for_exit = !main_pid_alien;
+
+ if (c->send_sighup)
+ kill(main_pid, SIGHUP);
+ }
}
if (control_pid > 0) {
@@ -3014,41 +2999,123 @@ int unit_kill_context(
log_warning_unit(u->id,
"Failed to kill control process %li (%s): %s",
(long) control_pid, strna(comm), strerror(-r));
- } else
+ } else {
wait_for_exit = true;
+
+ if (c->send_sighup)
+ kill(control_pid, SIGHUP);
+ }
}
- if (c->kill_mode == KILL_CONTROL_GROUP) {
+ if (c->kill_mode == KILL_CONTROL_GROUP && u->cgroup_path) {
_cleanup_set_free_ Set *pid_set = NULL;
- pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ /* Exclude the main/control pids from being killed via the cgroup */
+ pid_set = unit_pid_set(main_pid, control_pid);
if (!pid_set)
return -ENOMEM;
- /* Exclude the main/control pids from being killed via the cgroup */
- if (main_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(main_pid));
- if (r < 0)
- return r;
- }
-
- if (control_pid > 0) {
- r = set_put(pid_set, LONG_TO_PTR(control_pid));
- if (r < 0)
- return r;
- }
-
- r = cgroup_bonding_kill_list(u->cgroup_bondings, sig, true, false, pid_set, NULL);
+ r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set);
if (r < 0) {
if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r));
- } else if (r > 0)
+ } else if (r > 0) {
wait_for_exit = true;
+ if (c->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, true, true, false, pid_set);
+ }
+ }
}
return wait_for_exit;
}
+int unit_require_mounts_for(Unit *u, const char *path) {
+ char prefix[strlen(path) + 1], *p;
+ int r;
+
+ assert(u);
+ assert(path);
+
+ /* Registers a unit for requiring a certain path and all its
+ * prefixes. We keep a simple array of these paths in the
+ * unit, since its usually short. However, we build a prefix
+ * table for all possible prefixes so that new appearing mount
+ * units can easily determine which units to make themselves a
+ * dependency of. */
+
+ p = strdup(path);
+ if (!p)
+ return -ENOMEM;
+
+ path_kill_slashes(p);
+
+ if (!path_is_absolute(p)) {
+ free(p);
+ return -EINVAL;
+ }
+
+ if (!path_is_safe(p)) {
+ free(p);
+ return -EPERM;
+ }
+
+ if (strv_contains(u->requires_mounts_for, p)) {
+ free(p);
+ return 0;
+ }
+
+ r = strv_push(&u->requires_mounts_for, p);
+ if (r < 0) {
+ free(p);
+ return r;
+ }
+
+ PATH_FOREACH_PREFIX_MORE(prefix, p) {
+ Set *x;
+
+ x = hashmap_get(u->manager->units_requiring_mounts_for, prefix);
+ if (!x) {
+ char *q;
+
+ if (!u->manager->units_requiring_mounts_for) {
+ u->manager->units_requiring_mounts_for = hashmap_new(string_hash_func, string_compare_func);
+ if (!u->manager->units_requiring_mounts_for)
+ return -ENOMEM;
+ }
+
+ q = strdup(prefix);
+ if (!q)
+ return -ENOMEM;
+
+ x = set_new(NULL, NULL);
+ if (!x) {
+ free(q);
+ return -ENOMEM;
+ }
+
+ r = hashmap_put(u->manager->units_requiring_mounts_for, q, x);
+ if (r < 0) {
+ free(q);
+ set_free(x);
+ return r;
+ }
+ }
+
+ r = set_put(x, u);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = "active",
[UNIT_RELOADING] = "reloading",
diff --git a/src/core/unit.h b/src/core/unit.h
index b04475e4fb..6dd750f8c2 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -37,10 +37,10 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
#include "list.h"
#include "socket-util.h"
#include "execute.h"
+#include "cgroup.h"
#include "condition.h"
#include "install.h"
#include "unit-name.h"
-#include "cgroup-semantics.h"
enum UnitActiveState {
UNIT_ACTIVE,
@@ -115,8 +115,15 @@ enum UnitDependency {
#include "manager.h"
#include "job.h"
-#include "cgroup.h"
-#include "cgroup-attr.h"
+
+struct UnitRef {
+ /* Keeps tracks of references to a unit. This is useful so
+ * that we can merge two units if necessary and correct all
+ * references to them */
+
+ Unit* unit;
+ LIST_FIELDS(UnitRef, refs);
+};
struct Unit {
Manager *manager;
@@ -165,8 +172,10 @@ struct Unit {
dual_timestamp inactive_enter_timestamp;
/* Counterparts in the cgroup filesystem */
- CGroupBonding *cgroup_bondings;
- CGroupAttribute *cgroup_attributes;
+ char *cgroup_path;
+ CGroupControllerMask cgroup_mask;
+
+ UnitRef slice;
/* Per type list */
LIST_FIELDS(Unit, units_by_type);
@@ -186,6 +195,9 @@ struct Unit {
/* GC queue */
LIST_FIELDS(Unit, gc_queue);
+ /* CGroup realize members queue */
+ LIST_FIELDS(Unit, cgroup_queue);
+
/* Used during GC sweeps */
unsigned gc_marker;
@@ -228,25 +240,22 @@ struct Unit {
/* Did the last condition check succeed? */
bool condition_result;
+ /* Is this a transient unit? */
+ bool transient;
+
bool in_load_queue:1;
bool in_dbus_queue:1;
bool in_cleanup_queue:1;
bool in_gc_queue:1;
+ bool in_cgroup_queue:1;
bool sent_dbus_new_signal:1;
bool no_gc:1;
bool in_audit:1;
-};
-struct UnitRef {
- /* Keeps tracks of references to a unit. This is useful so
- * that we can merge two units if necessary and correct all
- * references to them */
-
- Unit* unit;
- LIST_FIELDS(UnitRef, refs);
+ bool cgroup_realized:1;
};
struct UnitStatusMessageFormats {
@@ -255,6 +264,12 @@ struct UnitStatusMessageFormats {
const char *finished_stop_job[_JOB_RESULT_MAX];
};
+typedef enum UnitSetPropertiesMode {
+ UNIT_CHECK = 0,
+ UNIT_RUNTIME = 1,
+ UNIT_PERSISTENT = 2,
+} UnitSetPropertiesMode;
+
#include "service.h"
#include "timer.h"
#include "socket.h"
@@ -265,6 +280,8 @@ struct UnitStatusMessageFormats {
#include "snapshot.h"
#include "swap.h"
#include "path.h"
+#include "slice.h"
+#include "scope.h"
struct UnitVTable {
/* How much memory does an object of this unit type need */
@@ -274,8 +291,12 @@ struct UnitVTable {
* ExecContext is found, if the unit type has that */
size_t exec_context_offset;
- /* The name of the section with the exec settings of ExecContext */
- const char *exec_section;
+ /* If greater than 0, the offset into the object where
+ * CGroupContext is found, if the unit type has that */
+ size_t cgroup_context_offset;
+
+ /* The name of the configuration file section with the private settings of this unit*/
+ const char *private_section;
/* Config file sections this unit type understands, separated
* by NUL chars */
@@ -347,7 +368,7 @@ struct UnitVTable {
/* Called whenever any of the cgroups this unit watches for
* ran empty */
- void (*cgroup_notify_empty)(Unit *u);
+ void (*notify_cgroup_empty)(Unit *u);
/* Called whenever a process of this unit sends us a message */
void (*notify_message)(Unit *u, pid_t pid, char **tags);
@@ -362,6 +383,12 @@ struct UnitVTable {
/* Called for each message received on the bus */
DBusHandlerResult (*bus_message_handler)(Unit *u, DBusConnection *c, DBusMessage *message);
+ /* Called for each property that is being set */
+ int (*bus_set_property)(Unit *u, const char *name, DBusMessageIter *i, UnitSetPropertiesMode mode, DBusError *error);
+
+ /* Called after at least one property got changed to apply the necessary change */
+ int (*bus_commit_properties)(Unit *u);
+
/* Return the unit this unit is following */
Unit *(*following)(Unit *u);
@@ -403,6 +430,9 @@ struct UnitVTable {
/* Exclude from automatic gc */
bool no_gc:1;
+
+ /* True if transient units of this type are OK */
+ bool can_transient:1;
};
extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
@@ -433,6 +463,8 @@ DEFINE_CAST(AUTOMOUNT, Automount);
DEFINE_CAST(SNAPSHOT, Snapshot);
DEFINE_CAST(SWAP, Swap);
DEFINE_CAST(PATH, Path);
+DEFINE_CAST(SLICE, Slice);
+DEFINE_CAST(SCOPE, Scope);
Unit *unit_new(Manager *m, size_t size);
void unit_free(Unit *u);
@@ -450,11 +482,6 @@ int unit_add_two_dependencies_by_name_inverse(Unit *u, UnitDependency d, UnitDep
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
-int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret);
-int unit_add_default_cgroups(Unit *u);
-CGroupBonding* unit_get_default_cgroup(Unit *u);
-int unit_add_cgroup_attribute(Unit *u, const CGroupSemantics *semantics, const char *controller, const char *name, const char *value, CGroupAttribute **ret);
-
int unit_choose_id(Unit *u, const char *name);
int unit_set_description(Unit *u, const char *description);
@@ -474,6 +501,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);
+
const char *unit_description(Unit *u) _pure_;
bool unit_has_name(Unit *u, const char *name);
@@ -536,6 +565,8 @@ void unit_reset_failed(Unit *u);
Unit *unit_following(Unit *u);
+const char *unit_slice_name(Unit *u);
+
bool unit_stop_pending(Unit *u) _pure_;
bool unit_inactive_or_pending(Unit *u) _pure_;
bool unit_active_or_pending(Unit *u);
@@ -557,19 +588,29 @@ Unit* unit_ref_set(UnitRef *ref, Unit *u);
void unit_ref_unset(UnitRef *ref);
#define UNIT_DEREF(ref) ((ref).unit)
+#define UNIT_ISSET(ref) (!!(ref).unit)
-int unit_add_one_mount_link(Unit *u, Mount *m);
int unit_add_mount_links(Unit *u);
int unit_exec_context_defaults(Unit *u, ExecContext *c);
ExecContext *unit_get_exec_context(Unit *u) _pure_;
+CGroupContext *unit_get_cgroup_context(Unit *u) _pure_;
-int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data);
-int unit_remove_drop_in(Unit *u, bool runtime, const char *name);
+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_attr_(4,5);
+
+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_attr_(4,5);
+
+int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name);
int unit_kill_context(Unit *u, KillContext *c, bool sigkill, pid_t main_pid, pid_t control_pid, bool main_pid_alien);
+int unit_make_transient(Unit *u);
+
+int unit_require_mounts_for(Unit *u, const char *path);
+
const char *unit_active_state_to_string(UnitActiveState i) _const_;
UnitActiveState unit_active_state_from_string(const char *s) _pure_;
diff --git a/src/core/user.conf b/src/core/user.conf
index 4252451eb7..4a0129a984 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -12,6 +12,5 @@
#LogTarget=console
#LogColor=yes
#LogLocation=no
-#DefaultControllers=cpu
#DefaultStandardOutput=inherit
#DefaultStandardError=inherit