#include <sys/socket.h>
#include <sys/stat.h>
#include <stddef.h>
+#include <sys/prctl.h>
#include <dbus/dbus.h>
static bool arg_full = false;
static bool arg_force = false;
static bool arg_defaults = false;
+static bool arg_ask_password = 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,
ACTION_HALT,
ACTION_POWEROFF,
ACTION_REBOOT,
+ ACTION_KEXEC,
+ ACTION_EXIT,
ACTION_RUNLEVEL2,
ACTION_RUNLEVEL3,
ACTION_RUNLEVEL4,
return t;
}
+static void spawn_ask_password_agent(void) {
+ pid_t parent, child;
+
+ /* We check STDIN here, not STDOUT, since this is about input,
+ * not output */
+ if (!isatty(STDIN_FILENO))
+ return;
+
+ if (!arg_ask_password)
+ return;
+
+ parent = getpid();
+
+ /* Spawns a temporary TTY agent, making sure it goes away when
+ * we go away */
+
+ if ((child = fork()) < 0)
+ return;
+
+ if (child == 0) {
+ /* In the child */
+
+ const char * const args[] = {
+ SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH,
+ "--watch",
+ NULL
+ };
+
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ _exit(EXIT_FAILURE);
+
+ /* Check whether our parent died before we were able
+ * to set the death signal */
+ if (getppid() != parent)
+ _exit(EXIT_SUCCESS);
+
+ execv(args[0], (char **) args);
+ _exit(EXIT_FAILURE);
+ }
+}
+
static const char *ansi_highlight(bool b) {
if (!on_tty())
[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!"
};
}
if (*p) {
- utmp_wall(p);
+ utmp_wall(p, NULL);
free(p);
return;
}
if (!table[action])
return;
- utmp_wall(table[action]);
+ utmp_wall(table[action], NULL);
}
struct unit_info {
}
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;
if (!output_show_job(u))
continue;
- if (!streq(u->load_state, "loaded")) {
+ n_shown++;
+
+ if (!streq(u->load_state, "loaded") &&
+ !streq(u->load_state, "banned")) {
on_loaded = ansi_highlight(true);
off_loaded = ansi_highlight(false);
} else
"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);
}
}
;
if (!arg_quiet && d.failed)
- log_error("Job failed, see system logs for details.");
+ log_error("Job failed. See system logs and 'systemctl status' for details.");
r = d.failed ? -EIO : 0;
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;
}
[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;
assert(bus);
+ spawn_ask_password_agent();
+
if (arg_action == ACTION_SYSTEMCTL) {
method =
streq(args[0], "stop") ? "StopUnit" :
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)
puts("unknown");
dbus_error_free(&error);
+ dbus_message_unref(m);
continue;
}
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;
const char *sub_state;
const char *description;
+ const char *following;
const char *path;
const char *default_control_group;
printf("\n");
- if (streq_ptr(i->load_state, "failed")) {
+ if (i->following)
+ printf("\t Follow: unit currently follows state of %s\n", i->following);
+
+ if (streq_ptr(i->load_state, "failed") ||
+ streq_ptr(i->load_state, "banned")) {
on = ansi_highlight(true);
off = ansi_highlight(false);
} else
s2 = format_timestamp(since2, sizeof(since2), timestamp);
if (s1)
- printf(" since [%s; %s]\n", s2, s1);
+ printf(" since %s; %s\n", s2, s1);
else if (s2)
- printf(" since [%s]\n", s2);
+ printf(" since %s\n", s2);
else
printf("\n");
if (i->sysfs_path)
printf("\t Device: %s\n", i->sysfs_path);
- else if (i->where)
+ if (i->where)
printf("\t Where: %s\n", i->where);
- else if (i->what)
+ if (i->what)
printf("\t What: %s\n", i->what);
if (i->accept)
i->where = s;
else if (streq(name, "What"))
i->what = s;
+ else if (streq(name, "Following"))
+ i->following = s;
}
break;
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(
static Hashmap *will_install = NULL, *have_installed = NULL;
static Set *remove_symlinks_to = NULL;
+static unsigned n_symlinks = 0;
static void install_info_free(InstallInfo *i) {
assert(i);
assert(will_install);
- if (!unit_name_is_valid_no_type(name)) {
+ if (!unit_name_is_valid_no_type(name, true)) {
log_warning("Unit name %s is not a valid unit name.", name);
return -EINVAL;
}
STRV_FOREACH(s, i->aliases) {
- if (!unit_name_is_valid_no_type(*s)) {
- log_error("Invalid name %s.", *s);
- r = -EINVAL;
- goto finish;
- }
-
free(alias_path);
if (!(alias_path = path_make_absolute(*s, config_path))) {
log_error("Out of memory");
assert(config_path);
STRV_FOREACH(s, i->wanted_by) {
- if (!unit_name_is_valid_no_type(*s)) {
+ if (!unit_name_is_valid_no_type(*s, true)) {
log_error("Invalid name %s.", *s);
r = -EINVAL;
goto finish;
return r;
}
+ n_symlinks += strv_length(i->aliases);
+ n_symlinks += strv_length(i->wanted_by);
+
fclose(f);
if ((r = install_info_symlink_alias(verb, i, config_path)) != 0)
if (streq(verb, "is-enabled"))
r = r > 0 ? 0 : -ENOENT;
- else if (bus &&
- /* Don't try to reload anything if the user asked us to not do this */
- !arg_no_reload &&
- /* Don't try to reload anything when updating a unit globally */
- !arg_global &&
- /* Don't try to reload anything if we are called for system changes but the system wasn't booted with systemd */
- (arg_session || sd_booted() > 0) &&
- /* Don't try to reload anything if we are running in a chroot environment */
- (arg_session || running_in_chroot() <= 0) ) {
- int q;
+ else {
+ if (n_symlinks <= 0)
+ log_warning("Unit files contain no applicable installation information. Ignoring.");
+
+ if (bus &&
+ /* Don't try to reload anything if the user asked us to not do this */
+ !arg_no_reload &&
+ /* Don't try to reload anything when updating a unit globally */
+ !arg_global &&
+ /* Don't try to reload anything if we are called for system changes but the system wasn't booted with systemd */
+ (arg_session || sd_booted() > 0) &&
+ /* Don't try to reload anything if we are running in a chroot environment */
+ (arg_session || running_in_chroot() <= 0) ) {
+ int q;
- if ((q = daemon_reload(bus, args, n)) < 0)
- r = q;
+ if ((q = daemon_reload(bus, args, n)) < 0)
+ r = q;
+ }
}
finish:
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"
+ " --no-ask-password\n"
+ " Do not ask for system passwords\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"
" 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"
" 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;
ARG_ORDER,
ARG_REQUIRE,
ARG_FULL,
- ARG_FORCE,
ARG_NO_RELOAD,
- ARG_DEFAULTS
+ ARG_DEFAULTS,
+ ARG_KILL_MODE,
+ ARG_KILL_WHO,
+ ARG_NO_ASK_PASSWORD
};
static const struct option options[] = {
{ "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' },
+ { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ NULL, 0, NULL, 0 }
};
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "ht:p:aq", options, NULL)) >= 0) {
+ /* Only when running as systemctl we ask for passwords */
+ arg_ask_password = true;
+
+ while ((c = getopt_long(argc, argv, "ht:p:aqfs:", options, NULL)) >= 0) {
switch (c) {
arg_quiet = true;
break;
- case ARG_FORCE:
+ case 'f':
arg_force = true;
break;
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 ARG_NO_ASK_PASSWORD:
+ arg_ask_password = false;
+ break;
+
case '?':
return -EINVAL;
{ "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 },
{ "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 },