diff options
Diffstat (limited to 'src/login')
-rw-r--r-- | src/login/loginctl.c | 382 | ||||
-rw-r--r-- | src/login/logind-dbus.c | 449 | ||||
-rw-r--r-- | src/login/logind-machine-dbus.c | 315 | ||||
-rw-r--r-- | src/login/logind-machine.c | 455 | ||||
-rw-r--r-- | src/login/logind-machine.h | 80 | ||||
-rw-r--r-- | src/login/logind-seat-dbus.c | 6 | ||||
-rw-r--r-- | src/login/logind-session-dbus.c | 8 | ||||
-rw-r--r-- | src/login/logind-session.c | 165 | ||||
-rw-r--r-- | src/login/logind-session.h | 9 | ||||
-rw-r--r-- | src/login/logind-user-dbus.c | 10 | ||||
-rw-r--r-- | src/login/logind-user.c | 150 | ||||
-rw-r--r-- | src/login/logind-user.h | 1 | ||||
-rw-r--r-- | src/login/logind.c | 340 | ||||
-rw-r--r-- | src/login/logind.h | 13 | ||||
-rw-r--r-- | src/login/sd-login.c | 4 | ||||
-rw-r--r-- | src/login/user-sessions.c | 121 |
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]); |