summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--TODO11
-rw-r--r--src/core/cgroup.c4
-rw-r--r--src/core/dbus-slice.c60
-rw-r--r--src/core/dbus-slice.h30
-rw-r--r--src/core/load-fragment-gperf.gperf.m429
-rw-r--r--src/core/load-fragment.c43
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/mount.c4
-rw-r--r--src/core/service.c4
-rw-r--r--src/core/slice.c298
-rw-r--r--src/core/slice.h44
-rw-r--r--src/core/socket.c7
-rw-r--r--src/core/special.h3
-rw-r--r--src/core/swap.c4
-rw-r--r--src/core/unit.c53
-rw-r--r--src/core/unit.h24
-rw-r--r--src/shared/cgroup-util.c50
-rw-r--r--src/shared/cgroup-util.h2
-rw-r--r--src/shared/unit-name.c12
-rw-r--r--src/shared/unit-name.h3
-rw-r--r--src/systemctl/systemctl.c4
-rw-r--r--src/test/test-cgroup-util.c20
23 files changed, 679 insertions, 35 deletions
diff --git a/Makefile.am b/Makefile.am
index a74c19df7b..5a285be343 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -843,6 +843,8 @@ libsystemd_core_la_SOURCES = \
src/core/timer.h \
src/core/path.c \
src/core/path.h \
+ src/core/slice.c \
+ src/core/slice.h \
src/core/load-dropin.c \
src/core/load-dropin.h \
src/core/execute.c \
@@ -881,6 +883,8 @@ libsystemd_core_la_SOURCES = \
src/core/dbus-kill.h \
src/core/dbus-path.c \
src/core/dbus-path.h \
+ src/core/dbus-slice.c \
+ src/core/dbus-slice.h \
src/core/cgroup.c \
src/core/cgroup.h \
src/core/selinux-access.c \
diff --git a/TODO b/TODO
index df3725ffe4..cfd2763a40 100644
--- a/TODO
+++ b/TODO
@@ -28,6 +28,17 @@ Fedora 19:
Features:
+* when a service changes state make reflect that in the
+ RUNNING/LISTENING states of its socket
+
+* slices:
+ - fix libsystemd-login to handle slices properly
+ - add option to pam_systemd to move login session into a slice
+ - add call to logind's dbus intrface to register a machine (also, calls for listing them)
+
+* when recursively showing the cgroup hierarchy, optionally also show
+ the hierarchies of child processes
+
* document logic of auto/noauto and fail/nofail in fstab in systemd.mount or systemd-fstab-generator man page
* we should properly escape hostnames we add into dbus server strings
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 83df0f3c9a..a995d1436d 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -340,14 +340,14 @@ int manager_setup_cgroup(Manager *m) {
}
if (m->running_as == SYSTEMD_SYSTEM)
- suffix = "/system";
+ suffix = NULL;
else {
sprintf(suffix_buffer, "/systemd-%lu", (unsigned long) getpid());
suffix = suffix_buffer;
}
free(m->cgroup_hierarchy);
- if (endswith(current, suffix)) {
+ if (!suffix || endswith(current, suffix)) {
/* We probably got reexecuted and can continue to use our root cgroup */
m->cgroup_hierarchy = current;
current = NULL;
diff --git a/src/core/dbus-slice.c b/src/core/dbus-slice.c
new file mode 100644
index 0000000000..8a318faa55
--- /dev/null
+++ b/src/core/dbus-slice.c
@@ -0,0 +1,60 @@
+/*-*- 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-slice.h"
+#include "dbus-common.h"
+#include "selinux-access.h"
+
+#define BUS_SLICE_INTERFACE \
+ " <interface name=\"org.freedesktop.systemd1.Slice\">\n" \
+ BUS_UNIT_CGROUP_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) {
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.systemd1.Unit", bus_unit_properties, u },
+ { "org.freedesktop.systemd1.Slice", bus_unit_cgroup_properties, u },
+ { NULL, }
+ };
+
+ SELINUX_UNIT_ACCESS_CHECK(u, c, message, "status");
+
+ return bus_default_message_handler(c, message, INTROSPECTION, INTERFACES_LIST, bps);
+}
diff --git a/src/core/dbus-slice.h b/src/core/dbus-slice.h
new file mode 100644
index 0000000000..7e7e29982d
--- /dev/null
+++ b/src/core/dbus-slice.h
@@ -0,0 +1,30 @@
+/*-*- 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);
+
+extern const char bus_slice_interface[];
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 4e1454ee6c..9e5a408a30 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)
@@ -94,6 +84,18 @@ m4_define(`KILL_CONTEXT_CONFIG_ITEMS',
$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.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'
+)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)
@@ -123,6 +125,7 @@ Unit.OnFailureIsolate, config_parse_bool, 0,
Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate)
Unit.IgnoreOnSnapshot, config_parse_bool, 0, offsetof(Unit, ignore_on_snapshot)
Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout)
+Unit.Slice, config_parse_unit_slice, 0, 0
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, 0
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, 0
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, 0
@@ -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,8 @@ 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 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..4a835b6e8b 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2058,6 +2058,48 @@ 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);
+
+ k = unit_name_printf(u, rvalue);
+ if (!k)
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Failed to resolve unit specifiers on %s. Ignoring.", rvalue);
+
+ r = manager_load_unit(u->manager, k ? k : rvalue, NULL, NULL, &slice);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -r,
+ "Failed to load slice unit %s. Ignoring.", k ? k : rvalue);
+ return 0;
+ }
+
+ if (slice->type != UNIT_SLICE) {
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Slice unit %s is not a slice. Ignoring.", k ? k : rvalue);
+ return 0;
+ }
+
+ unit_ref_set(&u->slice, slice);
+ return 0;
+}
+
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
@@ -2446,6 +2488,7 @@ 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" },
};
const char *prev = NULL;
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index ff7f22a6f0..f9677baa0f 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -78,6 +78,7 @@ int config_parse_unit_cgroup_attr_pretty(const char *unit, const char *filename,
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);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
diff --git a/src/core/mount.c b/src/core/mount.c
index 0ad3d951a5..e21e774d4d 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -647,6 +647,10 @@ static int mount_add_extras(Mount *m) {
return r;
}
+ r = unit_add_default_slice(u);
+ if (r < 0)
+ return r;
+
r = unit_add_default_cgroups(u);
if (r < 0)
return r;
diff --git a/src/core/service.c b/src/core/service.c
index dadd98123c..ac8cdb2c31 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1225,6 +1225,10 @@ static int service_load(Unit *u) {
if (r < 0)
return r;
+ r = unit_add_default_slice(u);
+ if (r < 0)
+ return r;
+
r = unit_add_default_cgroups(u);
if (r < 0)
return r;
diff --git a/src/core/slice.c b/src/core/slice.c
new file mode 100644
index 0000000000..c1c33fe5c6
--- /dev/null
+++ b/src/core/slice.c
@@ -0,0 +1,298 @@
+/*-*- 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_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_slice_link(Slice *s) {
+ char *a, *dash;
+ int r;
+ Unit *parent;
+
+ assert(s);
+
+ if (UNIT_DEREF(UNIT(s)->slice))
+ return 0;
+
+ a = strdupa(UNIT(s)->id);
+
+ dash = strrchr(a, '-');
+ if (!dash)
+ return 0;
+
+ strcpy(dash, ".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_dependency_by_name(UNIT(s), 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");
+
+ 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(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_slice_link(s);
+ if (r < 0)
+ return r;
+
+ if (u->default_dependencies) {
+ r = slice_add_default_dependencies(s);
+ if (r < 0)
+ return r;
+ }
+
+ r = unit_add_default_cgroups(UNIT(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));
+}
+
+static int slice_start(Unit *u) {
+ Slice *t = SLICE(u);
+ int r;
+
+ assert(t);
+ assert(t->state == SLICE_DEAD);
+
+ r = cgroup_bonding_realize_list(u->cgroup_bondings);
+ if (r < 0)
+ return r;
+
+ cgroup_attribute_apply_list(u->cgroup_attributes, u->cgroup_bondings);
+
+ 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 trim 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",
+
+ .no_alias = true,
+ .no_instances = true,
+
+ .load = slice_load,
+ .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,
+
+ .status_message_formats = {
+ .finished_start_job = {
+ [JOB_DONE] = "Installed slice %s.",
+ [JOB_DEPENDENCY] = "Dependency failed for %s.",
+ },
+ .finished_stop_job = {
+ [JOB_DONE] = "Deinstalled slice %s.",
+ },
+ },
+};
diff --git a/src/core/slice.h b/src/core/slice.h
new file mode 100644
index 0000000000..4320a6354b
--- /dev/null
+++ b/src/core/slice.h
@@ -0,0 +1,44 @@
+/*-*- 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;
+};
+
+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/socket.c b/src/core/socket.c
index 37ca228e6b..2b3b6813ca 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -395,7 +395,12 @@ 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;
+
+ r = unit_add_default_cgroups(u);
+ if (r < 0)
return r;
if (UNIT(s)->default_dependencies)
diff --git a/src/core/special.h b/src/core/special.h
index a9b50bce05..e183056a20 100644
--- a/src/core/special.h
+++ b/src/core/special.h
@@ -113,3 +113,6 @@
#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 by default */
+#define SPECIAL_SYSTEM_SLICE "system.slice"
diff --git a/src/core/swap.c b/src/core/swap.c
index d503fe20df..d6721a6b31 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -287,6 +287,10 @@ static int swap_load(Unit *u) {
if (r < 0)
return r;
+ r = unit_add_default_slice(u);
+ if (r < 0)
+ return r;
+
r = unit_add_default_cgroups(u);
if (r < 0)
return r;
diff --git a/src/core/unit.c b/src/core/unit.c
index 9b36b225fa..b97158ff06 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -60,7 +60,8 @@ 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 *unit_new(Manager *m, size_t size) {
@@ -853,6 +854,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 +870,17 @@ 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_DEREF(u->slice)) {
+ r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true);
+ if (r < 0)
+ return r;
+ }
return 0;
}
@@ -1977,10 +1987,17 @@ static int unit_add_cgroup(Unit *u, CGroupBonding *b) {
}
char *unit_default_cgroup_path(Unit *u) {
- _cleanup_free_ char *escaped_instance = NULL;
+ _cleanup_free_ char *escaped_instance = NULL, *slice = NULL;
+ int r;
assert(u);
+ if (UNIT_DEREF(u->slice)) {
+ r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
+ if (r < 0)
+ return NULL;
+ }
+
escaped_instance = cg_escape(u->id);
if (!escaped_instance)
return NULL;
@@ -1996,9 +2013,13 @@ char *unit_default_cgroup_path(Unit *u) {
if (!escaped_template)
return NULL;
- return strjoin(u->manager->cgroup_hierarchy, "/", escaped_template, "/", escaped_instance, NULL);
+ return strjoin(u->manager->cgroup_hierarchy, "/",
+ slice ? slice : "", slice ? "/" : "",
+ escaped_template, "/", escaped_instance, NULL);
} else
- return strjoin(u->manager->cgroup_hierarchy, "/", escaped_instance, NULL);
+ return strjoin(u->manager->cgroup_hierarchy, "/",
+ slice ? slice : "", slice ? "/" : "",
+ escaped_instance, NULL);
}
int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret) {
@@ -2143,6 +2164,26 @@ fail:
return r;
}
+int unit_add_default_slice(Unit *u) {
+ Unit *slice;
+ int r;
+
+ assert(u);
+
+ if (UNIT_DEREF(u->slice))
+ return 0;
+
+ if (u->manager->running_as != SYSTEMD_SYSTEM)
+ return 0;
+
+ r = manager_load_unit(u->manager, SPECIAL_SYSTEM_SLICE, NULL, NULL, &slice);
+ if (r < 0)
+ return r;
+
+ unit_ref_set(&u->slice, slice);
+ return 0;
+}
+
int unit_add_default_cgroups(Unit *u) {
CGroupAttribute *a;
char **c;
diff --git a/src/core/unit.h b/src/core/unit.h
index b04475e4fb..81b8adbfdc 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -118,6 +118,15 @@ enum UnitDependency {
#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;
@@ -168,6 +177,8 @@ struct Unit {
CGroupBonding *cgroup_bondings;
CGroupAttribute *cgroup_attributes;
+ UnitRef slice;
+
/* Per type list */
LIST_FIELDS(Unit, units_by_type);
@@ -240,15 +251,6 @@ struct Unit {
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);
-};
-
struct UnitStatusMessageFormats {
const char *starting_stopping[2];
const char *finished_start_job[_JOB_RESULT_MAX];
@@ -265,6 +267,7 @@ struct UnitStatusMessageFormats {
#include "snapshot.h"
#include "swap.h"
#include "path.h"
+#include "slice.h"
struct UnitVTable {
/* How much memory does an object of this unit type need */
@@ -433,6 +436,7 @@ DEFINE_CAST(AUTOMOUNT, Automount);
DEFINE_CAST(SNAPSHOT, Snapshot);
DEFINE_CAST(SWAP, Swap);
DEFINE_CAST(PATH, Path);
+DEFINE_CAST(SLICE, Slice);
Unit *unit_new(Manager *m, size_t size);
void unit_free(Unit *u);
@@ -474,6 +478,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);
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index 7af0c3c124..4f58affe38 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -1617,3 +1617,53 @@ bool cg_controller_is_valid(const char *p, bool allow_named) {
return true;
}
+
+int cg_slice_to_path(const char *unit, char **ret) {
+ _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
+ const char *dash;
+
+ assert(unit);
+ assert(ret);
+
+ if (!unit_name_is_valid(unit, false))
+ return -EINVAL;
+
+ if (!endswith(unit, ".slice"))
+ return -EINVAL;
+
+ p = unit_name_to_prefix(unit);
+ if (!p)
+ return -ENOMEM;
+
+ dash = strchr(p, '-');
+ while (dash) {
+ _cleanup_free_ char *escaped = NULL;
+ char n[dash - p + sizeof(".slice")];
+
+ strcpy(stpncpy(n, p, dash - p), ".slice");
+
+ if (!unit_name_is_valid(n, false))
+ return -EINVAL;
+
+ escaped = cg_escape(n);
+ if (!escaped)
+ return -ENOMEM;
+
+ if (!strextend(&s, escaped, "/", NULL))
+ return -ENOMEM;
+
+ dash = strchr(dash+1, '-');
+ }
+
+ e = cg_escape(unit);
+ if (!e)
+ return -ENOMEM;
+
+ if (!strextend(&s, e, NULL))
+ return -ENOMEM;
+
+ *ret = s;
+ s = NULL;
+
+ return 0;
+}
diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h
index 5835e04075..84274e605d 100644
--- a/src/shared/cgroup-util.h
+++ b/src/shared/cgroup-util.h
@@ -112,3 +112,5 @@ char *cg_escape(const char *p);
char *cg_unescape(const char *p) _pure_;
bool cg_controller_is_valid(const char *p, bool allow_named);
+
+int cg_slice_to_path(const char *unit, char **ret);
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
index a809713595..2d4cd8d9f3 100644
--- a/src/shared/unit-name.c
+++ b/src/shared/unit-name.c
@@ -44,6 +44,7 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
[UNIT_TIMER] = "timer",
[UNIT_SWAP] = "swap",
[UNIT_PATH] = "path",
+ [UNIT_SLICE] = "slice"
};
DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType);
@@ -184,6 +185,7 @@ char *unit_name_change_suffix(const char *n, const char *suffix) {
assert(n);
assert(unit_name_is_valid(n, true));
assert(suffix);
+ assert(suffix[0] == '.');
assert_se(e = strrchr(n, '.'));
a = e - n;
@@ -506,16 +508,18 @@ char *unit_name_mangle(const char *name) {
return r;
}
-char *snapshot_name_mangle(const char *name) {
+char *unit_name_mangle_with_suffix(const char *name, const char *suffix) {
char *r, *t;
const char *f;
assert(name);
+ assert(suffix);
+ assert(suffix[0] == '.');
/* Similar to unit_name_mangle(), but is called when we know
* that this is about snapshot units. */
- r = new(char, strlen(name) * 4 + 1 + sizeof(".snapshot")-1);
+ r = new(char, strlen(name) * 4 + strlen(suffix) + 1);
if (!r)
return NULL;
@@ -528,8 +532,8 @@ char *snapshot_name_mangle(const char *name) {
*(t++) = *f;
}
- if (!endswith(name, ".snapshot"))
- strcpy(t, ".snapshot");
+ if (!endswith(name, suffix))
+ strcpy(t, suffix);
else
*t = 0;
diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h
index 9eca8eb3c1..baa487a81d 100644
--- a/src/shared/unit-name.h
+++ b/src/shared/unit-name.h
@@ -41,6 +41,7 @@ enum UnitType {
UNIT_TIMER,
UNIT_SWAP,
UNIT_PATH,
+ UNIT_SLICE,
_UNIT_TYPE_MAX,
_UNIT_TYPE_INVALID = -1
};
@@ -94,4 +95,4 @@ char *unit_name_to_path(const char *name);
char *unit_dbus_path_from_name(const char *name);
char *unit_name_mangle(const char *name);
-char *snapshot_name_mangle(const char *name);
+char *unit_name_mangle_with_suffix(const char *name, const char *suffix);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index a453598c72..8d496ab709 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -3736,7 +3736,7 @@ static int snapshot(DBusConnection *bus, char **args) {
dbus_error_init(&error);
if (strv_length(args) > 1)
- n = snapshot_name_mangle(args[1]);
+ n = unit_name_mangle_with_suffix(args[1], ".snapshot");
else
n = strdup("");
if (!n)
@@ -3811,7 +3811,7 @@ static int delete_snapshot(DBusConnection *bus, char **args) {
_cleanup_free_ char *n = NULL;
int r;
- n = snapshot_name_mangle(*name);
+ n = unit_name_mangle_with_suffix(*name, ".snapshot");
if (!n)
return log_oom();
diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c
index c9634d42b0..d4d58b320b 100644
--- a/src/test/test-cgroup-util.c
+++ b/src/test/test-cgroup-util.c
@@ -170,6 +170,25 @@ static void test_controller_is_valid(void) {
assert_se(!cg_controller_is_valid("tatü", false));
}
+static void test_slice_to_path_one(const char *unit, const char *path, int error) {
+ _cleanup_free_ char *ret = NULL;
+
+ assert_se(cg_slice_to_path(unit, &ret) == error);
+ assert_se(streq_ptr(ret, path));
+}
+
+static void test_slice_to_path(void) {
+
+ test_slice_to_path_one("foobar.slice", "foobar.slice", 0);
+ test_slice_to_path_one("foobar-waldo.slice", "foobar.slice/foobar-waldo.slice", 0);
+ test_slice_to_path_one("foobar-waldo.service", NULL, -EINVAL);
+ test_slice_to_path_one("-.slice", NULL, -EINVAL);
+ test_slice_to_path_one("-foo-.slice", NULL, -EINVAL);
+ test_slice_to_path_one("-foo.slice", NULL, -EINVAL);
+ test_slice_to_path_one("a-b.slice", "a.slice/a-b.slice", 0);
+ test_slice_to_path_one("a-b-c-d-e.slice", "a.slice/a-b.slice/a-b-c.slice/a-b-c-d.slice/a-b-c-d-e.slice", 0);
+}
+
int main(void) {
test_path_decode_unit();
test_path_get_unit();
@@ -178,6 +197,7 @@ int main(void) {
test_proc();
test_escape();
test_controller_is_valid();
+ test_slice_to_path();
return 0;
}