units/swap.target \
units/slices.target \
units/system.slice \
- units/user.slice \
- units/machine.slice \
units/systemd-initctl.socket \
units/systemd-shutdownd.socket \
units/syslog.socket \
libsystemd-capability.la \
libsystemd-shared.la \
libsystemd-daemon.la \
- libsystemd-id128-internal.la
+ libsystemd-id128-internal.la \
+ libsystemd-bus.la
# ------------------------------------------------------------------------------
systemd_stdio_bridge_SOURCES = \
src/login/logind-seat.h \
src/login/logind-session.c \
src/login/logind-session.h \
+ src/login/logind-machine.c \
+ src/login/logind-machine.h \
src/login/logind-user.c \
src/login/logind-user.h \
src/login/logind-inhibit.c \
src/login/logind-session-dbus.c \
src/login/logind-seat-dbus.c \
src/login/logind-user-dbus.c \
+ src/login/logind-machine-dbus.c \
src/login/logind-acl.h
nodist_systemd_logind_SOURCES = \
libsystemd-shared.la \
libsystemd-daemon.la \
libsystemd-dbus.la \
+ libsystemd-id128-internal.la \
libudev.la
if HAVE_ACL
nodist_systemunit_DATA += \
units/systemd-logind.service \
units/systemd-user-sessions.service
+ units/user.slice \
+ units/machine.slice
dist_dbussystemservice_DATA += \
src/login/org.freedesktop.login1.service
GENERAL_ALIASES += \
$(systemunitdir)/remote-fs.target $(pkgsysconfdir)/system/multi-user.target.wants/remote-fs.target \
$(systemunitdir)/getty@.service $(pkgsysconfdir)/system/getty.target.wants/getty@tty1.service \
- $(systemunitdir)/machine.slice $(pkgsysconfdir)/system/slices.target.wants/machine.slice \
- $(systemunitdir)/user.slice $(pkgsysconfdir)/system/slices.target.wants/user.slice \
$(pkgsysconfdir)/user $(sysconfdir)/xdg/systemd/user \
../system-services/org.freedesktop.systemd1.service $(dbussessionservicedir)/org.freedesktop.systemd1.service
Features:
+* move systemctl dump to systemd-analyze
+
+* libsystemd-logind: sd_session_is_active() and friends: verify
+ validity of session name before appending it to a path
+
+* logind: when a PAM client calls ReleaseSession() start a timeout and
+ kill the session entirely after that is reached.
+
+* gparted needs to disable auto-activation of mount units somehow, or
+ maybe we should stop doing auto-activiation of this after boot
+ entirely. https://bugzilla.gnome.org/show_bug.cgi?id=701676
+
* 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)
+ - remove ControlGroup= setting
+ - in sd_pid_get_owner_uid() fallback to query session file
+ - add api to determine slice of unit
* 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
+* we should properly escape ssh hostnames we add into dbus server strings
* something pulls in pcre as so dep into our daemons such as hostnamed.
this directory become inaccessible. To see those over-mounted files,
please manually mount the underlying file system to a secondary
location.
+
+-- 24d8d4452573402496068381a6312df2
+Subject: A virtual machine or container has been started
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+The virtual machine @NAME@ with its leader PID @LEADER@ has been
+started is now ready to use.
+
+-- 58432bd3bace477cb514b56381b8a758
+Subject: A virtual machine or container has been terminated
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: http://www.freedesktop.org/wiki/Software/systemd/catalog/@MESSAGE_ID@
+
+The virtual machine @NAME@ with its leader PID @LEADER@ has been
+shut down.
#include "pager.h"
#include "build.h"
#include "output-mode.h"
+#include "fileio.h"
static bool arg_no_pager = false;
static bool arg_kernel_threads = false;
r = show_cgroup_by_path(p, NULL, 0,
arg_kernel_threads, output_flags);
} else {
- if (arg_machine)
- r = cg_get_machine_path(arg_machine, &root);
- else
+ if (arg_machine) {
+ char *m;
+ m = strappenda("/run/systemd/machines/", arg_machine);
+ r = parse_env_file(m, NEWLINE, "CGROUP", &root, NULL);
+ } else
r = cg_get_root_path(&root);
if (r < 0) {
log_error("Failed to get %s path: %s",
#include "log.h"
#include "strv.h"
#include "path-util.h"
+#include "special.h"
int cgroup_bonding_realize(CGroupBonding *b) {
int r;
LIST_FOREACH(by_unit, b, first) {
int r;
- if ((r = cgroup_bonding_is_empty(b)) < 0) {
+ r = cgroup_bonding_is_empty(b);
+ if (r < 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 */
}
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);
}
/* 1. Determine hierarchy */
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t);
+ 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 = NULL;
- 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 (!suffix || 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;
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 */
+ a = strappenda(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
+ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 0);
if (r < 0) {
log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
return r;
/* 6. Remove non-existing controllers from the default controllers list */
cg_shorten_controllers(m->default_controllers);
- /* 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");
- }
-
return 0;
}
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) {
" <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" \
{ "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) },
assert(property);
assert(u);
- if (UNIT_DEREF(u->slice))
- d = UNIT_DEREF(u->slice)->id;
- else
- d = "";
+ d = strempty(unit_slice_name(u));
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d))
return -ENOMEM;
/* Data specific to the cgroup subsystem */
Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
- char *cgroup_hierarchy;
+ char *cgroup_root;
usec_t gc_queue_timestamp;
int gc_marker;
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)));
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);
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));
#define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target"
#define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target"
-/* Where we add all our system units by default */
+/* 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"
#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) {
Unit *u = userdata;
}
static char *specifier_cgroup_root(char specifier, void *data, void *userdata) {
+ _cleanup_free_ char *p = NULL;
Unit *u = userdata;
- char *p;
- assert(u);
+ const char *slice;
+ int r;
- if (specifier == 'r')
- return strdup(u->manager->cgroup_hierarchy);
+ assert(u);
- if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0)
- return strdup("");
+ slice = unit_slice_name(u);
+ if (specifier == 'R' || !slice)
+ return strdup(u->manager->cgroup_root);
- if (streq(p, "/")) {
- free(p);
- return strdup("");
- }
+ r = cg_slice_to_path(slice, &p);
+ if (r < 0)
+ return NULL;
- return p;
+ return strjoin(u->manager->cgroup_root, "/", p, NULL);
}
static char *specifier_runtime(char specifier, void *data, void *userdata) {
*
* %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
"%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\tSlice: %s\n",
prefix, u->id,
prefix, unit_description(u),
prefix, strna(u->instance),
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, strna(unit_slice_name(u)));
SET_FOREACH(t, u->names, i)
fprintf(f, "%s\tName: %s\n", prefix, t);
return r;
}
- if (u->default_dependencies && UNIT_DEREF(u->slice)) {
+ if (u->default_dependencies && UNIT_ISSET(u->slice)) {
r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true);
if (r < 0)
return r;
assert(u);
- if (UNIT_DEREF(u->slice)) {
+ if (UNIT_ISSET(u->slice)) {
r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
if (r < 0)
return NULL;
if (!escaped_template)
return NULL;
- return strjoin(u->manager->cgroup_hierarchy, "/",
+ return strjoin(u->manager->cgroup_root, "/",
slice ? slice : "", slice ? "/" : "",
escaped_template, "/", escaped_instance, NULL);
} else
- return strjoin(u->manager->cgroup_hierarchy, "/",
+ return strjoin(u->manager->cgroup_root, "/",
slice ? slice : "", slice ? "/" : "",
escaped_instance, NULL);
}
assert(u);
- if (UNIT_DEREF(u->slice))
+ if (UNIT_ISSET(u->slice))
return 0;
if (u->manager->running_as != SYSTEMD_SYSTEM)
return 0;
}
+const char *unit_slice_name(Unit *u) {
+ assert(u);
+
+ if (!UNIT_ISSET(u->slice))
+ return NULL;
+
+ return UNIT_DEREF(u->slice)->id;
+}
+
int unit_add_default_cgroups(Unit *u) {
CGroupAttribute *a;
char **c;
/* Adds in the default cgroups, if they weren't specified
* otherwise. */
- if (!u->manager->cgroup_hierarchy)
+ if (!u->manager->cgroup_root)
return 0;
r = unit_add_one_default_cgroup(u, NULL);
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);
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);
return 0;
}
+static int list_machines(DBusConnection *bus, char **args, unsigned n) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub, sub2;
+ unsigned k = 0;
+ int r;
+
+ pager_open_if_enabled();
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "ListMachines",
+ &reply,
+ NULL,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (on_tty())
+ printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *name, *class, *service, *object;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &class, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &service, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ printf("%-32s %-9s %-16s\n", name, class, service);
+
+ k++;
+
+ dbus_message_iter_next(&sub);
+ }
+
+ if (on_tty())
+ printf("\n%u machines listed.\n", k);
+
+ return 0;
+}
+
typedef struct SessionStatusInfo {
const char *id;
uid_t uid;
const char *type;
const char *class;
const char *state;
+ const char *slice;
} SessionStatusInfo;
typedef struct UserStatusInfo {
const char *state;
char **sessions;
const char *display;
+ const char *slice;
} UserStatusInfo;
typedef struct SeatStatusInfo {
char **sessions;
} SeatStatusInfo;
+typedef struct MachineStatusInfo {
+ const char *name;
+ sd_id128_t id;
+ const char *default_control_group;
+ const char *class;
+ const char *service;
+ const char *slice;
+ const char *root_directory;
+ pid_t leader;
+ usec_t timestamp;
+} MachineStatusInfo;
+
static void print_session_status_info(SessionStatusInfo *i) {
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
char since2[FORMAT_TIMESTAMP_MAX], *s2;
printf("\t Since: %s\n", s2);
if (i->leader > 0) {
- char *t = NULL;
+ _cleanup_free_ char *t = NULL;
printf("\t Leader: %u", (unsigned) i->leader);
get_process_comm(i->leader, &t);
- if (t) {
+ if (t)
printf(" (%s)", t);
- free(t);
- }
printf("\n");
}
if (i->state)
printf("\t State: %s\n", i->state);
+ if (i->slice)
+ printf("\t Slice: %s\n", i->slice);
+
if (i->default_control_group) {
unsigned c;
int output_flags =
if (!isempty(i->state))
printf("\t State: %s\n", i->state);
+ if (i->slice)
+ printf("\t Slice: %s\n", i->slice);
+
if (!strv_isempty(i->sessions)) {
char **l;
printf("\tSessions:");
}
}
+static void print_machine_status_info(MachineStatusInfo *i) {
+ char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
+ char since2[FORMAT_TIMESTAMP_MAX], *s2;
+ assert(i);
+
+ fputs(strna(i->name), stdout);
+
+ if (!sd_id128_equal(i->id, SD_ID128_NULL))
+ printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
+ else
+ putchar('\n');
+
+ s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
+ s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
+
+ if (s1)
+ printf("\t Since: %s; %s\n", s2, s1);
+ else if (s2)
+ printf("\t Since: %s\n", s2);
+
+ if (i->leader > 0) {
+ _cleanup_free_ char *t = NULL;
+
+ printf("\t Leader: %u", (unsigned) i->leader);
+
+ get_process_comm(i->leader, &t);
+ if (t)
+ printf(" (%s)", t);
+
+ putchar('\n');
+ }
+
+ if (i->service) {
+ printf("\t Service: %s", i->service);
+
+ if (i->class)
+ printf("; class %s", i->class);
+
+ putchar('\n');
+ } else if (i->class)
+ printf("\t Class: %s\n", i->class);
+
+ if (i->slice)
+ printf("\t Slice: %s\n", i->slice);
+ if (i->root_directory)
+ printf("\t Root: %s\n", i->root_directory);
+
+ if (i->default_control_group) {
+ unsigned c;
+ int output_flags =
+ arg_all * OUTPUT_SHOW_ALL |
+ arg_full * OUTPUT_FULL_WIDTH;
+
+ printf("\t CGroup: %s\n", i->default_control_group);
+
+ if (arg_transport != TRANSPORT_SSH) {
+ c = columns();
+ if (c > 18)
+ c -= 18;
+ else
+ c = 0;
+
+ show_cgroup_and_extra_by_spec(i->default_control_group,
+ "\t\t ", c, false, &i->leader,
+ i->leader > 0 ? 1 : 0,
+ output_flags);
+ }
+ }
+}
+
static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
assert(name);
assert(iter);
i->type = s;
else if (streq(name, "Class"))
i->class = s;
+ else if (streq(name, "Slice"))
+ i->slice = s;
else if (streq(name, "State"))
i->state = s;
}
i->name = s;
else if (streq(name, "DefaultControlGroup"))
i->default_control_group = s;
+ else if (streq(name, "Slice"))
+ i->slice = s;
else if (streq(name, "State"))
i->state = s;
}
return 0;
}
+static int status_property_machine(const char *name, DBusMessageIter *iter, MachineStatusInfo *i) {
+ assert(name);
+ assert(iter);
+ assert(i);
+
+ switch (dbus_message_iter_get_arg_type(iter)) {
+
+ case DBUS_TYPE_STRING: {
+ const char *s;
+
+ dbus_message_iter_get_basic(iter, &s);
+
+ if (!isempty(s)) {
+ if (streq(name, "Name"))
+ i->name = s;
+ else if (streq(name, "DefaultControlGroup"))
+ i->default_control_group = s;
+ else if (streq(name, "Class"))
+ i->class = s;
+ else if (streq(name, "Service"))
+ i->service = s;
+ else if (streq(name, "Slice"))
+ i->slice = s;
+ else if (streq(name, "RootDirectory"))
+ i->root_directory = s;
+ }
+ break;
+ }
+
+ case DBUS_TYPE_UINT32: {
+ uint32_t u;
+
+ dbus_message_iter_get_basic(iter, &u);
+
+ if (streq(name, "Leader"))
+ i->leader = (pid_t) u;
+
+ break;
+ }
+
+ case DBUS_TYPE_UINT64: {
+ uint64_t u;
+
+ dbus_message_iter_get_basic(iter, &u);
+
+ if (streq(name, "Timestamp"))
+ i->timestamp = (usec_t) u;
+
+ break;
+ }
+
+ case DBUS_TYPE_ARRAY: {
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE && streq(name, "Id")) {
+ void *v;
+ int n;
+
+ dbus_message_iter_get_fixed_array(&sub, &v, &n);
+ if (n == 0)
+ i->id = SD_ID128_NULL;
+ else if (n == 16)
+ memcpy(&i->id, v, n);
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
static int print_property(const char *name, DBusMessageIter *iter) {
assert(name);
assert(iter);
SessionStatusInfo session_info = {};
UserStatusInfo user_info = {};
SeatStatusInfo seat_info = {};
+ MachineStatusInfo machine_info = {};
assert(path);
assert(new_line);
r = status_property_session(name, &sub3, &session_info);
else if (strstr(verb, "user"))
r = status_property_user(name, &sub3, &user_info);
- else
+ else if (strstr(verb, "seat"))
r = status_property_seat(name, &sub3, &seat_info);
+ else
+ r = status_property_machine(name, &sub3, &machine_info);
if (r < 0) {
log_error("Failed to parse reply.");
print_session_status_info(&session_info);
else if (strstr(verb, "user"))
print_user_status_info(&user_info);
- else
+ else if (strstr(verb, "seat"))
print_seat_status_info(&seat_info);
+ else
+ print_machine_status_info(&machine_info);
}
r = 0;
}
u = (uint32_t) uid;
- ret = bus_method_call_with_reply (
+ ret = bus_method_call_with_reply(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
NULL,
DBUS_TYPE_UINT32, &u,
DBUS_TYPE_INVALID);
- } else {
- ret = bus_method_call_with_reply (
+ } else if (strstr(args[0], "seat")) {
+
+ ret = bus_method_call_with_reply(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
NULL,
DBUS_TYPE_STRING, &args[i],
DBUS_TYPE_INVALID);
+
+ } else {
+
+ ret = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "GetMachine",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_INVALID);
}
- if (ret)
+
+ if (ret < 0)
goto finish;
if (!dbus_message_get_args(reply, &error,
return 0;
}
+static int kill_machine(DBusConnection *bus, char **args, unsigned n) {
+ unsigned i;
+
+ assert(args);
+
+ if (!arg_kill_who)
+ arg_kill_who = "all";
+
+ for (i = 1; i < n; i++) {
+ int r;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "KillMachine",
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, &arg_kill_who,
+ DBUS_TYPE_INT32, &arg_signal,
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
unsigned i;
dbus_bool_t b, interactive = true;
return 0;
}
+static int terminate_machine(DBusConnection *bus, char **args, unsigned n) {
+ unsigned i;
+
+ assert(args);
+
+ for (i = 1; i < n; i++) {
+ int r;
+
+ r = bus_method_call_with_reply (
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "TerminateMachine",
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_INVALID);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
static int help(void) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
" show-seat [NAME...] Show properties of one or more seats\n"
" attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
" flush-devices Flush all device associations\n"
- " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
+ " terminate-seat [NAME...] Terminate all sessions on one or more seats\n"
+ " list-machines List running VMs and containers\n"
+ " machine-status [NAME...] Show VM/container status\n"
+ " show-machine [NAME...] Show properties of one or more VMs/containers\n"
+ " terminate-machine [NAME...] Terminate one or more VMs/containers\n"
+ " kill-machine [NAME...] Send signal to processes of a VM/container\n",
program_invocation_short_name);
return 0;
const int argc;
int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
} verbs[] = {
- { "list-sessions", LESS, 1, list_sessions },
- { "session-status", MORE, 2, show },
- { "show-session", MORE, 1, show },
- { "activate", EQUAL, 2, activate },
- { "lock-session", MORE, 2, activate },
- { "unlock-session", MORE, 2, activate },
- { "lock-sessions", EQUAL, 1, lock_sessions },
- { "unlock-sessions", EQUAL, 1, lock_sessions },
- { "terminate-session", MORE, 2, activate },
- { "kill-session", MORE, 2, kill_session },
- { "list-users", EQUAL, 1, list_users },
- { "user-status", MORE, 2, show },
- { "show-user", MORE, 1, show },
- { "enable-linger", MORE, 2, enable_linger },
- { "disable-linger", MORE, 2, enable_linger },
- { "terminate-user", MORE, 2, terminate_user },
- { "kill-user", MORE, 2, kill_user },
- { "list-seats", EQUAL, 1, list_seats },
- { "seat-status", MORE, 2, show },
- { "show-seat", MORE, 1, show },
- { "attach", MORE, 3, attach },
- { "flush-devices", EQUAL, 1, flush_devices },
- { "terminate-seat", MORE, 2, terminate_seat },
+ { "list-sessions", LESS, 1, list_sessions },
+ { "session-status", MORE, 2, show },
+ { "show-session", MORE, 1, show },
+ { "activate", EQUAL, 2, activate },
+ { "lock-session", MORE, 2, activate },
+ { "unlock-session", MORE, 2, activate },
+ { "lock-sessions", EQUAL, 1, lock_sessions },
+ { "unlock-sessions", EQUAL, 1, lock_sessions },
+ { "terminate-session", MORE, 2, activate },
+ { "kill-session", MORE, 2, kill_session },
+ { "list-users", EQUAL, 1, list_users },
+ { "user-status", MORE, 2, show },
+ { "show-user", MORE, 1, show },
+ { "enable-linger", MORE, 2, enable_linger },
+ { "disable-linger", MORE, 2, enable_linger },
+ { "terminate-user", MORE, 2, terminate_user },
+ { "kill-user", MORE, 2, kill_user },
+ { "list-seats", EQUAL, 1, list_seats },
+ { "seat-status", MORE, 2, show },
+ { "show-seat", MORE, 1, show },
+ { "attach", MORE, 3, attach },
+ { "flush-devices", EQUAL, 1, flush_devices },
+ { "terminate-seat", MORE, 2, terminate_seat },
+ { "list-machines", EQUAL, 1, list_machines },
+ { "machine-status", MORE, 2, show },
+ { "show-machine", MORE, 1, show },
+ { "terminate-machine", MORE, 2, terminate_machine },
+ { "kill-machine", MORE, 2, kill_machine },
};
int left;
#include "systemd/sd-messages.h"
#include "fileio-label.h"
#include "label.h"
+#include "utf8.h"
+#include "unit-name.h"
#define BUS_MANAGER_INTERFACE \
" <interface name=\"org.freedesktop.login1.Manager\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"user\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"GetUserByPID\">\n" \
+ " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
+ " <arg name=\"user\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
" <method name=\"GetSeat\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"GetMachine\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"GetMachineByPID\">\n" \
+ " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
+ " <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
" <method name=\"ListSessions\">\n" \
" <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ListSeats\">\n" \
" <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n" \
" </method>\n" \
+ " <method name=\"ListMachines\">\n" \
+ " <arg name=\"machines\" type=\"a(ssso)\" direction=\"out\"/>\n" \
+ " </method>\n" \
" <method name=\"CreateSession\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
" <method name=\"ReleaseSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
+ " <method name=\"CreateMachine\">\n" \
+ " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"id\" type=\"ay\" direction=\"in\"/>\n" \
+ " <arg name=\"service\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
+ " <arg name=\"slice\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
+ " </method>\n" \
" <method name=\"ActivateSession\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"TerminateSeat\">\n" \
" <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
+ " <method name=\"TerminateMachine\">\n" \
+ " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
+ " </method>\n" \
" <method name=\"SetUserLinger\">\n" \
" <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"b\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"id\" type=\"s\"/>\n" \
" <arg name=\"path\" type=\"o\"/>\n" \
" </signal>\n" \
+ " <signal name=\"MachineNew\">\n" \
+ " <arg name=\"machine\" type=\"s\"/>\n" \
+ " <arg name=\"path\" type=\"o\"/>\n" \
+ " </signal>\n" \
+ " <signal name=\"MachineRemoved\">\n" \
+ " <arg name=\"machine\" type=\"s\"/>\n" \
+ " <arg name=\"path\" type=\"o\"/>\n" \
+ " </signal>\n" \
" <signal name=\"PrepareForShutdown\">\n" \
" <arg name=\"active\" type=\"b\"/>\n" \
" </signal>\n" \
" <signal name=\"PrepareForSleep\">\n" \
" <arg name=\"active\" type=\"b\"/>\n" \
" </signal>\n" \
- " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
dbus_message_iter_get_basic(&iter, &leader);
- if (leader <= 0 ||
- !dbus_message_iter_next(&iter) ||
+ if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return -EINVAL;
dbus_message_iter_get_basic(&iter, &kill_processes);
+ if (leader <= 0) {
+ leader = bus_get_unix_process_id(m->bus, dbus_message_get_sender(message), NULL);
+ if (leader == 0)
+ return -EINVAL;
+ }
+
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, leader, &cgroup);
if (r < 0)
goto fail;
if (r < 0)
goto fail;
- r = manager_add_session(m, user, id, &session);
+ r = manager_add_session(m, id, &session);
if (r < 0)
goto fail;
+ session_set_user(session, user);
+
session->leader = leader;
session->audit_id = audit_id;
session->type = t;
return r;
}
+static bool valid_machine_name(const char *p) {
+ size_t l;
+
+ if (!filename_is_safe(p))
+ return false;
+
+ if (!ascii_is_valid(p))
+ return false;
+
+ l = strlen(p);
+
+ if (l < 1 || l> 64)
+ return false;
+
+ return true;
+}
+
+static int bus_manager_create_machine(
+ Manager *manager,
+ DBusMessage *message,
+ DBusMessage **_reply) {
+
+ const char *name, *service, *class, *slice, *root_directory;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ _cleanup_free_ char *p = NULL;
+ DBusMessageIter iter, sub;
+ MachineClass c;
+ uint32_t leader;
+ sd_id128_t id;
+ dbus_bool_t b;
+ Machine *m;
+ int n, r;
+ void *v;
+
+ assert(manager);
+ assert(message);
+ assert(_reply);
+
+ if (!dbus_message_iter_init(message, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &name);
+
+ if (!valid_machine_name(name) ||
+ !dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_fixed_array(&sub, &v, &n);
+
+ if (n == 0)
+ id = SD_ID128_NULL;
+ else if (n == 16)
+ memcpy(&id, v, n);
+ else
+ return -EINVAL;
+
+ if (!dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &service);
+
+ if (!dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &class);
+
+ if (isempty(class))
+ c = _MACHINE_CLASS_INVALID;
+ else {
+ c = machine_class_from_string(class);
+ if (c < 0)
+ return -EINVAL;
+ }
+
+ if (!dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &leader);
+ if (!dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &slice);
+ if (!(isempty(slice) || (unit_name_is_valid(slice, false) && endswith(slice, ".slice"))) ||
+ !dbus_message_iter_next(&iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&iter, &root_directory);
+
+ if (!(isempty(root_directory) || path_is_absolute(root_directory)))
+ return -EINVAL;
+
+ if (hashmap_get(manager->machines, name))
+ return -EEXIST;
+
+ if (leader <= 0) {
+ leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
+ if (leader == 0)
+ return -EINVAL;
+ }
+
+ r = manager_add_machine(manager, name, &m);
+ if (r < 0)
+ goto fail;
+
+ m->leader = leader;
+ m->class = c;
+ m->id = id;
+
+ if (!isempty(service)) {
+ m->service = strdup(service);
+ if (!m->service) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!isempty(slice)) {
+ m->slice = strdup(slice);
+ if (!m->slice) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ if (!isempty(root_directory)) {
+ m->root_directory = strdup(root_directory);
+ if (!m->root_directory) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ r = machine_start(m);
+ if (r < 0)
+ goto fail;
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ p = machine_bus_path(m);
+ if (!p) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ b = dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID);
+ if (!b) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ *_reply = reply;
+ reply = NULL;
+ return 0;
+
+fail:
+ if (m)
+ machine_add_to_gc_queue(m);
+
+ return r;
+}
+
static int bus_manager_inhibit(
Manager *m,
DBusConnection *connection,
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_action, handle_action, HandleAction);
static const BusProperty bus_login_manager_properties[] = {
- { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_path), true },
{ "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
{ "ResetControllers", bus_property_append_strv, "as", offsetof(Manager, reset_controllers), true },
{ "NAutoVTs", bus_property_append_unsigned, "u", offsetof(Manager, n_autovts) },
if (!b)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUserByPID")) {
+ uint32_t pid;
+ char *p;
+ User *user;
+ bool b;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_UINT32, &pid,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ r = manager_get_user_by_pid(m, pid, &user);
+ if (r <= 0)
+ return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ p = user_bus_path(user);
+ if (!p)
+ goto oom;
+
+ b = dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID);
+ free(p);
+
+ if (!b)
+ goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetMachine")) {
+ Machine *machine;
+ const char *name;
+ char *p;
+ bool b;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ p = machine_bus_path(machine);
+ if (!p)
+ goto oom;
+
+ b = dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID);
+ free(p);
+
+ if (!b)
+ goto oom;
+
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetMachineByPID")) {
+ uint32_t pid;
+ char *p;
+ Machine *machine;
+ bool b;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_UINT32, &pid,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ r = manager_get_machine_by_pid(m, pid, &machine);
+ if (r <= 0)
+ return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ p = machine_bus_path(machine);
+ if (!p)
+ goto oom;
+
+ b = dbus_message_append_args(
+ reply,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID);
+ free(p);
+
+ if (!b)
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
const char *name;
char *p;
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
- char *p;
User *user;
Iterator i;
DBusMessageIter iter, sub;
goto oom;
HASHMAP_FOREACH(user, m->users, i) {
+ _cleanup_free_ char *p = NULL;
DBusMessageIter sub2;
uint32_t uid;
goto oom;
}
- free(p);
-
if (!dbus_message_iter_close_container(&sub, &sub2))
goto oom;
}
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
- char *p;
Seat *seat;
Iterator i;
DBusMessageIter iter, sub;
goto oom;
HASHMAP_FOREACH(seat, m->seats, i) {
+ _cleanup_free_ char *p = NULL;
DBusMessageIter sub2;
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
goto oom;
}
- free(p);
-
if (!dbus_message_iter_close_container(&sub, &sub2))
goto oom;
}
if (!dbus_message_iter_close_container(&iter, &sub))
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListMachines")) {
+ Machine *machine;
+ Iterator i;
+ DBusMessageIter iter, sub;
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
+ goto oom;
+
+ HASHMAP_FOREACH(machine, m->machines, i) {
+ _cleanup_free_ char *p = NULL;
+ DBusMessageIter sub2;
+ const char *class;
+
+ if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+ goto oom;
+
+ p = machine_bus_path(machine);
+ if (!p)
+ goto oom;
+
+ class = strempty(machine_class_to_string(machine->class));
+
+ if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
+ free(p);
+ goto oom;
+ }
+
+ if (!dbus_message_iter_close_container(&sub, &sub2))
+ goto oom;
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub))
+ goto oom;
+
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "Inhibit")) {
r = bus_manager_inhibit(m, connection, message, &error, &reply);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateMachine")) {
+
+ r = bus_manager_create_machine(m, message, &reply);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ReleaseSession")) {
const char *name;
if (!reply)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "KillMachine")) {
+ const char *swho;
+ int32_t signo;
+ KillWho who;
+ const char *name;
+ Machine *machine;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &swho,
+ DBUS_TYPE_INT32, &signo,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (isempty(swho))
+ who = KILL_ALL;
+ else {
+ who = kill_who_from_string(swho);
+ if (who < 0)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if (signo <= 0 || signo >= _NSIG)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+ r = machine_kill(machine, who, signo);
+ 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.login1.Manager", "TerminateSession")) {
const char *name;
Session *session;
if (!reply)
goto oom;
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateMachine")) {
+ const char *name;
+ Machine *machine;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ machine = hashmap_get(m->machines, name);
+ if (!machine)
+ return bus_send_error_reply(connection, message, &error, -ENOENT);
+
+ r = machine_stop(machine);
+ 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.login1.Manager", "SetUserLinger")) {
uint32_t uid;
struct passwd *pw;
--- /dev/null
+/*-*- 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 <errno.h>
+#include <string.h>
+
+#include "logind.h"
+#include "logind-machine.h"
+#include "dbus-common.h"
+
+#define BUS_MACHINE_INTERFACE \
+ " <interface name=\"org.freedesktop.login1.Machine\">\n" \
+ " <method name=\"Terminate\"/>\n" \
+ " <method name=\"Kill\">\n" \
+ " <arg name=\"who\" type=\"s\"/>\n" \
+ " <arg name=\"signal\" type=\"s\"/>\n" \
+ " </method>\n" \
+ " <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Id\" type=\"ay\" access=\"read\"/>\n" \
+ " <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
+ " <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
+ " <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>\n" \
+ " </interface>\n"
+
+#define INTROSPECTION \
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
+ "<node>\n" \
+ BUS_MACHINE_INTERFACE \
+ BUS_PROPERTIES_INTERFACE \
+ BUS_PEER_INTERFACE \
+ BUS_INTROSPECTABLE_INTERFACE \
+ "</node>\n"
+
+#define INTERFACES_LIST \
+ BUS_GENERIC_INTERFACES_LIST \
+ "org.freedesktop.login1.Machine\0"
+
+static int bus_machine_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
+ _cleanup_free_ char *t = NULL;
+ Machine *m = data;
+ int r;
+ bool success;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &t);
+ if (r < 0)
+ return r;
+
+ success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
+ return success ? 0 : -ENOMEM;
+}
+
+static int bus_machine_append_id(DBusMessageIter *i, const char *property, void *data) {
+ DBusMessageIter sub;
+ Machine *m = data;
+ dbus_bool_t b;
+ void *p;
+
+ assert(i);
+ assert(property);
+ assert(m);
+
+ if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "y", &sub))
+ return -ENOMEM;
+
+ p = &m->id;
+ b = dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &p, 16);
+ if (!b)
+ return -ENOMEM;
+
+ if (!dbus_message_iter_close_container(i, &sub))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int get_machine_for_path(Manager *m, const char *path, Machine **_machine) {
+ _cleanup_free_ char *e = NULL;
+ Machine *machine;
+
+ assert(m);
+ assert(path);
+ assert(_machine);
+
+ if (!startswith(path, "/org/freedesktop/login1/machine/"))
+ return -EINVAL;
+
+ e = bus_path_unescape(path + 32);
+ if (!e)
+ return -ENOMEM;
+
+ machine = hashmap_get(m->machines, e);
+ if (!machine)
+ return -ENOENT;
+
+ *_machine = machine;
+ return 0;
+}
+
+static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_machine_append_class, machine_class, MachineClass);
+
+static const BusProperty bus_login_machine_properties[] = {
+ { "Name", bus_property_append_string, "s", offsetof(Machine, name), true },
+ { "Id", bus_machine_append_id, "ay", 0 },
+ { "Timestamp", bus_property_append_usec, "t", offsetof(Machine, timestamp.realtime) },
+ { "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Machine, timestamp.monotonic) },
+ { "DefaultControlGroup", bus_machine_append_default_cgroup, "s", 0 },
+ { "Service", bus_property_append_string, "s", offsetof(Machine, service), true },
+ { "Slice", bus_property_append_string, "s", offsetof(Machine, slice), true },
+ { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
+ { "Class", bus_machine_append_class, "s", offsetof(Machine, class) },
+ { "RootDirectory", bus_property_append_string, "s", offsetof(Machine, root_directory), true },
+ { NULL, }
+};
+
+static DBusHandlerResult machine_message_dispatch(
+ Machine *m,
+ DBusConnection *connection,
+ DBusMessage *message) {
+
+ DBusError error;
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ int r;
+
+ assert(m);
+ assert(connection);
+ assert(message);
+
+ if (dbus_message_is_method_call(message, "org.freedesktop.login1.Machine", "Terminate")) {
+
+ r = machine_stop(m);
+ 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.login1.Machine", "Kill")) {
+ const char *swho;
+ int32_t signo;
+ KillWho who;
+
+ if (!dbus_message_get_args(
+ message,
+ &error,
+ DBUS_TYPE_STRING, &swho,
+ DBUS_TYPE_INT32, &signo,
+ DBUS_TYPE_INVALID))
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ if (isempty(swho))
+ who = KILL_ALL;
+ else {
+ who = kill_who_from_string(swho);
+ if (who < 0)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+ }
+
+ if (signo <= 0 || signo >= _NSIG)
+ return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+ r = machine_kill(m, who, signo);
+ if (r < 0)
+ return bus_send_error_reply(connection, message, NULL, r);
+
+ reply = dbus_message_new_method_return(message);
+ if (!reply)
+ goto oom;
+
+ } else {
+ const BusBoundProperties bps[] = {
+ { "org.freedesktop.login1.Machine", bus_login_machine_properties, m },
+ { NULL, }
+ };
+
+ return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
+ }
+
+ if (reply) {
+ if (!bus_maybe_send_reply(connection, message, reply))
+ goto oom;
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult machine_message_handler(
+ DBusConnection *connection,
+ DBusMessage *message,
+ void *userdata) {
+
+ Manager *manager = userdata;
+ Machine *m;
+ int r;
+
+ r = get_machine_for_path(manager, dbus_message_get_path(message), &m);
+ if (r < 0) {
+
+ if (r == -ENOMEM)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (r == -ENOENT) {
+ DBusError e;
+
+ dbus_error_init(&e);
+ dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown machine");
+ return bus_send_error_reply(connection, message, &e, r);
+ }
+
+ return bus_send_error_reply(connection, message, NULL, r);
+ }
+
+ return machine_message_dispatch(m, connection, message);
+}
+
+const DBusObjectPathVTable bus_machine_vtable = {
+ .message_function = machine_message_handler
+};
+
+char *machine_bus_path(Machine *m) {
+ _cleanup_free_ char *e = NULL;
+
+ assert(m);
+
+ e = bus_path_escape(m->name);
+ if (!e)
+ return NULL;
+
+ return strappend("/org/freedesktop/login1/machine/", e);
+}
+
+int machine_send_signal(Machine *m, bool new_machine) {
+ _cleanup_dbus_message_unref_ DBusMessage *msg = NULL;
+ _cleanup_free_ char *p = NULL;
+
+ assert(m);
+
+ msg = dbus_message_new_signal("/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ new_machine ? "MachineNew" : "MachineRemoved");
+
+ if (!m)
+ return -ENOMEM;
+
+ p = machine_bus_path(m);
+ if (!p)
+ return -ENOMEM;
+
+ if (!dbus_message_append_args(
+ msg,
+ DBUS_TYPE_STRING, &m->name,
+ DBUS_TYPE_OBJECT_PATH, &p,
+ DBUS_TYPE_INVALID))
+ return -ENOMEM;
+
+ if (!dbus_connection_send(m->manager->bus, msg, NULL))
+ return -ENOMEM;
+
+ return 0;
+}
+
+int machine_send_changed(Machine *m, const char *properties) {
+ _cleanup_dbus_message_unref_ DBusMessage *msg = NULL;
+ _cleanup_free_ char *p = NULL;
+
+ assert(m);
+
+ if (!m->started)
+ return 0;
+
+ p = machine_bus_path(m);
+ if (!p)
+ return -ENOMEM;
+
+ msg = bus_properties_changed_new(p, "org.freedesktop.login1.Machine", properties);
+ if (!msg)
+ return -ENOMEM;
+
+ if (!dbus_connection_send(m->manager->bus, msg, NULL))
+ return -ENOMEM;
+
+ return 0;
+}
--- /dev/null
+/*-*- 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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "logind-machine.h"
+#include "util.h"
+#include "mkdir.h"
+#include "cgroup-util.h"
+#include "hashmap.h"
+#include "strv.h"
+#include "fileio.h"
+#include "special.h"
+#include <systemd/sd-messages.h>
+
+Machine* machine_new(Manager *manager, const char *name) {
+ Machine *m;
+
+ assert(manager);
+ assert(name);
+
+ m = new0(Machine, 1);
+ if (!m)
+ return NULL;
+
+ m->name = strdup(name);
+ if (!m->name)
+ goto fail;
+
+ m->state_file = strappend("/run/systemd/machines/", m->name);
+ if (!m->state_file)
+ goto fail;
+
+ if (hashmap_put(manager->machines, m->name, m) < 0)
+ goto fail;
+
+ m->class = _MACHINE_CLASS_INVALID;
+ m->manager = manager;
+
+ return m;
+
+fail:
+ free(m->state_file);
+ free(m->name);
+ free(m);
+
+ return NULL;
+}
+
+void machine_free(Machine *m) {
+ assert(m);
+
+ if (m->in_gc_queue)
+ LIST_REMOVE(Machine, gc_queue, m->manager->machine_gc_queue, m);
+
+ if (m->cgroup_path) {
+ hashmap_remove(m->manager->machine_cgroups, m->cgroup_path);
+ free(m->cgroup_path);
+ }
+
+ hashmap_remove(m->manager->machines, m->name);
+
+ free(m->name);
+ free(m->state_file);
+ free(m->service);
+ free(m->slice);
+ free(m->root_directory);
+ free(m);
+}
+
+int machine_save(Machine *m) {
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(m);
+ assert(m->state_file);
+
+ if (!m->started)
+ return 0;
+
+ r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
+ if (r < 0)
+ goto finish;
+
+ r = fopen_temporary(m->state_file, &f, &temp_path);
+ if (r < 0)
+ goto finish;
+
+ fchmod(fileno(f), 0644);
+
+ fprintf(f,
+ "# This is private data. Do not parse.\n"
+ "NAME=%s\n",
+ m->name);
+
+ if (m->cgroup_path)
+ fprintf(f, "CGROUP=%s\n", m->cgroup_path);
+
+ if (m->service)
+ fprintf(f, "SERVICE=%s\n", m->service);
+
+ if (m->slice)
+ fprintf(f, "SLICE=%s\n", m->slice);
+
+ if (m->root_directory)
+ fprintf(f, "ROOT=%s\n", m->root_directory);
+
+ if (!sd_id128_equal(m->id, SD_ID128_NULL))
+ fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
+
+ if (m->leader != 0)
+ fprintf(f, "LEADER=%lu\n", (unsigned long) m->leader);
+
+ if (m->class != _MACHINE_CLASS_INVALID)
+ fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
+
+ if (dual_timestamp_is_set(&m->timestamp))
+ fprintf(f,
+ "REALTIME=%llu\n"
+ "MONOTONIC=%llu\n",
+ (unsigned long long) m->timestamp.realtime,
+ (unsigned long long) m->timestamp.monotonic);
+
+ fflush(f);
+
+ if (ferror(f) || rename(temp_path, m->state_file) < 0) {
+ r = -errno;
+ unlink(m->state_file);
+ unlink(temp_path);
+ }
+
+finish:
+ if (r < 0)
+ log_error("Failed to save machine data for %s: %s", m->name, strerror(-r));
+
+ return r;
+}
+
+int machine_load(Machine *m) {
+ _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
+ int r;
+
+ assert(m);
+
+ r = parse_env_file(m->state_file, NEWLINE,
+ "CGROUP", &m->cgroup_path,
+ "SERVICE", &m->service,
+ "SLICE", &m->slice,
+ "ROOT", &m->root_directory,
+ "ID", &id,
+ "LEADER", &leader,
+ "CLASS", &class,
+ "REALTIME", &realtime,
+ "MONOTONIC", &monotonic,
+ NULL);
+ if (r < 0) {
+ if (r == -ENOENT)
+ return 0;
+
+ log_error("Failed to read %s: %s", m->state_file, strerror(-r));
+ return r;
+ }
+
+ if (id)
+ sd_id128_from_string(id, &m->id);
+
+ if (leader)
+ parse_pid(leader, &m->leader);
+
+ if (class) {
+ MachineClass c;
+
+ c = machine_class_from_string(class);
+ if (c >= 0)
+ m->class = c;
+ }
+
+ if (realtime) {
+ unsigned long long l;
+ if (sscanf(realtime, "%llu", &l) > 0)
+ m->timestamp.realtime = l;
+ }
+
+ if (monotonic) {
+ unsigned long long l;
+ if (sscanf(monotonic, "%llu", &l) > 0)
+ m->timestamp.monotonic = l;
+ }
+
+ return r;
+}
+
+static int machine_create_one_group(Machine *m, const char *controller, const char *path) {
+ int r;
+
+ assert(m);
+ assert(path);
+
+ if (m->leader > 0)
+ r = cg_create_and_attach(controller, path, m->leader);
+ else
+ r = -EINVAL;
+
+ if (r < 0) {
+ r = cg_create(controller, path, NULL);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int machine_create_cgroup(Machine *m) {
+ char **k;
+ int r;
+
+ assert(m);
+
+ if (!m->slice) {
+ m->slice = strdup(SPECIAL_MACHINE_SLICE);
+ if (!m->slice)
+ return log_oom();
+ }
+
+ if (!m->cgroup_path) {
+ _cleanup_free_ char *escaped = NULL, *slice = NULL;
+ char *name;
+
+ name = strappenda(m->name, ".machine");
+
+ escaped = cg_escape(name);
+ if (!escaped)
+ return log_oom();
+
+ r = cg_slice_to_path(m->slice, &slice);
+ if (r < 0)
+ return r;
+
+ m->cgroup_path = strjoin(m->manager->cgroup_root, "/", slice, "/", escaped, NULL);
+ if (!m->cgroup_path)
+ return log_oom();
+ }
+
+ r = machine_create_one_group(m, SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path);
+ if (r < 0) {
+ log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", m->cgroup_path, strerror(-r));
+ return r;
+ }
+
+ STRV_FOREACH(k, m->manager->controllers) {
+
+ if (strv_contains(m->manager->reset_controllers, *k))
+ continue;
+
+ r = machine_create_one_group(m, *k, m->cgroup_path);
+ if (r < 0)
+ log_warning("Failed to create cgroup %s:%s: %s", *k, m->cgroup_path, strerror(-r));
+ }
+
+ if (m->leader > 0) {
+ STRV_FOREACH(k, m->manager->reset_controllers) {
+ r = cg_attach(*k, "/", m->leader);
+ if (r < 0)
+ log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
+ }
+ }
+
+ r = hashmap_put(m->manager->machine_cgroups, m->cgroup_path, m);
+ if (r < 0)
+ log_warning("Failed to create mapping between cgroup and machine");
+
+ return 0;
+}
+
+int machine_start(Machine *m) {
+ int r;
+
+ assert(m);
+
+ if (m->started)
+ return 0;
+
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_MACHINE_START),
+ "NAME=%s", m->name,
+ "LEADER=%lu", (unsigned long) m->leader,
+ "MESSAGE=New machine %s.", m->name,
+ NULL);
+
+ /* Create cgroup */
+ r = machine_create_cgroup(m);
+ if (r < 0)
+ return r;
+
+ if (!dual_timestamp_is_set(&m->timestamp))
+ dual_timestamp_get(&m->timestamp);
+
+ m->started = true;
+
+ /* Save new machine data */
+ machine_save(m);
+
+ machine_send_signal(m, true);
+
+ return 0;
+}
+
+static int machine_terminate_cgroup(Machine *m) {
+ int r;
+ char **k;
+
+ assert(m);
+
+ if (!m->cgroup_path)
+ return 0;
+
+ cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, false);
+
+ r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, true);
+ if (r < 0)
+ log_error("Failed to kill machine cgroup: %s", strerror(-r));
+
+ STRV_FOREACH(k, m->manager->controllers)
+ cg_trim(*k, m->cgroup_path, true);
+
+ hashmap_remove(m->manager->machine_cgroups, m->cgroup_path);
+
+ free(m->cgroup_path);
+ m->cgroup_path = NULL;
+
+ return r;
+}
+
+int machine_stop(Machine *m) {
+ int r = 0, k;
+ assert(m);
+
+ if (m->started)
+ log_struct(LOG_INFO,
+ MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
+ "NAME=%s", m->name,
+ "LEADER=%lu", (unsigned long) m->leader,
+ "MESSAGE=Machine %s terminated.", m->name,
+ NULL);
+
+ /* Kill cgroup */
+ k = machine_terminate_cgroup(m);
+ if (k < 0)
+ r = k;
+
+ unlink(m->state_file);
+ machine_add_to_gc_queue(m);
+
+ if (m->started)
+ machine_send_signal(m, false);
+
+ m->started = false;
+
+ return r;
+}
+
+int machine_check_gc(Machine *m, bool drop_not_started) {
+ int r;
+
+ assert(m);
+
+ if (drop_not_started && !m->started)
+ return 0;
+
+ if (m->cgroup_path) {
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, false);
+ if (r < 0)
+ return r;
+
+ if (r <= 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+void machine_add_to_gc_queue(Machine *m) {
+ assert(m);
+
+ if (m->in_gc_queue)
+ return;
+
+ LIST_PREPEND(Machine, gc_queue, m->manager->machine_gc_queue, m);
+ m->in_gc_queue = true;
+}
+
+int machine_kill(Machine *m, KillWho who, int signo) {
+ _cleanup_set_free_ Set *pid_set = NULL;
+ int r = 0;
+
+ assert(m);
+
+ if (!m->cgroup_path)
+ return -ESRCH;
+
+ if (m->leader <= 0 && who == KILL_LEADER)
+ return -ESRCH;
+
+ if (m->leader > 0)
+ if (kill(m->leader, signo) < 0)
+ r = -errno;
+
+ if (who == KILL_ALL) {
+ int q;
+
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set)
+ return log_oom();
+
+ if (m->leader > 0) {
+ q = set_put(pid_set, LONG_TO_PTR(m->leader));
+ if (q < 0)
+ r = q;
+ }
+
+ q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, signo, false, true, false, pid_set);
+ if (q < 0 && (q != -EAGAIN && q != -ESRCH && q != -ENOENT))
+ r = q;
+ }
+
+ return r;
+}
+
+static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
+ [MACHINE_CONTAINER] = "container",
+ [MACHINE_VM] = "vm"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
--- /dev/null
+/*-*- 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 Machine Machine;
+
+#include "list.h"
+#include "util.h"
+#include "logind.h"
+#include "logind-session.h"
+
+typedef enum MachineClass {
+ MACHINE_CONTAINER,
+ MACHINE_VM,
+ _MACHINE_CLASS_MAX,
+ _MACHINE_CLASS_INVALID = -1
+} MachineClass;
+
+struct Machine {
+ Manager *manager;
+
+ char *name;
+ sd_id128_t id;
+
+ MachineClass class;
+
+ char *state_file;
+ char *service;
+ char *cgroup_path;
+ char *slice;
+ char *root_directory;
+
+ pid_t leader;
+
+ dual_timestamp timestamp;
+
+ bool in_gc_queue:1;
+ bool started:1;
+
+ LIST_FIELDS(Machine, gc_queue);
+};
+
+Machine* machine_new(Manager *manager, const char *name);
+void machine_free(Machine *m);
+int machine_check_gc(Machine *m, bool drop_not_started);
+void machine_add_to_gc_queue(Machine *m);
+int machine_start(Machine *m);
+int machine_stop(Machine *m);
+int machine_save(Machine *m);
+int machine_load(Machine *m);
+int machine_kill(Machine *m, KillWho who, int signo);
+
+char *machine_bus_path(Machine *s);
+
+extern const DBusObjectPathVTable bus_machine_vtable;
+
+int machine_send_signal(Machine *m, bool new_machine);
+int machine_send_changed(Machine *m, const char *properties);
+
+const char* machine_class_to_string(MachineClass t) _const_;
+MachineClass machine_class_from_string(const char *s) _pure_;
}
static int get_seat_for_path(Manager *m, const char *path, Seat **_s) {
+ _cleanup_free_ char *id = NULL;
Seat *s;
- char *id;
assert(m);
assert(path);
return -ENOMEM;
s = hashmap_get(m->seats, id);
- free(id);
-
if (!s)
return -ENOENT;
};
char *seat_bus_path(Seat *s) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
assert(s);
" <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
}
static int get_session_for_path(Manager *m, const char *path, Session **_s) {
+ _cleanup_free_ char *id = NULL;
Session *s;
- char *id;
assert(m);
assert(path);
return -ENOMEM;
s = hashmap_get(m->sessions, id);
- free(id);
-
if (!s)
return -ENOENT;
{ "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
{ "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
{ "Service", bus_property_append_string, "s", offsetof(Session, service), true },
+ { "Slice", bus_property_append_string, "s", offsetof(Session, slice), true },
{ "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
{ "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
{ "Type", bus_session_append_type, "s", offsetof(Session, type) },
};
char *session_bus_path(Session *s) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
assert(s);
#include "logind-session.h"
#include "fileio.h"
-Session* session_new(Manager *m, User *u, const char *id) {
+Session* session_new(Manager *m, const char *id) {
Session *s;
assert(m);
s->manager = m;
s->fifo_fd = -1;
- s->user = u;
-
- LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
return s;
}
free(s->remote_host);
free(s->remote_user);
free(s->service);
+ free(s->slice);
hashmap_remove(s->manager->sessions, s->id);
session_remove_fifo(s);
free(s);
}
+void session_set_user(Session *s, User *u) {
+ assert(s);
+ assert(!s->user);
+
+ s->user = u;
+ LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
+}
+
int session_save(Session *s) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *temp_path = NULL;
assert(s);
+ if (!s->user)
+ return -ESTALE;
+
if (!s->started)
return 0;
if (s->service)
fprintf(f, "SERVICE=%s\n", s->service);
+ if (s->seat)
+ fprintf(f, "SLICE=%s\n", s->slice);
+
if (s->seat && seat_can_multi_session(s->seat))
fprintf(f, "VTNR=%i\n", s->vtnr);
if (s->audit_id > 0)
fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
+ if (dual_timestamp_is_set(&s->timestamp))
+ fprintf(f,
+ "REALTIME=%llu\n"
+ "MONOTONIC=%llu\n",
+ (unsigned long long) s->timestamp.realtime,
+ (unsigned long long) s->timestamp.monotonic);
+
fflush(f);
if (ferror(f) || rename(temp_path, s->state_file) < 0) {
}
int session_load(Session *s) {
- char *remote = NULL,
+ _cleanup_free_ char *remote = NULL,
*kill_processes = NULL,
*seat = NULL,
*vtnr = NULL,
*leader = NULL,
*audit_id = NULL,
*type = NULL,
- *class = NULL;
+ *class = NULL,
+ *uid = NULL,
+ *realtime = NULL,
+ *monotonic = NULL;
int k, r;
"REMOTE_HOST", &s->remote_host,
"REMOTE_USER", &s->remote_user,
"SERVICE", &s->service,
+ "SLICE", &s->slice,
"VTNR", &vtnr,
"LEADER", &leader,
"TYPE", &type,
"CLASS", &class,
+ "UID", &uid,
+ "REALTIME", &realtime,
+ "MONOTONIC", &monotonic,
NULL);
- if (r < 0)
- goto finish;
+ if (r < 0) {
+ log_error("Failed to read %s: %s", s->state_file, strerror(-r));
+ return r;
+ }
+
+ if (!s->user) {
+ uid_t u;
+ User *user;
+
+ if (!uid) {
+ log_error("UID not specified for session %s", s->id);
+ return -ENOENT;
+ }
+
+ r = parse_uid(uid, &u);
+ if (r < 0) {
+ log_error("Failed to parse UID value %s for session %s.", uid, s->id);
+ return r;
+ }
+
+ user = hashmap_get(s->manager->users, ULONG_TO_PTR((unsigned long) u));
+ if (!user) {
+ log_error("User of session %s not known.", s->id);
+ return -ENOENT;
+ }
+
+ session_set_user(s, user);
+ }
if (remote) {
k = parse_boolean(remote);
close_nointr_nofail(fd);
}
-finish:
- free(remote);
- free(kill_processes);
- free(seat);
- free(vtnr);
- free(leader);
- free(audit_id);
- free(class);
+ if (realtime) {
+ unsigned long long l;
+ if (sscanf(realtime, "%llu", &l) > 0)
+ s->timestamp.realtime = l;
+ }
+
+ if (monotonic) {
+ unsigned long long l;
+ if (sscanf(monotonic, "%llu", &l) > 0)
+ s->timestamp.monotonic = l;
+ }
return r;
}
int r;
assert(s);
+ assert(s->user);
if (s->vtnr < 0)
return -ENOTSUP;
int r;
assert(s);
+ assert(s->user);
assert(path);
- if (s->leader > 0) {
+ if (s->leader > 0)
r = cg_create_and_attach(controller, path, s->leader);
- if (r < 0)
- r = cg_create(controller, path, NULL);
- } else
- r = cg_create(controller, path, NULL);
+ else
+ r = -EINVAL;
- if (r < 0)
- return r;
+ if (r < 0) {
+ r = cg_create(controller, path, NULL);
+ if (r < 0)
+ return r;
+ }
r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
if (r >= 0)
static int session_create_cgroup(Session *s) {
char **k;
- char *p;
int r;
assert(s);
if (!escaped)
return log_oom();
- p = strjoin(s->user->cgroup_path, "/", escaped, NULL);
- if (!p)
+ if (s->slice) {
+ _cleanup_free_ char *slice = NULL;
+
+ r = cg_slice_to_path(s->slice, &slice);
+ if (r < 0)
+ return r;
+
+ s->cgroup_path = strjoin(s->manager->cgroup_root, "/", slice, "/", escaped, NULL);
+ } else
+ s->cgroup_path = strjoin(s->user->cgroup_path, "/", escaped, NULL);
+
+ if (!s->cgroup_path)
+ return log_oom();
+ }
+
+ if (!s->slice) {
+ s->slice = strdup(s->user->slice);
+ if (!s->slice)
return log_oom();
- } else
- p = s->cgroup_path;
+ }
- r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
+ r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
if (r < 0) {
- log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
- free(p);
- s->cgroup_path = NULL;
+ log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", s->cgroup_path, strerror(-r));
return r;
}
- s->cgroup_path = p;
-
STRV_FOREACH(k, s->controllers) {
if (strv_contains(s->reset_controllers, *k))
continue;
- r = session_create_one_group(s, *k, p);
+ r = session_create_one_group(s, *k, s->cgroup_path);
if (r < 0)
- log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
+ log_warning("Failed to create %s:%s: %s", *k, s->cgroup_path, strerror(-r));
}
STRV_FOREACH(k, s->manager->controllers) {
strv_contains(s->controllers, *k))
continue;
- r = session_create_one_group(s, *k, p);
+ r = session_create_one_group(s, *k, s->cgroup_path);
if (r < 0)
- log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
+ log_warning("Failed to create %s:%s: %s", *k, s->cgroup_path, strerror(-r));
}
if (s->leader > 0) {
r = cg_attach(*k, "/", s->leader);
if (r < 0)
log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
-
}
}
int r;
assert(s);
- assert(s->user);
+
+ if (!s->user)
+ return -ESTALE;
if (s->started)
return 0;
/* Create X11 symlink */
session_link_x11_socket(s);
- dual_timestamp_get(&s->timestamp);
+ if (!dual_timestamp_is_set(&s->timestamp))
+ dual_timestamp_get(&s->timestamp);
if (s->seat)
seat_read_active_vt(s->seat);
assert(s);
+ if (!s->user)
+ return -ESTALE;
+
if (s->started)
log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
if (drop_not_started && !s->started)
return 0;
+ if (!s->user)
+ return 0;
+
if (s->fifo_fd >= 0) {
r = pipe_eof(s->fifo_fd);
}
int session_kill(Session *s, KillWho who, int signo) {
+ _cleanup_set_free_ Set *pid_set = NULL;
int r = 0;
- Set *pid_set = NULL;
assert(s);
pid_set = set_new(trivial_hash_func, trivial_compare_func);
if (!pid_set)
- return -ENOMEM;
+ return log_oom();
if (s->leader > 0) {
q = set_put(pid_set, LONG_TO_PTR(s->leader));
r = q;
}
- if (pid_set)
- set_free(pid_set);
-
return r;
}
***/
typedef struct Session Session;
+typedef enum KillWho KillWho;
#include "list.h"
#include "util.h"
_SESSION_TYPE_INVALID = -1
} SessionType;
-typedef enum KillWho {
+enum KillWho {
KILL_LEADER,
KILL_ALL,
_KILL_WHO_MAX,
_KILL_WHO_INVALID = -1
-} KillWho;
+};
struct Session {
Manager *manager;
char *remote_host;
char *service;
+ char *slice;
int vtnr;
Seat *seat;
LIST_FIELDS(Session, gc_queue);
};
-Session *session_new(Manager *m, User *u, const char *id);
+Session *session_new(Manager *m, const char *id);
void session_free(Session *s);
+void session_set_user(Session *s, User *u);
int session_check_gc(Session *s, bool drop_not_started);
void session_add_to_gc_queue(Session *s);
int session_activate(Session *s);
" <property name=\"RuntimePath\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
+ " <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Display\" type=\"(so)\" access=\"read\"/>\n" \
" <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
" <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
- " </interface>\n" \
+ " </interface>\n"
#define INTROSPECTION \
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
assert(path);
assert(_u);
- if (!startswith(path, "/org/freedesktop/login1/user/"))
+ if (!startswith(path, "/org/freedesktop/login1/user/_"))
return -EINVAL;
- r = safe_atolu(path + 29, &lu);
+ r = safe_atolu(path + 30, &lu);
if (r < 0)
return r;
{ "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true },
{ "DefaultControlGroup", bus_user_append_default_cgroup, "s", 0 },
{ "Service", bus_property_append_string, "s", offsetof(User, service), true },
+ { "Slice", bus_property_append_string, "s", offsetof(User, slice), true },
{ "Display", bus_user_append_display, "(so)", 0 },
{ "State", bus_user_append_state, "s", 0 },
{ "Sessions", bus_user_append_sessions, "a(so)", 0 },
assert(u);
- if (asprintf(&s, "/org/freedesktop/login1/user/%llu", (unsigned long long) u->uid) < 0)
+ if (asprintf(&s, "/org/freedesktop/login1/user/_%llu", (unsigned long long) u->uid) < 0)
return NULL;
return s;
#include "hashmap.h"
#include "strv.h"
#include "fileio.h"
+#include "special.h"
User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
User *u;
return NULL;
u->name = strdup(name);
- if (!u->name) {
- free(u);
- return NULL;
- }
+ if (!u->name)
+ goto fail;
- if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
- free(u->name);
- free(u);
- return NULL;
- }
+ if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
+ goto fail;
- if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
- free(u->state_file);
- free(u->name);
- free(u);
- return NULL;
- }
+ if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0)
+ goto fail;
u->manager = m;
u->uid = uid;
u->gid = gid;
return u;
+
+fail:
+ free(u->state_file);
+ free(u->name);
+ free(u);
+
+ return NULL;
}
void user_free(User *u) {
while (u->sessions)
session_free(u->sessions);
- if (u->cgroup_path)
+ if (u->cgroup_path) {
hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
- free(u->cgroup_path);
+ free(u->cgroup_path);
+ }
free(u->service);
free(u->runtime_path);
free(u->name);
free(u->state_file);
+ free(u->slice);
free(u);
}
int user_save(User *u) {
- FILE *f;
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
int r;
- char *temp_path;
assert(u);
assert(u->state_file);
user_state_to_string(user_get_state(u)));
if (u->cgroup_path)
- fprintf(f,
- "CGROUP=%s\n",
- u->cgroup_path);
+ fprintf(f, "CGROUP=%s\n", u->cgroup_path);
if (u->runtime_path)
- fprintf(f,
- "RUNTIME=%s\n",
- u->runtime_path);
+ fprintf(f, "RUNTIME=%s\n", u->runtime_path);
if (u->service)
- fprintf(f,
- "SERVICE=%s\n",
- u->service);
+ fprintf(f, "SERVICE=%s\n", u->service);
+
+ if (u->slice)
+ fprintf(f, "SLICE=%s\n", u->slice);
if (u->display)
+ fprintf(f, "DISPLAY=%s\n", u->display->id);
+
+ if (dual_timestamp_is_set(&u->timestamp))
fprintf(f,
- "DISPLAY=%s\n",
- u->display->id);
+ "REALTIME=%llu\n"
+ "MONOTONIC=%llu\n",
+ (unsigned long long) u->timestamp.realtime,
+ (unsigned long long) u->timestamp.monotonic);
if (u->sessions) {
Session *i;
unlink(temp_path);
}
- fclose(f);
- free(temp_path);
-
finish:
if (r < 0)
log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
}
int user_load(User *u) {
- int r;
- char *display = NULL;
+ _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
Session *s = NULL;
+ int r;
assert(u);
r = parse_env_file(u->state_file, NEWLINE,
- "CGROUP", &u->cgroup_path,
- "RUNTIME", &u->runtime_path,
- "SERVICE", &u->service,
- "DISPLAY", &display,
+ "CGROUP", &u->cgroup_path,
+ "RUNTIME", &u->runtime_path,
+ "SERVICE", &u->service,
+ "DISPLAY", &display,
+ "SLICE", &u->slice,
+ "REALTIME", &realtime,
+ "MONOTONIC", &monotonic,
NULL);
if (r < 0) {
- free(display);
-
if (r == -ENOENT)
return 0;
return r;
}
- if (display) {
+ if (display)
s = hashmap_get(u->manager->sessions, display);
- free(display);
- }
if (s && s->display && display_is_local(s->display))
u->display = s;
+ if (realtime) {
+ unsigned long long l;
+ if (sscanf(realtime, "%llu", &l) > 0)
+ u->timestamp.realtime = l;
+ }
+
+ if (monotonic) {
+ unsigned long long l;
+ if (sscanf(monotonic, "%llu", &l) > 0)
+ u->timestamp.monotonic = l;
+ }
+
return r;
}
static int user_create_cgroup(User *u) {
char **k;
- char *p;
int r;
assert(u);
+ if (!u->slice) {
+ u->slice = strdup(SPECIAL_USER_SLICE);
+ if (!u->slice)
+ return log_oom();
+ }
+
if (!u->cgroup_path) {
- _cleanup_free_ char *name = NULL, *escaped = NULL;
+ _cleanup_free_ char *name = NULL, *escaped = NULL, *slice = NULL;
if (asprintf(&name, "%lu.user", (unsigned long) u->uid) < 0)
return log_oom();
if (!escaped)
return log_oom();
- p = strjoin(u->manager->cgroup_path, "/", escaped, NULL);
- if (!p)
+ r = cg_slice_to_path(u->slice, &slice);
+ if (r < 0)
+ return r;
+
+ u->cgroup_path = strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
+ if (!u->cgroup_path)
return log_oom();
- } else
- p = u->cgroup_path;
+ }
- r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p, NULL);
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL);
if (r < 0) {
- log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
- free(p);
- u->cgroup_path = NULL;
+ log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", u->cgroup_path, strerror(-r));
return r;
}
- u->cgroup_path = p;
-
STRV_FOREACH(k, u->manager->controllers) {
if (strv_contains(u->manager->reset_controllers, *k))
continue;
- r = cg_create(*k, p, NULL);
+ r = cg_create(*k, u->cgroup_path, NULL);
if (r < 0)
- log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
+ log_warning("Failed to create cgroup %s:%s: %s", *k, u->cgroup_path, strerror(-r));
}
r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
if (r < 0)
return r;
- dual_timestamp_get(&u->timestamp);
+ if (!dual_timestamp_is_set(&u->timestamp))
+ dual_timestamp_get(&u->timestamp);
u->started = true;
}
int user_kill(User *u, int signo) {
- int r = 0, q;
- Set *pid_set = NULL;
+ _cleanup_set_free_ Set *pid_set = NULL;
+ int r;
assert(u);
if (!pid_set)
return -ENOMEM;
- q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
- if (q < 0)
- if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
-
- if (pid_set)
- set_free(pid_set);
+ r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
+ if (r < 0 && (r != -EAGAIN && r != -ESRCH && r != -ENOENT))
+ return r;
- return r;
+ return 0;
}
static const char* const user_state_table[_USER_STATE_MAX] = {
char *runtime_path;
char *service;
char *cgroup_path;
+ char *slice;
Session *display;
m->users = hashmap_new(trivial_hash_func, trivial_compare_func);
m->inhibitors = hashmap_new(string_hash_func, string_compare_func);
m->buttons = hashmap_new(string_hash_func, string_compare_func);
+ m->machines = hashmap_new(string_hash_func, string_compare_func);
m->user_cgroups = hashmap_new(string_hash_func, string_compare_func);
m->session_cgroups = hashmap_new(string_hash_func, string_compare_func);
+ m->machine_cgroups = hashmap_new(string_hash_func, string_compare_func);
m->session_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
m->inhibitor_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
m->button_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
- if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons ||
- !m->user_cgroups || !m->session_cgroups ||
+ if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->machines ||
+ !m->user_cgroups || !m->session_cgroups || !m->machine_cgroups ||
!m->session_fds || !m->inhibitor_fds || !m->button_fds) {
manager_free(m);
return NULL;
return NULL;
}
- if (cg_get_user_path(&m->cgroup_path) < 0) {
+ if (cg_get_root_path(&m->cgroup_root) < 0) {
manager_free(m);
return NULL;
}
+ if (streq(m->cgroup_root, "/"))
+ m->cgroup_root[0] = 0;
+
return m;
}
Seat *s;
Inhibitor *i;
Button *b;
+ Machine *machine;
assert(m);
while ((b = hashmap_first(m->buttons)))
button_free(b);
+ while ((machine = hashmap_first(m->machines)))
+ machine_free(machine);
+
hashmap_free(m->devices);
hashmap_free(m->seats);
hashmap_free(m->sessions);
hashmap_free(m->users);
hashmap_free(m->inhibitors);
hashmap_free(m->buttons);
+ hashmap_free(m->machines);
hashmap_free(m->user_cgroups);
hashmap_free(m->session_cgroups);
+ hashmap_free(m->machine_cgroups);
hashmap_free(m->session_fds);
hashmap_free(m->inhibitor_fds);
free(m->action_job);
- free(m->cgroup_path);
+ free(m->cgroup_root);
free(m);
}
return 0;
}
-int manager_add_session(Manager *m, User *u, const char *id, Session **_session) {
+int manager_add_session(Manager *m, const char *id, Session **_session) {
Session *s;
assert(m);
return 0;
}
- s = session_new(m, u, id);
+ s = session_new(m, id);
if (!s)
return -ENOMEM;
return 0;
}
+int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
+ Machine *machine;
+
+ assert(m);
+ assert(name);
+
+ machine = hashmap_get(m->machines, name);
+ if (machine) {
+ if (_machine)
+ *_machine = machine;
+
+ return 0;
+ }
+
+ machine = machine_new(m, name);
+ if (!m)
+ return -ENOMEM;
+
+ if (_machine)
+ *_machine = machine;
+
+ return 0;
+}
+
int manager_process_seat_device(Manager *m, struct udev_device *d) {
Device *device;
int r;
}
int manager_enumerate_seats(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
Seat *s;
int k;
r = k;
}
- closedir(d);
-
- return r;
-}
-
-static int manager_enumerate_users_from_cgroup(Manager *m) {
- _cleanup_closedir_ DIR *d = NULL;
- int r = 0, k;
- char *name;
-
- r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
-
- log_error("Failed to open %s: %s", m->cgroup_path, strerror(-r));
- return r;
- }
-
- while ((k = cg_read_subgroup(d, &name)) > 0) {
- User *user;
- char *e;
-
- e = endswith(name, ".user");
- if (e) {
- *e = 0;
-
- k = manager_add_user_by_name(m, name, &user);
- if (k < 0) {
- free(name);
- r = k;
- continue;
- }
-
- user_add_to_gc_queue(user);
-
- if (!user->cgroup_path) {
- user->cgroup_path = strjoin(m->cgroup_path, "/", name, NULL);
- if (!user->cgroup_path) {
- k = log_oom();
- free(name);
- break;
- }
- }
- }
-
- free(name);
- }
-
- if (k < 0)
- r = k;
-
return r;
}
static int manager_enumerate_linger_users(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
+ assert(m);
+
d = opendir("/var/lib/systemd/linger");
if (!d) {
if (errno == ENOENT)
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
int k;
if (!dirent_is_file(de))
}
}
- closedir(d);
-
return r;
}
int manager_enumerate_users(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r, k;
assert(m);
- /* First, enumerate user cgroups */
- r = manager_enumerate_users_from_cgroup(m);
-
- /* Second, add lingering users on top */
+ /* Add lingering users */
k = manager_enumerate_linger_users(m);
if (k < 0)
r = k;
- /* Third, read in user data stored on disk */
+ /* Read in user data stored on disk */
d = opendir("/run/systemd/users");
if (!d) {
if (errno == ENOENT)
return -errno;
}
- while ((de = readdir(d))) {
- uid_t uid;
+ FOREACH_DIRENT(de, d, return -errno) {
User *u;
if (!dirent_is_file(de))
continue;
- k = parse_uid(de->d_name, &uid);
+ k = manager_add_user_by_name(m, de->d_name, &u);
if (k < 0) {
- log_error("Failed to parse file name %s: %s", de->d_name, strerror(-k));
- continue;
- }
+ log_error("Failed to add user by file name %s: %s", de->d_name, strerror(-k));
- u = hashmap_get(m->users, ULONG_TO_PTR(uid));
- if (!u) {
- unlinkat(dirfd(d), de->d_name, 0);
+ r = k;
continue;
}
+ user_add_to_gc_queue(u);
+
k = user_load(u);
if (k < 0)
r = k;
}
- closedir(d);
-
return r;
}
-static int manager_enumerate_sessions_from_cgroup(Manager *m) {
- User *u;
- Iterator i;
+int manager_enumerate_sessions(Manager *m) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
int r = 0;
- HASHMAP_FOREACH(u, m->users, i) {
- _cleanup_closedir_ DIR *d = NULL;
- char *name;
+ assert(m);
+
+ /* Read in session data stored on disk */
+ d = opendir("/run/systemd/sessions");
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open /run/systemd/sessions: %m");
+ return -errno;
+ }
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ struct Session *s;
int k;
- if (!u->cgroup_path)
+ if (!dirent_is_file(de))
continue;
- k = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &d);
+ k = manager_add_session(m, de->d_name, &s);
if (k < 0) {
- if (k == -ENOENT)
- continue;
+ log_error("Failed to add session by file name %s: %s", de->d_name, strerror(-k));
- log_error("Failed to open %s: %s", u->cgroup_path, strerror(-k));
r = k;
continue;
}
- while ((k = cg_read_subgroup(d, &name)) > 0) {
- Session *session;
- char *e;
-
- e = endswith(name, ".session");
- if (e) {
- *e = 0;
-
- k = manager_add_session(m, u, name, &session);
- if (k < 0) {
- free(name);
- r = k;
- continue;
- }
-
- session_add_to_gc_queue(session);
-
- if (!session->cgroup_path) {
- session->cgroup_path = strjoin(m->cgroup_path, "/", name, NULL);
- if (!session->cgroup_path) {
- k = log_oom();
- free(name);
- break;
- }
- }
- }
-
- free(name);
- }
+ session_add_to_gc_queue(s);
+ k = session_load(s);
if (k < 0)
r = k;
}
return r;
}
-int manager_enumerate_sessions(Manager *m) {
- DIR *d;
+int manager_enumerate_inhibitors(Manager *m) {
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
assert(m);
- /* First enumerate session cgroups */
- r = manager_enumerate_sessions_from_cgroup(m);
-
- /* Second, read in session data stored on disk */
- d = opendir("/run/systemd/sessions");
+ d = opendir("/run/systemd/inhibit");
if (!d) {
if (errno == ENOENT)
return 0;
- log_error("Failed to open /run/systemd/sessions: %m");
+ log_error("Failed to open /run/systemd/inhibit: %m");
return -errno;
}
- while ((de = readdir(d))) {
- struct Session *s;
+ FOREACH_DIRENT(de, d, return -errno) {
int k;
+ Inhibitor *i;
if (!dirent_is_file(de))
continue;
- s = hashmap_get(m->sessions, de->d_name);
- if (!s) {
- unlinkat(dirfd(d), de->d_name, 0);
+ k = manager_add_inhibitor(m, de->d_name, &i);
+ if (k < 0) {
+ log_notice("Couldn't add inhibitor %s: %s", de->d_name, strerror(-k));
+ r = k;
continue;
}
- k = session_load(s);
+ k = inhibitor_load(i);
if (k < 0)
r = k;
}
- closedir(d);
-
return r;
}
-int manager_enumerate_inhibitors(Manager *m) {
- DIR *d;
+int manager_enumerate_machines(Manager *m) {
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
assert(m);
- d = opendir("/run/systemd/inhibit");
+ /* Read in machine data stored on disk */
+ d = opendir("/run/systemd/machines");
if (!d) {
if (errno == ENOENT)
return 0;
- log_error("Failed to open /run/systemd/inhibit: %m");
+ log_error("Failed to open /run/systemd/machines: %m");
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
+ struct Machine *machine;
int k;
- Inhibitor *i;
if (!dirent_is_file(de))
continue;
- k = manager_add_inhibitor(m, de->d_name, &i);
+ k = manager_add_machine(m, de->d_name, &machine);
if (k < 0) {
- log_notice("Couldn't add inhibitor %s: %s", de->d_name, strerror(-k));
+ log_error("Failed to add machine by file name %s: %s", de->d_name, strerror(-k));
+
r = k;
continue;
}
- k = inhibitor_load(i);
+ machine_add_to_gc_queue(machine);
+
+ k = machine_load(machine);
if (k < 0)
r = k;
}
- closedir(d);
-
return r;
}
}
}
+int manager_get_machine_by_cgroup(Manager *m, const char *cgroup, Machine **machine) {
+ Machine *u;
+ char *p;
+
+ assert(m);
+ assert(cgroup);
+ assert(machine);
+
+ u = hashmap_get(m->machine_cgroups, cgroup);
+ if (u) {
+ *machine = u;
+ return 1;
+ }
+
+ p = strdupa(cgroup);
+ if (!p)
+ return log_oom();
+
+ for (;;) {
+ char *e;
+
+ e = strrchr(p, '/');
+ if (!e || e == p) {
+ *machine = NULL;
+ return 0;
+ }
+
+ *e = 0;
+
+ u = hashmap_get(m->machine_cgroups, p);
+ if (u) {
+ *machine = u;
+ return 1;
+ }
+ }
+}
+
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
_cleanup_free_ char *p = NULL;
int r;
return manager_get_session_by_cgroup(m, p, session);
}
+int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(m);
+ assert(pid >= 1);
+ assert(user);
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
+ if (r < 0)
+ return r;
+
+ return manager_get_user_by_cgroup(m, p, user);
+}
+
+int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(m);
+ assert(pid >= 1);
+ assert(machine);
+
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
+ if (r < 0)
+ return r;
+
+ return manager_get_machine_by_cgroup(m, p, machine);
+}
+
void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
+ Machine *machine;
Session *s;
User *u;
int r;
r = manager_get_user_by_cgroup(m, cgroup, &u);
if (r > 0)
user_add_to_gc_queue(u);
+
+ r = manager_get_machine_by_cgroup(m, cgroup, &machine);
+ if (r > 0)
+ machine_add_to_gc_queue(machine);
}
static void manager_dispatch_other(Manager *m, int fd) {
!dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/seat", &bus_seat_vtable, m) ||
!dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/session", &bus_session_vtable, m) ||
!dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/user", &bus_user_vtable, m) ||
+ !dbus_connection_register_fallback(m->bus, "/org/freedesktop/login1/machine", &bus_machine_vtable, m) ||
!dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) {
r = log_oom();
goto fail;
Seat *seat;
Session *session;
User *user;
+ Machine *machine;
assert(m);
user_free(user);
}
}
+
+ while ((machine = m->machine_gc_queue)) {
+ LIST_REMOVE(Machine, gc_queue, m->machine_gc_queue, machine);
+ machine->in_gc_queue = false;
+
+ if (machine_check_gc(machine, drop_not_started) == 0) {
+ machine_stop(machine);
+ machine_free(machine);
+ }
+ }
}
int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
Session *session;
User *user;
Inhibitor *inhibitor;
+ Machine *machine;
Iterator i;
assert(m);
manager_enumerate_sessions(m);
manager_enumerate_inhibitors(m);
manager_enumerate_buttons(m);
+ manager_enumerate_machines(m);
/* Remove stale objects before we start them */
manager_gc(m, false);
HASHMAP_FOREACH(inhibitor, m->inhibitors, i)
inhibitor_start(inhibitor);
+ HASHMAP_FOREACH(machine, m->machines, i)
+ machine_start(machine);
+
manager_dispatch_idle_action(m);
return 0;
mkdir_label("/run/systemd/seats", 0755);
mkdir_label("/run/systemd/users", 0755);
mkdir_label("/run/systemd/sessions", 0755);
+ mkdir_label("/run/systemd/machines", 0755);
m = manager_new();
if (!m) {
#include "logind-inhibit.h"
#include "logind-button.h"
#include "logind-action.h"
+#include "logind-machine.h"
struct Manager {
DBusConnection *bus;
Hashmap *users;
Hashmap *inhibitors;
Hashmap *buttons;
+ Hashmap *machines;
LIST_HEAD(Seat, seat_gc_queue);
LIST_HEAD(Session, session_gc_queue);
LIST_HEAD(User, user_gc_queue);
+ LIST_HEAD(Machine, machine_gc_queue);
struct udev *udev;
struct udev_monitor *udev_seat_monitor, *udev_vcsa_monitor, *udev_button_monitor;
Seat *vtconsole;
- char *cgroup_path;
+ char *cgroup_root;
char **controllers, **reset_controllers;
char **kill_only_users, **kill_exclude_users;
Hashmap *session_cgroups;
Hashmap *user_cgroups;
+ Hashmap *machine_cgroups;
Hashmap *session_fds;
Hashmap *inhibitor_fds;
int manager_add_device(Manager *m, const char *sysfs, Device **_device);
int manager_add_button(Manager *m, const char *name, Button **_button);
int manager_add_seat(Manager *m, const char *id, Seat **_seat);
-int manager_add_session(Manager *m, User *u, const char *id, Session **_session);
+int manager_add_session(Manager *m, const char *id, Session **_session);
int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user);
int manager_add_user_by_name(Manager *m, const char *name, User **_user);
int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user);
int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor);
+int manager_add_machine(Manager *m, const char *name, Machine **_machine);
int manager_process_seat_device(Manager *m, struct udev_device *d);
int manager_process_button_device(Manager *m, struct udev_device *d);
int manager_enumerate_sessions(Manager *m);
int manager_enumerate_users(Manager *m);
int manager_enumerate_inhibitors(Manager *m);
+int manager_enumerate_machines(Manager *m);
int manager_startup(Manager *m);
int manager_run(Manager *m);
int manager_get_idle_hint(Manager *m, dual_timestamp *t);
int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user);
+int manager_get_user_by_pid(Manager *m, pid_t pid, User **user);
int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **session);
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session);
+int manager_get_machine_by_cgroup(Manager *m, const char *cgroup, Machine **machine);
+int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine);
extern const DBusObjectPathVTable bus_manager_vtable;
char *n;
int c = 0, r;
- r = cg_get_machine_path(NULL, &md);
+ r = cg_get_root_path(&md);
if (r < 0)
return r;
_cleanup_free_ char *md = NULL, *p = NULL;
int r;
- r = cg_get_machine_path(NULL, &md);
+ r = cg_get_root_path(&md);
if (r < 0)
return r;
#include "cgroup-util.h"
#include "fileio.h"
+static int kill_all_users(void) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r = 0;
+
+ d = opendir("/run/systemd/users");
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open /run/systemd/users: %m");
+ return -errno;
+ }
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ _cleanup_free_ char *cgroup = NULL;
+ char *a;
+ int k;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ a = strappenda("/run/systemd/users/", de->d_name);
+
+ k = parse_env_file(a, NEWLINE, "CGROUP", &cgroup, NULL);
+ if (k < 0) {
+ if (k != -ENOENT) {
+ log_error("Failed to read user data: %s", strerror(-k));
+ r = k;
+ }
+
+ continue;
+ }
+
+ if (!cgroup) {
+ log_error("User data did not contain cgroup field.");
+ r = -ENOENT;
+ continue;
+ }
+
+ k = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, cgroup, true);
+ if (k < 0) {
+ log_error("Failed to kill cgroup %s: %s", cgroup, strerror(-k));
+ r = k;
+ }
+ }
+
+ return r;
+}
+
+static int kill_all_sessions(void) {
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+ int r = 0;
+
+ d = opendir("/run/systemd/sessions");
+ if (!d) {
+ if (errno == ENOENT)
+ return 0;
+
+ log_error("Failed to open /run/systemd/sessions: %m");
+ return -errno;
+ }
+
+ FOREACH_DIRENT(de, d, return -errno) {
+ _cleanup_free_ char *cgroup = NULL;
+ char *a;
+ int k;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ a = strappenda("/run/systemd/sessions/", de->d_name);
+
+ k = parse_env_file(a, NEWLINE, "CGROUP", &cgroup, NULL);
+ if (k < 0) {
+ if (k != -ENOENT) {
+ log_error("Failed to read session data: %s", strerror(-k));
+ r = k;
+ }
+
+ continue;
+ }
+
+ if (!cgroup) {
+ log_error("Session data did not contain cgroup field.");
+ r = -ENOENT;
+ continue;
+ }
+
+ k = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, cgroup, true);
+ if (k < 0) {
+ log_error("Failed to kill cgroup %s: %s", cgroup, strerror(-k));
+ r = k;
+ }
+ }
+
+ return r;
+}
+
int main(int argc, char*argv[]) {
int ret = EXIT_FAILURE;
} else if (streq(argv[1], "stop")) {
int r, q;
- char *cgroup_user_tree = NULL;
r = write_string_file_atomic("/run/nologin", "System is going down.");
if (r < 0)
log_error("Failed to create /run/nologin: %s", strerror(-r));
- q = cg_get_user_path(&cgroup_user_tree);
- if (q < 0) {
- log_error("Failed to determine use path: %s", strerror(-q));
- goto finish;
- }
-
- q = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, cgroup_user_tree, true);
- free(cgroup_user_tree);
- if (q < 0) {
- log_error("Failed to kill sessions: %s", strerror(-q));
- goto finish;
- }
+ q = kill_all_users();
+ if (q < 0 && r >= 0)
+ r = q;
- if (r < 0)
- goto finish;
+ q = kill_all_sessions();
+ if (q < 0 && r >= 0)
+ r = q;
} else {
log_error("Unknown verb %s.", argv[1]);
#include <sys/un.h>
#include <sys/socket.h>
-#ifdef HAVE_XATTR
-#include <attr/xattr.h>
-#endif
-
#include <systemd/sd-daemon.h>
+#include <systemd/sd-bus.h>
#include "log.h"
#include "util.h"
#include "fdset.h"
#include "build.h"
#include "fileio.h"
+#include "bus-internal.h"
+#include "bus-message.h"
#ifndef TTY_GID
#define TTY_GID 5
static char *arg_directory = NULL;
static char *arg_user = NULL;
-static char **arg_controllers = NULL;
-static char *arg_uuid = NULL;
+static sd_id128_t arg_uuid = {};
static char *arg_machine = NULL;
+static const char *arg_slice = NULL;
static bool arg_private_network = false;
static bool arg_read_only = false;
static bool arg_boot = false;
" -D --directory=NAME Root directory for the container\n"
" -b --boot Boot up full system (i.e. invoke init)\n"
" -u --user=USER Run the command under specified user or uid\n"
- " -C --controllers=LIST Put the container in specified comma-separated\n"
- " cgroup hierarchies\n"
" --uuid=UUID Set a specific machine UUID for the container\n"
" -M --machine=NAME Set the machine name for the container\n"
+ " -S --slice=SLICE Place the container in the specified slice\n"
" --private-network Disable network in container\n"
" --read-only Mount the root directory read-only\n"
" --capability=CAP In addition to the default, retain specified\n"
{ "version", no_argument, NULL, ARG_VERSION },
{ "directory", required_argument, NULL, 'D' },
{ "user", required_argument, NULL, 'u' },
- { "controllers", required_argument, NULL, 'C' },
{ "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK },
{ "boot", no_argument, NULL, 'b' },
{ "uuid", required_argument, NULL, ARG_UUID },
{ "bind", required_argument, NULL, ARG_BIND },
{ "bind-ro", required_argument, NULL, ARG_BIND_RO },
{ "machine", required_argument, NULL, 'M' },
+ { "slice", required_argument, NULL, 'S' },
{ NULL, 0, NULL, 0 }
};
- int c;
+ int c, r;
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "+hD:u:C:bM:j", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "+hD:u:C:bM:jS:", options, NULL)) >= 0) {
switch (c) {
break;
- case 'C':
- strv_free(arg_controllers);
- arg_controllers = strv_split(optarg, ",");
- if (!arg_controllers)
- return log_oom();
-
- cg_shorten_controllers(arg_controllers);
- break;
-
case ARG_PRIVATE_NETWORK:
arg_private_network = true;
break;
break;
case ARG_UUID:
- if (!id128_is_valid(optarg)) {
+ r = sd_id128_from_string(optarg, &arg_uuid);
+ if (r < 0) {
log_error("Invalid UUID: %s", optarg);
- return -EINVAL;
+ return r;
}
+ break;
- arg_uuid = optarg;
+ case 'S':
+ arg_slice = strdup(optarg);
break;
case 'M':
_cleanup_free_ char *a = NULL, *b = NULL;
char *e;
char ***x;
- int r;
x = c == ARG_BIND ? &arg_bind : &arg_bind_ro;
return 0;
}
-static int setup_cgroup(const char *path) {
- char **c;
- int r;
-
- r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, path, 1);
- if (r < 0) {
- log_error("Failed to create cgroup: %s", strerror(-r));
- return r;
- }
-
- STRV_FOREACH(c, arg_controllers) {
- r = cg_create_and_attach(*c, path, 1);
- if (r < 0)
- log_warning("Failed to create cgroup in controller %s: %s", *c, strerror(-r));
- }
-
- return 0;
-}
-
-static int save_attributes(const char *cgroup, pid_t pid, const char *uuid, const char *directory) {
-#ifdef HAVE_XATTR
- _cleanup_free_ char *path = NULL;
- char buf[DECIMAL_STR_MAX(pid_t)];
- int r = 0, k;
-
- assert(cgroup);
- assert(pid >= 0);
- assert(arg_directory);
-
- assert_se(snprintf(buf, sizeof(buf), "%lu", (unsigned long) pid) < (int) sizeof(buf));
-
- r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &path);
- if (r < 0) {
- log_error("Failed to get path: %s", strerror(-r));
- return r;
- }
-
- r = setxattr(path, "trusted.init_pid", buf, strlen(buf), XATTR_CREATE);
- if (r < 0)
- log_warning("Failed to set %s attribute on %s: %m", "trusted.init_pid", path);
-
- if (uuid) {
- k = setxattr(path, "trusted.machine_id", uuid, strlen(uuid), XATTR_CREATE);
- if (k < 0) {
- log_warning("Failed to set %s attribute on %s: %m", "trusted.machine_id", path);
- if (r == 0)
- r = k;
- }
- }
-
- k = setxattr(path, "trusted.root_directory", directory, strlen(directory), XATTR_CREATE);
- if (k < 0) {
- log_warning("Failed to set %s attribute on %s: %m", "trusted.root_directory", path);
- if (r == 0)
- r = k;
- }
- return r;
-#else
- return 0;
-#endif
-}
-
static int drop_capabilities(void) {
return capability_bounding_set_drop(~arg_retain, false);
}
return r;
}
+static int register_machine(void) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_bus_unref_ sd_bus *bus = NULL;
+ int r;
+
+ r = sd_bus_open_system(&bus);
+ if (r < 0) {
+ log_error("Failed to open system bus: %s", strerror(-r));
+ return r;
+ }
+
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ "CreateMachine",
+ &error,
+ NULL,
+ "sayssuss",
+ arg_machine,
+ SD_BUS_APPEND_ID128(arg_uuid),
+ "nspawn",
+ "container",
+ (uint32_t) 0,
+ strempty(arg_slice),
+ strempty(arg_directory));
+ if (r < 0) {
+ log_error("Failed to register machine: %s", error.message);
+ return r;
+ }
+
+ return 0;
+}
+
static bool audit_enabled(void) {
int fd;
int main(int argc, char *argv[]) {
pid_t pid = 0;
int r = EXIT_FAILURE, k;
- _cleanup_free_ char *newcg = NULL;
_cleanup_close_ int master = -1;
int n_fd_passed;
const char *console = NULL;
fdset_close_others(fds);
log_open();
- k = cg_get_machine_path(arg_machine, &newcg);
- if (k < 0) {
- log_error("Failed to determine machine cgroup path: %s", strerror(-k));
- goto finish;
- }
-
- k = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
- if (k <= 0 && k != -ENOENT) {
- log_error("Container already running.");
-
- free(newcg);
- newcg = NULL;
-
- goto finish;
- }
-
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
if (master < 0) {
log_error("Failed to acquire pseudo tty: %m");
goto child_fail;
}
- if (setup_cgroup(newcg) < 0)
- goto child_fail;
-
close_pipe(pipefd2);
+ r = register_machine();
+ if (r < 0)
+ goto finish;
+
/* Mark everything as slave, so that we still
* receive mounts from the real root, but don't
* propagate mounts to the real root. */
goto child_fail;
}
- if (arg_uuid) {
- if (asprintf((char**)(envp + n_env++), "container_uuid=%s", arg_uuid) < 0) {
+ if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) {
+ if (asprintf((char**)(envp + n_env++), "container_uuid=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(arg_uuid)) < 0) {
log_oom();
goto child_fail;
}
fd_wait_for_event(pipefd2[0], POLLHUP, -1);
close_nointr_nofail(pipefd2[0]);
- save_attributes(newcg, pid, arg_uuid, arg_directory);
-
fdset_free(fds);
fds = NULL;
close_pipe(kmsg_socket_pair);
- if (newcg)
- cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
+ if (pid > 0)
+ kill(pid, SIGKILL);
free(arg_directory);
free(arg_machine);
- strv_free(arg_controllers);
fdset_free(fds);
#include "strv.h"
#include "unit-name.h"
#include "fileio.h"
+#include "special.h"
int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
_cleanup_free_ char *fs = NULL;
static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
char *t = NULL;
- if (controller) {
- if (path && suffix)
+ if (!isempty(controller)) {
+ if (!isempty(path) && !isempty(suffix))
t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
- else if (path)
+ else if (!isempty(path))
t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
- else if (suffix)
+ else if (!isempty(suffix))
t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
else
t = strappend("/sys/fs/cgroup/", controller);
} else {
- if (path && suffix)
+ if (!isempty(path) && !isempty(suffix))
t = strjoin(path, "/", suffix, NULL);
- else if (path)
+ else if (!isempty(path))
t = strdup(path);
else
return -EINVAL;
return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
}
-int cg_get_system_path(char **path) {
- char *p;
- int r;
-
- assert(path);
-
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
- if (r < 0) {
- p = strdup("/system");
- if (!p)
- return -ENOMEM;
- }
-
- if (endswith(p, "/system"))
- *path = p;
- else {
- char *q;
-
- q = strappend(p, "/system");
- free(p);
- if (!q)
- return -ENOMEM;
-
- *path = q;
- }
-
- return 0;
-}
-
int cg_get_root_path(char **path) {
- char *root, *e;
+ char *p, *e;
int r;
assert(path);
- r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
if (r < 0)
return r;
- e = endswith(root, "/system");
- if (e == root)
- e[1] = 0;
- else if (e)
+ e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
+ if (e)
*e = 0;
- *path = root;
- return 0;
-}
-
-int cg_get_user_path(char **path) {
- _cleanup_free_ char *root = NULL;
- char *p;
-
- assert(path);
-
- /* Figure out the place to put user cgroups below. We use the
- * same as PID 1 has but with the "/system" suffix replaced by
- * "/user" */
-
- if (cg_get_root_path(&root) < 0 || streq(root, "/"))
- p = strdup("/user");
- else
- p = strappend(root, "/user");
-
- if (!p)
- return -ENOMEM;
-
- *path = p;
- return 0;
-}
-
-int cg_get_machine_path(const char *machine, char **path) {
- _cleanup_free_ char *root = NULL, *escaped = NULL;
- char *p;
-
- assert(path);
-
- if (machine) {
- const char *name = strappenda(machine, ".nspawn");
-
- escaped = cg_escape(name);
- if (!escaped)
- return -ENOMEM;
- }
-
- p = strjoin(cg_get_root_path(&root) >= 0 && !streq(root, "/") ? root : "",
- "/machine", machine ? "/" : "", machine ? escaped : "", NULL);
- if (!p)
- return -ENOMEM;
-
*path = p;
return 0;
}
return 0;
}
+static const char *skip_slices(const char *p) {
+ size_t n;
+
+ /* Skips over all slice assignments */
+
+ for (;;) {
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
+ return p;
+
+ p += n;
+ }
+}
+
int cg_path_get_unit(const char *path, char **unit) {
const char *e;
assert(path);
assert(unit);
- e = path_startswith(path, "/system/");
- if (!e)
- return -ENOENT;
+ e = skip_slices(path);
return cg_path_decode_unit(e, unit);
}
return cg_path_get_unit(cgroup, unit);
}
-_pure_ static const char *skip_label(const char *e) {
- assert(e);
+static const char *skip_user(const char *p) {
+ size_t n;
- e = strchr(e, '/');
- if (!e)
+ assert(p);
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n <= 5 || memcmp(p + n - 5, ".user", 5) != 0)
+ return p;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+}
+
+static const char *skip_session(const char *p) {
+ size_t n;
+
+ assert(p);
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n <= 8 || memcmp(p + n - 8, ".session", 8) != 0)
return NULL;
- e += strspn(e, "/");
- return e;
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
+}
+
+static const char *skip_systemd_label(const char *p) {
+ size_t n;
+
+ assert(p);
+
+ p += strspn(p, "/");
+
+ n = strcspn(p, "/");
+ if (n < 8 || memcmp(p, "systemd-", 8) != 0)
+ return p;
+
+ p += n;
+ p += strspn(p, "/");
+
+ return p;
}
int cg_path_get_user_unit(const char *path, char **unit) {
* cgroups might have arbitrary child cgroups and we shouldn't get
* confused by those */
- e = path_startswith(path, "/user/");
- if (!e)
- return -ENOENT;
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
- /* Skip the user name */
- e = skip_label(e);
- if (!e)
- return -ENOENT;
+ /* Skip the user name, if there is one */
+ e = skip_user(e);
- /* Skip the session ID */
- e = skip_label(e);
+ /* Skip the session ID, require that there is one */
+ e = skip_session(e);
if (!e)
return -ENOENT;
- /* Skip the systemd cgroup */
- e = skip_label(e);
- if (!e)
- return -ENOENT;
+ /* Skip the systemd cgroup, if there is one */
+ e = skip_systemd_label(e);
return cg_path_decode_unit(e, unit);
}
}
int cg_path_get_machine_name(const char *path, char **machine) {
- const char *e, *n;
+ const char *e, *n, *x;
char *s, *r;
assert(path);
assert(machine);
- e = path_startswith(path, "/machine/");
- if (!e)
- return -ENOENT;
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
n = strchrnul(e, '/');
if (e == n)
return -ENOENT;
s = strndupa(e, n - e);
+ s = cg_unescape(s);
+
+ x = endswith(s, ".machine");
+ if (!x)
+ return -ENOENT;
- r = strdup(cg_unescape(s));
+ r = strndup(s, x - s);
if (!r)
return -ENOMEM;
assert(path);
assert(session);
- e = path_startswith(path, "/user/");
- if (!e)
- return -ENOENT;
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
- /* Skip the user name */
- e = skip_label(e);
- if (!e)
- return -ENOENT;
+ /* Skip the user name, if there is one */
+ e = skip_user(e);
n = strchrnul(e, '/');
if (n - e < 8)
assert(path);
assert(uid);
- e = path_startswith(path, "/user/");
- if (!e)
- return -ENOENT;
+ /* Skip slices, if there are any */
+ e = skip_slices(path);
n = strchrnul(e, '/');
if (n - e < 5)
int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self);
int cg_get_root_path(char **path);
-int cg_get_system_path(char **path);
-int cg_get_user_path(char **path);
-int cg_get_machine_path(const char *machine, char **path);
int cg_path_get_session(const char *path, char **session);
int cg_path_get_owner_uid(const char *path, uid_t *uid);
}
char *unit_dbus_path_from_name(const char *name) {
- char *e, *p;
+ _cleanup_free_ char *e = NULL;
assert(name);
if (!e)
return NULL;
- p = strappend("/org/freedesktop/systemd1/unit/", e);
- free(e);
-
- return p;
+ return strappend("/org/freedesktop/systemd1/unit/", e);
}
char *unit_name_mangle(const char *name) {
int sd_bus_error_is_set(const sd_bus_error *e);
int sd_bus_error_has_name(const sd_bus_error *e, const char *name);
+#define SD_BUS_APPEND_ID128(x) 16, \
+ (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \
+ (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \
+ (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], \
+ (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15]
+
#ifdef __cplusplus
}
#endif
/* Hey! If you add a new message here, you *must* also update the
* message catalog with an appropriate explanation */
+/* And if you add a new ID here, make sure to generate a random one
+ * with journalctl --new-id128. Do not use any other IDs, and do not
+ * count them up manually. */
+
#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b)
#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b)
#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e)
#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a)
#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b)
#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5)
+#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2)
+#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58)
#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27)
#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90)
assert_se(streq_ptr(unit, result));
}
+static void test_path_get_unit(void) {
+ check_p_g_u("/system.slice/foobar.service/sdfdsaf", 0, "foobar.service");
+ check_p_g_u("/system.slice/getty@.service/getty@tty5.service", 0, "getty@tty5.service");
+ check_p_g_u("/system.slice/getty@.service/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service");
+ check_p_g_u("/system.slice/getty@.service/getty@tty5.service/", 0, "getty@tty5.service");
+ check_p_g_u("/system.slice/getty@tty6.service/tty5", 0, "getty@tty6.service");
+ check_p_g_u("sadfdsafsda", -EINVAL, NULL);
+ check_p_g_u("/system.slice/getty####@tty6.service/tty5", -EINVAL, NULL);
+ check_p_g_u("/system.slice/system-waldo.slice/foobar.service/sdfdsaf", 0, "foobar.service");
+}
+
static void check_p_g_u_u(const char *path, int code, const char *result) {
_cleanup_free_ char *unit = NULL;
assert_se(streq_ptr(unit, result));
}
-static void test_path_get_unit(void) {
- check_p_g_u("/system/foobar.service/sdfdsaf", 0, "foobar.service");
- check_p_g_u("/system/getty@.service/getty@tty5.service", 0, "getty@tty5.service");
- check_p_g_u("/system/getty@.service/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service");
- check_p_g_u("/system/getty@.service/getty@tty5.service/", 0, "getty@tty5.service");
- check_p_g_u("/system/getty@tty6.service/tty5", 0, "getty@tty6.service");
- check_p_g_u("sadfdsafsda", -ENOENT, NULL);
- check_p_g_u("/system/getty####@tty6.service/tty5", -EINVAL, NULL);
+static void test_path_get_user_unit(void) {
+ check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/foobar.service", 0, "foobar.service");
+ check_p_g_u_u("/user.slice/1002.user/2.session/systemd-21548/foobar.service/waldo", 0, "foobar.service");
+ check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/foobar.service/waldo/uuuux", 0, "foobar.service");
+ check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/waldo/waldo/uuuux", -EINVAL, NULL);
+ check_p_g_u_u("/user.slice/1000.user/2.session/foobar.service", 0, "foobar.service");
+ check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service");
+ check_p_g_u_u("/2.session/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service");
+ check_p_g_u_u("/xyz.slice/xyz-waldo.slice/77.session/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service");
+ check_p_g_u_u("/meh.service", -ENOENT, NULL);
}
-static void test_path_get_user_unit(void) {
- check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service", 0, "foobar.service");
- check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service/waldo", 0, "foobar.service");
- check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service/waldo/uuuux", 0, "foobar.service");
- check_p_g_u_u("/user/lennart/2/systemd-21548/waldo/waldo/uuuux", -EINVAL, NULL);
- check_p_g_u_u("/user/lennart/2/foobar.service", -ENOENT, NULL);
- check_p_g_u_u("/user/lennart/2/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service");
+static void check_p_g_s(const char *path, int code, const char *result) {
+ _cleanup_free_ char *s = NULL;
+
+ assert_se(cg_path_get_session(path, &s) == code);
+ assert_se(streq_ptr(s, result));
}
-static void test_get_paths(void) {
- _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL;
+static void test_path_get_session(void) {
+ check_p_g_s("/user.slice/1000.user/2.session/systemd-21548/foobar.service", 0, "2");
+ check_p_g_s("/3.session", 0, "3");
+ check_p_g_s("", -ENOENT, 0);
+}
- assert_se(cg_get_root_path(&a) >= 0);
- log_info("Root = %s", a);
+static void check_p_g_o_u(const char *path, int code, uid_t result) {
+ uid_t uid = 0;
- assert_se(cg_get_system_path(&b) >= 0);
- log_info("System = %s", b);
+ assert_se(cg_path_get_owner_uid(path, &uid) == code);
+ assert_se(uid == result);
+}
+
+static void test_path_get_owner_uid(void) {
+ check_p_g_o_u("/user.slice/1000.user/2.session/systemd-21548/foobar.service", 0, 1000);
+ check_p_g_o_u("/1006.user", 0, 1006);
+ check_p_g_o_u("", -ENOENT, 0);
+}
- assert_se(cg_get_user_path(&c) >= 0);
- log_info("User = %s", c);
+static void check_p_g_m_n(const char *path, int code, const char *result) {
+ _cleanup_free_ char *m = NULL;
- assert_se(cg_get_machine_path("harley", &d) >= 0);
- log_info("Machine = %s", d);
+ assert_se(cg_path_get_machine_name(path, &m) == code);
+ assert_se(streq_ptr(m, result));
+}
+
+static void test_path_get_machine_name(void) {
+ check_p_g_m_n("/user.slice/foobar.machine", 0, "foobar");
+ check_p_g_m_n("/foobar.machine", 0, "foobar");
+ check_p_g_m_n("/user.slice/user-kuux.slice/foobar.machine", 0, "foobar");
+ check_p_g_m_n("/user.slice/user-kuux.slice/foobar.machine/asjhdkj", 0, "foobar");
+ check_p_g_m_n("", -ENOENT, NULL);
+}
+
+static void test_get_paths(void) {
+ _cleanup_free_ char *a = NULL;
+
+ assert_se(cg_get_root_path(&a) >= 0);
+ log_info("Root = %s", a);
}
static void test_proc(void) {
test_path_decode_unit();
test_path_get_unit();
test_path_get_user_unit();
+ test_path_get_session();
+ test_path_get_owner_uid();
+ test_path_get_machine_name();
test_get_paths();
test_proc();
test_escape();
[Unit]
Description=Virtual Machine and Container Slice
Documentation=man:systemd.special(7)
-
-[Install]
-WantedBy=slices.target
+Before=slices.target
Description=System Slice
Documentation=man:systemd.special(7)
DefaultDependencies=no
+Before=slices.target
Description=Login Service
Documentation=man:systemd-logind.service(8) man:logind.conf(5)
Documentation=http://www.freedesktop.org/wiki/Software/systemd/multiseat
-After=nss-user-lookup.target
+Wants=user.slice machine.slice
+After=nss-user-lookup.target user.slice machine.slice
[Service]
ExecStart=@rootlibexecdir@/systemd-logind
[Unit]
Description=User and Session Slice
Documentation=man:systemd.special(7)
-
-[Install]
-WantedBy=slices.target
+Before=slices.target