diff options
Diffstat (limited to 'src')
34 files changed, 2479 insertions, 686 deletions
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index 155c5cca7c..c3229ad2d3 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -34,6 +34,7 @@ #include "pager.h" #include "build.h" #include "output-mode.h" +#include "fileio.h" static bool arg_no_pager = false; static bool arg_kernel_threads = false; @@ -184,9 +185,11 @@ int main(int argc, char *argv[]) { r = show_cgroup_by_path(p, NULL, 0, arg_kernel_threads, output_flags); } else { - if (arg_machine) - r = cg_get_machine_path(arg_machine, &root); - else + if (arg_machine) { + char *m; + m = strappenda("/run/systemd/machines/", arg_machine); + r = parse_env_file(m, NEWLINE, "CGROUP", &root, NULL); + } else r = cg_get_root_path(&root); if (r < 0) { log_error("Failed to get %s path: %s", diff --git a/src/core/cgroup.c b/src/core/cgroup.c index a995d1436d..5065329739 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -32,6 +32,7 @@ #include "log.h" #include "strv.h" #include "path-util.h" +#include "special.h" int cgroup_bonding_realize(CGroupBonding *b) { int r; @@ -304,7 +305,8 @@ int cgroup_bonding_is_empty_list(CGroupBonding *first) { LIST_FOREACH(by_unit, b, first) { int r; - if ((r = cgroup_bonding_is_empty(b)) < 0) { + r = cgroup_bonding_is_empty(b); + if (r < 0) { /* If this returned -EAGAIN, then we don't know if the * group is empty, so let's see if another group can * tell us */ @@ -319,10 +321,9 @@ int cgroup_bonding_is_empty_list(CGroupBonding *first) { } int manager_setup_cgroup(Manager *m) { - _cleanup_free_ char *current = NULL, *path = NULL; - char suffix_buffer[sizeof("/systemd-") + DECIMAL_STR_MAX(pid_t)]; - const char *suffix; + _cleanup_free_ char *path = NULL; int r; + char *e, *a; assert(m); @@ -333,37 +334,30 @@ int manager_setup_cgroup(Manager *m) { } /* 1. Determine hierarchy */ - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t); + free(m->cgroup_root); + m->cgroup_root = NULL; + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root); if (r < 0) { log_error("Cannot determine cgroup we are running in: %s", strerror(-r)); return r; } - if (m->running_as == SYSTEMD_SYSTEM) - suffix = NULL; - else { - sprintf(suffix_buffer, "/systemd-%lu", (unsigned long) getpid()); - suffix = suffix_buffer; + /* Already in /system.slice? If so, let's cut this off again */ + if (m->running_as == SYSTEMD_SYSTEM) { + e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); + if (e) + *e = 0; } - free(m->cgroup_hierarchy); - if (!suffix || endswith(current, suffix)) { - /* We probably got reexecuted and can continue to use our root cgroup */ - m->cgroup_hierarchy = current; - current = NULL; - } else { - /* We need a new root cgroup */ - if (streq(current, "/")) - m->cgroup_hierarchy = strdup(suffix); - else - m->cgroup_hierarchy = strappend(current, suffix); - - if (!m->cgroup_hierarchy) - return log_oom(); - } + /* And make sure to store away the root value without trailing + * slash, even for the root dir, so that we can easily prepend + * it everywhere. */ + if (streq(m->cgroup_root, "/")) + m->cgroup_root[0] = 0; /* 2. Show data */ - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path); + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path); if (r < 0) { log_error("Cannot find cgroup mount point: %s", strerror(-r)); return r; @@ -382,8 +376,9 @@ int manager_setup_cgroup(Manager *m) { log_debug("Release agent already installed."); } - /* 4. Realize the group */ - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0); + /* 4. Realize the system slice and put us in there */ + a = strappenda(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 0); if (r < 0) { log_error("Failed to create root cgroup hierarchy: %s", strerror(-r)); return r; @@ -402,30 +397,24 @@ int manager_setup_cgroup(Manager *m) { /* 6. Remove non-existing controllers from the default controllers list */ cg_shorten_controllers(m->default_controllers); - /* 7. Let's create the user and machine hierarchies - * right-away, so that people can inotify on them, if they - * wish, without this being racy. */ - if (m->running_as == SYSTEMD_SYSTEM) { - cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../user"); - cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../machine"); - } - return 0; } void manager_shutdown_cgroup(Manager *m, bool delete) { assert(m); - if (delete && m->cgroup_hierarchy) - cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy); + /* We can't really delete the group, since we are in it. But + * let's trim it. */ + if (delete && m->cgroup_root) + cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false); if (m->pin_cgroupfs_fd >= 0) { close_nointr_nofail(m->pin_cgroupfs_fd); m->pin_cgroupfs_fd = -1; } - free(m->cgroup_hierarchy); - m->cgroup_hierarchy = NULL; + free(m->cgroup_root); + m->cgroup_root = NULL; } int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 25d38cc491..d41b6ae15f 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -301,7 +301,6 @@ " <property name=\"ConfirmSpawn\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"ShowStatus\" type=\"b\" access=\"read\"/>\n" \ " <property name=\"UnitPath\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"DefaultControllers\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"DefaultStandardOutput\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"DefaultStandardError\" type=\"s\" access=\"read\"/>\n" \ @@ -614,7 +613,6 @@ static const BusProperty bus_manager_properties[] = { { "ConfirmSpawn", bus_property_append_bool, "b", offsetof(Manager, confirm_spawn) }, { "ShowStatus", bus_property_append_bool, "b", offsetof(Manager, show_status) }, { "UnitPath", bus_property_append_strv, "as", offsetof(Manager, lookup_paths.unit_path), true }, - { "ControlGroupHierarchy", bus_property_append_string, "s", offsetof(Manager, cgroup_hierarchy), true }, { "DefaultControllers", bus_property_append_strv, "as", offsetof(Manager, default_controllers), true }, { "DefaultStandardOutput", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_output) }, { "DefaultStandardError", bus_manager_append_exec_output, "s", offsetof(Manager, default_std_error) }, diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index b7391b5506..8a7ab349d1 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -89,10 +89,7 @@ static int bus_unit_append_slice(DBusMessageIter *i, const char *property, void assert(property); assert(u); - if (UNIT_DEREF(u->slice)) - d = UNIT_DEREF(u->slice)->id; - else - d = ""; + d = strempty(unit_slice_name(u)); if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &d)) return -ENOMEM; diff --git a/src/core/manager.h b/src/core/manager.h index dcc4ebed92..e21c8f7abf 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -199,7 +199,7 @@ struct Manager { /* Data specific to the cgroup subsystem */ Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */ - char *cgroup_hierarchy; + char *cgroup_root; usec_t gc_queue_timestamp; int gc_marker; diff --git a/src/core/service.c b/src/core/service.c index ac8cdb2c31..a0c648a85b 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -220,7 +220,7 @@ static void service_close_socket_fd(Service *s) { static void service_connection_unref(Service *s) { assert(s); - if (!UNIT_DEREF(s->accept_socket)) + if (!UNIT_ISSET(s->accept_socket)) return; socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); diff --git a/src/core/socket.c b/src/core/socket.c index 2b3b6813ca..2f25e25aa6 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1005,7 +1005,7 @@ static int socket_open_fds(Socket *s) { if ((r = socket_instantiate_service(s)) < 0) return r; - if (UNIT_DEREF(s->service) && + if (UNIT_ISSET(s->service) && SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) { r = label_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label); @@ -1633,7 +1633,7 @@ static int socket_start(Unit *u) { return 0; /* Cannot run this without the service being around */ - if (UNIT_DEREF(s->service)) { + if (UNIT_ISSET(s->service)) { Service *service; service = SERVICE(UNIT_DEREF(s->service)); diff --git a/src/core/special.h b/src/core/special.h index e183056a20..337a0a43e9 100644 --- a/src/core/special.h +++ b/src/core/special.h @@ -114,5 +114,7 @@ #define SPECIAL_RUNLEVEL4_TARGET "runlevel4.target" #define SPECIAL_RUNLEVEL5_TARGET "runlevel5.target" -/* Where we add all our system units by default */ +/* Where we add all our system units, users and machines by default */ #define SPECIAL_SYSTEM_SLICE "system.slice" +#define SPECIAL_USER_SLICE "user.slice" +#define SPECIAL_MACHINE_SLICE "machine.slice" diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 85a05b872a..caf51259d2 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -27,6 +27,8 @@ #include "unit-name.h" #include "unit-printf.h" #include "macro.h" +#include "cgroup-util.h" +#include "special.h" static char *specifier_prefix_and_instance(char specifier, void *data, void *userdata) { Unit *u = userdata; @@ -86,22 +88,22 @@ static char *specifier_cgroup(char specifier, void *data, void *userdata) { } static char *specifier_cgroup_root(char specifier, void *data, void *userdata) { + _cleanup_free_ char *p = NULL; Unit *u = userdata; - char *p; - assert(u); + const char *slice; + int r; - if (specifier == 'r') - return strdup(u->manager->cgroup_hierarchy); + assert(u); - if (path_get_parent(u->manager->cgroup_hierarchy, &p) < 0) - return strdup(""); + slice = unit_slice_name(u); + if (specifier == 'R' || !slice) + return strdup(u->manager->cgroup_root); - if (streq(p, "/")) { - free(p); - return strdup(""); - } + r = cg_slice_to_path(slice, &p); + if (r < 0) + return NULL; - return p; + return strjoin(u->manager->cgroup_root, "/", p, NULL); } static char *specifier_runtime(char specifier, void *data, void *userdata) { @@ -256,8 +258,8 @@ char *unit_full_printf(Unit *u, const char *format) { * * %f the the instance if set, otherwise the id * %c cgroup path of unit - * %r root cgroup path of this systemd instance (e.g. "/user/lennart/shared/systemd-4711") - * %R parent of root cgroup path (e.g. "/usr/lennart/shared") + * %r where units in this slice are place in the cgroup tree + * %R the root of this systemd's instance tree * %t the runtime directory to place sockets in (e.g. "/run" or $XDG_RUNTIME_DIR) * %U the UID of the configured user or running user * %u the username of the configured user or running user diff --git a/src/core/unit.c b/src/core/unit.c index 90ff43da66..f75045dc48 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -672,7 +672,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tActive Exit Timestamp: %s\n" "%s\tInactive Enter Timestamp: %s\n" "%s\tGC Check Good: %s\n" - "%s\tNeed Daemon Reload: %s\n", + "%s\tNeed Daemon Reload: %s\n" + "%s\tSlice: %s\n", prefix, u->id, prefix, unit_description(u), prefix, strna(u->instance), @@ -683,7 +684,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(format_timestamp(timestamp3, sizeof(timestamp3), u->active_exit_timestamp.realtime)), prefix, strna(format_timestamp(timestamp4, sizeof(timestamp4), u->inactive_enter_timestamp.realtime)), prefix, yes_no(unit_check_gc(u)), - prefix, yes_no(unit_need_daemon_reload(u))); + prefix, yes_no(unit_need_daemon_reload(u)), + prefix, strna(unit_slice_name(u))); SET_FOREACH(t, u->names, i) fprintf(f, "%s\tName: %s\n", prefix, t); @@ -878,7 +880,7 @@ static int unit_add_default_dependencies(Unit *u) { return r; } - if (u->default_dependencies && UNIT_DEREF(u->slice)) { + if (u->default_dependencies && UNIT_ISSET(u->slice)) { r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, UNIT_DEREF(u->slice), true); if (r < 0) return r; @@ -1994,7 +1996,7 @@ char *unit_default_cgroup_path(Unit *u) { assert(u); - if (UNIT_DEREF(u->slice)) { + if (UNIT_ISSET(u->slice)) { r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice); if (r < 0) return NULL; @@ -2015,11 +2017,11 @@ char *unit_default_cgroup_path(Unit *u) { if (!escaped_template) return NULL; - return strjoin(u->manager->cgroup_hierarchy, "/", + return strjoin(u->manager->cgroup_root, "/", slice ? slice : "", slice ? "/" : "", escaped_template, "/", escaped_instance, NULL); } else - return strjoin(u->manager->cgroup_hierarchy, "/", + return strjoin(u->manager->cgroup_root, "/", slice ? slice : "", slice ? "/" : "", escaped_instance, NULL); } @@ -2172,7 +2174,7 @@ int unit_add_default_slice(Unit *u) { assert(u); - if (UNIT_DEREF(u->slice)) + if (UNIT_ISSET(u->slice)) return 0; if (u->manager->running_as != SYSTEMD_SYSTEM) @@ -2186,6 +2188,15 @@ int unit_add_default_slice(Unit *u) { return 0; } +const char *unit_slice_name(Unit *u) { + assert(u); + + if (!UNIT_ISSET(u->slice)) + return NULL; + + return UNIT_DEREF(u->slice)->id; +} + int unit_add_default_cgroups(Unit *u) { CGroupAttribute *a; char **c; @@ -2196,7 +2207,7 @@ int unit_add_default_cgroups(Unit *u) { /* Adds in the default cgroups, if they weren't specified * otherwise. */ - if (!u->manager->cgroup_hierarchy) + if (!u->manager->cgroup_root) return 0; r = unit_add_one_default_cgroup(u, NULL); diff --git a/src/core/unit.h b/src/core/unit.h index 81b8adbfdc..da52101bd2 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -542,6 +542,8 @@ void unit_reset_failed(Unit *u); Unit *unit_following(Unit *u); +const char *unit_slice_name(Unit *u); + bool unit_stop_pending(Unit *u) _pure_; bool unit_inactive_or_pending(Unit *u) _pure_; bool unit_active_or_pending(Unit *u); @@ -563,6 +565,7 @@ Unit* unit_ref_set(UnitRef *ref, Unit *u); void unit_ref_unset(UnitRef *ref); #define UNIT_DEREF(ref) ((ref).unit) +#define UNIT_ISSET(ref) (!!(ref).unit) int unit_add_one_mount_link(Unit *u, Mount *m); int unit_add_mount_links(Unit *u); 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]); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 32cfe05dcd..83be00231c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -43,11 +43,8 @@ #include <sys/un.h> #include <sys/socket.h> -#ifdef HAVE_XATTR -#include <attr/xattr.h> -#endif - #include <systemd/sd-daemon.h> +#include <systemd/sd-bus.h> #include "log.h" #include "util.h" @@ -64,6 +61,8 @@ #include "fdset.h" #include "build.h" #include "fileio.h" +#include "bus-internal.h" +#include "bus-message.h" #ifndef TTY_GID #define TTY_GID 5 @@ -78,9 +77,9 @@ typedef enum LinkJournal { static char *arg_directory = NULL; static char *arg_user = NULL; -static char **arg_controllers = NULL; -static char *arg_uuid = NULL; +static sd_id128_t arg_uuid = {}; static char *arg_machine = NULL; +static const char *arg_slice = NULL; static bool arg_private_network = false; static bool arg_read_only = false; static bool arg_boot = false; @@ -123,10 +122,9 @@ static int help(void) { " -D --directory=NAME Root directory for the container\n" " -b --boot Boot up full system (i.e. invoke init)\n" " -u --user=USER Run the command under specified user or uid\n" - " -C --controllers=LIST Put the container in specified comma-separated\n" - " cgroup hierarchies\n" " --uuid=UUID Set a specific machine UUID for the container\n" " -M --machine=NAME Set the machine name for the container\n" + " -S --slice=SLICE Place the container in the specified slice\n" " --private-network Disable network in container\n" " --read-only Mount the root directory read-only\n" " --capability=CAP In addition to the default, retain specified\n" @@ -159,7 +157,6 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "directory", required_argument, NULL, 'D' }, { "user", required_argument, NULL, 'u' }, - { "controllers", required_argument, NULL, 'C' }, { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK }, { "boot", no_argument, NULL, 'b' }, { "uuid", required_argument, NULL, ARG_UUID }, @@ -169,15 +166,16 @@ static int parse_argv(int argc, char *argv[]) { { "bind", required_argument, NULL, ARG_BIND }, { "bind-ro", required_argument, NULL, ARG_BIND_RO }, { "machine", required_argument, NULL, 'M' }, + { "slice", required_argument, NULL, 'S' }, { NULL, 0, NULL, 0 } }; - int c; + int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hD:u:C:bM:j", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "+hD:u:C:bM:jS:", options, NULL)) >= 0) { switch (c) { @@ -208,15 +206,6 @@ static int parse_argv(int argc, char *argv[]) { break; - case 'C': - strv_free(arg_controllers); - arg_controllers = strv_split(optarg, ","); - if (!arg_controllers) - return log_oom(); - - cg_shorten_controllers(arg_controllers); - break; - case ARG_PRIVATE_NETWORK: arg_private_network = true; break; @@ -226,12 +215,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_UUID: - if (!id128_is_valid(optarg)) { + r = sd_id128_from_string(optarg, &arg_uuid); + if (r < 0) { log_error("Invalid UUID: %s", optarg); - return -EINVAL; + return r; } + break; - arg_uuid = optarg; + case 'S': + arg_slice = strdup(optarg); break; case 'M': @@ -301,7 +293,6 @@ static int parse_argv(int argc, char *argv[]) { _cleanup_free_ char *a = NULL, *b = NULL; char *e; char ***x; - int r; x = c == ARG_BIND ? &arg_bind : &arg_bind_ro; @@ -912,68 +903,6 @@ static int setup_journal(const char *directory) { return 0; } -static int setup_cgroup(const char *path) { - char **c; - int r; - - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, path, 1); - if (r < 0) { - log_error("Failed to create cgroup: %s", strerror(-r)); - return r; - } - - STRV_FOREACH(c, arg_controllers) { - r = cg_create_and_attach(*c, path, 1); - if (r < 0) - log_warning("Failed to create cgroup in controller %s: %s", *c, strerror(-r)); - } - - return 0; -} - -static int save_attributes(const char *cgroup, pid_t pid, const char *uuid, const char *directory) { -#ifdef HAVE_XATTR - _cleanup_free_ char *path = NULL; - char buf[DECIMAL_STR_MAX(pid_t)]; - int r = 0, k; - - assert(cgroup); - assert(pid >= 0); - assert(arg_directory); - - assert_se(snprintf(buf, sizeof(buf), "%lu", (unsigned long) pid) < (int) sizeof(buf)); - - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, cgroup, NULL, &path); - if (r < 0) { - log_error("Failed to get path: %s", strerror(-r)); - return r; - } - - r = setxattr(path, "trusted.init_pid", buf, strlen(buf), XATTR_CREATE); - if (r < 0) - log_warning("Failed to set %s attribute on %s: %m", "trusted.init_pid", path); - - if (uuid) { - k = setxattr(path, "trusted.machine_id", uuid, strlen(uuid), XATTR_CREATE); - if (k < 0) { - log_warning("Failed to set %s attribute on %s: %m", "trusted.machine_id", path); - if (r == 0) - r = k; - } - } - - k = setxattr(path, "trusted.root_directory", directory, strlen(directory), XATTR_CREATE); - if (k < 0) { - log_warning("Failed to set %s attribute on %s: %m", "trusted.root_directory", path); - if (r == 0) - r = k; - } - return r; -#else - return 0; -#endif -} - static int drop_capabilities(void) { return capability_bounding_set_drop(~arg_retain, false); } @@ -1220,6 +1149,41 @@ finish: return r; } +static int register_machine(void) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_unref_ sd_bus *bus = NULL; + int r; + + r = sd_bus_open_system(&bus); + if (r < 0) { + log_error("Failed to open system bus: %s", strerror(-r)); + return r; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "CreateMachine", + &error, + NULL, + "sayssuss", + arg_machine, + SD_BUS_APPEND_ID128(arg_uuid), + "nspawn", + "container", + (uint32_t) 0, + strempty(arg_slice), + strempty(arg_directory)); + if (r < 0) { + log_error("Failed to register machine: %s", error.message); + return r; + } + + return 0; +} + static bool audit_enabled(void) { int fd; @@ -1234,7 +1198,6 @@ static bool audit_enabled(void) { int main(int argc, char *argv[]) { pid_t pid = 0; int r = EXIT_FAILURE, k; - _cleanup_free_ char *newcg = NULL; _cleanup_close_ int master = -1; int n_fd_passed; const char *console = NULL; @@ -1325,22 +1288,6 @@ int main(int argc, char *argv[]) { fdset_close_others(fds); log_open(); - k = cg_get_machine_path(arg_machine, &newcg); - if (k < 0) { - log_error("Failed to determine machine cgroup path: %s", strerror(-k)); - goto finish; - } - - k = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, newcg, true); - if (k <= 0 && k != -ENOENT) { - log_error("Container already running."); - - free(newcg); - newcg = NULL; - - goto finish; - } - master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); if (master < 0) { log_error("Failed to acquire pseudo tty: %m"); @@ -1484,11 +1431,12 @@ int main(int argc, char *argv[]) { goto child_fail; } - if (setup_cgroup(newcg) < 0) - goto child_fail; - close_pipe(pipefd2); + r = register_machine(); + if (r < 0) + goto finish; + /* Mark everything as slave, so that we still * receive mounts from the real root, but don't * propagate mounts to the real root. */ @@ -1639,8 +1587,8 @@ int main(int argc, char *argv[]) { goto child_fail; } - if (arg_uuid) { - if (asprintf((char**)(envp + n_env++), "container_uuid=%s", arg_uuid) < 0) { + if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) { + if (asprintf((char**)(envp + n_env++), "container_uuid=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(arg_uuid)) < 0) { log_oom(); goto child_fail; } @@ -1702,8 +1650,6 @@ int main(int argc, char *argv[]) { fd_wait_for_event(pipefd2[0], POLLHUP, -1); close_nointr_nofail(pipefd2[0]); - save_attributes(newcg, pid, arg_uuid, arg_directory); - fdset_free(fds); fds = NULL; @@ -1756,12 +1702,11 @@ finish: close_pipe(kmsg_socket_pair); - if (newcg) - cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true); + if (pid > 0) + kill(pid, SIGKILL); free(arg_directory); free(arg_machine); - strv_free(arg_controllers); fdset_free(fds); diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index 4f58affe38..05d026a587 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -38,6 +38,7 @@ #include "strv.h" #include "unit-name.h" #include "fileio.h" +#include "special.h" int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { _cleanup_free_ char *fs = NULL; @@ -470,19 +471,19 @@ static const char *normalize_controller(const char *controller) { static int join_path(const char *controller, const char *path, const char *suffix, char **fs) { char *t = NULL; - if (controller) { - if (path && suffix) + if (!isempty(controller)) { + if (!isempty(path) && !isempty(suffix)) t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL); - else if (path) + else if (!isempty(path)) t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL); - else if (suffix) + else if (!isempty(suffix)) t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL); else t = strappend("/sys/fs/cgroup/", controller); } else { - if (path && suffix) + if (!isempty(path) && !isempty(suffix)) t = strjoin(path, "/", suffix, NULL); - else if (path) + else if (!isempty(path)) t = strdup(path); else return -EINVAL; @@ -1091,96 +1092,20 @@ int cg_mangle_path(const char *path, char **result) { return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result); } -int cg_get_system_path(char **path) { - char *p; - int r; - - assert(path); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p); - if (r < 0) { - p = strdup("/system"); - if (!p) - return -ENOMEM; - } - - if (endswith(p, "/system")) - *path = p; - else { - char *q; - - q = strappend(p, "/system"); - free(p); - if (!q) - return -ENOMEM; - - *path = q; - } - - return 0; -} - int cg_get_root_path(char **path) { - char *root, *e; + char *p, *e; int r; assert(path); - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &root); + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p); if (r < 0) return r; - e = endswith(root, "/system"); - if (e == root) - e[1] = 0; - else if (e) + e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); + if (e) *e = 0; - *path = root; - return 0; -} - -int cg_get_user_path(char **path) { - _cleanup_free_ char *root = NULL; - char *p; - - assert(path); - - /* Figure out the place to put user cgroups below. We use the - * same as PID 1 has but with the "/system" suffix replaced by - * "/user" */ - - if (cg_get_root_path(&root) < 0 || streq(root, "/")) - p = strdup("/user"); - else - p = strappend(root, "/user"); - - if (!p) - return -ENOMEM; - - *path = p; - return 0; -} - -int cg_get_machine_path(const char *machine, char **path) { - _cleanup_free_ char *root = NULL, *escaped = NULL; - char *p; - - assert(path); - - if (machine) { - const char *name = strappenda(machine, ".nspawn"); - - escaped = cg_escape(name); - if (!escaped) - return -ENOMEM; - } - - p = strjoin(cg_get_root_path(&root) >= 0 && !streq(root, "/") ? root : "", - "/machine", machine ? "/" : "", machine ? escaped : "", NULL); - if (!p) - return -ENOMEM; - *path = p; return 0; } @@ -1301,15 +1226,29 @@ int cg_path_decode_unit(const char *cgroup, char **unit){ return 0; } +static const char *skip_slices(const char *p) { + size_t n; + + /* Skips over all slice assignments */ + + for (;;) { + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) + return p; + + p += n; + } +} + int cg_path_get_unit(const char *path, char **unit) { const char *e; assert(path); assert(unit); - e = path_startswith(path, "/system/"); - if (!e) - return -ENOENT; + e = skip_slices(path); return cg_path_decode_unit(e, unit); } @@ -1327,15 +1266,55 @@ int cg_pid_get_unit(pid_t pid, char **unit) { return cg_path_get_unit(cgroup, unit); } -_pure_ static const char *skip_label(const char *e) { - assert(e); +static const char *skip_user(const char *p) { + size_t n; - e = strchr(e, '/'); - if (!e) + assert(p); + + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (n <= 5 || memcmp(p + n - 5, ".user", 5) != 0) + return p; + + p += n; + p += strspn(p, "/"); + + return p; +} + +static const char *skip_session(const char *p) { + size_t n; + + assert(p); + + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (n <= 8 || memcmp(p + n - 8, ".session", 8) != 0) return NULL; - e += strspn(e, "/"); - return e; + p += n; + p += strspn(p, "/"); + + return p; +} + +static const char *skip_systemd_label(const char *p) { + size_t n; + + assert(p); + + p += strspn(p, "/"); + + n = strcspn(p, "/"); + if (n < 8 || memcmp(p, "systemd-", 8) != 0) + return p; + + p += n; + p += strspn(p, "/"); + + return p; } int cg_path_get_user_unit(const char *path, char **unit) { @@ -1348,24 +1327,19 @@ int cg_path_get_user_unit(const char *path, char **unit) { * cgroups might have arbitrary child cgroups and we shouldn't get * confused by those */ - e = path_startswith(path, "/user/"); - if (!e) - return -ENOENT; + /* Skip slices, if there are any */ + e = skip_slices(path); - /* Skip the user name */ - e = skip_label(e); - if (!e) - return -ENOENT; + /* Skip the user name, if there is one */ + e = skip_user(e); - /* Skip the session ID */ - e = skip_label(e); + /* Skip the session ID, require that there is one */ + e = skip_session(e); if (!e) return -ENOENT; - /* Skip the systemd cgroup */ - e = skip_label(e); - if (!e) - return -ENOENT; + /* Skip the systemd cgroup, if there is one */ + e = skip_systemd_label(e); return cg_path_decode_unit(e, unit); } @@ -1384,23 +1358,27 @@ int cg_pid_get_user_unit(pid_t pid, char **unit) { } int cg_path_get_machine_name(const char *path, char **machine) { - const char *e, *n; + const char *e, *n, *x; char *s, *r; assert(path); assert(machine); - e = path_startswith(path, "/machine/"); - if (!e) - return -ENOENT; + /* Skip slices, if there are any */ + e = skip_slices(path); n = strchrnul(e, '/'); if (e == n) return -ENOENT; s = strndupa(e, n - e); + s = cg_unescape(s); + + x = endswith(s, ".machine"); + if (!x) + return -ENOENT; - r = strdup(cg_unescape(s)); + r = strndup(s, x - s); if (!r) return -ENOMEM; @@ -1428,14 +1406,11 @@ int cg_path_get_session(const char *path, char **session) { assert(path); assert(session); - e = path_startswith(path, "/user/"); - if (!e) - return -ENOENT; + /* Skip slices, if there are any */ + e = skip_slices(path); - /* Skip the user name */ - e = skip_label(e); - if (!e) - return -ENOENT; + /* Skip the user name, if there is one */ + e = skip_user(e); n = strchrnul(e, '/'); if (n - e < 8) @@ -1471,9 +1446,8 @@ int cg_path_get_owner_uid(const char *path, uid_t *uid) { assert(path); assert(uid); - e = path_startswith(path, "/user/"); - if (!e) - return -ENOENT; + /* Skip slices, if there are any */ + e = skip_slices(path); n = strchrnul(e, '/'); if (n - e < 5) diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h index 84274e605d..0485c11ade 100644 --- a/src/shared/cgroup-util.h +++ b/src/shared/cgroup-util.h @@ -84,9 +84,6 @@ int cg_is_empty_by_spec(const char *spec, bool ignore_self); int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self); int cg_get_root_path(char **path); -int cg_get_system_path(char **path); -int cg_get_user_path(char **path); -int cg_get_machine_path(const char *machine, char **path); int cg_path_get_session(const char *path, char **session); int cg_path_get_owner_uid(const char *path, uid_t *uid); diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c index 2d4cd8d9f3..0910e86f10 100644 --- a/src/shared/unit-name.c +++ b/src/shared/unit-name.c @@ -455,7 +455,7 @@ char *unit_name_to_path(const char *name) { } char *unit_dbus_path_from_name(const char *name) { - char *e, *p; + _cleanup_free_ char *e = NULL; assert(name); @@ -463,10 +463,7 @@ char *unit_dbus_path_from_name(const char *name) { if (!e) return NULL; - p = strappend("/org/freedesktop/systemd1/unit/", e); - free(e); - - return p; + return strappend("/org/freedesktop/systemd1/unit/", e); } char *unit_name_mangle(const char *name) { diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index eeca69dd1a..878001ccb5 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -205,6 +205,12 @@ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e); int sd_bus_error_is_set(const sd_bus_error *e); int sd_bus_error_has_name(const sd_bus_error *e, const char *name); +#define SD_BUS_APPEND_ID128(x) 16, \ + (x).bytes[0], (x).bytes[1], (x).bytes[2], (x).bytes[3], \ + (x).bytes[4], (x).bytes[5], (x).bytes[6], (x).bytes[7], \ + (x).bytes[8], (x).bytes[9], (x).bytes[10], (x).bytes[11], \ + (x).bytes[12], (x).bytes[13], (x).bytes[14], (x).bytes[15] + #ifdef __cplusplus } #endif diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index c8de331691..2f80749c0f 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -31,6 +31,10 @@ extern "C" { /* Hey! If you add a new message here, you *must* also update the * message catalog with an appropriate explanation */ +/* And if you add a new ID here, make sure to generate a random one + * with journalctl --new-id128. Do not use any other IDs, and do not + * count them up manually. */ + #define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) #define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) #define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) @@ -42,6 +46,8 @@ extern "C" { #define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) #define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) #define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) +#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) +#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) #define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) #define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index d4d58b320b..aea8a7a1c3 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -47,6 +47,17 @@ static void check_p_g_u(const char *path, int code, const char *result) { assert_se(streq_ptr(unit, result)); } +static void test_path_get_unit(void) { + check_p_g_u("/system.slice/foobar.service/sdfdsaf", 0, "foobar.service"); + check_p_g_u("/system.slice/getty@.service/getty@tty5.service", 0, "getty@tty5.service"); + check_p_g_u("/system.slice/getty@.service/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service"); + check_p_g_u("/system.slice/getty@.service/getty@tty5.service/", 0, "getty@tty5.service"); + check_p_g_u("/system.slice/getty@tty6.service/tty5", 0, "getty@tty6.service"); + check_p_g_u("sadfdsafsda", -EINVAL, NULL); + check_p_g_u("/system.slice/getty####@tty6.service/tty5", -EINVAL, NULL); + check_p_g_u("/system.slice/system-waldo.slice/foobar.service/sdfdsaf", 0, "foobar.service"); +} + static void check_p_g_u_u(const char *path, int code, const char *result) { _cleanup_free_ char *unit = NULL; @@ -54,39 +65,64 @@ static void check_p_g_u_u(const char *path, int code, const char *result) { assert_se(streq_ptr(unit, result)); } -static void test_path_get_unit(void) { - check_p_g_u("/system/foobar.service/sdfdsaf", 0, "foobar.service"); - check_p_g_u("/system/getty@.service/getty@tty5.service", 0, "getty@tty5.service"); - check_p_g_u("/system/getty@.service/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service"); - check_p_g_u("/system/getty@.service/getty@tty5.service/", 0, "getty@tty5.service"); - check_p_g_u("/system/getty@tty6.service/tty5", 0, "getty@tty6.service"); - check_p_g_u("sadfdsafsda", -ENOENT, NULL); - check_p_g_u("/system/getty####@tty6.service/tty5", -EINVAL, NULL); +static void test_path_get_user_unit(void) { + check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/foobar.service", 0, "foobar.service"); + check_p_g_u_u("/user.slice/1002.user/2.session/systemd-21548/foobar.service/waldo", 0, "foobar.service"); + check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/foobar.service/waldo/uuuux", 0, "foobar.service"); + check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/waldo/waldo/uuuux", -EINVAL, NULL); + check_p_g_u_u("/user.slice/1000.user/2.session/foobar.service", 0, "foobar.service"); + check_p_g_u_u("/user.slice/1000.user/2.session/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service"); + check_p_g_u_u("/2.session/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service"); + check_p_g_u_u("/xyz.slice/xyz-waldo.slice/77.session/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service"); + check_p_g_u_u("/meh.service", -ENOENT, NULL); } -static void test_path_get_user_unit(void) { - check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service", 0, "foobar.service"); - check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service/waldo", 0, "foobar.service"); - check_p_g_u_u("/user/lennart/2/systemd-21548/foobar.service/waldo/uuuux", 0, "foobar.service"); - check_p_g_u_u("/user/lennart/2/systemd-21548/waldo/waldo/uuuux", -EINVAL, NULL); - check_p_g_u_u("/user/lennart/2/foobar.service", -ENOENT, NULL); - check_p_g_u_u("/user/lennart/2/systemd-21548/foobar@.service/foobar@pie.service/pa/po", 0, "foobar@pie.service"); +static void check_p_g_s(const char *path, int code, const char *result) { + _cleanup_free_ char *s = NULL; + + assert_se(cg_path_get_session(path, &s) == code); + assert_se(streq_ptr(s, result)); } -static void test_get_paths(void) { - _cleanup_free_ char *a = NULL, *b = NULL, *c = NULL, *d = NULL; +static void test_path_get_session(void) { + check_p_g_s("/user.slice/1000.user/2.session/systemd-21548/foobar.service", 0, "2"); + check_p_g_s("/3.session", 0, "3"); + check_p_g_s("", -ENOENT, 0); +} - assert_se(cg_get_root_path(&a) >= 0); - log_info("Root = %s", a); +static void check_p_g_o_u(const char *path, int code, uid_t result) { + uid_t uid = 0; - assert_se(cg_get_system_path(&b) >= 0); - log_info("System = %s", b); + assert_se(cg_path_get_owner_uid(path, &uid) == code); + assert_se(uid == result); +} + +static void test_path_get_owner_uid(void) { + check_p_g_o_u("/user.slice/1000.user/2.session/systemd-21548/foobar.service", 0, 1000); + check_p_g_o_u("/1006.user", 0, 1006); + check_p_g_o_u("", -ENOENT, 0); +} - assert_se(cg_get_user_path(&c) >= 0); - log_info("User = %s", c); +static void check_p_g_m_n(const char *path, int code, const char *result) { + _cleanup_free_ char *m = NULL; - assert_se(cg_get_machine_path("harley", &d) >= 0); - log_info("Machine = %s", d); + assert_se(cg_path_get_machine_name(path, &m) == code); + assert_se(streq_ptr(m, result)); +} + +static void test_path_get_machine_name(void) { + check_p_g_m_n("/user.slice/foobar.machine", 0, "foobar"); + check_p_g_m_n("/foobar.machine", 0, "foobar"); + check_p_g_m_n("/user.slice/user-kuux.slice/foobar.machine", 0, "foobar"); + check_p_g_m_n("/user.slice/user-kuux.slice/foobar.machine/asjhdkj", 0, "foobar"); + check_p_g_m_n("", -ENOENT, NULL); +} + +static void test_get_paths(void) { + _cleanup_free_ char *a = NULL; + + assert_se(cg_get_root_path(&a) >= 0); + log_info("Root = %s", a); } static void test_proc(void) { @@ -193,6 +229,9 @@ int main(void) { test_path_decode_unit(); test_path_get_unit(); test_path_get_user_unit(); + test_path_get_session(); + test_path_get_owner_uid(); + test_path_get_machine_name(); test_get_paths(); test_proc(); test_escape(); |