chiark / gitweb /
ask-password: add basic tty agent
[elogind.git] / src / systemctl.c
index 671745b7d001500ac5bfcf45d126960b88cdbee2..ffbe4db541ebe0be9f67c5eccd6c252e405b7bee 100644 (file)
@@ -74,6 +74,9 @@ static bool arg_full = false;
 static bool arg_force = false;
 static bool arg_defaults = 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 usec_t arg_when = 0;
 static enum action {
         ACTION_INVALID,
@@ -81,6 +84,8 @@ static enum action {
         ACTION_HALT,
         ACTION_POWEROFF,
         ACTION_REBOOT,
+        ACTION_KEXEC,
+        ACTION_EXIT,
         ACTION_RUNLEVEL2,
         ACTION_RUNLEVEL3,
         ACTION_RUNLEVEL4,
@@ -193,6 +198,7 @@ static void warn_wall(enum action action) {
                 [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!"
         };
@@ -262,7 +268,7 @@ static bool output_show_job(const struct unit_info *u) {
 }
 
 static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
-        unsigned active_len, sub_len, job_len;
+        unsigned active_len, sub_len, job_len, n_shown = 0;
         const struct unit_info *u;
 
         active_len = sizeof("ACTIVE")-1;
@@ -297,6 +303,8 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
                 if (!output_show_job(u))
                         continue;
 
+                n_shown++;
+
                 if (!streq(u->load_state, "loaded") &&
                     !streq(u->load_state, "banned")) {
                         on_loaded = ansi_highlight(true);
@@ -349,9 +357,9 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
                        "JOB    = Pending job for the unit.\n");
 
                 if (arg_all)
-                        printf("\n%u units listed.\n", c);
+                        printf("\n%u units listed.\n", n_shown);
                 else
-                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", c);
+                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
         }
 }
 
@@ -1220,12 +1228,16 @@ static enum action verb_to_action(const char *verb) {
                 return ACTION_POWEROFF;
         else if (streq(verb, "reboot"))
                 return ACTION_REBOOT;
+        else if (streq(verb, "kexec"))
+                return ACTION_KEXEC;
         else if (streq(verb, "rescue"))
                 return ACTION_RESCUE;
         else if (streq(verb, "emergency"))
                 return ACTION_EMERGENCY;
         else if (streq(verb, "default"))
                 return ACTION_DEFAULT;
+        else if (streq(verb, "exit"))
+                return ACTION_EXIT;
         else
                 return ACTION_INVALID;
 }
@@ -1236,13 +1248,15 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 [ACTION_HALT] = SPECIAL_HALT_TARGET,
                 [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
                 [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
+                [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
                 [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
                 [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
                 [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
                 [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
                 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
                 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
-                [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET
+                [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
+                [ACTION_EXIT] = SPECIAL_EXIT_TARGET
         };
 
         int r, ret = 0;
@@ -1337,6 +1351,14 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
         assert(bus);
         assert(args);
 
+        if (arg_force &&
+            (streq(args[0], "halt") ||
+             streq(args[0], "poweroff") ||
+             streq(args[0], "reboot") ||
+             streq(args[0], "kexec") ||
+             streq(args[0], "exit")))
+                return daemon_reload(bus, args, n);
+
         r = start_unit(bus, args, n);
 
         if (r >= 0)
@@ -1389,6 +1411,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
                                 puts("unknown");
 
                         dbus_error_free(&error);
+                        dbus_message_unref(m);
                         continue;
                 }
 
@@ -1467,6 +1490,71 @@ finish:
         return r;
 }
 
+static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
+        DBusMessage *m = NULL, *reply = NULL;
+        int r = 0;
+        DBusError error;
+        unsigned i;
+
+        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";
+
+        for (i = 1; i < n; i++) {
+
+                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, &args[i],
+                                              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;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
 typedef struct ExecStatusInfo {
         char *path;
         char **argv;
@@ -2968,12 +3056,16 @@ static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
                 assert(arg_action == ACTION_SYSTEMCTL);
 
                 method =
-                        streq(args[0], "clear-jobs")        ||
-                        streq(args[0], "cancel")            ? "ClearJobs" :
-                        streq(args[0], "daemon-reexec")     ? "Reexecute" :
-                        streq(args[0], "reset-failed")      ? "ResetFailed" :
-                        streq(args[0], "daemon-exit")       ? "Exit" :
-                                                              "Reload";
+                        streq(args[0], "clear-jobs")    ||
+                        streq(args[0], "cancel")        ? "ClearJobs" :
+                        streq(args[0], "daemon-reexec") ? "Reexecute" :
+                        streq(args[0], "reset-failed")  ? "ResetFailed" :
+                        streq(args[0], "halt")          ? "Halt" :
+                        streq(args[0], "poweroff")      ? "PowerOff" :
+                        streq(args[0], "reboot")        ? "Reboot" :
+                        streq(args[0], "kexec")         ? "KExec" :
+                        streq(args[0], "exit")          ? "Exit" :
+                                    /* "daemon-reload" */ "Reload";
         }
 
         if (!(m = dbus_message_new_method_call(
@@ -3900,26 +3992,30 @@ static int systemctl_help(void) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
                "Send control commands to or query the systemd manager.\n\n"
-               "  -h --help          Show this help\n"
-               "     --version       Show package version\n"
-               "  -t --type=TYPE     List only units of a particular type\n"
-               "  -p --property=NAME Show only properties by this name\n"
-               "  -a --all           Show all units/properties, including dead/empty ones\n"
-               "     --full          Don't ellipsize unit names on output\n"
-               "     --fail          When queueing a new job, fail if conflicting jobs are\n"
-               "                     pending\n"
-               "  -q --quiet         Suppress output\n"
-               "     --no-block      Do not wait until operation finished\n"
-               "     --system        Connect to system bus\n"
-               "     --session       Connect to session bus\n"
-               "     --order         When generating graph for dot, show only order\n"
-               "     --require       When generating graph for dot, show only requirement\n"
-               "     --no-wall       Don't send wall message before halt/power-off/reboot\n"
-               "     --global        Enable/disable unit files globally\n"
-               "     --no-reload     When enabling/disabling unit files, don't reload daemon\n"
-               "                     configuration\n"
-               "     --force         When enabling unit files, override existing symlinks\n"
-               "     --defaults      When disabling unit files, remove default symlinks only\n\n"
+               "  -h --help           Show this help\n"
+               "     --version        Show package version\n"
+               "  -t --type=TYPE      List only units of a particular type\n"
+               "  -p --property=NAME  Show only properties by this name\n"
+               "  -a --all            Show all units/properties, including dead/empty ones\n"
+               "     --full           Don't ellipsize unit names on output\n"
+               "     --fail           When queueing a new job, fail if conflicting jobs are\n"
+               "                      pending\n"
+               "  -q --quiet          Suppress output\n"
+               "     --no-block       Do not wait until operation finished\n"
+               "     --system          Connect to system bus\n"
+               "     --session        Connect to session bus\n"
+               "     --order          When generating graph for dot, show only order\n"
+               "     --require        When generating graph for dot, show only requirement\n"
+               "     --no-wall        Don't send wall message before halt/power-off/reboot\n"
+               "     --global         Enable/disable unit files globally\n"
+               "     --no-reload      When enabling/disabling unit files, don't reload daemon\n"
+               "                      configuration\n"
+               "     --kill-mode=MODE How to send signal\n"
+               "     --kill-who=WHO   Who to send signal to\n"
+               "  -s --signal=SIGNAL  Which signal to send\n"
+               "  -f --force          When enabling unit files, override existing symlinks\n"
+               "                      When shutting down, execute action immediately\n"
+               "     --defaults       When disabling unit files, remove default symlinks only\n\n"
                "Commands:\n"
                "  list-units                      List units\n"
                "  start [NAME...]                 Start (activate) one or more units\n"
@@ -3932,6 +4028,7 @@ static int systemctl_help(void) {
                "  reload-or-try-restart [NAME...] Reload one or more units is 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"
                "  is-active [NAME...]             Check whether units are active\n"
                "  status [NAME...|PID...]         Show runtime status of one or more units\n"
                "  show [NAME...|JOB...]           Show properties of one or more\n"
@@ -3951,16 +4048,17 @@ static int systemctl_help(void) {
                "  delete [NAME...]                Remove one or more snapshots\n"
                "  daemon-reload                   Reload systemd manager configuration\n"
                "  daemon-reexec                   Reexecute systemd manager\n"
-               "  daemon-exit                     Ask the systemd manager to quit\n"
                "  show-environment                Dump environment\n"
                "  set-environment [NAME=VALUE...] Set one or more environment variables\n"
                "  unset-environment [NAME...]     Unset one or more environment variables\n"
+               "  default                         Enter system default mode\n"
+               "  rescue                          Enter system rescue mode\n"
+               "  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"
-               "  rescue                          Enter system rescue mode\n"
-               "  emergency                       Enter system emergency mode\n"
-               "  default                         Enter system default mode\n",
+               "  kexec                           Shut down and reboot the system with kexec\n"
+               "  exit                            Ask for session termination\n",
                program_invocation_short_name);
 
         return 0;
@@ -4045,9 +4143,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_ORDER,
                 ARG_REQUIRE,
                 ARG_FULL,
-                ARG_FORCE,
                 ARG_NO_RELOAD,
-                ARG_DEFAULTS
+                ARG_DEFAULTS,
+                ARG_KILL_MODE,
+                ARG_KILL_WHO
         };
 
         static const struct option options[] = {
@@ -4066,9 +4165,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "quiet",     no_argument,       NULL, 'q'           },
                 { "order",     no_argument,       NULL, ARG_ORDER     },
                 { "require",   no_argument,       NULL, ARG_REQUIRE   },
-                { "force",     no_argument,       NULL, ARG_FORCE     },
+                { "force",     no_argument,       NULL, 'f'           },
                 { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
                 { "defaults",  no_argument,       NULL, ARG_DEFAULTS  },
+                { "kill-mode", required_argument, NULL, ARG_KILL_MODE },
+                { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
+                { "signal",    required_argument, NULL, 's'           },
                 { NULL,        0,                 NULL, 0             }
         };
 
@@ -4077,7 +4179,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "ht:p:aq", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "ht:p:aqfs:", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -4151,7 +4253,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_quiet = true;
                         break;
 
-                case ARG_FORCE:
+                case 'f':
                         arg_force = true;
                         break;
 
@@ -4168,6 +4270,21 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_defaults = true;
                         break;
 
+                case ARG_KILL_WHO:
+                        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);
+                                return -EINVAL;
+                        }
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -4761,6 +4878,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "force-reload",          MORE,  2, start_unit        }, /* For compatibility with SysV */
                 { "condrestart",           MORE,  2, start_unit        }, /* For compatibility with RH */
                 { "isolate",               EQUAL, 2, start_unit        },
+                { "kill",                  MORE,  2, kill_unit         },
                 { "is-active",             MORE,  2, check_unit        },
                 { "check",                 MORE,  2, check_unit        },
                 { "show",                  MORE,  1, show              },
@@ -4772,16 +4890,17 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "delete",                MORE,  2, delete_snapshot   },
                 { "daemon-reload",         EQUAL, 1, daemon_reload     },
                 { "daemon-reexec",         EQUAL, 1, daemon_reload     },
-                { "daemon-exit",           EQUAL, 1, daemon_reload     },
                 { "show-environment",      EQUAL, 1, show_enviroment   },
                 { "set-environment",       MORE,  2, set_environment   },
                 { "unset-environment",     MORE,  2, set_environment   },
                 { "halt",                  EQUAL, 1, start_special     },
                 { "poweroff",              EQUAL, 1, start_special     },
                 { "reboot",                EQUAL, 1, start_special     },
+                { "kexec",                 EQUAL, 1, start_special     },
                 { "default",               EQUAL, 1, start_special     },
                 { "rescue",                EQUAL, 1, start_special     },
                 { "emergency",             EQUAL, 1, start_special     },
+                { "exit",                  EQUAL, 1, start_special     },
                 { "reset-failed",          MORE,  1, reset_failed      },
                 { "enable",                MORE,  2, enable_unit       },
                 { "disable",               MORE,  2, enable_unit       },