X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=b75f335ae9be30a7959a6f82a84220e4a4487099;hb=a1074881b43416018aef2fb8f62ef62f92f0bae7;hp=a305c3d87d96fce6535aaf442c7ce3f8c63d11f2;hpb=b8ab2dc6c9eb8d6c738524f2ab48931855e3a08a;p=elogind.git diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index a305c3d87..b75f335ae 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -20,6 +20,8 @@ ***/ #include +#include +#include #include #include #include @@ -72,6 +74,7 @@ static char **arg_types = NULL; static char **arg_states = NULL; static char **arg_properties = NULL; static bool arg_all = false; +static bool original_stdout_is_tty; static enum dependency { DEPENDENCY_FORWARD, DEPENDENCY_REVERSE, @@ -286,14 +289,16 @@ static bool output_show_unit(const struct unit_info *u) { } 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; + unsigned id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len; + unsigned n_shown = 0; const struct unit_info *u; int job_count = 0; - max_id_len = sizeof("UNIT")-1; - active_len = sizeof("ACTIVE")-1; - sub_len = sizeof("SUB")-1; - job_len = sizeof("JOB")-1; + max_id_len = strlen("UNIT"); + load_len = strlen("LOAD"); + active_len = strlen("ACTIVE"); + sub_len = strlen("SUB"); + job_len = strlen("JOB"); desc_len = 0; for (u = unit_infos; u < unit_infos + c; u++) { @@ -301,6 +306,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { continue; max_id_len = MAX(max_id_len, strlen(u->id)); + load_len = MAX(load_len, strlen(u->load_state)); active_len = MAX(active_len, strlen(u->active_state)); sub_len = MAX(sub_len, strlen(u->sub_state)); if (u->job_id != 0) { @@ -309,7 +315,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { } } - if (!arg_full) { + if (!arg_full && original_stdout_is_tty) { unsigned basic_len; id_len = MIN(max_id_len, 25u); basic_len = 5 + id_len + 5 + active_len + sub_len; @@ -343,7 +349,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) { continue; if (!n_shown && !arg_no_legend) { - printf("%-*s %-6s %-*s %-*s ", id_len, "UNIT", "LOAD", + printf("%-*s %-*s %-*s %-*s ", id_len, "UNIT", load_len, "LOAD", active_len, "ACTIVE", sub_len, "SUB"); if (job_count) printf("%-*s ", job_len, "JOB"); @@ -370,13 +376,13 @@ 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%s %s%-6s%s %s%-*s %-*s%s %-*s", + printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s", on, id_len, e ? e : u->id, off, - on_loaded, u->load_state, off_loaded, + on_loaded, load_len, u->load_state, off_loaded, on_active, active_len, u->active_state, sub_len, u->sub_state, off_active, job_count ? job_len + 1 : 0, u->job_id ? u->job_type : ""); - if (!arg_full && arg_no_pager) + if (desc_len > 0) printf("%.*s\n", desc_len, u->description); else printf("%s\n", u->description); @@ -470,7 +476,7 @@ static int list_units(DBusConnection *bus, char **args) { if (r < 0) return r; - qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); + qsort_safe(unit_infos, c, sizeof(struct unit_info), compare_unit_info); output_units_list(unit_infos, c); @@ -732,8 +738,8 @@ static int list_sockets(DBusConnection *bus, char **args) { listen = triggered = NULL; /* avoid cleanup */ } - qsort(socket_infos, cs, sizeof(struct socket_info), - (__compar_fn_t) socket_info_compare); + qsort_safe(socket_infos, cs, sizeof(struct socket_info), + (__compar_fn_t) socket_info_compare); output_sockets_list(socket_infos, cs); @@ -747,7 +753,7 @@ static int list_sockets(DBusConnection *bus, char **args) { } free(socket_infos); - return 0; + return r; } static int compare_unit_file_list(const void *a, const void *b) { @@ -1107,7 +1113,7 @@ static int list_dependencies_one(DBusConnection *bus, const char *name, int leve if (r < 0) return r; - qsort(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); + qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); STRV_FOREACH(c, deps) { if (strv_contains(u, *c)) { @@ -1495,6 +1501,7 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) { uint32_t id; const char *path, *result, *unit; + char *r; if (dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &id, @@ -1503,7 +1510,11 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID)) { - free(set_remove(d->set, (char*) path)); + r = set_remove(d->set, (char*) path); + if (!r) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + free(r); if (!isempty(result)) d->result = strdup(result); @@ -1523,7 +1534,11 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me /* Compatibility with older systemd versions < * 183 during upgrades. This should be dropped * one day. */ - free(set_remove(d->set, (char*) path)); + r = set_remove(d->set, (char*) path); + if (!r) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + free(r); if (*result) d->result = strdup(result); @@ -3009,7 +3024,7 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn return r; } - LIST_PREPEND(ExecStatusInfo, exec, i->exec, info); + LIST_PREPEND(exec, i->exec, info); dbus_message_iter_next(&sub); } @@ -3464,7 +3479,7 @@ static int show_one(const char *verb, } while ((p = info.exec)) { - LIST_REMOVE(ExecStatusInfo, exec, info.exec, p); + LIST_REMOVE(exec, info.exec, p); exec_status_info_free(p); } @@ -3522,7 +3537,7 @@ static int show_all(const char* verb, if (r < 0) return r; - qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); + qsort_safe(unit_infos, c, sizeof(struct unit_info), compare_unit_info); for (u = unit_infos; u < unit_infos + c; u++) { _cleanup_free_ char *p = NULL; @@ -3648,7 +3663,7 @@ static int append_assignment(DBusMessageIter *iter, const char *assignment) { !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &b)) return log_oom(); - } else if (streq(field, "MemoryLimit") || streq(field, "MemorySoftLimit")) { + } else if (streq(field, "MemoryLimit")) { off_t bytes; uint64_t u; @@ -3819,7 +3834,8 @@ static int append_assignment(DBusMessageIter *iter, const char *assignment) { static int set_property(DBusConnection *bus, char **args) { - _cleanup_free_ DBusMessage *m = NULL, *reply = NULL; + _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; + _cleanup_free_ char *n = NULL; DBusMessageIter iter, sub; dbus_bool_t runtime; DBusError error; @@ -3840,7 +3856,11 @@ static int set_property(DBusConnection *bus, char **args) { runtime = arg_runtime; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[1]) || + n = unit_name_mangle(args[1]); + if (!n) + return log_oom(); + + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) || !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &runtime) || !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub)) return log_oom(); @@ -4203,13 +4223,12 @@ static int set_environment(DBusConnection *bus, char **args) { return 0; } -static int enable_sysv_units(char **args) { +static int enable_sysv_units(const char *verb, char **args) { int r = 0; #if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG) - const char *verb = args[0]; unsigned f = 1, t = 1; - LookupPaths paths = {}; + _cleanup_lookup_paths_free_ LookupPaths paths = {}; if (arg_scope != UNIT_FILE_SYSTEM) return 0; @@ -4227,7 +4246,7 @@ static int enable_sysv_units(char **args) { return r; r = 0; - for (f = 1; args[f]; f++) { + for (f = 0; args[f]; f++) { const char *name; _cleanup_free_ char *p = NULL, *q = NULL; bool found_native = false, found_sysv; @@ -4347,10 +4366,8 @@ static int enable_sysv_units(char **args) { } finish: - lookup_paths_free(&paths); - /* Drop all SysV units */ - for (f = 1, t = 1; args[f]; f++) { + for (f = 0, t = 0; args[f]; f++) { if (isempty(args[f])) continue; @@ -4408,16 +4425,16 @@ static int enable_unit(DBusConnection *bus, char **args) { dbus_error_init(&error); - r = enable_sysv_units(args); - if (r < 0) - return r; - if (!args[1]) return 0; r = mangle_names(args+1, &mangled_names); if (r < 0) - goto finish; + return r; + + r = enable_sysv_units(verb, mangled_names); + if (r < 0) + return r; if (!bus || avoid_bus()) { if (streq(verb, "enable")) { @@ -4609,11 +4626,15 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; bool enabled; char **name; - char *n; + _cleanup_strv_free_ char **mangled_names = NULL; dbus_error_init(&error); - r = enable_sysv_units(args); + r = mangle_names(args+1, &mangled_names); + if (r < 0) + return r; + + r = enable_sysv_units(args[0], mangled_names); if (r < 0) return r; @@ -4621,16 +4642,10 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { if (!bus || avoid_bus()) { - STRV_FOREACH(name, args+1) { + STRV_FOREACH(name, mangled_names) { UnitFileState state; - n = unit_name_mangle(*name); - if (!n) - return log_oom(); - - state = unit_file_get_state(arg_scope, arg_root, n); - - free(n); + state = unit_file_get_state(arg_scope, arg_root, *name); if (state < 0) return state; @@ -4645,13 +4660,9 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { } } else { - STRV_FOREACH(name, args+1) { + STRV_FOREACH(name, mangled_names) { const char *s; - n = unit_name_mangle(*name); - if (!n) - return log_oom(); - r = bus_method_call_with_reply ( bus, "org.freedesktop.systemd1", @@ -4660,11 +4671,9 @@ static int unit_is_enabled(DBusConnection *bus, char **args) { "GetUnitFileState", &reply, NULL, - DBUS_TYPE_STRING, &n, + DBUS_TYPE_STRING, name, DBUS_TYPE_INVALID); - free(n); - if (r) return r; @@ -4737,7 +4746,7 @@ static int systemctl_help(void) { " -f --force When enabling unit files, override existing symlinks\n" " When shutting down, execute action immediately\n" " --root=PATH Enable unit files in the specified root directory\n" - " -n --lines=INTEGER Numer of journal entries to show\n" + " -n --lines=INTEGER Number of journal entries to show\n" " -o --output=STRING Change journal output mode (short, short-monotonic,\n" " verbose, export, json, json-pretty, json-sse, cat)\n\n" "Unit Commands:\n" @@ -4800,7 +4809,7 @@ static int systemctl_help(void) { " emergency Enter system emergency mode\n" " halt Shut down and halt the system\n" " poweroff Shut down and power-off the system\n" - " reboot Shut down and reboot the system\n" + " reboot [ARG] Shut down and reboot the system\n" " kexec Shut down and reboot the system with kexec\n" " exit Request user instance exit\n" " switch-root [ROOT] [INIT] Change to a different root file system\n" @@ -4814,7 +4823,7 @@ static int systemctl_help(void) { static int halt_help(void) { - printf("%s [OPTIONS...]\n\n" + printf("%s [OPTIONS...]%s\n\n" "%s the system.\n\n" " --help Show this help\n" " --halt Halt the machine\n" @@ -4825,6 +4834,7 @@ static int halt_help(void) { " -d --no-wtmp Don't write wtmp record\n" " --no-wall Don't send wall message before halt/power-off/reboot\n", program_invocation_short_name, + arg_action == ACTION_REBOOT ? " [ARG]" : "", arg_action == ACTION_REBOOT ? "Reboot" : arg_action == ACTION_POWEROFF ? "Power off" : "Halt"); @@ -4957,7 +4967,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "output", required_argument, NULL, 'o' }, { "plain", no_argument, NULL, ARG_PLAIN }, { "state", required_argument, NULL, ARG_STATE }, - { NULL, 0, NULL, 0 } + {} }; int c; @@ -4970,8 +4980,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { switch (c) { case 'h': - systemctl_help(); - return 0; + return systemctl_help(); case ARG_VERSION: puts(PACKAGE_STRING); @@ -5217,8 +5226,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code '%c'.", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } @@ -5248,10 +5256,10 @@ static int halt_parse_argv(int argc, char *argv[]) { { "wtmp-only", no_argument, NULL, 'w' }, { "no-wtmp", no_argument, NULL, 'd' }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { NULL, 0, NULL, 0 } + {} }; - int c, runlevel; + int c, r, runlevel; assert(argc >= 0); assert(argv); @@ -5264,8 +5272,7 @@ static int halt_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - halt_help(); - return 0; + return halt_help(); case ARG_HALT: arg_action = ACTION_HALT; @@ -5306,12 +5313,18 @@ static int halt_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code '%c'.", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } - if (optind < argc) { + if (arg_action == ACTION_REBOOT && argc == optind + 1) { + r = write_string_file(REBOOT_PARAM_FILE, argv[optind]); + if (r < 0) { + log_error("Failed to write reboot param to " + REBOOT_PARAM_FILE": %s", strerror(-r)); + return r; + } + } else if (optind < argc) { log_error("Too many arguments."); return -EINVAL; } @@ -5382,7 +5395,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { { "reboot", no_argument, NULL, 'r' }, { "kexec", no_argument, NULL, 'K' }, /* not documented extension */ { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { NULL, 0, NULL, 0 } + {} }; int c, r; @@ -5394,8 +5407,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - shutdown_help(); - return 0; + return shutdown_help(); case 'H': arg_action = ACTION_HALT; @@ -5442,8 +5454,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code '%c'.", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } @@ -5478,7 +5489,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, ARG_HELP }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, - { NULL, 0, NULL, 0 } + {} }; static const struct { @@ -5510,8 +5521,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - telinit_help(); - return 0; + return telinit_help(); case ARG_NO_WALL: arg_no_wall = true; @@ -5521,8 +5531,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { return -EINVAL; default: - log_error("Unknown option code '%c'.", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } @@ -5565,7 +5574,7 @@ static int runlevel_parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, ARG_HELP }, - { NULL, 0, NULL, 0 } + {} }; int c; @@ -5577,15 +5586,14 @@ static int runlevel_parse_argv(int argc, char *argv[]) { switch (c) { case ARG_HELP: - runlevel_help(); + return runlevel_help(); return 0; case '?': return -EINVAL; default: - log_error("Unknown option code '%c'.", c); - return -EINVAL; + assert_not_reached("Unhandled option"); } } @@ -5663,94 +5671,6 @@ _pure_ static int action_to_runlevel(void) { return table[arg_action]; } -static int talk_upstart(void) { - _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL; - _cleanup_dbus_error_free_ DBusError error; - int previous, rl, r; - char - env1_buf[] = "RUNLEVEL=X", - env2_buf[] = "PREVLEVEL=X"; - char *env1 = env1_buf, *env2 = env2_buf; - const char *emit = "runlevel"; - dbus_bool_t b_false = FALSE; - DBusMessageIter iter, sub; - DBusConnection *bus; - - dbus_error_init(&error); - - if (!(rl = action_to_runlevel())) - return 0; - - if (utmp_get_runlevel(&previous, NULL) < 0) - previous = 'N'; - - if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) { - if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) { - r = 0; - goto finish; - } - - log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - if ((r = bus_check_peercred(bus)) < 0) { - log_error("Failed to verify owner of bus."); - goto finish; - } - - if (!(m = dbus_message_new_method_call( - "com.ubuntu.Upstart", - "/com/ubuntu/Upstart", - "com.ubuntu.Upstart0_6", - "EmitEvent"))) { - - log_error("Could not allocate message."); - r = -ENOMEM; - goto finish; - } - - dbus_message_iter_init_append(m, &iter); - - env1_buf[sizeof(env1_buf)-2] = rl; - env2_buf[sizeof(env2_buf)-2] = previous; - - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) || - !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) || - !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) || - !dbus_message_iter_close_container(&iter, &sub) || - !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) { - 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 (bus_error_is_no_service(&error)) { - r = -EADDRNOTAVAIL; - goto finish; - } - - log_error("Failed to issue method call: %s", bus_error_message(&error)); - r = -EIO; - goto finish; - } - - r = 1; - -finish: - if (bus) { - dbus_connection_flush(bus); - dbus_connection_close(bus); - dbus_connection_unref(bus); - } - - return r; -} - static int talk_initctl(void) { struct init_request request = {}; int r; @@ -6022,11 +5942,6 @@ static int start_with_fallback(DBusConnection *bus) { goto done; } - /* Hmm, talking to systemd via D-Bus didn't work. Then - * let's try to talk to Upstart via D-Bus. */ - if (talk_upstart() > 0) - goto done; - /* Nothing else worked, so let's try * /dev/initctl */ if (talk_initctl() > 0) @@ -6042,6 +5957,8 @@ done: static _noreturn_ void halt_now(enum action a) { + _cleanup_free_ char *param = NULL; + /* Make sure C-A-D is handled by the kernel from this * point on... */ reboot(RB_ENABLE_CAD); @@ -6059,8 +5976,14 @@ static _noreturn_ void halt_now(enum action a) { break; case ACTION_REBOOT: - log_info("Rebooting."); - reboot(RB_AUTOBOOT); + if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) == 0) { + log_info("Rebooting with arg '%s'.", param); + syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART2, param); + } else { + log_info("Rebooting."); + reboot(RB_AUTOBOOT); + } break; default: @@ -6168,6 +6091,11 @@ int main(int argc, char*argv[]) { log_parse_environment(); log_open(); + /* Explicitly not on_tty() to avoid setting cached value. + * This becomes relevant for piping output which might be + * ellipsized. */ + original_stdout_is_tty = isatty(STDOUT_FILENO); + r = parse_argv(argc, argv); if (r < 0) goto finish;