chiark / gitweb /
systemctl: introduce systemctl kill
authorLennart Poettering <lennart@poettering.net>
Fri, 22 Oct 2010 14:11:50 +0000 (16:11 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 22 Oct 2010 14:11:50 +0000 (16:11 +0200)
17 files changed:
man/systemctl.xml
src/bus-errors.h
src/dbus-manager.c
src/dbus-unit.c
src/dbus-unit.h
src/execute.c
src/execute.h
src/load-fragment.c
src/mount.c
src/service.c
src/socket.c
src/swap.c
src/systemctl.c
src/unit.c
src/unit.h
src/util.c
src/util.h

index 2eb153e..ca654ca 100644 (file)
                         </varlistentry>
 
                         <varlistentry>
+                                <term><option>--kill-mode=</option></term>
+
+                                <listitem><para>When used with
+                                <command>kill</command>, choose the
+                                mode how to kill the selected
+                                processes. Must be one of
+                                <option>control-group</option>,
+                                <option>process-group</option> or
+                                <option>process</option> to select
+                                whether to kill the entire control
+                                group, the process group or only the
+                                selected process itself. If ommitted
+                                defaults to
+                                <option>control-group</option> if
+                                <option>--kill-who=all</option> is
+                                set, or <option>process</option>
+                                otherwise. You probably never need to
+                                use this switch.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><option>--kill-who=</option></term>
+
+                                <listitem><para>When used with
+                                <command>kill</command>, choose which
+                                processes to kill. Must be one of
+                                <option>main</option>,
+                                <option>control</option> or
+                                <option>all</option> to select whether
+                                to kill only the main process of the
+                                unit, the control process or all
+                                processes of the unit. If ommitted
+                                defaults to
+                                <option>all</option>.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
+                                <term><option>---signal=</option></term>
+                                <term><option>-s</option></term>
+
+                                <listitem><para>When used with
+                                <command>kill</command>, choose which
+                                signal to send to selected
+                                processes. Must be one of the well
+                                know signal specifiers such as
+                                SIGTERM, SIGINT or SIGSTOP. If
+                                ommitted defaults to
+                                <option>SIGTERM</option>.</para></listitem>
+                        </varlistentry>
+
+                        <varlistentry>
                                 <term><option>--force</option></term>
 
                                 <listitem><para>When used with
                                 for details.</para></listitem>
                         </varlistentry>
                         <varlistentry>
+                                <term><command>kill [NAME...]</command></term>
+
+                                <listitem><para>Send a signal to one
+                                or more processes of the unit. Use
+                                <option>--kill-who=</option> to select
+                                which process to kill. Use
+                                <option>--kill-mode=</option> to
+                                select the kill mode and
+                                <option>--signal=</option> to select
+                                the signal to send.</para></listitem>
+                        </varlistentry>
+                        <varlistentry>
                                 <term><command>is-active [NAME...]</command></term>
 
                                 <listitem><para>Check whether any of
index d6ccd74..82d4e99 100644 (file)
@@ -43,6 +43,7 @@
 #define BUS_ERROR_TRANSACTION_JOBS_CONFLICTING "org.freedesktop.systemd1.TransactionJobsConflicting"
 #define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
 #define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
+#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess"
 
 static inline const char *bus_error(const DBusError *e, int r) {
         if (e && e->message)
index 5263843..c195031 100644 (file)
         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
         "  </method>\n"                                                 \
+        "  <method name=\"KillUnit\">\n"                                \
+        "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
+        "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
+        "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
+        "   <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n"       \
+        "  </method>\n"                                                 \
         "  <method name=\"ResetFailedUnit\">\n"                         \
         "   <arg name=\"name\" type=\"s\" direction=\"in\"/>\n"         \
         "  </method>\n"                                                 \
@@ -430,6 +436,40 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "ReloadOrTryRestartUnit")) {
                 reload_if_possible = true;
                 job_type = JOB_TRY_RESTART;
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "KillUnit")) {
+                const char *name, *swho, *smode;
+                int32_t signo;
+                Unit *u;
+                KillMode mode;
+                KillWho who;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &name,
+                                    DBUS_TYPE_STRING, &swho,
+                                    DBUS_TYPE_STRING, &smode,
+                                    DBUS_TYPE_INT32, &signo,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(m, connection, message, &error, -EINVAL);
+
+                if ((mode = kill_mode_from_string(smode)) < 0 ||
+                    (who = kill_who_from_string(swho)) < 0 ||
+                    signo <= 0 ||
+                    signo >= _NSIG)
+                        return bus_send_error_reply(m, connection, message, &error, -EINVAL);
+
+                if (!(u = manager_get_unit(m, name))) {
+                        dbus_set_error(&error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s is not loaded.", name);
+                        return bus_send_error_reply(m, connection, message, &error, -ENOENT);
+                }
+
+                if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
+                        return bus_send_error_reply(m, connection, message, &error, r);
+
+                if (!(reply = dbus_message_new_method_return(message)))
+                        goto oom;
+
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetJob")) {
                 uint32_t id;
                 Job *j;
index 45eba8a..47aa8d3 100644 (file)
@@ -367,6 +367,34 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ReloadOrTryRestart")) {
                 reload_if_possible = true;
                 job_type = JOB_TRY_RESTART;
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "Kill")) {
+                const char *swho, *smode;
+                int32_t signo;
+                KillMode mode;
+                KillWho who;
+                int r;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &swho,
+                                    DBUS_TYPE_STRING, &smode,
+                                    DBUS_TYPE_INT32, &signo,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(m, connection, message, &error, -EINVAL);
+
+                if ((mode = kill_mode_from_string(smode)) < 0 ||
+                    (who = kill_who_from_string(swho)) < 0 ||
+                    signo <= 0 ||
+                    signo >= _NSIG)
+                        return bus_send_error_reply(m, connection, message, &error, -EINVAL);
+
+                if ((r = unit_kill(u, who, mode, signo, &error)) < 0)
+                        return bus_send_error_reply(m, connection, message, &error, r);
+
+                if (!(reply = dbus_message_new_method_return(message)))
+                        goto oom;
+
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Unit", "ResetFailed")) {
 
                 unit_reset_failed(u);
index 800498b..94e084f 100644 (file)
         "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
         "   <arg name=\"job\" type=\"o\" direction=\"out\"/>\n"         \
         "  </method>\n"                                                 \
-        "  <method name=\"ResetFailed\"/>\n"                       \
+        "  <method name=\"Kill\">\n"                                    \
+        "   <arg name=\"who\" type=\"s\" direction=\"in\"/>\n"          \
+        "   <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n"         \
+        "   <arg name=\"signal\" type=\"i\" direction=\"in\"/>\n"       \
+        "  </method>\n"                                                 \
+        "  <method name=\"ResetFailed\"/>\n"                            \
         "  <property name=\"Id\" type=\"s\" access=\"read\"/>\n"        \
         "  <property name=\"Names\" type=\"as\" access=\"read\"/>\n"    \
         "  <property name=\"Following\" type=\"s\" access=\"read\"/>\n" \
index b5afa68..48e55ea 100644 (file)
@@ -1797,6 +1797,8 @@ static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
         [EXEC_INPUT_SOCKET] = "socket"
 };
 
+DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
+
 static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
         [EXEC_OUTPUT_INHERIT] = "inherit",
         [EXEC_OUTPUT_NULL] = "null",
@@ -1808,4 +1810,19 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
 
-DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
+static const char* const kill_mode_table[_KILL_MODE_MAX] = {
+        [KILL_CONTROL_GROUP] = "control-group",
+        [KILL_PROCESS_GROUP] = "process-group",
+        [KILL_PROCESS] = "process",
+        [KILL_NONE] = "none"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
+
+static const char* const kill_who_table[_KILL_WHO_MAX] = {
+        [KILL_MAIN] = "main",
+        [KILL_CONTROL] = "control",
+        [KILL_ALL] = "all"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
index ed61e3c..dd84c3d 100644 (file)
@@ -55,6 +55,14 @@ typedef enum KillMode {
         _KILL_MODE_INVALID = -1
 } KillMode;
 
+typedef enum KillWho {
+        KILL_MAIN,
+        KILL_CONTROL,
+        KILL_ALL,
+        _KILL_WHO_MAX,
+        _KILL_WHO_INVALID = -1
+} KillWho;
+
 typedef enum ExecInput {
         EXEC_INPUT_NULL,
         EXEC_INPUT_TTY,
@@ -202,4 +210,10 @@ int exec_output_from_string(const char *s);
 const char* exec_input_to_string(ExecInput i);
 int exec_input_from_string(const char *s);
 
+const char *kill_mode_to_string(KillMode k);
+KillMode kill_mode_from_string(const char *s);
+
+const char *kill_who_to_string(KillWho k);
+KillWho kill_who_from_string(const char *s);
+
 #endif
index 0502fc4..74fe786 100644 (file)
@@ -1047,11 +1047,7 @@ static int config_parse_kill_signal(
         assert(rvalue);
         assert(sig);
 
-        if ((r = signal_from_string(rvalue)) <= 0)
-                if (startswith(rvalue, "SIG"))
-                        r = signal_from_string(rvalue+3);
-
-        if (r <= 0) {
+        if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
                 log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
                 return 0;
         }
index bbc29d8..3fc0f13 100644 (file)
@@ -35,6 +35,7 @@
 #include "unit-name.h"
 #include "dbus-mount.h"
 #include "special.h"
+#include "bus-errors.h"
 
 static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
         [MOUNT_DEAD] = UNIT_INACTIVE,
@@ -1636,6 +1637,52 @@ static void mount_reset_failed(Unit *u) {
         m->failure = false;
 }
 
+static int mount_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+        Mount *m = MOUNT(u);
+        int r = 0;
+        Set *pid_set = NULL;
+
+        assert(m);
+
+        if (who == KILL_MAIN) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Mount units have no main processes");
+                return -EINVAL;
+        }
+
+        if (m->control_pid <= 0 && who == KILL_CONTROL) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+                return -ENOENT;
+        }
+
+        if (m->control_pid > 0)
+                if (kill(mode == KILL_PROCESS_GROUP ? -m->control_pid : m->control_pid, signo) < 0)
+                        r = -errno;
+
+        if (mode == KILL_CONTROL_GROUP) {
+                int q;
+
+                if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+                        return -ENOMEM;
+
+                /* Exclude the control pid from being killed via the cgroup */
+                if (m->control_pid > 0)
+                        if ((q = set_put(pid_set, LONG_TO_PTR(m->control_pid))) < 0) {
+                                r = q;
+                                goto finish;
+                        }
+
+                if ((q = cgroup_bonding_kill_list(m->meta.cgroup_bondings, signo, pid_set)) < 0)
+                        if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+                                r = q;
+        }
+
+finish:
+        if (pid_set)
+                set_free(pid_set);
+
+        return r;
+}
+
 static const char* const mount_state_table[_MOUNT_STATE_MAX] = {
         [MOUNT_DEAD] = "dead",
         [MOUNT_MOUNTING] = "mounting",
@@ -1682,6 +1729,8 @@ const UnitVTable mount_vtable = {
         .stop = mount_stop,
         .reload = mount_reload,
 
+        .kill = mount_kill,
+
         .serialize = mount_serialize,
         .deserialize_item = mount_deserialize_item,
 
index 3ebe60e..6d6c540 100644 (file)
@@ -3145,6 +3145,62 @@ static void service_reset_failed(Unit *u) {
         s->failure = false;
 }
 
+static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+        Service *s = SERVICE(u);
+        int r = 0;
+        Set *pid_set = NULL;
+
+        assert(s);
+
+        if (s->main_pid <= 0 && who == KILL_MAIN) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
+                return -EINVAL;
+        }
+
+        if (s->control_pid <= 0 && who == KILL_CONTROL) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+                return -ENOENT;
+        }
+
+        if (s->control_pid > 0)
+                if (kill(mode == KILL_PROCESS_GROUP ? -s->control_pid : s->control_pid, signo) < 0)
+                        r = -errno;
+
+        if (s->main_pid > 0)
+                if (kill(mode == KILL_PROCESS_GROUP ? -s->main_pid : s->main_pid, signo) < 0)
+                        r = -errno;
+
+        if (mode == KILL_CONTROL_GROUP) {
+                int q;
+
+                if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+                        return -ENOMEM;
+
+                /* Exclude the control/main pid from being killed via the cgroup */
+                if (s->control_pid > 0)
+                        if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
+                                r = q;
+                                goto finish;
+                        }
+
+                if (s->main_pid > 0)
+                        if ((q = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) {
+                                r = q;
+                                goto finish;
+                        }
+
+                if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, pid_set)) < 0)
+                        if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+                                r = q;
+        }
+
+finish:
+        if (pid_set)
+                set_free(pid_set);
+
+        return r;
+}
+
 static const char* const service_state_table[_SERVICE_STATE_MAX] = {
         [SERVICE_DEAD] = "dead",
         [SERVICE_START_PRE] = "start-pre",
@@ -3222,6 +3278,8 @@ const UnitVTable service_vtable = {
 
         .can_reload = service_can_reload,
 
+        .kill = service_kill,
+
         .serialize = service_serialize,
         .deserialize_item = service_deserialize_item,
 
index fc6088c..e8c016c 100644 (file)
@@ -1784,6 +1784,52 @@ static void socket_reset_failed(Unit *u) {
         s->failure = false;
 }
 
+static int socket_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+        Socket *s = SOCKET(u);
+        int r = 0;
+        Set *pid_set = NULL;
+
+        assert(s);
+
+        if (who == KILL_MAIN) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Socket units have no main processes");
+                return -EINVAL;
+        }
+
+        if (s->control_pid <= 0 && who == KILL_CONTROL) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+                return -ENOENT;
+        }
+
+        if (s->control_pid > 0)
+                if (kill(mode == KILL_PROCESS_GROUP ? -s->control_pid : s->control_pid, signo) < 0)
+                        r = -errno;
+
+        if (mode == KILL_CONTROL_GROUP) {
+                int q;
+
+                if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+                        return -ENOMEM;
+
+                /* Exclude the control pid from being killed via the cgroup */
+                if (s->control_pid > 0)
+                        if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
+                                r = q;
+                                goto finish;
+                        }
+
+                if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, pid_set)) < 0)
+                        if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+                                r = q;
+        }
+
+finish:
+        if (pid_set)
+                set_free(pid_set);
+
+        return r;
+}
+
 static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
         [SOCKET_DEAD] = "dead",
         [SOCKET_START_PRE] = "start-pre",
@@ -1817,6 +1863,8 @@ const UnitVTable socket_vtable = {
         .done = socket_done,
         .load = socket_load,
 
+        .kill = socket_kill,
+
         .coldplug = socket_coldplug,
 
         .dump = socket_dump,
index cf9644f..f7f9530 100644 (file)
@@ -35,6 +35,7 @@
 #include "unit-name.h"
 #include "dbus-swap.h"
 #include "special.h"
+#include "bus-errors.h"
 
 static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
         [SWAP_DEAD] = UNIT_INACTIVE,
@@ -1213,6 +1214,52 @@ static void swap_reset_failed(Unit *u) {
         s->failure = false;
 }
 
+static int swap_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+        Swap *s = SWAP(u);
+        int r = 0;
+        Set *pid_set = NULL;
+
+        assert(s);
+
+        if (who == KILL_MAIN) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "Swap units have no main processes");
+                return -EINVAL;
+        }
+
+        if (s->control_pid <= 0 && who == KILL_CONTROL) {
+                dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+                return -ENOENT;
+        }
+
+        if (s->control_pid > 0)
+                if (kill(mode == KILL_PROCESS_GROUP ? -s->control_pid : s->control_pid, signo) < 0)
+                        r = -errno;
+
+        if (mode == KILL_CONTROL_GROUP) {
+                int q;
+
+                if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+                        return -ENOMEM;
+
+                /* Exclude the control pid from being killed via the cgroup */
+                if (s->control_pid > 0)
+                        if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
+                                r = q;
+                                goto finish;
+                        }
+
+                if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, pid_set)) < 0)
+                        if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+                                r = q;
+        }
+
+finish:
+        if (pid_set)
+                set_free(pid_set);
+
+        return r;
+}
+
 static const char* const swap_state_table[_SWAP_STATE_MAX] = {
         [SWAP_DEAD] = "dead",
         [SWAP_ACTIVATING] = "activating",
@@ -1253,6 +1300,8 @@ const UnitVTable swap_vtable = {
         .start = swap_start,
         .stop = swap_stop,
 
+        .kill = swap_kill,
+
         .serialize = swap_serialize,
         .deserialize_item = swap_deserialize_item,
 
index a037690..ffbe4db 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,
@@ -1408,6 +1411,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
                                 puts("unknown");
 
                         dbus_error_free(&error);
+                        dbus_message_unref(m);
                         continue;
                 }
 
@@ -1486,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;
@@ -3923,27 +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"
-               "  -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"
+               "  -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"
@@ -3956,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"
@@ -4071,7 +4144,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_REQUIRE,
                 ARG_FULL,
                 ARG_NO_RELOAD,
-                ARG_DEFAULTS
+                ARG_DEFAULTS,
+                ARG_KILL_MODE,
+                ARG_KILL_WHO
         };
 
         static const struct option options[] = {
@@ -4093,6 +4168,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "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             }
         };
 
@@ -4101,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:aqf", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "ht:p:aqfs:", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -4192,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;
 
@@ -4785,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              },
index ab6eb20..d2f6080 100644 (file)
@@ -2252,6 +2252,22 @@ bool unit_name_is_valid(const char *n, bool template_ok) {
         return unit_name_is_valid_no_type(n, template_ok);
 }
 
+int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error) {
+        assert(u);
+        assert(w >= 0 && w < _KILL_WHO_MAX);
+        assert(m >= 0 && m < _KILL_MODE_MAX);
+        assert(signo > 0);
+        assert(signo < _NSIG);
+
+        if (m == KILL_NONE)
+                return 0;
+
+        if (!UNIT_VTABLE(u)->kill)
+                return -ENOTSUP;
+
+        return UNIT_VTABLE(u)->kill(u, w, m, signo, error);
+}
+
 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
         [UNIT_STUB] = "stub",
         [UNIT_LOADED] = "loaded",
@@ -2292,12 +2308,3 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
-
-static const char* const kill_mode_table[_KILL_MODE_MAX] = {
-        [KILL_CONTROL_GROUP] = "control-group",
-        [KILL_PROCESS_GROUP] = "process-group",
-        [KILL_PROCESS] = "process",
-        [KILL_NONE] = "none"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode);
index fa869ec..3f0ef01 100644 (file)
@@ -277,6 +277,8 @@ struct UnitVTable {
         int (*stop)(Unit *u);
         int (*reload)(Unit *u);
 
+        int (*kill)(Unit *u, KillWho w, KillMode m, int signo, DBusError *error);
+
         bool (*can_reload)(Unit *u);
 
         /* Write all data that cannot be restored from other sources
@@ -458,6 +460,8 @@ int unit_start(Unit *u);
 int unit_stop(Unit *u);
 int unit_reload(Unit *u);
 
+int unit_kill(Unit *u, KillWho w, KillMode m, int signo, DBusError *error);
+
 void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns);
 
 int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w);
@@ -520,7 +524,4 @@ UnitActiveState unit_active_state_from_string(const char *s);
 const char *unit_dependency_to_string(UnitDependency i);
 UnitDependency unit_dependency_from_string(const char *s);
 
-const char *kill_mode_to_string(KillMode k);
-KillMode kill_mode_from_string(const char *s);
-
 #endif
index 58f67b0..cf3cf29 100644 (file)
@@ -3339,6 +3339,17 @@ DIR *xopendirat(int fd, const char *name) {
         return fdopendir(openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
 }
 
+int signal_from_string_try_harder(const char *s) {
+        int signo;
+        assert(s);
+
+        if ((signo = signal_from_string(s)) <= 0)
+                if (startswith(s, "SIG"))
+                        return signal_from_string(s+3);
+
+        return signo;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
index e87fb23..b257ee5 100644 (file)
@@ -391,4 +391,6 @@ int ip_tos_from_string(const char *s);
 const char *signal_to_string(int i);
 int signal_from_string(const char *s);
 
+int signal_from_string_try_harder(const char *s);
+
 #endif