summaryrefslogtreecommitdiff
path: root/src/login
diff options
context:
space:
mode:
Diffstat (limited to 'src/login')
-rw-r--r--src/login/loginctl.c382
-rw-r--r--src/login/logind-dbus.c449
-rw-r--r--src/login/logind-machine-dbus.c315
-rw-r--r--src/login/logind-machine.c455
-rw-r--r--src/login/logind-machine.h80
-rw-r--r--src/login/logind-seat-dbus.c6
-rw-r--r--src/login/logind-session-dbus.c8
-rw-r--r--src/login/logind-session.c165
-rw-r--r--src/login/logind-session.h9
-rw-r--r--src/login/logind-user-dbus.c10
-rw-r--r--src/login/logind-user.c150
-rw-r--r--src/login/logind-user.h1
-rw-r--r--src/login/logind.c340
-rw-r--r--src/login/logind.h13
-rw-r--r--src/login/sd-login.c4
-rw-r--r--src/login/user-sessions.c121
16 files changed, 2166 insertions, 342 deletions
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 1b9723fdbf..3637a408dc 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -261,6 +261,69 @@ static int list_seats(DBusConnection *bus, char **args, unsigned n) {
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;
@@ -279,6 +342,7 @@ typedef struct SessionStatusInfo {
const char *type;
const char *class;
const char *state;
+ const char *slice;
} SessionStatusInfo;
typedef struct UserStatusInfo {
@@ -289,6 +353,7 @@ typedef struct UserStatusInfo {
const char *state;
char **sessions;
const char *display;
+ const char *slice;
} UserStatusInfo;
typedef struct SeatStatusInfo {
@@ -297,6 +362,18 @@ 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;
@@ -318,15 +395,13 @@ static void print_session_status_info(SessionStatusInfo *i) {
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");
}
@@ -375,6 +450,9 @@ static void print_session_status_info(SessionStatusInfo *i) {
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 =
@@ -419,6 +497,9 @@ static void print_user_status_info(UserStatusInfo *i) {
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:");
@@ -488,6 +569,76 @@ static void print_seat_status_info(SeatStatusInfo *i) {
}
}
+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);
@@ -521,6 +672,8 @@ static int status_property_session(const char *name, DBusMessageIter *iter, Sess
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;
}
@@ -606,6 +759,8 @@ static int status_property_user(const char *name, DBusMessageIter *iter, UserSta
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;
}
@@ -757,6 +912,80 @@ static int status_property_seat(const char *name, DBusMessageIter *iter, SeatSta
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);
@@ -838,6 +1067,7 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
SessionStatusInfo session_info = {};
UserStatusInfo user_info = {};
SeatStatusInfo seat_info = {};
+ MachineStatusInfo machine_info = {};
assert(path);
assert(new_line);
@@ -901,8 +1131,10 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
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.");
@@ -917,8 +1149,10 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
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;
@@ -981,7 +1215,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
}
u = (uint32_t) uid;
- ret = bus_method_call_with_reply (
+ ret = bus_method_call_with_reply(
bus,
"org.freedesktop.login1",
"/org/freedesktop/login1",
@@ -991,9 +1225,10 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
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",
@@ -1003,8 +1238,22 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
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,
@@ -1085,6 +1334,36 @@ static int kill_session(DBusConnection *bus, char **args, unsigned n) {
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;
@@ -1288,6 +1567,31 @@ static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
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"
@@ -1326,7 +1630,12 @@ static int help(void) {
" 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;
@@ -1452,29 +1761,34 @@ static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
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;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 4a84b860f1..631006924f 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -36,6 +36,8 @@
#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" \
@@ -51,10 +53,22 @@
" <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" \
@@ -64,6 +78,9 @@
" <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" \
@@ -91,6 +108,16 @@
" <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" \
@@ -124,6 +151,9 @@
" <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" \
@@ -201,13 +231,20 @@
" <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" \
@@ -342,8 +379,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
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;
@@ -507,6 +543,12 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
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;
@@ -601,10 +643,12 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
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;
@@ -718,6 +762,183 @@ fail:
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,
@@ -1387,7 +1608,6 @@ static int bus_manager_do_shutdown_or_sleep(
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) },
@@ -1530,6 +1750,107 @@ static DBusHandlerResult manager_message_handler(
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;
@@ -1612,7 +1933,6 @@ static DBusHandlerResult manager_message_handler(
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
- char *p;
User *user;
Iterator i;
DBusMessageIter iter, sub;
@@ -1627,6 +1947,7 @@ static DBusHandlerResult manager_message_handler(
goto oom;
HASHMAP_FOREACH(user, m->users, i) {
+ _cleanup_free_ char *p = NULL;
DBusMessageIter sub2;
uint32_t uid;
@@ -1646,8 +1967,6 @@ static DBusHandlerResult manager_message_handler(
goto oom;
}
- free(p);
-
if (!dbus_message_iter_close_container(&sub, &sub2))
goto oom;
}
@@ -1656,7 +1975,6 @@ static DBusHandlerResult manager_message_handler(
goto oom;
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
- char *p;
Seat *seat;
Iterator i;
DBusMessageIter iter, sub;
@@ -1671,6 +1989,7 @@ static DBusHandlerResult manager_message_handler(
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))
@@ -1686,8 +2005,6 @@ static DBusHandlerResult manager_message_handler(
goto oom;
}
- free(p);
-
if (!dbus_message_iter_close_container(&sub, &sub2))
goto oom;
}
@@ -1739,6 +2056,49 @@ static DBusHandlerResult manager_message_handler(
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);
@@ -1758,6 +2118,11 @@ static DBusHandlerResult manager_message_handler(
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;
@@ -1945,6 +2310,45 @@ static DBusHandlerResult manager_message_handler(
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;
@@ -2014,6 +2418,29 @@ static DBusHandlerResult manager_message_handler(
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;
diff --git a/src/login/logind-machine-dbus.c b/src/login/logind-machine-dbus.c
new file mode 100644
index 0000000000..7feea2e92a
--- /dev/null
+++ b/src/login/logind-machine-dbus.c
@@ -0,0 +1,315 @@
+/*-*- 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;
+}
diff --git a/src/login/logind-machine.c b/src/login/logind-machine.c
new file mode 100644
index 0000000000..347e5aa022
--- /dev/null
+++ b/src/login/logind-machine.c
@@ -0,0 +1,455 @@
+/*-*- 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);
diff --git a/src/login/logind-machine.h b/src/login/logind-machine.h
new file mode 100644
index 0000000000..cd5174ff9e
--- /dev/null
+++ b/src/login/logind-machine.h
@@ -0,0 +1,80 @@
+/*-*- 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_;
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index 5c535ba0ec..230f7f082a 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -209,8 +209,8 @@ static int bus_seat_append_idle_hint_since(DBusMessageIter *i, const char *prope
}
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);
@@ -224,8 +224,6 @@ static int get_seat_for_path(Manager *m, const char *path, Seat **_s) {
return -ENOMEM;
s = hashmap_get(m->seats, id);
- free(id);
-
if (!s)
return -ENOENT;
@@ -348,7 +346,7 @@ const DBusObjectPathVTable bus_seat_vtable = {
};
char *seat_bus_path(Seat *s) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
assert(s);
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index ec823af547..e306eabb32 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -56,6 +56,7 @@
" <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" \
@@ -233,8 +234,8 @@ static int bus_session_append_state(DBusMessageIter *i, const char *property, vo
}
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);
@@ -248,8 +249,6 @@ static int get_session_for_path(Manager *m, const char *path, Session **_s) {
return -ENOMEM;
s = hashmap_get(m->sessions, id);
- free(id);
-
if (!s)
return -ENOENT;
@@ -270,6 +269,7 @@ static const BusProperty bus_login_session_properties[] = {
{ "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) },
@@ -448,7 +448,7 @@ const DBusObjectPathVTable bus_session_vtable = {
};
char *session_bus_path(Session *s) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL;
assert(s);
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 4fd3985811..aba517d1f7 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -35,7 +35,7 @@
#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);
@@ -61,9 +61,6 @@ Session* session_new(Manager *m, User *u, const char *id) {
s->manager = m;
s->fifo_fd = -1;
- s->user = u;
-
- LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
return s;
}
@@ -99,6 +96,7 @@ void session_free(Session *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);
@@ -107,6 +105,14 @@ void session_free(Session *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;
@@ -114,6 +120,9 @@ int session_save(Session *s) {
assert(s);
+ if (!s->user)
+ return -ESTALE;
+
if (!s->started)
return 0;
@@ -174,6 +183,9 @@ int session_save(Session *s) {
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);
@@ -183,6 +195,13 @@ int session_save(Session *s) {
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) {
@@ -199,14 +218,17 @@ finish:
}
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;
@@ -223,14 +245,44 @@ int session_load(Session *s) {
"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);
@@ -295,14 +347,17 @@ int session_load(Session *s) {
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;
}
@@ -311,6 +366,7 @@ int session_activate(Session *s) {
int r;
assert(s);
+ assert(s->user);
if (s->vtnr < 0)
return -ENOTSUP;
@@ -407,17 +463,19 @@ static int session_create_one_group(Session *s, const char *controller, const ch
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)
@@ -428,7 +486,6 @@ static int session_create_one_group(Session *s, const char *controller, const ch
static int session_create_cgroup(Session *s) {
char **k;
- char *p;
int r;
assert(s);
@@ -446,30 +503,41 @@ static int session_create_cgroup(Session *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) {
@@ -479,9 +547,9 @@ static int session_create_cgroup(Session *s) {
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) {
@@ -502,7 +570,6 @@ static int session_create_cgroup(Session *s) {
r = cg_attach(*k, "/", s->leader);
if (r < 0)
log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
-
}
}
@@ -517,7 +584,9 @@ int session_start(Session *s) {
int r;
assert(s);
- assert(s->user);
+
+ if (!s->user)
+ return -ESTALE;
if (s->started)
return 0;
@@ -542,7 +611,8 @@ int session_start(Session *s) {
/* 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);
@@ -667,6 +737,9 @@ int session_stop(Session *s) {
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),
@@ -932,6 +1005,9 @@ int session_check_gc(Session *s, bool drop_not_started) {
if (drop_not_started && !s->started)
return 0;
+ if (!s->user)
+ return 0;
+
if (s->fifo_fd >= 0) {
r = pipe_eof(s->fifo_fd);
@@ -978,8 +1054,8 @@ SessionState session_get_state(Session *s) {
}
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);
@@ -998,7 +1074,7 @@ int session_kill(Session *s, KillWho who, int signo) {
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));
@@ -1012,9 +1088,6 @@ int session_kill(Session *s, KillWho who, int signo) {
r = q;
}
- if (pid_set)
- set_free(pid_set);
-
return r;
}
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index a73df3a3bc..60597c2362 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -22,6 +22,7 @@
***/
typedef struct Session Session;
+typedef enum KillWho KillWho;
#include "list.h"
#include "util.h"
@@ -54,12 +55,12 @@ typedef enum SessionType {
_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;
@@ -82,6 +83,7 @@ struct Session {
char *remote_host;
char *service;
+ char *slice;
int vtnr;
Seat *seat;
@@ -108,8 +110,9 @@ struct Session {
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);
diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c
index 3ec3ff8e61..2a2825308d 100644
--- a/src/login/logind-user-dbus.c
+++ b/src/login/logind-user-dbus.c
@@ -40,13 +40,14 @@
" <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 \
@@ -212,10 +213,10 @@ static int get_user_for_path(Manager *m, const char *path, User **_u) {
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;
@@ -236,6 +237,7 @@ static const BusProperty bus_login_user_properties[] = {
{ "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 },
@@ -348,7 +350,7 @@ char *user_bus_path(User *u) {
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;
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 9e2cbf646b..fb0c9b75d7 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -30,6 +30,7 @@
#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;
@@ -42,29 +43,27 @@ User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
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) {
@@ -76,9 +75,10 @@ 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);
@@ -87,13 +87,14 @@ void user_free(User *u) {
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);
@@ -119,24 +120,26 @@ int user_save(User *u) {
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;
@@ -233,9 +236,6 @@ int user_save(User *u) {
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));
@@ -244,21 +244,22 @@ finish:
}
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;
@@ -266,14 +267,24 @@ int user_load(User *u) {
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;
}
@@ -309,13 +320,18 @@ static int user_mkdir_runtime_path(User *u) {
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();
@@ -324,30 +340,29 @@ static int user_create_cgroup(User *u) {
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);
@@ -390,7 +405,8 @@ int user_start(User *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;
@@ -633,8 +649,8 @@ UserState user_get_state(User *u) {
}
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);
@@ -645,15 +661,11 @@ int user_kill(User *u, int signo) {
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] = {
diff --git a/src/login/logind-user.h b/src/login/logind-user.h
index 080354da74..16d798541a 100644
--- a/src/login/logind-user.h
+++ b/src/login/logind-user.h
@@ -49,6 +49,7 @@ struct User {
char *runtime_path;
char *service;
char *cgroup_path;
+ char *slice;
Session *display;
diff --git a/src/login/logind.c b/src/login/logind.c
index 5a394401dc..d0d18ae540 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -74,16 +74,18 @@ Manager *manager_new(void) {
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;
@@ -102,11 +104,14 @@ Manager *manager_new(void) {
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;
}
@@ -117,6 +122,7 @@ void manager_free(Manager *m) {
Seat *s;
Inhibitor *i;
Button *b;
+ Machine *machine;
assert(m);
@@ -138,15 +144,20 @@ void manager_free(Manager *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);
@@ -190,7 +201,7 @@ void manager_free(Manager *m) {
free(m->action_job);
- free(m->cgroup_path);
+ free(m->cgroup_root);
free(m);
}
@@ -242,7 +253,7 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
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);
@@ -256,7 +267,7 @@ int manager_add_session(Manager *m, User *u, const char *id, Session **_session)
return 0;
}
- s = session_new(m, u, id);
+ s = session_new(m, id);
if (!s)
return -ENOMEM;
@@ -366,6 +377,30 @@ int manager_add_button(Manager *m, const char *name, Button **_button) {
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;
@@ -554,7 +589,7 @@ finish:
}
int manager_enumerate_seats(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
@@ -573,7 +608,7 @@ int manager_enumerate_seats(Manager *m) {
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
Seat *s;
int k;
@@ -591,66 +626,16 @@ int manager_enumerate_seats(Manager *m) {
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)
@@ -660,7 +645,7 @@ static int manager_enumerate_linger_users(Manager *m) {
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
int k;
if (!dirent_is_file(de))
@@ -673,27 +658,22 @@ static int manager_enumerate_linger_users(Manager *m) {
}
}
- 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)
@@ -703,88 +683,65 @@ int manager_enumerate_users(Manager *m) {
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;
}
@@ -792,86 +749,83 @@ static int manager_enumerate_sessions_from_cgroup(Manager *m) {
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;
}
@@ -1109,6 +1063,43 @@ int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user) {
}
}
+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;
@@ -1124,7 +1115,38 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
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;
@@ -1136,6 +1158,10 @@ void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
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) {
@@ -1197,6 +1223,7 @@ static int manager_connect_bus(Manager *m) {
!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;
@@ -1371,6 +1398,7 @@ void manager_gc(Manager *m, bool drop_not_started) {
Seat *seat;
Session *session;
User *user;
+ Machine *machine;
assert(m);
@@ -1403,6 +1431,16 @@ void manager_gc(Manager *m, bool drop_not_started) {
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) {
@@ -1521,6 +1559,7 @@ int manager_startup(Manager *m) {
Session *session;
User *user;
Inhibitor *inhibitor;
+ Machine *machine;
Iterator i;
assert(m);
@@ -1560,6 +1599,7 @@ int manager_startup(Manager *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);
@@ -1580,6 +1620,9 @@ int manager_startup(Manager *m) {
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;
@@ -1731,6 +1774,7 @@ int main(int argc, char *argv[]) {
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) {
diff --git a/src/login/logind.h b/src/login/logind.h
index 904dc20467..ce25211878 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -41,6 +41,7 @@ typedef struct Manager Manager;
#include "logind-inhibit.h"
#include "logind-button.h"
#include "logind-action.h"
+#include "logind-machine.h"
struct Manager {
DBusConnection *bus;
@@ -51,10 +52,12 @@ struct Manager {
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;
@@ -74,7 +77,7 @@ struct Manager {
Seat *vtconsole;
- char *cgroup_path;
+ char *cgroup_root;
char **controllers, **reset_controllers;
char **kill_only_users, **kill_exclude_users;
@@ -86,6 +89,7 @@ struct Manager {
Hashmap *session_cgroups;
Hashmap *user_cgroups;
+ Hashmap *machine_cgroups;
Hashmap *session_fds;
Hashmap *inhibitor_fds;
@@ -139,11 +143,12 @@ void manager_free(Manager *m);
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);
@@ -160,6 +165,7 @@ int manager_enumerate_seats(Manager *m);
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);
@@ -172,8 +178,11 @@ void manager_gc(Manager *m, bool drop_not_started);
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;
diff --git a/src/login/sd-login.c b/src/login/sd-login.c
index d0dc42f685..d2e95034e1 100644
--- a/src/login/sd-login.c
+++ b/src/login/sd-login.c
@@ -598,7 +598,7 @@ _public_ int sd_get_machine_names(char ***machines) {
char *n;
int c = 0, r;
- r = cg_get_machine_path(NULL, &md);
+ r = cg_get_root_path(&md);
if (r < 0)
return r;
@@ -681,7 +681,7 @@ _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
_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;
diff --git a/src/login/user-sessions.c b/src/login/user-sessions.c
index 41d32044e9..18066ccc39 100644
--- a/src/login/user-sessions.c
+++ b/src/login/user-sessions.c
@@ -28,6 +28,106 @@
#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;
@@ -68,27 +168,18 @@ int main(int argc, char*argv[]) {
} 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]);