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 . +***/ + +#include + +#include "dbus-unit.h" +#include "dbus-slice.h" +#include "dbus-common.h" +#include "selinux-access.h" + +#define BUS_SLICE_INTERFACE \ + " \n" \ + BUS_UNIT_CGROUP_INTERFACE \ + " \n" + +#define INTROSPECTION \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "\n" \ + BUS_UNIT_INTERFACE \ + BUS_SLICE_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_PEER_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE \ + "\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 . +***/ + +#include + +#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 . +***/ + +#include +#include +#include + +#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 . +***/ + +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; }