X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=876e782e7dc83b886df0ebde27a8b26633f73d5c;hp=66f4113eb223c91c7e05848778f56245f3e92ba7;hb=b08121d00467874ac9aa0dc6f59787877f1bb427;hpb=07719a21b6425d378b36bb8d7f47ad5ec5296d28 diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 66f4113eb..876e782e7 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -63,8 +64,10 @@ #include "install.h" #include "logs-show.h" #include "path-util.h" +#include "socket-util.h" static const char *arg_type = NULL; +static const char *arg_load_state = NULL; static char **arg_property = NULL; static bool arg_all = false; static const char *arg_job_mode = "replace"; @@ -73,7 +76,6 @@ static bool arg_no_block = false; static bool arg_no_legend = false; static bool arg_no_pager = false; static bool arg_no_wtmp = false; -static bool arg_no_sync = false; static bool arg_no_wall = false; static bool arg_no_reload = false; static bool arg_dry = false; @@ -85,7 +87,6 @@ static bool arg_failed = false; static bool arg_runtime = false; static char **arg_wall = NULL; static const char *arg_kill_who = NULL; -static const char *arg_kill_mode = NULL; static int arg_signal = SIGTERM; static const char *arg_root = NULL; static usec_t arg_when = 0; @@ -99,6 +100,7 @@ static enum action { ACTION_EXIT, ACTION_SUSPEND, ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP, ACTION_RUNLEVEL2, ACTION_RUNLEVEL3, ACTION_RUNLEVEL4, @@ -123,7 +125,6 @@ static enum transport { TRANSPORT_POLKIT } arg_transport = TRANSPORT_NORMAL; static const char *arg_host = NULL; -static bool arg_follow = false; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; @@ -132,26 +133,8 @@ static bool private_bus = false; static int daemon_reload(DBusConnection *bus, char **args); static void halt_now(enum action a); -static bool on_tty(void) { - static int t = -1; - - /* Note that this is invoked relatively early, before we start - * the pager. That means the value we return reflects whether - * we originally were started on a tty, not if we currently - * are. But this is intended, since we want colour and so on - * when run in our own pager. */ - - if (_unlikely_(t < 0)) - t = isatty(STDOUT_FILENO) > 0; - - return t; -} - static void pager_open_if_enabled(void) { - /* Cache result before we open the pager */ - on_tty(); - if (arg_no_pager) return; @@ -171,6 +154,7 @@ static void ask_password_agent_open_if_enabled(void) { ask_password_agent_open(); } +#ifdef HAVE_LOGIND static void polkit_agent_open_if_enabled(void) { /* Open the polkit agent as a child process if necessary */ @@ -183,36 +167,30 @@ static void polkit_agent_open_if_enabled(void) { polkit_agent_open(); } +#endif -static const char *ansi_highlight_red(bool b) { +static const char *ansi_highlight(bool b) { if (!on_tty()) return ""; - return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF; + return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF; } -static const char *ansi_highlight_green(bool b) { +static const char *ansi_highlight_red(bool b) { if (!on_tty()) return ""; - return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF; + return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF; } -static bool error_is_no_service(const DBusError *error) { - assert(error); - - if (!dbus_error_is_set(error)) - return false; - - if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER)) - return true; +static const char *ansi_highlight_green(bool b) { - if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN)) - return true; + if (!on_tty()) + return ""; - return startswith(error->name, "org.freedesktop.DBus.Error.Spawn."); + return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF; } static int translate_bus_error_to_exit_status(int r, const DBusError *error) { @@ -245,12 +223,13 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) { static void warn_wall(enum action a) { static const char *table[_ACTION_MAX] = { - [ACTION_HALT] = "The system is going down for system halt NOW!", - [ACTION_REBOOT] = "The system is going down for reboot NOW!", - [ACTION_POWEROFF] = "The system is going down for power-off NOW!", - [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!", - [ACTION_RESCUE] = "The system is going down to rescue mode NOW!", - [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!" + [ACTION_HALT] = "The system is going down for system halt NOW!", + [ACTION_REBOOT] = "The system is going down for reboot NOW!", + [ACTION_POWEROFF] = "The system is going down for power-off NOW!", + [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!", + [ACTION_RESCUE] = "The system is going down to rescue mode NOW!", + [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!", + [ACTION_CANCEL_SHUTDOWN] = "The system shutdown has been cancelled NOW!" }; if (arg_no_wall) @@ -335,12 +314,15 @@ static bool output_show_unit(const struct unit_info *u) { return (!arg_type || ((dot = strrchr(u->id, '.')) && streq(dot+1, arg_type))) && - (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0); + (!arg_load_state || streq(u->load_state, arg_load_state)) && + (arg_all || !(streq(u->active_state, "inactive") + || u->following[0]) || u->job_id > 0); } static void output_units_list(const struct unit_info *unit_infos, unsigned c) { unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0; const struct unit_info *u; + int job_count = 0; max_id_len = sizeof("UNIT")-1; active_len = sizeof("ACTIVE")-1; @@ -355,14 +337,18 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { max_id_len = MAX(max_id_len, strlen(u->id)); active_len = MAX(active_len, strlen(u->active_state)); sub_len = MAX(sub_len, strlen(u->sub_state)); - if (u->job_id != 0) + if (u->job_id != 0) { job_len = MAX(job_len, strlen(u->job_type)); + job_count++; + } } if (!arg_full) { unsigned basic_len; id_len = MIN(max_id_len, 25); - basic_len = 5 + id_len + 6 + active_len + sub_len + job_len; + basic_len = 5 + id_len + 5 + active_len + sub_len; + if (job_count) + basic_len += job_len + 1; if (basic_len < (unsigned) columns()) { unsigned extra_len, incr; extra_len = columns() - basic_len; @@ -382,15 +368,6 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { } else id_len = max_id_len; - if (!arg_no_legend) { - printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD", - active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB"); - if (!arg_full && arg_no_pager) - printf("%.*s\n", desc_len, "DESCRIPTION"); - else - printf("%s\n", "DESCRIPTION"); - } - for (u = unit_infos; u < unit_infos + c; u++) { char *e; const char *on_loaded, *off_loaded; @@ -399,6 +376,17 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { if (!output_show_unit(u)) continue; + if (!n_shown && !arg_no_legend) { + printf("%-*s %-6s %-*s %-*s ", id_len, "UNIT", "LOAD", + active_len, "ACTIVE", sub_len, "SUB"); + if (job_count) + printf("%-*s ", job_len, "JOB"); + if (!arg_full && arg_no_pager) + printf("%.*s\n", desc_len, "DESCRIPTION"); + else + printf("%s\n", "DESCRIPTION"); + } + n_shown++; if (streq(u->load_state, "error")) { @@ -415,12 +403,12 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { e = arg_full ? NULL : ellipsize(u->id, id_len, 33); - printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ", + printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s", id_len, e ? e : u->id, on_loaded, u->load_state, off_loaded, on_active, active_len, u->active_state, sub_len, u->sub_state, off_active, - job_len, u->job_id ? u->job_type : ""); + job_count ? job_len + 1 : 0, u->job_id ? u->job_type : ""); if (!arg_full && arg_no_pager) printf("%.*s\n", desc_len, u->description); else @@ -430,46 +418,53 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { } if (!arg_no_legend) { - printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n" - "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n" - "SUB = The low-level unit activation state, values depend on unit type.\n" - "JOB = Pending job for the unit.\n"); + const char *on, *off; + + if (n_shown) { + printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n" + "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n" + "SUB = The low-level unit activation state, values depend on unit type.\n"); + if (job_count) + printf("JOB = Pending job for the unit.\n"); + puts(""); + on = ansi_highlight(true); + off = ansi_highlight(false); + } else { + on = ansi_highlight_red(true); + off = ansi_highlight_red(false); + } if (arg_all) - printf("\n%u units listed.\n", n_shown); + printf("%s%u loaded units listed.%s\n" + "To show all installed unit files use 'systemctl list-unit-files'.\n", + on, n_shown, off); else - printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown); + printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n" + "To show all installed unit files use 'systemctl list-unit-files'.\n", + on, n_shown, off); } } static int list_units(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; + DBusMessage *reply = NULL; int r; DBusMessageIter iter, sub, sub2; unsigned c = 0, n_units = 0; struct unit_info *unit_infos = NULL; - dbus_error_init(&error); - - assert(bus); - pager_open_if_enabled(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || @@ -533,19 +528,12 @@ static int list_units(DBusConnection *bus, char **args) { output_units_list(unit_infos, c); } - r = 0; - finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); free(unit_infos); - dbus_error_free(&error); - return r; } @@ -611,7 +599,8 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { if (u->state == UNIT_FILE_MASKED || u->state == UNIT_FILE_MASKED_RUNTIME || - u->state == UNIT_FILE_DISABLED) { + u->state == UNIT_FILE_DISABLED || + u->state == UNIT_FILE_INVALID) { on = ansi_highlight_red(true); off = ansi_highlight_red(false); } else if (u->state == UNIT_FILE_ENABLED) { @@ -636,15 +625,12 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { } static int list_unit_files(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; + DBusMessage *reply = NULL; int r; DBusMessageIter iter, sub, sub2; unsigned c = 0, n_units = 0; UnitFileList *units = NULL; - dbus_error_init(&error); - pager_open_if_enabled(); if (avoid_bus()) { @@ -653,10 +639,8 @@ static int list_unit_files(DBusConnection *bus, char **args) { Iterator i; h = hashmap_new(string_hash_func, string_compare_func); - if (!h) { - log_error("Out of memory"); - return -ENOMEM; - } + if (!h) + return log_oom(); r = unit_file_get_list(arg_scope, arg_root, h); if (r < 0) { @@ -669,8 +653,7 @@ static int list_unit_files(DBusConnection *bus, char **args) { units = new(UnitFileList, n_units); if (!units) { unit_file_list_free(h); - log_error("Out of memory"); - return -ENOMEM; + return log_oom(); } HASHMAP_FOREACH(u, h, i) { @@ -680,24 +663,17 @@ static int list_unit_files(DBusConnection *bus, char **args) { hashmap_free(h); } else { - assert(bus); - - m = dbus_message_new_method_call( + r = bus_method_call_with_reply ( + bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "ListUnitFiles"); - if (!m) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + "ListUnitFiles", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || @@ -760,16 +736,11 @@ static int list_unit_files(DBusConnection *bus, char **args) { r = 0; finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); free(units); - dbus_error_free(&error); - return r; } @@ -779,7 +750,7 @@ static int dot_one_property(const char *name, const char *prop, DBusMessageIter "RequiresOverridable", "[color=\"black\"]", "Requisite", "[color=\"darkblue\"]", "RequisiteOverridable", "[color=\"darkblue\"]", - "Wants", "[color=\"darkgrey\"]", + "Wants", "[color=\"grey66\"]", "Conflicts", "[color=\"red\"]", "ConflictedBy", "[color=\"red\"]", "After", "[color=\"green\"]" @@ -832,40 +803,25 @@ static int dot_one_property(const char *name, const char *prop, DBusMessageIter } static int dot_one(DBusConnection *bus, const char *name, const char *path) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *reply = NULL; const char *interface = "org.freedesktop.systemd1.Unit"; int r; - DBusError error; DBusMessageIter iter, sub, sub2, sub3; - assert(bus); assert(path); - dbus_error_init(&error); - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || @@ -911,44 +867,29 @@ static int dot_one(DBusConnection *bus, const char *name, const char *path) { dbus_message_iter_next(&sub); } - r = 0; - finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); - dbus_error_free(&error); - return r; } static int dot(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; + DBusMessage *reply = NULL; int r; DBusMessageIter iter, sub, sub2; - dbus_error_init(&error); - - assert(bus); - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || @@ -1006,44 +947,31 @@ static int dot(DBusConnection *bus, char **args) { r = 0; finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); - dbus_error_free(&error); - return r; } static int list_jobs(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; + DBusMessage *reply = NULL; int r; DBusMessageIter iter, sub, sub2; unsigned k = 0; - dbus_error_init(&error); - - assert(bus); - pager_open_if_enabled(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListJobs"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListJobs", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || @@ -1097,81 +1025,44 @@ static int list_jobs(DBusConnection *bus, char **args) { r = 0; finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); - dbus_error_free(&error); - return r; } static int load_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL; - DBusError error; - int r; - char **name; - - dbus_error_init(&error); + int r = 0; + char **name, *n; - assert(bus); assert(args); STRV_FOREACH(name, args+1) { - DBusMessage *reply; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "LoadUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + n = unit_name_mangle(*name); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LoadUnit", + NULL, + NULL, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_INVALID); + free(n); + if (r) goto finish; - } - - dbus_message_unref(m); - dbus_message_unref(reply); - - m = reply = NULL; } - r = 0; - finish: - if (m) - dbus_message_unref(m); - - dbus_error_free(&error); - return r; } static int cancel_job(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; - int r; + DBusMessage *reply = NULL; + int r = 0; char **name; - dbus_error_init(&error); - - assert(bus); assert(args); if (strv_length(args) <= 1) @@ -1181,105 +1072,79 @@ static int cancel_job(DBusConnection *bus, char **args) { unsigned id; const char *path; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetJob"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if ((r = safe_atou(*name, &id)) < 0) { + r = safe_atou(*name, &id); + if (r < 0) { log_error("Failed to parse job id: %s", strerror(-r)); goto finish; } - assert_cc(sizeof(uint32_t) == sizeof(id)); - if (!dbus_message_append_args(m, - DBUS_TYPE_UINT32, &id, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetJob", + &reply, + NULL, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_INVALID); + if (r) goto finish; - } - if (!dbus_message_get_args(reply, &error, + if (!dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { - log_error("Failed to parse reply: %s", bus_error_message(&error)); + log_error("Failed to parse reply"); + dbus_message_unref(reply); r = -EIO; goto finish; } - - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Job", - "Cancel"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - dbus_message_unref(m); - dbus_message_unref(reply); - m = reply = NULL; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Job", + "Cancel", + NULL, + NULL, + DBUS_TYPE_INVALID); + if (r) + goto finish; } - r = 0; - finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - return r; } static bool need_daemon_reload(DBusConnection *bus, const char *unit) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *reply = NULL; dbus_bool_t b = FALSE; DBusMessageIter iter, sub; const char *interface = "org.freedesktop.systemd1.Unit", *property = "NeedDaemonReload", *path; + char *n; + int r; /* We ignore all errors here, since this is used to show a warning only */ - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"))) - goto finish; - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &unit, - DBUS_TYPE_INVALID)) - goto finish; - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL))) + n = unit_name_mangle(unit); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit", + &reply, + NULL, + DBUS_TYPE_STRING, n ? (const char**) &n : &unit, + DBUS_TYPE_INVALID); + free(n); + if (r) goto finish; if (!dbus_message_get_args(reply, NULL, @@ -1287,23 +1152,19 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) { DBUS_TYPE_INVALID)) goto finish; - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "Get"))) - goto finish; - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - goto finish; - } - - dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL))) + dbus_message_unref(reply); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r) goto finish; if (!dbus_message_iter_init(reply, &iter) || @@ -1318,9 +1179,6 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) { dbus_message_iter_get_basic(&sub, &b); finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); @@ -1329,6 +1187,8 @@ finish: typedef struct WaitData { Set *set; + + char *name; char *result; } WaitData; @@ -1367,9 +1227,12 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me p = set_remove(d->set, (char*) path); free(p); - if (*result) + if (!isempty(result)) d->result = strdup(result); + if (!isempty(unit)) + d->name = strdup(unit); + goto finish; } #ifndef LEGACY @@ -1451,7 +1314,7 @@ static int enable_wait_for_jobs(DBusConnection *bus) { } static int wait_for_jobs(DBusConnection *bus, Set *s) { - int r; + int r = 0; WaitData d; assert(bus); @@ -1460,44 +1323,209 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) { zero(d); d.set = s; - if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) { - log_error("Failed to add filter."); - r = -ENOMEM; + if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) + return log_oom(); + + while (!set_isempty(s)) { + + if (!dbus_connection_read_write_dispatch(bus, -1)) { + log_error("Disconnected from bus."); + return -ECONNREFUSED; + } + + if (!d.result) + goto free_name; + + if (!arg_quiet) { + if (streq(d.result, "timeout")) + log_error("Job for %s timed out.", strna(d.name)); + else if (streq(d.result, "canceled")) + log_error("Job for %s canceled.", strna(d.name)); + else if (streq(d.result, "dependency")) + log_error("A dependency job for %s failed. See 'journalctl -xn' for details.", strna(d.name)); + else if (!streq(d.result, "done") && !streq(d.result, "skipped")) + log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d.name), strna(d.name)); + } + + if (streq_ptr(d.result, "timeout")) + r = -ETIME; + else if (streq_ptr(d.result, "canceled")) + r = -ECANCELED; + else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped")) + r = -EIO; + + free(d.result); + d.result = NULL; + + free_name: + free(d.name); + d.name = NULL; + } + + dbus_connection_remove_filter(bus, wait_filter, &d); + return r; +} + +static int check_one_unit(DBusConnection *bus, char *name, bool quiet) { + DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + const char + *interface = "org.freedesktop.systemd1.Unit", + *property = "ActiveState"; + const char *path = NULL; + const char *state; + int r; + char *n; + + assert(name); + + n = unit_name_mangle(name); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit", + &reply, + NULL, + DBUS_TYPE_STRING, n ? &n : &name, + DBUS_TYPE_INVALID); + free(n); + if (r) { + if ((r != -ENOMEM) && (!quiet)) + puts("unknown"); + goto finish; + } + + if (!dbus_message_get_args(reply, NULL, + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) { + log_error("Failed to parse reply."); + r = -EIO; goto finish; } - while (!set_isempty(s) && - dbus_connection_read_write_dispatch(bus, -1)) - ; + dbus_message_unref(reply); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r) + goto finish; - if (!arg_quiet && d.result) { - if (streq(d.result, "timeout")) - log_error("Job timed out."); - else if (streq(d.result, "canceled")) - log_error("Job canceled."); - else if (streq(d.result, "dependency")) - log_error("A dependency job failed. See system journal for details."); - else if (!streq(d.result, "done") && !streq(d.result, "skipped")) - log_error("Job failed. See system journal and 'systemctl status' for details."); + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; } - if (streq_ptr(d.result, "timeout")) - r = -ETIME; - else if (streq_ptr(d.result, "canceled")) - r = -ECANCELED; - else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped")) + dbus_message_iter_recurse(&iter, &sub); + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + log_error("Failed to parse reply."); r = -EIO; - else - r = 0; + goto finish; + } + + dbus_message_iter_get_basic(&sub, &state); - free(d.result); + if (!quiet) + puts(state); + + if (streq(state, "active") || streq(state, "reloading")) + r = 0; + else + r = 3; /* According to LSB: "program is not running" */ finish: - /* This is slightly dirty, since we don't undo the filter registration. */ + if (reply) + dbus_message_unref(reply); return r; } +static void check_triggering_units( + DBusConnection *bus, + const char *unit_name) { + + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + char *service_trigger = NULL; + const char *interface = "org.freedesktop.systemd1.Unit", + *triggered_by_property = "TriggeredBy"; + + char _cleanup_free_ *unit_path = NULL, *n = NULL; + bool print_warning_label = true; + int r; + + n = unit_name_mangle(unit_name); + if (!n) { + log_oom(); + return; + } + + unit_path = unit_dbus_path_from_name(n); + if (!unit_path) { + log_oom(); + return; + } + + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + unit_path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &triggered_by_property, + DBUS_TYPE_INVALID); + if (r) + return; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return; + } + + dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_recurse(&sub, &iter); + sub = iter; + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { + log_error("Failed to parse reply."); + return; + } + + dbus_message_iter_get_basic(&sub, &service_trigger); + + r = check_one_unit(bus, service_trigger, true); + if (r < 0) + return; + if (r == 0) { + if (print_warning_label) { + log_warning("Warning: Stopping %s, but it can still be activated by:", unit_name); + print_warning_label = false; + } + log_warning(" %s", service_trigger); + } + + dbus_message_iter_next(&sub); + } +} + static int start_unit_one( DBusConnection *bus, const char *method, @@ -1506,88 +1534,68 @@ static int start_unit_one( DBusError *error, Set *s) { - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; const char *path; int r; + _cleanup_free_ char *n, *p = NULL; - assert(bus); assert(method); assert(name); assert(mode); assert(error); - assert(arg_no_block || s); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } + n = unit_name_mangle(name); + if (!n) + return log_oom(); - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &mode, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) { - - if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) { + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + &reply, + error, + DBUS_TYPE_STRING, &n, + DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID); + if (r) { + if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL) /* There's always a fallback possible for * legacy actions. */ r = -EADDRNOTAVAIL; - goto finish; - } + else + log_error("Failed to issue method call: %s", bus_error_message(error)); - log_error("Failed to issue method call: %s", bus_error_message(error)); - r = -EIO; - goto finish; + return r; } 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)); - r = -EIO; - goto finish; + return -EIO; } - if (need_daemon_reload(bus, name)) - log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.", - arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); + if (need_daemon_reload(bus, n)) + log_warning("Warning: Unit file of %s changed on disk, 'systemctl %s daemon-reload' recommended.", + n, arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); - if (!arg_no_block) { - char *p; + if (s) { + p = strdup(path); + if (!p) + return log_oom(); - if (!(p = strdup(path))) { - log_error("Failed to duplicate path."); - r = -ENOMEM; - goto finish; - } - - if ((r = set_put(s, p)) < 0) { - free(p); + r = set_put(s, p); + if (r < 0) { log_error("Failed to add path to set."); - goto finish; + return r; } - } - - r = 0; -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); + p = NULL; + } - return r; + return 0; } static enum action verb_to_action(const char *verb) { @@ -1611,6 +1619,8 @@ static enum action verb_to_action(const char *verb) { return ACTION_SUSPEND; else if (streq(verb, "hibernate")) return ACTION_HIBERNATE; + else if (streq(verb, "hybrid-sleep")) + return ACTION_HYBRID_SLEEP; else return ACTION_INVALID; } @@ -1631,7 +1641,8 @@ static int start_unit(DBusConnection *bus, char **args) { [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET, [ACTION_EXIT] = SPECIAL_EXIT_TARGET, [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET + [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, + [ACTION_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET }; int r, ret = 0; @@ -1688,39 +1699,53 @@ static int start_unit(DBusConnection *bus, char **args) { } if (!arg_no_block) { - if ((ret = enable_wait_for_jobs(bus)) < 0) { + ret = enable_wait_for_jobs(bus); + if (ret < 0) { log_error("Could not watch jobs: %s", strerror(-ret)); goto finish; } - if (!(s = set_new(string_hash_func, string_compare_func))) { - log_error("Failed to allocate set."); - ret = -ENOMEM; + s = set_new(string_hash_func, string_compare_func); + if (!s) { + ret = log_oom(); goto finish; } } if (one_name) { - if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0) - goto finish; + ret = start_unit_one(bus, method, one_name, mode, &error, s); + if (ret < 0) + ret = translate_bus_error_to_exit_status(ret, &error); } else { - STRV_FOREACH(name, args+1) - if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) { + STRV_FOREACH(name, args+1) { + r = start_unit_one(bus, method, *name, mode, &error, s); + if (r < 0) { ret = translate_bus_error_to_exit_status(r, &error); dbus_error_free(&error); } + } } - if (!arg_no_block) - if ((r = wait_for_jobs(bus, s)) < 0) { + if (!arg_no_block) { + r = wait_for_jobs(bus, s); + if (r < 0) { ret = r; goto finish; } -finish: - if (s) - set_free_free(s); + /* When stopping units, warn if they can still be triggered by + * another active unit (socket, path, timer) */ + if (!arg_quiet && streq(method, "StopUnit")) { + if (one_name) + check_triggering_units(bus, one_name); + else + STRV_FOREACH(name, args+1) + check_triggering_units(bus, *name); + } + } +finish: + set_free_free(s); dbus_error_free(&error); return ret; @@ -1731,12 +1756,7 @@ finish: static int reboot_with_logind(DBusConnection *bus, enum action a) { #ifdef HAVE_LOGIND const char *method; - DBusMessage *m = NULL, *reply = NULL; - DBusError error; dbus_bool_t interactive = true; - int r; - - dbus_error_init(&error); polkit_agent_open_if_enabled(); @@ -1758,295 +1778,121 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) { method = "Hibernate"; break; + case ACTION_HYBRID_SLEEP: + method = "HybridSleep"; + break; + default: return -EINVAL; } - m = dbus_message_new_method_call( - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - method); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_BOOLEAN, &interactive, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - if (error_is_no_service(&error)) { - log_debug("Failed to issue method call: %s", bus_error_message(&error)); - r = -ENOENT; - goto finish; - } - - if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) { - log_debug("Failed to issue method call: %s", bus_error_message(&error)); - r = -EACCES; - goto finish; - } - - log_info("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + return bus_method_call_with_reply ( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + method, + NULL, + NULL, + DBUS_TYPE_BOOLEAN, &interactive, + DBUS_TYPE_INVALID); #else return -ENOSYS; #endif } static int start_special(DBusConnection *bus, char **args) { - enum action a; - int r; - - assert(args); - - a = verb_to_action(args[0]); - - if (arg_force >= 2 && geteuid() != 0) { - log_error("Must be root."); - return -EPERM; - } - - if (arg_force >= 2 && - (a == ACTION_HALT || - a == ACTION_POWEROFF || - a == ACTION_REBOOT)) - halt_now(a); - - if (arg_force >= 1 && - (a == ACTION_HALT || - a == ACTION_POWEROFF || - a == ACTION_REBOOT || - a == ACTION_KEXEC || - a == ACTION_EXIT)) - return daemon_reload(bus, args); - - /* first try logind, to allow authentication with polkit */ - if (geteuid() != 0 && - (a == ACTION_POWEROFF || - a == ACTION_REBOOT || - a == ACTION_SUSPEND || - a == ACTION_HIBERNATE)) { - r = reboot_with_logind(bus, a); - if (r >= 0) - return r; - } - - r = start_unit(bus, args); - if (r >= 0) - warn_wall(a); - - return r; -} - -static int check_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - const char - *interface = "org.freedesktop.systemd1.Unit", - *property = "ActiveState"; - int r = 3; /* According to LSB: "program is not running" */ - DBusError error; - char **name; - - assert(bus); - assert(args); - - dbus_error_init(&error); - - STRV_FOREACH(name, args+1) { - const char *path = NULL; - const char *state; - DBusMessageIter iter, sub; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - - /* Hmm, cannot figure out anything about this unit... */ - if (!arg_quiet) - puts("unknown"); - - dbus_error_free(&error); - dbus_message_unref(m); - m = NULL; - continue; - } - - 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)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "Get"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - if (!dbus_message_iter_init(reply, &iter) || - dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } + enum action a; + int r; - dbus_message_iter_recurse(&iter, &sub); + assert(args); - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) { - log_error("Failed to parse reply."); - r = -EIO; - goto finish; - } + a = verb_to_action(args[0]); - dbus_message_iter_get_basic(&sub, &state); + if (arg_force >= 2 && geteuid() != 0) { + log_error("Must be root."); + return -EPERM; + } - if (!arg_quiet) - puts(state); + if (arg_force >= 2 && + (a == ACTION_HALT || + a == ACTION_POWEROFF || + a == ACTION_REBOOT)) + halt_now(a); - if (streq(state, "active") || streq(state, "reloading")) - r = 0; + if (arg_force >= 1 && + (a == ACTION_HALT || + a == ACTION_POWEROFF || + a == ACTION_REBOOT || + a == ACTION_KEXEC || + a == ACTION_EXIT)) + return daemon_reload(bus, args); - dbus_message_unref(m); - dbus_message_unref(reply); - m = reply = NULL; + /* first try logind, to allow authentication with polkit */ + if (geteuid() != 0 && + (a == ACTION_POWEROFF || + a == ACTION_REBOOT || + a == ACTION_SUSPEND || + a == ACTION_HIBERNATE || + a == ACTION_HYBRID_SLEEP)) { + r = reboot_with_logind(bus, a); + if (r >= 0) + return r; } -finish: - if (m) - dbus_message_unref(m); + r = start_unit(bus, args); + if (r >= 0) + warn_wall(a); - if (reply) - dbus_message_unref(reply); + return r; +} - dbus_error_free(&error); +static int check_unit(DBusConnection *bus, char **args) { + char **name; + int r = 3; /* According to LSB: "program is not running" */ + + assert(bus); + assert(args); + + STRV_FOREACH(name, args+1) { + int state = check_one_unit(bus, *name, arg_quiet); + if (state < 0) + return state; + if (state == 0) + r = 0; + } return r; } static int kill_unit(DBusConnection *bus, char **args) { - DBusMessage *m = NULL; int r = 0; - DBusError error; - char **name; + char **name, *n; - assert(bus); assert(args); - dbus_error_init(&error); - if (!arg_kill_who) arg_kill_who = "all"; - if (!arg_kill_mode) - arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process"; - STRV_FOREACH(name, args+1) { - DBusMessage *reply; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "KillUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_STRING, &arg_kill_who, - DBUS_TYPE_STRING, &arg_kill_mode, - DBUS_TYPE_INT32, &arg_signal, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - dbus_error_free(&error); - r = -EIO; - } - - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - m = reply = NULL; + n = unit_name_mangle(*name); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "KillUnit", + NULL, + NULL, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_STRING, &arg_kill_who, + DBUS_TYPE_INT32, &arg_signal, + DBUS_TYPE_INVALID); + free(n); + if (r) + return r; } - -finish: - if (m) - dbus_message_unref(m); - - dbus_error_free(&error); - - return r; + return 0; } typedef struct ExecStatusInfo { @@ -2210,7 +2056,7 @@ static void print_status_info(UnitStatusInfo *i) { ExecStatusInfo *p; const char *on, *off, *ss; usec_t timestamp; - char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1; + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; char since2[FORMAT_TIMESTAMP_MAX], *s2; const char *path; @@ -2279,7 +2125,7 @@ static void print_status_info(UnitStatusInfo *i) { streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp : i->active_exit_timestamp; - s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp); + s1 = format_timestamp_relative(since1, sizeof(since1), timestamp); s2 = format_timestamp(since2, sizeof(since2), timestamp); if (s1) @@ -2290,7 +2136,7 @@ static void print_status_info(UnitStatusInfo *i) { printf("\n"); if (!i->condition_result && i->condition_timestamp > 0) { - s1 = format_timestamp_pretty(since1, sizeof(since1), i->condition_timestamp); + s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp); s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp); if (s1) @@ -2334,7 +2180,7 @@ static void print_status_info(UnitStatusInfo *i) { printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t)); free(t); - good = is_clean_exit_lsb(p->code, p->status); + good = is_clean_exit_lsb(p->code, p->status, NULL); if (!good) { on = ansi_highlight_red(true); off = ansi_highlight_red(false); @@ -2419,7 +2265,8 @@ static void print_status_info(UnitStatusInfo *i) { if (i->status_text) printf("\t Status: \"%s\"\n", i->status_text); - if (i->default_control_group) { + if (i->default_control_group && + (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_by_spec(i->default_control_group, false) == 0)) { unsigned c; printf("\t CGroup: %s\n", i->default_control_group); @@ -2445,8 +2292,20 @@ static void print_status_info(UnitStatusInfo *i) { } if (i->id && arg_transport != TRANSPORT_SSH) { + int flags = + arg_all * OUTPUT_SHOW_ALL | + (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | + on_tty() * OUTPUT_COLOR | + !arg_quiet * OUTPUT_WARN_CUTOFF; + printf("\n"); - show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow); + show_journal_by_unit(stdout, + i->id, + arg_output, + 0, + i->inactive_exit_timestamp_monotonic, + arg_lines, + flags); } if (i->need_daemon_reload) @@ -2456,6 +2315,73 @@ static void print_status_info(UnitStatusInfo *i) { arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user"); } +static void show_unit_help(UnitStatusInfo *i) { + char **p; + + assert(i); + + if (!i->documentation) { + log_info("Documentation for %s not known.", i->id); + return; + } + + STRV_FOREACH(p, i->documentation) { + + if (startswith(*p, "man:")) { + size_t k; + char *e = NULL; + char *page = NULL, *section = NULL; + const char *args[4] = { "man", NULL, NULL, NULL }; + pid_t pid; + + k = strlen(*p); + + if ((*p)[k-1] == ')') + e = strrchr(*p, '('); + + if (e) { + page = strndup((*p) + 4, e - *p - 4); + if (!page) { + log_oom(); + return; + } + + section = strndup(e + 1, *p + k - e - 2); + if (!section) { + free(page); + log_oom(); + return; + } + + args[1] = section; + args[2] = page; + } else + args[1] = *p + 4; + + pid = fork(); + if (pid < 0) { + log_error("Failed to fork: %m"); + free(page); + free(section); + continue; + } + + if (pid == 0) { + /* Child */ + execvp(args[0], (char**) args); + log_error("Failed to execute man: %m"); + _exit(EXIT_FAILURE); + } + + free(page); + free(section); + + wait_for_terminate(pid, NULL); + } else + log_info("Can't show: %s", *p); + } +} + static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) { assert(name); @@ -2855,44 +2781,30 @@ static int print_property(const char *name, DBusMessageIter *iter) { } static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *reply = NULL; const char *interface = ""; int r; - DBusError error; DBusMessageIter iter, sub, sub2, sub3; UnitStatusInfo info; ExecStatusInfo *p; - assert(bus); assert(path); assert(new_line); zero(info); - dbus_error_init(&error); - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || @@ -2950,8 +2862,12 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo r = 0; - if (!show_properties) - print_status_info(&info); + if (!show_properties) { + if (streq(verb, "help")) + show_unit_help(&info); + else + print_status_info(&info); + } strv_free(info.documentation); @@ -2967,50 +2883,32 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo } finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); - dbus_error_free(&error); - return r; } static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, bool *new_line) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *reply = NULL; const char *path = NULL; DBusError error; int r; dbus_error_init(&error); - m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitByPID"); - if (!m) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_UINT32, &pid, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitByPID", + &reply, + NULL, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path, @@ -3023,9 +2921,6 @@ static int show_one_by_pid(const char *verb, DBusConnection *bus, uint32_t pid, r = show_one(verb, bus, path, false, new_line); finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); @@ -3042,7 +2937,7 @@ static int show(DBusConnection *bus, char **args) { assert(bus); assert(args); - show_properties = !streq(args[0], "status"); + show_properties = streq(args[0], "show"); if (show_properties) pager_open_if_enabled(); @@ -3058,17 +2953,14 @@ static int show(DBusConnection *bus, char **args) { uint32_t id; if (safe_atou32(*name, &id) < 0) { - + char *p, *n; /* Interpret as unit name */ - char *e, *p; - e = bus_path_escape(*name); - if (!e) - return -ENOMEM; - p = strappend("/org/freedesktop/systemd1/unit/", e); - free(e); + n = unit_name_mangle(*name); + p = unit_dbus_path_from_name(n ? n : *name); + free(n); if (!p) - return -ENOMEM; + return log_oom(); r = show_one(args[0], bus, p, show_properties, &new_line); free(p); @@ -3082,7 +2974,7 @@ static int show(DBusConnection *bus, char **args) { char *p; if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0) - return -ENOMEM; + return log_oom(); r = show_one(args[0], bus, p, show_properties, &new_line); free(p); @@ -3104,7 +2996,7 @@ static int show(DBusConnection *bus, char **args) { } static int dump(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *reply = NULL; DBusError error; int r; const char *text; @@ -3113,20 +3005,17 @@ static int dump(DBusConnection *bus, char **args) { pager_open_if_enabled(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Dump"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Dump", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &text, @@ -3138,12 +3027,7 @@ static int dump(DBusConnection *bus, char **args) { fputs(text, stdout); - r = 0; - finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); @@ -3153,44 +3037,37 @@ finish: } static int snapshot(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *reply = NULL; DBusError error; int r; - const char *name = "", *path, *id; dbus_bool_t cleanup = FALSE; DBusMessageIter iter, sub; const char + *name = "", *path, *id, *interface = "org.freedesktop.systemd1.Unit", *property = "Id"; + char *n; dbus_error_init(&error); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "CreateSnapshot"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - if (strv_length(args) > 1) name = args[1]; - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &cleanup, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + n = unit_name_mangle(name); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "CreateSnapshot", + &reply, + NULL, + DBUS_TYPE_STRING, n ? (const char**) &n : &name, + DBUS_TYPE_BOOLEAN, &cleanup, + DBUS_TYPE_INVALID); + free(n); + if (r) goto finish; - } if (!dbus_message_get_args(reply, &error, DBUS_TYPE_OBJECT_PATH, &path, @@ -3200,31 +3077,20 @@ static int snapshot(DBusConnection *bus, char **args) { goto finish; } - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "Get"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { @@ -3245,12 +3111,8 @@ static int snapshot(DBusConnection *bus, char **args) { if (!arg_quiet) puts(id); - r = 0; finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); @@ -3260,95 +3122,66 @@ finish: } static int delete_snapshot(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - int r; + DBusMessage *reply = NULL; + int r = 0; DBusError error; char **name; - assert(bus); assert(args); dbus_error_init(&error); STRV_FOREACH(name, args+1) { const char *path = NULL; + char *n; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + n = unit_name_mangle(*name); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnit", + &reply, + NULL, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_INVALID); + free(n); + if (r) 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)); r = -EIO; + dbus_message_unref(reply); + dbus_error_free(&error); goto finish; } - - dbus_message_unref(m); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Snapshot", - "Remove"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_unref(reply); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - dbus_message_unref(m); dbus_message_unref(reply); - m = reply = NULL; - } - r = 0; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Snapshot", + "Remove", + NULL, + NULL, + DBUS_TYPE_INVALID); + if (r) + goto finish; + } finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - return r; } static int daemon_reload(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; int r; const char *method; - - dbus_error_init(&error); + DBusError error; if (arg_action == ACTION_RELOAD) method = "Reload"; @@ -3370,141 +3203,82 @@ static int daemon_reload(DBusConnection *bus, char **args) { /* "daemon-reload" */ "Reload"; } - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - - if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) { - /* There's always a fallback possible for - * legacy actions. */ - r = -EADDRNOTAVAIL; - goto finish; - } - - if (streq(method, "Reexecute") && dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY)) { - /* On reexecution, we expect a disconnect, not - * a reply */ - r = 0; - goto finish; - } - + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + NULL, + &error, + DBUS_TYPE_INVALID); + + if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL) + /* There's always a fallback possible for + * legacy actions. */ + r = -EADDRNOTAVAIL; + else if (r == -ETIMEDOUT && streq(method, "Reexecute")) + /* On reexecution, we expect a disconnect, not + * a reply */ + r = 0; + else if (r) log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); return r; } static int reset_failed(DBusConnection *bus, char **args) { - DBusMessage *m = NULL; - int r; - DBusError error; - char **name; - - assert(bus); - dbus_error_init(&error); + int r = 0; + char **name, *n; if (strv_length(args) <= 1) return daemon_reload(bus, args); STRV_FOREACH(name, args+1) { - DBusMessage *reply; - - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ResetFailedUnit"))) { - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + n = unit_name_mangle(*name); + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ResetFailedUnit", + NULL, + NULL, + DBUS_TYPE_STRING, n ? &n : name, + DBUS_TYPE_INVALID); + free(n); + if (r) goto finish; - } - - dbus_message_unref(m); - dbus_message_unref(reply); - m = reply = NULL; } - r = 0; - finish: - if (m) - dbus_message_unref(m); - - dbus_error_free(&error); - return r; } static int show_enviroment(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; - DBusError error; + DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2; int r; const char *interface = "org.freedesktop.systemd1.Manager", *property = "Environment"; - dbus_error_init(&error); - pager_open_if_enabled(); - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.DBus.Properties", - "Get"))) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_STRING, &property, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + r = bus_method_call_with_reply ( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.DBus.Properties", + "Get", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_STRING, &property, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_iter_init(reply, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { @@ -3542,25 +3316,15 @@ static int show_enviroment(DBusConnection *bus, char **args) { r = 0; finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); - dbus_error_free(&error); - return r; } static int switch_root(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; unsigned l; const char *root, *init; - DBusError error; - int r; - - dbus_error_init(&error); l = strv_length(args); if (l < 2 || l > 3) { @@ -3571,54 +3335,27 @@ static int switch_root(DBusConnection *bus, char **args) { root = args[1]; init = l >= 3 ? args[2] : ""; - m = dbus_message_new_method_call( + return bus_method_call_with_reply ( + bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "SwitchRoot"); - if (!m) { - log_error("Could not allocate message."); - return -ENOMEM; - } - - if (!dbus_message_append_args( - m, - DBUS_TYPE_STRING, &root, - DBUS_TYPE_STRING, &init, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - r = 0; - -finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return r; + "SwitchRoot", + NULL, + NULL, + DBUS_TYPE_STRING, &root, + DBUS_TYPE_STRING, &init, + DBUS_TYPE_INVALID); } static int set_environment(DBusConnection *bus, char **args) { - DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; DBusError error; - int r; const char *method; - DBusMessageIter iter, sub; - char **name; + DBusMessageIter iter; + int r; + + assert(bus); dbus_error_init(&error); @@ -3626,38 +3363,22 @@ static int set_environment(DBusConnection *bus, char **args) { ? "SetEnvironment" : "UnsetEnvironment"; - if (!(m = dbus_message_new_method_call( - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method))) { - - log_error("Could not allocate message."); - return -ENOMEM; - } + m = dbus_message_new_method_call( + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method); + if (!m) + return log_oom(); dbus_message_iter_init_append(m, &iter); - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - STRV_FOREACH(name, args+1) - if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_iter_close_container(&iter, &sub)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } + r = bus_append_strv_iter(&iter, args + 1); + if (r < 0) + return log_oom(); - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { + reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); + if (!reply) { log_error("Failed to issue method call: %s", bus_error_message(&error)); r = -EIO; goto finish; @@ -3666,21 +3387,14 @@ static int set_environment(DBusConnection *bus, char **args) { r = 0; finish: - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); - return r; } static int enable_sysv_units(char **args) { int r = 0; -#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA)) +#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA)) const char *verb = args[0]; unsigned f = 1, t = 1; LookupPaths paths; @@ -3697,7 +3411,7 @@ static int enable_sysv_units(char **args) { * afterwards only the native units remain */ zero(paths); - r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, NULL, NULL, NULL); + r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, NULL, NULL, NULL); if (r < 0) return r; @@ -3730,8 +3444,7 @@ static int enable_sysv_units(char **args) { asprintf(&p, "%s/%s", *k, name); if (!p) { - log_error("No memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -3751,8 +3464,7 @@ static int enable_sysv_units(char **args) { else asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name); if (!p) { - log_error("No memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -3780,10 +3492,9 @@ static int enable_sysv_units(char **args) { l = strv_join((char**)argv, " "); if (!l) { - log_error("No memory."); free(q); free(p); - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -3853,6 +3564,38 @@ finish: return r; } +static int mangle_names(char **original_names, char ***mangled_names) { + char **i, **l, **name; + + l = new(char*, strv_length(original_names) + 1); + if (!l) + return log_oom(); + + i = l; + STRV_FOREACH(name, original_names) { + + /* When enabling units qualified path names are OK, + * too, hence allow them explicitly. */ + + if (is_path(*name)) + *i = strdup(*name); + else + *i = unit_name_mangle(*name); + + if (!*i) { + strv_free(l); + return log_oom(); + } + + i++; + } + + *i = NULL; + *mangled_names = l; + + return 0; +} + static int enable_unit(DBusConnection *bus, char **args) { const char *verb = args[0]; UnitFileChange *changes = NULL; @@ -3861,6 +3604,7 @@ static int enable_unit(DBusConnection *bus, char **args) { DBusMessage *m = NULL, *reply = NULL; int r; DBusError error; + char **mangled_names = NULL; r = enable_sysv_units(args); if (r < 0) @@ -3906,6 +3650,7 @@ static int enable_unit(DBusConnection *bus, char **args) { } } + r = 0; } else { const char *method; bool send_force = true, expect_carries_install_info = false; @@ -3940,14 +3685,17 @@ static int enable_unit(DBusConnection *bus, char **args) { "org.freedesktop.systemd1.Manager", method); if (!m) { - log_error("Out of memory"); - r = -ENOMEM; + r = log_oom(); goto finish; } dbus_message_iter_init_append(m, &iter); - r = bus_append_strv_iter(&iter, args+1); + r = mangle_names(args+1, &mangled_names); + if(r < 0) + goto finish; + + r = bus_append_strv_iter(&iter, mangled_names); if (r < 0) { log_error("Failed to append unit files."); goto finish; @@ -4035,7 +3783,16 @@ static int enable_unit(DBusConnection *bus, char **args) { } if (carries_install_info == 0) - log_warning("Warning: unit files do not carry install information. No operation executed."); + log_warning( +"The unit files have no [Install] section. They are not meant to be enabled\n" +"using systemctl.\n" +"Possible reasons for having this kind of units are:\n" +"1) A unit may be statically enabled by being symlinked from another unit's\n" +" .wants/ or .requires/ directory.\n" +"2) A unit's purpose may be to act as a helper for some other unit which has\n" +" a requirement dependency on it.\n" +"3) A unit may be started when needed via activation (socket, path, timer,\n" +" D-Bus, udev, scripted systemctl call, ...).\n"); finish: if (m) @@ -4047,13 +3804,16 @@ finish: unit_file_changes_free(changes, n_changes); dbus_error_free(&error); + + strv_free(mangled_names); + return r; } static int unit_is_enabled(DBusConnection *bus, char **args) { DBusError error; int r; - DBusMessage *m = NULL, *reply = NULL; + DBusMessage *reply = NULL; bool enabled; char **name; @@ -4089,31 +3849,18 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { STRV_FOREACH(name, args+1) { const char *s; - m = dbus_message_new_method_call( + r = bus_method_call_with_reply ( + bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "GetUnitFileState"); - if (!m) { - log_error("Out of memory"); - r = -ENOMEM; - goto finish; - } - - if (!dbus_message_append_args(m, - DBUS_TYPE_STRING, name, - DBUS_TYPE_INVALID)) { - log_error("Could not append arguments to message."); - r = -ENOMEM; - goto finish; - } - - reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error); - if (!reply) { - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; + "GetUnitFileState", + &reply, + NULL, + DBUS_TYPE_STRING, name, + DBUS_TYPE_INVALID); + if (r) goto finish; - } if (!dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &s, @@ -4123,9 +3870,8 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { goto finish; } - dbus_message_unref(m); dbus_message_unref(reply); - m = reply = NULL; + reply = NULL; if (streq(s, "enabled") || streq(s, "enabled-runtime") || @@ -4140,9 +3886,6 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { r = enabled ? 0 : 1; finish: - if (m) - dbus_message_unref(m); - if (reply) dbus_message_unref(reply); @@ -4191,9 +3934,8 @@ static int systemctl_help(void) { " --root=PATH Enable unit files in the specified root directory\n" " --runtime Enable unit files only temporarily until next reboot\n" " -n --lines=INTEGER Journal entries to show\n" - " --follow Follow journal\n" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, cat)\n\n" + " verbose, export, json, json-pretty, json-sse, cat)\n\n" "Unit Commands:\n" " list-units List loaded units\n" " start [NAME...] Start (activate) one or more units\n" @@ -4201,9 +3943,9 @@ static int systemctl_help(void) { " reload [NAME...] Reload one or more units\n" " restart [NAME...] Start or restart one or more units\n" " try-restart [NAME...] Restart one or more units if active\n" - " reload-or-restart [NAME...] Reload one or more units is possible,\n" + " reload-or-restart [NAME...] Reload one or more units if possible,\n" " otherwise start or restart\n" - " reload-or-try-restart [NAME...] Reload one or more units is possible,\n" + " reload-or-try-restart [NAME...] Reload one or more units if possible,\n" " otherwise restart if active\n" " isolate [NAME] Start one unit and stop all others\n" " kill [NAME...] Send signal to processes of a unit\n" @@ -4211,6 +3953,7 @@ static int systemctl_help(void) { " status [NAME...|PID...] Show runtime status of one or more units\n" " show [NAME...|JOB...] Show properties of one or more\n" " units/jobs or the manager\n" + " help [NAME...|PID...] Show manual for one or more units\n" " reset-failed [NAME...] Reset failed state for all, one, or more\n" " units\n" " load [NAME...] Load one or more units\n\n" @@ -4253,7 +3996,8 @@ static int systemctl_help(void) { " exit Request user instance exit\n" " switch-root [ROOT] [INIT] Change to a different root file system\n" " suspend Suspend the system\n" - " hibernate Hibernate the system\n", + " hibernate Hibernate the system\n" + " hybrid-sleep Hibernate and suspend the system\n", program_invocation_short_name); return 0; @@ -4270,7 +4014,6 @@ static int halt_help(void) { " -f --force Force immediate halt/power-off/reboot\n" " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n" " -d --no-wtmp Don't write wtmp record\n" - " -n --no-sync Don't sync before halt/power-off/reboot\n" " --no-wall Don't send wall message before halt/power-off/reboot\n", program_invocation_short_name, arg_action == ACTION_REBOOT ? "Reboot" : @@ -4325,6 +4068,22 @@ static int runlevel_help(void) { return 0; } +static int help_types(void) { + int i; + + puts("Available unit types:"); + for(i = UNIT_SERVICE; i < _UNIT_TYPE_MAX; i++) + if (unit_type_table[i]) + puts(unit_type_table[i]); + + puts("\nAvailable unit load states: "); + for(i = UNIT_STUB; i < _UNIT_LOAD_STATE_MAX; i++) + if (unit_type_table[i]) + puts(unit_load_state_table[i]); + + return 0; +} + static int systemctl_parse_argv(int argc, char *argv[]) { enum { @@ -4343,12 +4102,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_ROOT, ARG_FULL, ARG_NO_RELOAD, - ARG_KILL_MODE, ARG_KILL_WHO, ARG_NO_ASK_PASSWORD, ARG_FAILED, ARG_RUNTIME, - ARG_FOLLOW, ARG_FORCE }; @@ -4375,7 +4132,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "root", required_argument, NULL, ARG_ROOT }, { "force", no_argument, NULL, ARG_FORCE }, { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, - { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */ { "kill-who", required_argument, NULL, ARG_KILL_WHO }, { "signal", required_argument, NULL, 's' }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, @@ -4383,7 +4139,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "privileged",no_argument, NULL, 'P' }, { "runtime", no_argument, NULL, ARG_RUNTIME }, { "lines", required_argument, NULL, 'n' }, - { "follow", no_argument, NULL, ARG_FOLLOW }, { "output", required_argument, NULL, 'o' }, { NULL, 0, NULL, 0 } }; @@ -4408,9 +4163,23 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return 0; case 't': - arg_type = optarg; - break; + if (streq(optarg, "help")) { + help_types(); + return 0; + } + if (unit_type_from_string(optarg) >= 0) { + arg_type = optarg; + break; + } + if (unit_load_state_from_string(optarg) >= 0) { + arg_load_state = optarg; + break; + } + log_error("Unkown unit type or load state '%s'.", + optarg); + log_info("Use -t help to see a list of allowed values."); + return -EINVAL; case 'p': { char **l; @@ -4495,14 +4264,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_force ++; break; - case ARG_FOLLOW: - arg_follow = true; - break; - case 'f': - /* -f is short for both --follow and --force! */ arg_force ++; - arg_follow = true; break; case ARG_NO_RELOAD: @@ -4513,10 +4276,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_kill_who = optarg; break; - case ARG_KILL_MODE: - arg_kill_mode = optarg; - break; - case 's': if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) { log_error("Failed to parse signal string %s.", optarg); @@ -4560,7 +4319,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -4590,7 +4349,6 @@ static int halt_parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, 'f' }, { "wtmp-only", no_argument, NULL, 'w' }, { "no-wtmp", no_argument, NULL, 'd' }, - { "no-sync", no_argument, NULL, 'n' }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, { NULL, 0, NULL, 0 } }; @@ -4636,16 +4394,13 @@ static int halt_parse_argv(int argc, char *argv[]) { arg_no_wtmp = true; break; - case 'n': - arg_no_sync = true; - break; - case ARG_NO_WALL: arg_no_wall = true; break; case 'i': case 'h': + case 'n': /* Compatibility nops */ break; @@ -4653,7 +4408,7 @@ static int halt_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -4790,12 +4545,12 @@ static int shutdown_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } - if (argc > optind) { + if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) { r = parse_time_spec(argv[optind], &arg_when); if (r < 0) { log_error("Failed to parse time specification: %s", argv[optind]); @@ -4804,8 +4559,11 @@ static int shutdown_parse_argv(int argc, char *argv[]) { } else arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE; - /* We skip the time argument */ - if (argc > optind + 1) + if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN) + /* No time argument for shutdown cancel */ + arg_wall = argv + optind; + else if (argc > optind + 1) + /* We skip the time argument */ arg_wall = argv + optind + 1; optind = argc; @@ -4866,7 +4624,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -4891,7 +4649,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { break; if (i >= ELEMENTSOF(table)) { - log_error("Unknown command %s.", argv[optind]); + log_error("Unknown command '%s'.", argv[optind]); return -EINVAL; } @@ -4929,7 +4687,7 @@ static int runlevel_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code %c", c); + log_error("Unknown option code '%c'.", c); return -EINVAL; } } @@ -5074,7 +4832,7 @@ static int talk_upstart(void) { if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) { - if (error_is_no_service(&error)) { + if (bus_error_is_no_service(&error)) { r = -EADDRNOTAVAIL; goto finish; } @@ -5174,6 +4932,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "check", MORE, 2, check_unit }, { "show", MORE, 1, show }, { "status", MORE, 2, show }, + { "help", MORE, 2, show }, { "dump", EQUAL, 1, dump }, { "dot", EQUAL, 1, dot }, { "snapshot", LESS, 2, snapshot }, @@ -5189,6 +4948,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "kexec", EQUAL, 1, start_special }, { "suspend", EQUAL, 1, start_special }, { "hibernate", EQUAL, 1, start_special }, + { "hybrid-sleep", EQUAL, 1, start_special }, { "default", EQUAL, 1, start_special }, { "rescue", EQUAL, 1, start_special }, { "emergency", EQUAL, 1, start_special }, @@ -5218,9 +4978,10 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError /* Special rule: no arguments means "list-units" */ i = 0; else { - if (streq(argv[optind], "help")) { - systemctl_help(); - return 0; + if (streq(argv[optind], "help") && !argv[optind+1]) { + log_error("This command expects one or more " + "unit names. Did you mean --help?"); + return -EINVAL; } for (i = 0; i < ELEMENTSOF(verbs); i++) @@ -5228,7 +4989,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError break; if (i >= ELEMENTSOF(verbs)) { - log_error("Unknown operation %s", argv[optind]); + log_error("Unknown operation '%s'.", argv[optind]); return -EINVAL; } } @@ -5392,7 +5153,7 @@ done: return 0; } -static void halt_now(enum action a) { +static _noreturn_ void halt_now(enum action a) { /* Make sure C-A-D is handled by the kernel from this * point on... */ @@ -5482,9 +5243,6 @@ static int halt_main(DBusConnection *bus) { } } - if (!arg_no_sync) - sync(); - if (arg_dry) return 0; @@ -5516,6 +5274,7 @@ int main(int argc, char*argv[]) { dbus_error_init(&error); + setlocale(LC_ALL, ""); log_parse_environment(); log_open(); @@ -5582,9 +5341,22 @@ int main(int argc, char*argv[]) { r = reload_with_fallback(bus); break; - case ACTION_CANCEL_SHUTDOWN: - r = send_shutdownd(0, 0, false, false, NULL); + case ACTION_CANCEL_SHUTDOWN: { + char *m = NULL; + + if (arg_wall) { + m = strv_join(arg_wall, " "); + if (!m) { + retval = EXIT_FAILURE; + goto finish; + } + } + r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m); + if (r < 0) + log_warning("Failed to talk to shutdownd, shutdown hasn't been cancelled: %s", strerror(-r)); + free(m); break; + } case ACTION_INVALID: case ACTION_RUNLEVEL: