diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/login/loginctl.c | 309 | ||||
-rw-r--r-- | src/login/logind-dbus.c | 378 | ||||
-rw-r--r-- | src/login/logind-user.c | 12 | ||||
-rw-r--r-- | src/login/logind.c | 117 | ||||
-rw-r--r-- | src/login/logind.h | 7 | ||||
l--------- | src/machine/Makefile | 1 | ||||
-rw-r--r-- | src/machine/machine-dbus.c (renamed from src/login/logind-machine-dbus.c) | 27 | ||||
-rw-r--r-- | src/machine/machine.c (renamed from src/login/logind-machine.c) | 22 | ||||
-rw-r--r-- | src/machine/machine.h (renamed from src/login/logind-machine.h) | 14 | ||||
-rw-r--r-- | src/machine/machinectl.c | 764 | ||||
-rw-r--r-- | src/machine/machined-dbus.c | 824 | ||||
-rw-r--r-- | src/machine/machined.c | 408 | ||||
-rw-r--r-- | src/machine/machined.h | 73 | ||||
-rw-r--r-- | src/machine/org.freedesktop.machine1.conf | 46 | ||||
-rw-r--r-- | src/machine/org.freedesktop.machine1.service | 12 | ||||
-rw-r--r-- | src/nspawn/nspawn.c | 6 |
16 files changed, 2183 insertions, 837 deletions
diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 3637a408dc..e118deffda 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -261,69 +261,6 @@ 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; @@ -362,18 +299,6 @@ 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; @@ -569,76 +494,6 @@ 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); @@ -912,80 +767,6 @@ 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); @@ -1067,7 +848,6 @@ 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); @@ -1131,10 +911,8 @@ 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 if (strstr(verb, "seat")) - r = status_property_seat(name, &sub3, &seat_info); else - r = status_property_machine(name, &sub3, &machine_info); + r = status_property_seat(name, &sub3, &seat_info); if (r < 0) { log_error("Failed to parse reply."); @@ -1149,10 +927,8 @@ 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 if (strstr(verb, "seat")) - print_seat_status_info(&seat_info); else - print_machine_status_info(&machine_info); + print_seat_status_info(&seat_info); } r = 0; @@ -1226,7 +1002,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) { DBUS_TYPE_UINT32, &u, DBUS_TYPE_INVALID); - } else if (strstr(args[0], "seat")) { + } else { ret = bus_method_call_with_reply( bus, @@ -1239,18 +1015,6 @@ static int show(DBusConnection *bus, char **args, unsigned n) { 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 < 0) @@ -1334,36 +1098,6 @@ 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; @@ -1567,31 +1301,6 @@ 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" @@ -1630,12 +1339,7 @@ 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" - " 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", + " terminate-seat [NAME...] Terminate all sessions on one or more seats\n", program_invocation_short_name); return 0; @@ -1784,11 +1488,6 @@ static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "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 d8d25b0041..9a19932af4 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -63,14 +63,6 @@ " <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" \ @@ -80,9 +72,6 @@ " <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" \ @@ -108,16 +97,6 @@ " <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=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \ - " <arg name=\"scope_properties\" type=\"a(sv)\" 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" \ @@ -151,9 +130,6 @@ " <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" \ @@ -231,22 +207,12 @@ " <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=\"Controllers\" type=\"as\" access=\"read\"/>\n" \ - " <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \ " <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \ @@ -689,148 +655,6 @@ 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) { - - const char *name, *service, *class, *slice, *root_directory; - _cleanup_free_ char *p = NULL; - DBusMessageIter iter, sub; - MachineClass c; - uint32_t leader; - sd_id128_t id; - Machine *m; - int n, r; - void *v; - - assert(manager); - assert(message); - - 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(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; - - m->create_message = dbus_message_ref(message); - - return 0; - -fail: - if (m) - machine_add_to_gc_queue(m); - - return r; -} - static int bus_manager_inhibit( Manager *m, DBusConnection *connection, @@ -1673,73 +1497,6 @@ static DBusHandlerResult manager_message_handler( 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; @@ -1946,48 +1703,6 @@ 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")) { @@ -2009,12 +1724,6 @@ 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); - 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; Session *session; @@ -2201,45 +1910,6 @@ 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; @@ -2309,29 +1979,6 @@ 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; @@ -2701,7 +2348,6 @@ DBusHandlerResult bus_message_filter( m->action_what = 0; } else { - Machine *mm; Session *s; User *u; @@ -2738,25 +2384,6 @@ DBusHandlerResult bus_message_filter( user_add_to_gc_queue(u); } - - mm = hashmap_get(m->machine_units, unit); - if (mm) { - if (streq_ptr(path, mm->scope_job)) { - free(mm->scope_job); - mm->scope_job = NULL; - - if (mm->started) { - if (streq(result, "done")) - machine_send_create_reply(mm, NULL); - else { - dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); - machine_send_create_reply(mm, &error); - } - } - } - - machine_add_to_gc_queue(mm); - } } } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) { @@ -2771,7 +2398,6 @@ DBusHandlerResult bus_message_filter( unit_name_from_dbus_path(path, &unit); if (unit) { - Machine *mm; Session *s; User *u; @@ -2782,10 +2408,6 @@ DBusHandlerResult bus_message_filter( u = hashmap_get(m->user_units, unit); if (u) user_add_to_gc_queue(u); - - mm = hashmap_get(m->machine_units, unit); - if (mm) - machine_add_to_gc_queue(mm); } } diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 316c4cd095..fb961bf64b 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -354,11 +354,11 @@ static int user_start_slice(User *u) { if (r < 0) { log_error("Failed to start user slice: %s", bus_error(&error, r)); dbus_error_free(&error); + } else { + free(u->slice_job); + u->slice_job = job; } - free(u->slice_job); - u->slice_job = job; - return 0; } @@ -388,11 +388,11 @@ static int user_start_service(User *u) { if (r < 0) { log_error("Failed to start user service: %s", bus_error(&error, r)); dbus_error_free(&error); + } else { + free(u->service_job); + u->service_job = job; } - free(u->service_job); - u->service_job = job; - return 0; } diff --git a/src/login/logind.c b/src/login/logind.c index 7040ac9e8a..e37a1071a2 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -74,18 +74,16 @@ 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_units = hashmap_new(string_hash_func, string_compare_func); m->session_units = hashmap_new(string_hash_func, string_compare_func); - m->machine_units = 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->machines || - !m->user_units || !m->session_units || !m->machine_units || + if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || + !m->user_units || !m->session_units || !m->session_fds || !m->inhibitor_fds || !m->button_fds) { manager_free(m); return NULL; @@ -113,7 +111,6 @@ void manager_free(Manager *m) { Seat *s; Inhibitor *i; Button *b; - Machine *machine; assert(m); @@ -135,20 +132,15 @@ 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_units); hashmap_free(m->session_units); - hashmap_free(m->machine_units); hashmap_free(m->session_fds); hashmap_free(m->inhibitor_fds); @@ -364,30 +356,6 @@ 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; @@ -772,48 +740,6 @@ int manager_enumerate_inhibitors(Manager *m) { return r; } -int manager_enumerate_machines(Manager *m) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(m); - - /* 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/machines: %m"); - return -errno; - } - - FOREACH_DIRENT(de, d, return -errno) { - struct Machine *machine; - int k; - - if (!dirent_is_file(de)) - continue; - - k = manager_add_machine(m, de->d_name, &machine); - if (k < 0) { - log_error("Failed to add machine by file name %s: %s", de->d_name, strerror(-k)); - - r = k; - continue; - } - - machine_add_to_gc_queue(machine); - - k = machine_load(machine); - if (k < 0) - r = k; - } - - return r; -} - int manager_dispatch_seat_udev(Manager *m) { struct udev_device *d; int r; @@ -1018,27 +944,6 @@ int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) { return 1; } -int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { - _cleanup_free_ char *unit = NULL; - Machine *mm; - int r; - - assert(m); - assert(pid >= 1); - assert(machine); - - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - return r; - - mm = hashmap_get(m->machine_units, unit); - if (!mm) - return 0; - - *machine = mm; - return 1; -} - static void manager_dispatch_other(Manager *m, int fd) { Session *s; Inhibitor *i; @@ -1098,7 +1003,6 @@ 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; @@ -1298,7 +1202,6 @@ void manager_gc(Manager *m, bool drop_not_started) { Seat *seat; Session *session; User *user; - Machine *machine; assert(m); @@ -1331,16 +1234,6 @@ 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) { @@ -1459,7 +1352,6 @@ int manager_startup(Manager *m) { Session *session; User *user; Inhibitor *inhibitor; - Machine *machine; Iterator i; assert(m); @@ -1496,7 +1388,6 @@ 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); @@ -1517,9 +1408,6 @@ 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; @@ -1671,7 +1559,6 @@ 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 b7277af73d..f7457c0537 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -41,7 +41,6 @@ typedef struct Manager Manager; #include "logind-inhibit.h" #include "logind-button.h" #include "logind-action.h" -#include "logind-machine.h" struct Manager { DBusConnection *bus; @@ -52,12 +51,10 @@ 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; @@ -85,7 +82,6 @@ struct Manager { Hashmap *session_units; Hashmap *user_units; - Hashmap *machine_units; Hashmap *session_fds; Hashmap *inhibitor_fds; @@ -144,7 +140,6 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, 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); @@ -161,7 +156,6 @@ 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); @@ -173,7 +167,6 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t); int manager_get_user_by_pid(Manager *m, pid_t pid, User **user); int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session); -int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine); extern const DBusObjectPathVTable bus_manager_vtable; diff --git a/src/machine/Makefile b/src/machine/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/machine/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/login/logind-machine-dbus.c b/src/machine/machine-dbus.c index ae8c5d720a..424f98edd5 100644 --- a/src/login/logind-machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -22,12 +22,12 @@ #include <errno.h> #include <string.h> -#include "logind.h" -#include "logind-machine.h" +#include "machined.h" +#include "machine.h" #include "dbus-common.h" #define BUS_MACHINE_INTERFACE \ - " <interface name=\"org.freedesktop.login1.Machine\">\n" \ + " <interface name=\"org.freedesktop.machine1.Machine\">\n" \ " <method name=\"Terminate\"/>\n" \ " <method name=\"Kill\">\n" \ " <arg name=\"who\" type=\"s\"/>\n" \ @@ -56,7 +56,7 @@ #define INTERFACES_LIST \ BUS_GENERIC_INTERFACES_LIST \ - "org.freedesktop.login1.Machine\0" + "org.freedesktop.machine1.Machine\0" static int bus_machine_append_id(DBusMessageIter *i, const char *property, void *data) { DBusMessageIter sub; @@ -106,7 +106,7 @@ static int get_machine_for_path(Manager *m, const char *path, Machine **_machine assert(path); assert(_machine); - if (!startswith(path, "/org/freedesktop/login1/machine/")) + if (!startswith(path, "/org/freedesktop/machine1/machine/")) return -EINVAL; e = bus_path_unescape(path + 32); @@ -123,14 +123,13 @@ static int get_machine_for_path(Manager *m, const char *path, Machine **_machine static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_machine_append_class, machine_class, MachineClass); -static const BusProperty bus_login_machine_properties[] = { +static const BusProperty bus_machine_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) }, { "Service", bus_property_append_string, "s", offsetof(Machine, service), true }, { "Scope", bus_property_append_string, "s", offsetof(Machine, scope), true }, - { "Leader", bus_property_append_pid, "u", offsetof(Session, leader) }, { "Class", bus_machine_append_class, "s", offsetof(Machine, class) }, { "State", bus_machine_append_state, "s", 0 }, { "RootDirectory", bus_property_append_string, "s", offsetof(Machine, root_directory), true }, @@ -150,7 +149,7 @@ static DBusHandlerResult machine_message_dispatch( assert(connection); assert(message); - if (dbus_message_is_method_call(message, "org.freedesktop.login1.Machine", "Terminate")) { + if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Machine", "Terminate")) { r = machine_stop(m); if (r < 0) @@ -160,7 +159,7 @@ static DBusHandlerResult machine_message_dispatch( if (!reply) goto oom; - } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Machine", "Kill")) { + } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Machine", "Kill")) { const char *swho; int32_t signo; KillWho who; @@ -194,7 +193,7 @@ static DBusHandlerResult machine_message_dispatch( } else { const BusBoundProperties bps[] = { - { "org.freedesktop.login1.Machine", bus_login_machine_properties, m }, + { "org.freedesktop.machine1.Machine", bus_machine_machine_properties, m }, { NULL, } }; @@ -256,7 +255,7 @@ char *machine_bus_path(Machine *m) { if (!e) return NULL; - return strappend("/org/freedesktop/login1/machine/", e); + return strappend("/org/freedesktop/machine1/machine/", e); } int machine_send_signal(Machine *m, bool new_machine) { @@ -265,8 +264,8 @@ int machine_send_signal(Machine *m, bool new_machine) { assert(m); - msg = dbus_message_new_signal("/org/freedesktop/login1", - "org.freedesktop.login1.Manager", + msg = dbus_message_new_signal("/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", new_machine ? "MachineNew" : "MachineRemoved"); if (!m) @@ -302,7 +301,7 @@ int machine_send_changed(Machine *m, const char *properties) { if (!p) return -ENOMEM; - msg = bus_properties_changed_new(p, "org.freedesktop.login1.Machine", properties); + msg = bus_properties_changed_new(p, "org.freedesktop.machine1.Machine", properties); if (!msg) return -ENOMEM; diff --git a/src/login/logind-machine.c b/src/machine/machine.c index a1342c155e..7d64abe5dd 100644 --- a/src/login/logind-machine.c +++ b/src/machine/machine.c @@ -27,14 +27,13 @@ #include "util.h" #include "mkdir.h" -#include "cgroup-util.h" #include "hashmap.h" #include "strv.h" #include "fileio.h" #include "special.h" #include "unit-name.h" #include "dbus-common.h" -#include "logind-machine.h" +#include "machine.h" Machine* machine_new(Manager *manager, const char *name) { Machine *m; @@ -229,13 +228,15 @@ static int machine_start_scope(Machine *m) { dbus_error_init(&error); if (!m->scope) { - _cleanup_free_ char *escaped = NULL; + char *escaped = NULL; escaped = unit_name_escape(m->name); if (!escaped) return log_oom(); - m->scope = strjoin("machine-", m->name, ".scope", NULL); + m->scope = strjoin("machine-", escaped, ".scope", NULL); + free(escaped); + if (!m->scope) return log_oom(); @@ -250,11 +251,11 @@ static int machine_start_scope(Machine *m) { if (r < 0) { log_error("Failed to start machine scope: %s", bus_error(&error, r)); dbus_error_free(&error); + } else { + free(m->scope_job); + m->scope_job = job; } - free(m->scope_job); - m->scope_job = job; - return r; } @@ -401,3 +402,10 @@ static const char* const machine_state_table[_MACHINE_STATE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState); + +static const char* const kill_who_table[_KILL_WHO_MAX] = { + [KILL_LEADER] = "leader", + [KILL_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/login/logind-machine.h b/src/machine/machine.h index a09f07195a..7501fa372e 100644 --- a/src/login/logind-machine.h +++ b/src/machine/machine.h @@ -22,11 +22,11 @@ ***/ typedef struct Machine Machine; +typedef enum KillWho KillWho; #include "list.h" #include "util.h" -#include "logind.h" -#include "logind-session.h" +#include "machined.h" typedef enum MachineState { MACHINE_OPENING, /* Machine is being registered */ @@ -43,6 +43,13 @@ typedef enum MachineClass { _MACHINE_CLASS_INVALID = -1 } MachineClass; +enum KillWho { + KILL_LEADER, + KILL_ALL, + _KILL_WHO_MAX, + _KILL_WHO_INVALID = -1 +}; + struct Machine { Manager *manager; @@ -97,3 +104,6 @@ MachineClass machine_class_from_string(const char *s) _pure_; const char* machine_state_to_string(MachineState t) _const_; MachineState machine_state_from_string(const char *s) _pure_; + +const char *kill_who_to_string(KillWho k) _const_; +KillWho kill_who_from_string(const char *s) _pure_; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c new file mode 100644 index 0000000000..5d107f360a --- /dev/null +++ b/src/machine/machinectl.c @@ -0,0 +1,764 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <dbus/dbus.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <pwd.h> +#include <locale.h> + +#include "log.h" +#include "util.h" +#include "macro.h" +#include "pager.h" +#include "dbus-common.h" +#include "build.h" +#include "strv.h" +#include "cgroup-show.h" +#include "spawn-polkit-agent.h" + +static char **arg_property = NULL; +static bool arg_all = false; +static bool arg_full = false; +static bool arg_no_pager = false; +static const char *arg_kill_who = NULL; +static int arg_signal = SIGTERM; +static enum transport { + TRANSPORT_NORMAL, + TRANSPORT_SSH, + TRANSPORT_POLKIT +} arg_transport = TRANSPORT_NORMAL; +static bool arg_ask_password = true; +static char *arg_host = NULL; +static char *arg_user = NULL; + +static void pager_open_if_enabled(void) { + + /* Cache result before we open the pager */ + if (arg_no_pager) + return; + + pager_open(false); +} + +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.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.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 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_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_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); + + if (arg_property && !strv_find(arg_property, name)) + return 0; + + if (generic_print_property(name, iter, arg_all) > 0) + return 0; + + if (arg_all) + printf("%s=[unprintable]\n", name); + + return 0; +} + +static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = ""; + int r; + DBusMessageIter iter, sub, sub2, sub3; + MachineStatusInfo machine_info = {}; + + assert(path); + assert(new_line); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.machine1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + goto finish; + + 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_DICT_ENTRY) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + if (*new_line) + printf("\n"); + + *new_line = true; + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *name; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + if (show_properties) + r = print_property(name, &sub3); + else + r = status_property_machine(name, &sub3, &machine_info); + + if (r < 0) { + log_error("Failed to parse reply."); + goto finish; + } + + dbus_message_iter_next(&sub); + } + + if (!show_properties) + print_machine_status_info(&machine_info); + + r = 0; + +finish: + + return r; +} + +static int show(DBusConnection *bus, char **args, unsigned n) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + int r, ret = 0; + DBusError error; + unsigned i; + bool show_properties, new_line = false; + + assert(bus); + assert(args); + + dbus_error_init(&error); + + show_properties = !strstr(args[0], "status"); + + pager_open_if_enabled(); + + if (show_properties && n <= 1) { + /* If not argument is specified inspect the manager + * itself */ + + ret = show_one(args[0], bus, "/org/freedesktop/machine1", show_properties, &new_line); + goto finish; + } + + for (i = 1; i < n; i++) { + const char *path = NULL; + + ret = bus_method_call_with_reply( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachine", + &reply, + NULL, + DBUS_TYPE_STRING, &args[i], + DBUS_TYPE_INVALID); + if (ret < 0) + goto finish; + + if (!dbus_message_get_args(reply, &error, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply: %s", bus_error_message(&error)); + ret = -EIO; + goto finish; + } + + r = show_one(args[0], bus, path, show_properties, &new_line); + if (r != 0) + ret = r; + } + +finish: + dbus_error_free(&error); + + return ret; +} + +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.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.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 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.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.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" + "Send control commands to or query the virtual machine and container registration manager.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " -p --property=NAME Show only properties by this name\n" + " -a --all Show all properties, including empty ones\n" + " --kill-who=WHO Who to send signal to\n" + " -l --full Do not ellipsize output\n" + " -s --signal=SIGNAL Which signal to send\n" + " --no-ask-password Don't prompt for password\n" + " -H --host=[USER@]HOST Show information for remote host\n" + " -P --privileged Acquire privileges before execution\n" + " --no-pager Do not pipe output into a pager\n\n" + "Commands:\n" + " list List running VMs and containers\n" + " status [NAME...] Show VM/container status\n" + " show[NAME...] Show properties of one or more VMs/containers\n" + " terminate [NAME...] Terminate one or more VMs/containers\n" + " kill [NAME...] Send signal to processes of a VM/container\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_KILL_WHO, + ARG_NO_ASK_PASSWORD, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "property", required_argument, NULL, 'p' }, + { "all", no_argument, NULL, 'a' }, + { "full", no_argument, NULL, 'l' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "kill-who", required_argument, NULL, ARG_KILL_WHO }, + { "signal", required_argument, NULL, 's' }, + { "host", required_argument, NULL, 'H' }, + { "privileged", no_argument, NULL, 'P' }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(SYSTEMD_FEATURES); + return 0; + + case 'p': { + char **l; + + l = strv_append(arg_property, optarg); + if (!l) + return -ENOMEM; + + strv_free(arg_property); + arg_property = l; + + /* If the user asked for a particular + * property, show it to him, even if it is + * empty. */ + arg_all = true; + break; + } + + case 'a': + arg_all = true; + break; + + case 'l': + arg_full = true; + break; + + case ARG_NO_PAGER: + arg_no_pager = true; + break; + + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + + case ARG_KILL_WHO: + arg_kill_who = optarg; + break; + + case 's': + arg_signal = signal_from_string_try_harder(optarg); + if (arg_signal < 0) { + log_error("Failed to parse signal string %s.", optarg); + return -EINVAL; + } + break; + + case 'P': + arg_transport = TRANSPORT_POLKIT; + break; + + case 'H': + arg_transport = TRANSPORT_SSH; + parse_user_at_host(optarg, &arg_user, &arg_host); + break; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +static int machinectl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) { + + static const struct { + const char* verb; + const enum { + MORE, + LESS, + EQUAL + } argc_cmp; + const int argc; + int (* const dispatch)(DBusConnection *bus, char **args, unsigned n); + } verbs[] = { + { "list", LESS, 1, list_machines }, + { "status", MORE, 2, show }, + { "show", MORE, 1, show }, + { "terminate", MORE, 2, terminate_machine }, + { "kill", MORE, 2, kill_machine }, + }; + + int left; + unsigned i; + + assert(argc >= 0); + assert(argv); + assert(error); + + left = argc - optind; + + if (left <= 0) + /* Special rule: no arguments means "list-sessions" */ + i = 0; + else { + if (streq(argv[optind], "help")) { + help(); + return 0; + } + + for (i = 0; i < ELEMENTSOF(verbs); i++) + if (streq(argv[optind], verbs[i].verb)) + break; + + if (i >= ELEMENTSOF(verbs)) { + log_error("Unknown operation %s", argv[optind]); + return -EINVAL; + } + } + + switch (verbs[i].argc_cmp) { + + case EQUAL: + if (left != verbs[i].argc) { + log_error("Invalid number of arguments."); + return -EINVAL; + } + + break; + + case MORE: + if (left < verbs[i].argc) { + log_error("Too few arguments."); + return -EINVAL; + } + + break; + + case LESS: + if (left > verbs[i].argc) { + log_error("Too many arguments."); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Unknown comparison operator."); + } + + if (!bus) { + log_error("Failed to get D-Bus connection: %s", error->message); + return -EIO; + } + + return verbs[i].dispatch(bus, argv + optind, left); +} + +int main(int argc, char*argv[]) { + int r, retval = EXIT_FAILURE; + DBusConnection *bus = NULL; + DBusError error; + + dbus_error_init(&error); + + setlocale(LC_ALL, ""); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r < 0) + goto finish; + else if (r == 0) { + retval = EXIT_SUCCESS; + goto finish; + } + + if (arg_transport == TRANSPORT_NORMAL) + bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + else if (arg_transport == TRANSPORT_POLKIT) + bus_connect_system_polkit(&bus, &error); + else if (arg_transport == TRANSPORT_SSH) + bus_connect_system_ssh(NULL, arg_host, &bus, &error); + else + assert_not_reached("Uh, invalid transport..."); + + r = machinectl_main(bus, argc, argv, &error); + retval = r < 0 ? EXIT_FAILURE : r; + +finish: + if (bus) { + dbus_connection_flush(bus); + dbus_connection_close(bus); + dbus_connection_unref(bus); + } + + dbus_error_free(&error); + dbus_shutdown(); + + strv_free(arg_property); + + pager_close(); + + return retval; +} diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c new file mode 100644 index 0000000000..a2e00d7102 --- /dev/null +++ b/src/machine/machined-dbus.c @@ -0,0 +1,824 @@ +/*-*- 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 <unistd.h> +#include <pwd.h> + +#include <systemd/sd-id128.h> +#include <systemd/sd-messages.h> + +#include "machined.h" +#include "dbus-common.h" +#include "strv.h" +#include "mkdir.h" +#include "path-util.h" +#include "special.h" +#include "sleep-config.h" +#include "fileio-label.h" +#include "label.h" +#include "utf8.h" +#include "unit-name.h" +#include "bus-errors.h" +#include "virt.h" + +#define BUS_MANAGER_INTERFACE \ + " <interface name=\"org.freedesktop.machine1.Manager\">\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=\"ListMachines\">\n" \ + " <arg name=\"machines\" type=\"a(ssso)\" direction=\"out\"/>\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=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \ + " <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \ + " </method>\n" \ + " <method name=\"KillMachine\">\n" \ + " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \ + " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\n" \ + " <method name=\"TerminateMachine\">\n" \ + " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \ + " </method>\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" \ + " </interface>\n" + +#define INTROSPECTION_BEGIN \ + DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ + "<node>\n" \ + BUS_MANAGER_INTERFACE \ + BUS_PROPERTIES_INTERFACE \ + BUS_PEER_INTERFACE \ + BUS_INTROSPECTABLE_INTERFACE + +#define INTROSPECTION_END \ + "</node>\n" + +#define INTERFACES_LIST \ + BUS_GENERIC_INTERFACES_LIST \ + "org.freedesktop.machine1.Manager\0" + +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) { + + const char *name, *service, *class, *slice, *root_directory; + _cleanup_free_ char *p = NULL; + DBusMessageIter iter, sub; + MachineClass c; + uint32_t leader; + sd_id128_t id; + Machine *m; + int n, r; + void *v; + + assert(manager); + assert(message); + + 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(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; + + m->create_message = dbus_message_ref(message); + + return 0; + +fail: + if (m) + machine_add_to_gc_queue(m); + + return r; +} + +static DBusHandlerResult manager_message_handler( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + Manager *m = userdata; + + DBusError error; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + int r; + + assert(connection); + assert(message); + assert(m); + + dbus_error_init(&error); + + if (dbus_message_is_method_call(message, "org.freedesktop.machine1.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.machine1.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.machine1.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.machine1.Manager", "CreateMachine")) { + + r = bus_manager_create_machine(m, message); + if (r < 0) + return bus_send_error_reply(connection, message, NULL, r); + + } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.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.machine1.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.DBus.Introspectable", "Introspect")) { + char *introspection = NULL; + FILE *f; + Iterator i; + Machine *machine; + size_t size; + char *p; + + reply = dbus_message_new_method_return(message); + if (!reply) + goto oom; + + /* We roll our own introspection code here, instead of + * relying on bus_default_message_handler() because we + * need to generate our introspection string + * dynamically. */ + + f = open_memstream(&introspection, &size); + if (!f) + goto oom; + + fputs(INTROSPECTION_BEGIN, f); + + HASHMAP_FOREACH(machine, m->machines, i) { + p = bus_path_escape(machine->name); + + if (p) { + fprintf(f, "<node name=\"machine/%s\"/>", p); + free(p); + } + } + + fputs(INTROSPECTION_END, f); + + if (ferror(f)) { + fclose(f); + free(introspection); + goto oom; + } + + fclose(f); + + if (!introspection) + goto oom; + + if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) { + free(introspection); + goto oom; + } + + free(introspection); + } else + return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL); + + 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; +} + +const DBusObjectPathVTable bus_manager_vtable = { + .message_function = manager_message_handler +}; + +DBusHandlerResult bus_message_filter( + DBusConnection *connection, + DBusMessage *message, + void *userdata) { + + Manager *m = userdata; + DBusError error; + + assert(m); + assert(connection); + assert(message); + + dbus_error_init(&error); + + log_debug("Got message: %s %s %s", strna(dbus_message_get_sender(message)), strna(dbus_message_get_interface(message)), strna(dbus_message_get_member(message))); + + if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) { + const char *path, *result, *unit; + Machine *mm; + uint32_t id; + + if (!dbus_message_get_args(message, &error, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_STRING, &unit, + DBUS_TYPE_STRING, &result, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error)); + goto finish; + } + + + mm = hashmap_get(m->machine_units, unit); + if (mm) { + if (streq_ptr(path, mm->scope_job)) { + free(mm->scope_job); + mm->scope_job = NULL; + + if (mm->started) { + if (streq(result, "done")) + machine_send_create_reply(mm, NULL); + else { + dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result); + machine_send_create_reply(mm, &error); + } + } + } + + machine_add_to_gc_queue(mm); + } + + } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) { + + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *unit = NULL; + const char *path; + + path = dbus_message_get_path(message); + if (!path) + goto finish; + + unit_name_from_dbus_path(path, &unit); + if (unit) { + Machine *mm; + + mm = hashmap_get(m->machine_units, unit); + if (mm) + machine_add_to_gc_queue(mm); + } + } + +finish: + dbus_error_free(&error); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +int manager_start_scope( + Manager *manager, + const char *scope, + pid_t pid, + const char *slice, + const char *description, + DBusError *error, + char **job) { + + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + DBusMessageIter iter, sub, sub2, sub3, sub4; + const char *timeout_stop_property = "TimeoutStopUSec"; + const char *pids_property = "PIDs"; + uint64_t timeout = 500 * USEC_PER_MSEC; + const char *fail = "fail"; + uint32_t u; + + assert(manager); + assert(scope); + assert(pid > 1); + + if (!slice) + slice = ""; + + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (!m) + return log_oom(); + + dbus_message_iter_init_append(m, &iter); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) || + !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub)) + return log_oom(); + + if (!isempty(slice)) { + const char *slice_property = "Slice"; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) || + !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) || + !dbus_message_iter_close_container(&sub2, &sub3) || + !dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + } + + if (!isempty(description)) { + const char *description_property = "Description"; + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) || + !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) || + !dbus_message_iter_close_container(&sub2, &sub3) || + !dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + } + + /* cgroup empty notification is not available in containers + * currently. To make this less problematic, let's shorten the + * stop timeout for sessions, so that we don't wait + * forever. */ + + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) || + !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) || + !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) || + !dbus_message_iter_close_container(&sub2, &sub3) || + !dbus_message_iter_close_container(&sub, &sub2)) + return log_oom(); + + u = pid; + if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) || + !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) || + !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) || + !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) || + !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) || + !dbus_message_iter_close_container(&sub3, &sub4) || + !dbus_message_iter_close_container(&sub2, &sub3) || + !dbus_message_iter_close_container(&sub, &sub2) || + !dbus_message_iter_close_container(&iter, &sub)) + return log_oom(); + + reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error); + if (!reply) + return -EIO; + + if (job) { + const char *j; + char *copy; + + if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID)) + return -EIO; + + copy = strdup(j); + if (!copy) + return -ENOMEM; + + *job = copy; + } + + return 0; +} + +int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *fail = "fail"; + int r; + + assert(manager); + assert(unit); + + r = bus_method_call_with_reply( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit", + &reply, + error, + DBUS_TYPE_STRING, &unit, + DBUS_TYPE_STRING, &fail, + DBUS_TYPE_INVALID); + if (r < 0) { + log_error("Failed to stop unit %s: %s", unit, bus_error(error, r)); + return r; + } + + if (job) { + const char *j; + char *copy; + + if (!dbus_message_get_args(reply, error, + DBUS_TYPE_OBJECT_PATH, &j, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply."); + return -EIO; + } + + copy = strdup(j); + if (!copy) + return -ENOMEM; + + *job = copy; + } + + return 0; +} + +int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *w; + int r; + + assert(manager); + assert(unit); + + w = who == KILL_LEADER ? "process" : "cgroup"; + assert_cc(sizeof(signo) == sizeof(int32_t)); + + r = bus_method_call_with_reply( + manager->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + &reply, + error, + DBUS_TYPE_STRING, &unit, + DBUS_TYPE_STRING, &w, + DBUS_TYPE_INT32, &signo, + DBUS_TYPE_INVALID); + if (r < 0) { + log_error("Failed to stop unit %s: %s", unit, bus_error(error, r)); + return r; + } + + return 0; +} + +int manager_unit_is_active(Manager *manager, const char *unit) { + + const char *interface = "org.freedesktop.systemd1.Unit"; + const char *property = "ActiveState"; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + _cleanup_free_ char *path = NULL; + DBusMessageIter iter, sub; + const char *state; + DBusError error; + int r; + + assert(manager); + assert(unit); + + dbus_error_init(&error); + + path = unit_dbus_path_from_name(unit); + if (!path) + return -ENOMEM; + + r = bus_method_call_with_reply( + manager->bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + &error, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + + if (r < 0) { + log_error("Failed to query ActiveState: %s", bus_error(&error, r)); + dbus_error_free(&error); + return r; + } + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EINVAL; + } + + dbus_message_iter_recurse(&iter, &sub); + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + log_error("Failed to parse reply."); + return -EINVAL; + } + + dbus_message_iter_get_basic(&sub, &state); + + return !streq(state, "inactive") && !streq(state, "failed"); +} diff --git a/src/machine/machined.c b/src/machine/machined.c new file mode 100644 index 0000000000..3cf7f92f96 --- /dev/null +++ b/src/machine/machined.c @@ -0,0 +1,408 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <pwd.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <sys/epoll.h> + +#include <systemd/sd-daemon.h> + +#include "machined.h" +#include "dbus-common.h" +#include "dbus-loop.h" +#include "strv.h" +#include "conf-parser.h" +#include "mkdir.h" +#include "cgroup-util.h" + +Manager *manager_new(void) { + Manager *m; + + m = new0(Manager, 1); + if (!m) + return NULL; + + m->bus_fd = -1; + m->epoll_fd = -1; + + m->machines = hashmap_new(string_hash_func, string_compare_func); + m->machine_units = hashmap_new(string_hash_func, string_compare_func); + + if (!m->machines || !m->machine_units) { + manager_free(m); + return NULL; + } + + return m; +} + +void manager_free(Manager *m) { + Machine *machine; + + assert(m); + + while ((machine = hashmap_first(m->machines))) + machine_free(machine); + + hashmap_free(m->machines); + hashmap_free(m->machine_units); + + if (m->bus) { + dbus_connection_flush(m->bus); + dbus_connection_close(m->bus); + dbus_connection_unref(m->bus); + } + + if (m->bus_fd >= 0) + close_nointr_nofail(m->bus_fd); + + if (m->epoll_fd >= 0) + close_nointr_nofail(m->epoll_fd); + + free(m); +} + +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_enumerate_machines(Manager *m) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; + + assert(m); + + /* 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/machines: %m"); + return -errno; + } + + FOREACH_DIRENT(de, d, return -errno) { + struct Machine *machine; + int k; + + if (!dirent_is_file(de)) + continue; + + k = manager_add_machine(m, de->d_name, &machine); + if (k < 0) { + log_error("Failed to add machine by file name %s: %s", de->d_name, strerror(-k)); + + r = k; + continue; + } + + machine_add_to_gc_queue(machine); + + k = machine_load(machine); + if (k < 0) + r = k; + } + + return r; +} + +int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { + _cleanup_free_ char *unit = NULL; + Machine *mm; + int r; + + assert(m); + assert(pid >= 1); + assert(machine); + + r = cg_pid_get_unit(pid, &unit); + if (r < 0) + return r; + + mm = hashmap_get(m->machine_units, unit); + if (!mm) + return 0; + + *machine = mm; + return 1; +} + +static int manager_connect_bus(Manager *m) { + DBusError error; + int r; + struct epoll_event ev = { + .events = EPOLLIN, + .data.u32 = FD_BUS, + }; + + assert(m); + assert(!m->bus); + assert(m->bus_fd < 0); + + dbus_error_init(&error); + + m->bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error); + if (!m->bus) { + log_error("Failed to get system D-Bus connection: %s", bus_error_message(&error)); + r = -ECONNREFUSED; + goto fail; + } + + if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/machine1", &bus_manager_vtable, m) || + !dbus_connection_register_fallback(m->bus, "/org/freedesktop/machine1/machine", &bus_machine_vtable, m) || + !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) { + r = log_oom(); + goto fail; + } + + dbus_bus_add_match(m->bus, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'", + &error); + if (dbus_error_is_set(&error)) { + log_error("Failed to add match for JobRemoved: %s", bus_error_message(&error)); + dbus_error_free(&error); + } + + dbus_bus_add_match(m->bus, + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.DBus.Properties'," + "member='PropertiesChanged'", + &error); + if (dbus_error_is_set(&error)) { + log_error("Failed to add match for PropertiesChanged: %s", bus_error_message(&error)); + dbus_error_free(&error); + } + + r = bus_method_call_with_reply( + m->bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Subscribe", + NULL, + &error, + DBUS_TYPE_INVALID); + if (r < 0) { + log_error("Failed to enable subscription: %s", bus_error(&error, r)); + dbus_error_free(&error); + } + + r = dbus_bus_request_name(m->bus, "org.freedesktop.machine1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error); + if (dbus_error_is_set(&error)) { + log_error("Failed to register name on bus: %s", bus_error_message(&error)); + r = -EIO; + goto fail; + } + + if (r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + log_error("Failed to acquire name."); + r = -EEXIST; + goto fail; + } + + m->bus_fd = bus_loop_open(m->bus); + if (m->bus_fd < 0) { + r = m->bus_fd; + goto fail; + } + + if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0) + goto fail; + + return 0; + +fail: + dbus_error_free(&error); + + return r; +} + +void manager_gc(Manager *m, bool drop_not_started) { + Machine *machine; + + assert(m); + + 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_startup(Manager *m) { + int r; + Machine *machine; + Iterator i; + + assert(m); + assert(m->epoll_fd <= 0); + + m->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (m->epoll_fd < 0) + return -errno; + + /* Connect to the bus */ + r = manager_connect_bus(m); + if (r < 0) + return r; + + /* Deserialize state */ + manager_enumerate_machines(m); + + /* Remove stale objects before we start them */ + manager_gc(m, false); + + /* And start everything */ + HASHMAP_FOREACH(machine, m->machines, i) + machine_start(machine); + + return 0; +} + +int manager_run(Manager *m) { + assert(m); + + for (;;) { + struct epoll_event event; + int n; + + manager_gc(m, true); + + if (dbus_connection_dispatch(m->bus) != DBUS_DISPATCH_COMPLETE) + continue; + + manager_gc(m, true); + + n = epoll_wait(m->epoll_fd, &event, 1, -1); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + + log_error("epoll() failed: %m"); + return -errno; + } + + if (n == 0) + continue; + + switch (event.data.u32) { + + case FD_BUS: + bus_loop_dispatch(m->bus_fd); + break; + + default: + assert_not_reached("Unknown fd"); + } + } + + return 0; +} + +int main(int argc, char *argv[]) { + Manager *m = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_set_facility(LOG_AUTH); + log_parse_environment(); + log_open(); + + umask(0022); + + if (argc != 1) { + log_error("This program takes no arguments."); + r = -EINVAL; + goto finish; + } + + /* Always create the directories people can create inotify + * watches in. Note that some applications might check for the + * existence of /run/systemd/seats/ to determine whether + * machined is available, so please always make sure this check + * stays in. */ + mkdir_label("/run/systemd/machines", 0755); + + m = manager_new(); + if (!m) { + r = log_oom(); + goto finish; + } + + r = manager_startup(m); + if (r < 0) { + log_error("Failed to fully start up daemon: %s", strerror(-r)); + goto finish; + } + + log_debug("systemd-machined running as pid %lu", (unsigned long) getpid()); + + sd_notify(false, + "READY=1\n" + "STATUS=Processing requests..."); + + r = manager_run(m); + + log_debug("systemd-machined stopped as pid %lu", (unsigned long) getpid()); + +finish: + sd_notify(false, + "STATUS=Shutting down..."); + + if (m) + manager_free(m); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/machine/machined.h b/src/machine/machined.h new file mode 100644 index 0000000000..32a15fe1ab --- /dev/null +++ b/src/machine/machined.h @@ -0,0 +1,73 @@ +/*-*- 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/>. +***/ + +#include <stdbool.h> +#include <inttypes.h> +#include <dbus/dbus.h> + +#include "util.h" +#include "list.h" +#include "hashmap.h" + +typedef struct Manager Manager; + +#include "machine.h" + +struct Manager { + DBusConnection *bus; + + int bus_fd; + int epoll_fd; + + Hashmap *machines; + Hashmap *machine_units; + + LIST_HEAD(Machine, machine_gc_queue); +}; + +enum { + FD_BUS +}; + +Manager *manager_new(void); +void manager_free(Manager *m); + +int manager_add_machine(Manager *m, const char *name, Machine **_machine); + +int manager_enumerate_machines(Manager *m); + +int manager_startup(Manager *m); +int manager_run(Manager *m); + +void manager_gc(Manager *m, bool drop_not_started); + +int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine); + +extern const DBusObjectPathVTable bus_manager_vtable; + +DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, void *userdata); + +int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, DBusError *error, char **job); +int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job); +int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error); +int manager_unit_is_active(Manager *manager, const char *unit); diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf new file mode 100644 index 0000000000..2aad42019f --- /dev/null +++ b/src/machine/org.freedesktop.machine1.conf @@ -0,0 +1,46 @@ +<?xml version="1.0"?> <!--*-nxml-*--> +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> + +<!-- + This file is part of systemd. + + 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. +--> + +<busconfig> + + <policy user="root"> + <allow own="org.freedesktop.machine1"/> + <allow send_destination="org.freedesktop.machine1"/> + <allow receive_sender="org.freedesktop.machine1"/> + </policy> + + <policy context="default"> + <deny send_destination="org.freedesktop.machine1"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.DBus.Introspectable"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.DBus.Peer"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.DBus.Properties" + send_member="Get"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.DBus.Properties" + send_member="GetAll"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" + send_member="ListMachines"/> + + <allow receive_sender="org.freedesktop.machine1"/> + </policy> + +</busconfig> diff --git a/src/machine/org.freedesktop.machine1.service b/src/machine/org.freedesktop.machine1.service new file mode 100644 index 0000000000..d3dc99852b --- /dev/null +++ b/src/machine/org.freedesktop.machine1.service @@ -0,0 +1,12 @@ +# This file is part of systemd. +# +# 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. + +[D-BUS Service] +Name=org.freedesktop.machine1 +Exec=/bin/false +User=root +SystemdService=dbus-org.freedesktop.machine1.service diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 66c2228ca2..913e73673a 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1162,9 +1162,9 @@ static int register_machine(void) { r = sd_bus_call_method( bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", "CreateMachine", &error, NULL, |