diff --git a/man/systemctl.xml b/man/systemctl.xml index 2eb153e73..ca654ca5b 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -234,6 +234,57 @@ changes. + + + + When used with + kill, choose the + mode how to kill the selected + processes. Must be one of + , + or + to select + whether to kill the entire control + group, the process group or only the + selected process itself. If ommitted + defaults to + if + is + set, or + otherwise. You probably never need to + use this switch. + + + + + + When used with + kill, choose which + processes to kill. Must be one of + , + or + 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 + . + + + + + + + When used with + kill, 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 + . + + @@ -358,6 +409,18 @@ systemd.unit5 for details. + + kill [NAME...] + + Send a signal to one + or more processes of the unit. Use + to select + which process to kill. Use + to + select the kill mode and + to select + the signal to send. + is-active [NAME...] diff --git a/src/bus-errors.h b/src/bus-errors.h index d6ccd7407..82d4e99ee 100644 --- a/src/bus-errors.h +++ b/src/bus-errors.h @@ -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) diff --git a/src/dbus-manager.c b/src/dbus-manager.c index 52638435c..c1950319f 100644 --- a/src/dbus-manager.c +++ b/src/dbus-manager.c @@ -84,6 +84,12 @@ " \n" \ " \n" \ " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \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; diff --git a/src/dbus-unit.c b/src/dbus-unit.c index 45eba8ab5..47aa8d338 100644 --- a/src/dbus-unit.c +++ b/src/dbus-unit.c @@ -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); diff --git a/src/dbus-unit.h b/src/dbus-unit.h index 800498b8f..94e084f6e 100644 --- a/src/dbus-unit.h +++ b/src/dbus-unit.h @@ -56,7 +56,12 @@ " \n" \ " \n" \ " \n" \ - " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" \ " \n" \ " \n" \ diff --git a/src/execute.c b/src/execute.c index b5afa6810..48e55ea4c 100644 --- a/src/execute.c +++ b/src/execute.c @@ -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); diff --git a/src/execute.h b/src/execute.h index ed61e3c62..dd84c3d29 100644 --- a/src/execute.h +++ b/src/execute.h @@ -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 diff --git a/src/load-fragment.c b/src/load-fragment.c index 0502fc45d..74fe78647 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -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; } diff --git a/src/mount.c b/src/mount.c index bbc29d882..3fc0f1371 100644 --- a/src/mount.c +++ b/src/mount.c @@ -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, diff --git a/src/service.c b/src/service.c index 3ebe60e46..6d6c540a7 100644 --- a/src/service.c +++ b/src/service.c @@ -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, diff --git a/src/socket.c b/src/socket.c index fc6088c26..e8c016cc7 100644 --- a/src/socket.c +++ b/src/socket.c @@ -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, diff --git a/src/swap.c b/src/swap.c index cf9644fc6..f7f9530a0 100644 --- a/src/swap.c +++ b/src/swap.c @@ -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, diff --git a/src/systemctl.c b/src/systemctl.c index a03769034..ffbe4db54 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -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 }, diff --git a/src/unit.c b/src/unit.c index ab6eb2022..d2f60801a 100644 --- a/src/unit.c +++ b/src/unit.c @@ -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); diff --git a/src/unit.h b/src/unit.h index fa869ece8..3f0ef01c5 100644 --- a/src/unit.h +++ b/src/unit.h @@ -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 diff --git a/src/util.c b/src/util.c index 58f67b0d1..cf3cf292a 100644 --- a/src/util.c +++ b/src/util.c @@ -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", diff --git a/src/util.h b/src/util.h index e87fb23bf..b257ee560 100644 --- a/src/util.h +++ b/src/util.h @@ -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