Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ Lesser General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <sys/prctl.h>
#include <dbus/dbus.h>
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-shutdown.h>
+
#include "log.h"
#include "util.h"
#include "macro.h"
#include "list.h"
#include "path-lookup.h"
#include "conf-parser.h"
-#include "sd-daemon.h"
-#include "shutdownd.h"
#include "exit-status.h"
#include "bus-errors.h"
#include "build.h"
#include "unit-name.h"
#include "pager.h"
-#include "spawn-agent.h"
+#include "spawn-ask-password-agent.h"
+#include "spawn-polkit-agent.h"
#include "install.h"
+#include "logs-show.h"
static const char *arg_type = NULL;
static char **arg_property = NULL;
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
static bool arg_immediate = false;
static bool arg_no_block = false;
+static bool arg_no_legend = false;
static bool arg_no_pager = false;
static bool arg_no_wtmp = false;
static bool arg_no_sync = false;
static bool arg_dry = false;
static bool arg_quiet = false;
static bool arg_full = false;
-static bool arg_force = false;
-static bool arg_ask_password = false;
+static int arg_force = 0;
+static bool arg_ask_password = true;
static bool arg_failed = false;
static bool arg_runtime = false;
static char **arg_wall = NULL;
TRANSPORT_POLKIT
} arg_transport = TRANSPORT_NORMAL;
static const char *arg_host = NULL;
+static bool arg_follow = false;
+static unsigned arg_lines = 10;
+static OutputMode arg_output = OUTPUT_SHORT;
static bool private_bus = false;
static int daemon_reload(DBusConnection *bus, char **args);
+static void halt_now(enum action a);
static bool on_tty(void) {
static int t = -1;
}
static void pager_open_if_enabled(void) {
+
+ /* Cache result before we open the pager */
on_tty();
if (arg_no_pager)
pager_open();
}
-static void agent_open_if_enabled(void) {
+static void ask_password_agent_open_if_enabled(void) {
/* Open the password agent as a child process if necessary */
if (arg_scope != UNIT_FILE_SYSTEM)
return;
- agent_open();
+ ask_password_agent_open();
+}
+
+static void polkit_agent_open_if_enabled(void) {
+
+ /* Open the polkit agent as a child process if necessary */
+
+ if (!arg_ask_password)
+ return;
+
+ if (arg_scope != UNIT_FILE_SYSTEM)
+ return;
+
+ polkit_agent_open();
}
-static const char *ansi_highlight(bool b) {
+static const char *ansi_highlight_red(bool b) {
if (!on_tty())
return "";
- return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
+ return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF;
}
static const char *ansi_highlight_green(bool b) {
return EXIT_FAILURE;
}
-static void warn_wall(enum action action) {
+static void warn_wall(enum action a) {
static const char *table[_ACTION_MAX] = {
[ACTION_HALT] = "The system is going down for system halt NOW!",
[ACTION_REBOOT] = "The system is going down for reboot NOW!",
if (arg_wall) {
char *p;
- if (!(p = strv_join(arg_wall, " "))) {
+ p = strv_join(arg_wall, " ");
+ if (!p) {
log_error("Failed to join strings.");
return;
}
free(p);
}
- if (!table[action])
+ if (!table[a])
return;
- utmp_wall(table[action], NULL);
+ utmp_wall(table[a], NULL);
}
static bool avoid_bus(void) {
}
static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
- unsigned active_len, sub_len, job_len, n_shown = 0;
+ unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0;
const struct unit_info *u;
+ max_id_len = sizeof("UNIT")-1;
active_len = sizeof("ACTIVE")-1;
sub_len = sizeof("SUB")-1;
job_len = sizeof("JOB")-1;
+ desc_len = 0;
for (u = unit_infos; u < unit_infos + c; u++) {
if (!output_show_unit(u))
continue;
+ max_id_len = MAX(max_id_len, strlen(u->id));
active_len = MAX(active_len, strlen(u->active_state));
sub_len = MAX(sub_len, strlen(u->sub_state));
if (u->job_id != 0)
job_len = MAX(job_len, strlen(u->job_type));
}
- if (on_tty()) {
- printf("%-25s %-6s %-*s %-*s %-*s", "UNIT", "LOAD",
+ if (!arg_full) {
+ unsigned basic_len;
+ id_len = MIN(max_id_len, 25);
+ basic_len = 5 + id_len + 6 + active_len + sub_len + job_len;
+ if (basic_len < (unsigned) columns()) {
+ unsigned extra_len, incr;
+ extra_len = columns() - basic_len;
+ /* Either UNIT already got 25, or is fully satisfied.
+ * Grant up to 25 to DESC now. */
+ incr = MIN(extra_len, 25);
+ desc_len += incr;
+ extra_len -= incr;
+ /* split the remaining space between UNIT and DESC,
+ * but do not give UNIT more than it needs. */
+ if (extra_len > 0) {
+ incr = MIN(extra_len / 2, max_id_len - id_len);
+ id_len += incr;
+ desc_len += extra_len - incr;
+ }
+ }
+ } else
+ id_len = max_id_len;
+
+ if (!arg_no_legend) {
+ printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD",
active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
- if (columns() >= 80+12 || arg_full || !arg_no_pager)
- printf(" %s\n", "DESCRIPTION");
+ if (!arg_full && arg_no_pager)
+ printf("%.*s\n", desc_len, "DESCRIPTION");
else
- printf("\n");
+ printf("%s\n", "DESCRIPTION");
}
for (u = unit_infos; u < unit_infos + c; u++) {
char *e;
- int a = 0, b = 0;
const char *on_loaded, *off_loaded;
const char *on_active, *off_active;
n_shown++;
- if (!streq(u->load_state, "loaded") &&
- !streq(u->load_state, "banned")) {
- on_loaded = ansi_highlight(true);
- off_loaded = ansi_highlight(false);
+ if (streq(u->load_state, "error")) {
+ on_loaded = ansi_highlight_red(true);
+ off_loaded = ansi_highlight_red(false);
} else
on_loaded = off_loaded = "";
if (streq(u->active_state, "failed")) {
- on_active = ansi_highlight(true);
- off_active = ansi_highlight(false);
+ on_active = ansi_highlight_red(true);
+ off_active = ansi_highlight_red(false);
} else
on_active = off_active = "";
- e = arg_full ? NULL : ellipsize(u->id, 25, 33);
+ e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
- printf("%-25s %s%-6s%s %s%-*s %-*s%s%n",
- e ? e : u->id,
+ printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ",
+ id_len, e ? e : u->id,
on_loaded, u->load_state, off_loaded,
on_active, active_len, u->active_state,
sub_len, u->sub_state, off_active,
- &a);
-
- free(e);
-
- a -= strlen(on_loaded) + strlen(off_loaded);
- a -= strlen(on_active) + strlen(off_active);
-
- if (u->job_id != 0)
- printf(" %-*s", job_len, u->job_type);
+ job_len, u->job_id ? u->job_type : "");
+ if (!arg_full && arg_no_pager)
+ printf("%.*s\n", desc_len, u->description);
else
- b = 1 + job_len;
+ printf("%s\n", u->description);
- if (a + b + 1 < columns()) {
- if (u->job_id == 0)
- printf(" %-*s", job_len, "");
-
- if (arg_full || !arg_no_pager)
- printf(" %s", u->description);
- else
- printf(" %.*s", columns() - a - b - 1, u->description);
- }
-
- fputs("\n", stdout);
+ free(e);
}
- if (on_tty()) {
+ if (!arg_no_legend) {
printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n"
"ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
"SUB = The low-level unit activation state, values depend on unit type.\n"
}
static void output_unit_file_list(const UnitFileList *units, unsigned c) {
- unsigned n_shown = 0;
+ unsigned max_id_len, id_cols, state_cols, n_shown = 0;
const UnitFileList *u;
- if (on_tty())
- printf("%-25s %-6s\n", "UNIT FILE", "STATE");
+ max_id_len = sizeof("UNIT FILE")-1;
+ state_cols = sizeof("STATE")-1;
+ for (u = units; u < units + c; u++) {
+ if (!output_show_unit_file(u))
+ continue;
+
+ max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path)));
+ state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
+ }
+
+ if (!arg_full) {
+ unsigned basic_cols;
+ id_cols = MIN(max_id_len, 25);
+ basic_cols = 1 + id_cols + state_cols;
+ if (basic_cols < (unsigned) columns())
+ id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
+ } else
+ id_cols = max_id_len;
+
+ if (!arg_no_legend)
+ printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE");
for (u = units; u < units + c; u++) {
char *e;
if (u->state == UNIT_FILE_MASKED ||
u->state == UNIT_FILE_MASKED_RUNTIME ||
u->state == UNIT_FILE_DISABLED) {
- on = ansi_highlight(true);
- off = ansi_highlight(false);
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
} else if (u->state == UNIT_FILE_ENABLED) {
on = ansi_highlight_green(true);
off = ansi_highlight_green(false);
id = file_name_from_path(u->path);
- e = arg_full ? NULL : ellipsize(id, 25, 33);
+ e = arg_full ? NULL : ellipsize(id, id_cols, 33);
- printf("%-25s %s%-6s%s\n",
- e ? e : id,
- on, unit_file_state_to_string(u->state), off);
+ printf("%-*s %s%-*s%s\n",
+ id_cols, e ? e : id,
+ on, state_cols, unit_file_state_to_string(u->state), off);
free(e);
}
- if (on_tty())
+ if (!arg_no_legend)
printf("\n%u unit files listed.\n", n_shown);
}
dbus_error_init(&error);
- assert(bus);
-
pager_open_if_enabled();
if (avoid_bus()) {
r = unit_file_get_list(arg_scope, arg_root, h);
if (r < 0) {
+ unit_file_list_free(h);
log_error("Failed to get unit file list: %s", strerror(-r));
return r;
}
hashmap_free(h);
} else {
+ assert(bus);
+
m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
}
static int load_unit(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
+ DBusMessage *m = NULL;
DBusError error;
int r;
char **name;
assert(args);
STRV_FOREACH(name, args+1) {
+ DBusMessage *reply;
if (!(m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
if (m)
dbus_message_unref(m);
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return r;
else if (streq(d.result, "canceled"))
log_error("Job canceled.");
else if (streq(d.result, "dependency"))
- log_error("A dependency job failed. See system logs for details.");
+ log_error("A dependency job failed. See system journal for details.");
else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
- log_error("Job failed. See system logs and 'systemctl status' for details.");
+ log_error("Job failed. See system journal and 'systemctl status' for details.");
}
if (streq_ptr(d.result, "timeout"))
assert(bus);
- agent_open_if_enabled();
+ ask_password_agent_open_if_enabled();
if (arg_action == ACTION_SYSTEMCTL) {
method =
return ret;
}
+/* Ask systemd-logind, which might grant access to unprivileged users
+ * through PolicyKit */
+static int reboot_with_logind(DBusConnection *bus, enum action a) {
+#ifdef HAVE_LOGIND
+ const char *method;
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ dbus_bool_t interactive = true;
+ int r;
+
+ dbus_error_init(&error);
+
+ polkit_agent_open_if_enabled();
+
+ switch (a) {
+
+ case ACTION_REBOOT:
+ method = "Reboot";
+ break;
+
+ case ACTION_POWEROFF:
+ method = "PowerOff";
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ method);
+ if (!m) {
+ log_error("Could not allocate message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_BOOLEAN, &interactive,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ if (error_is_no_service(&error)) {
+ log_debug("Failed to issue method call: %s", bus_error_message(&error));
+ r = -ENOENT;
+ goto finish;
+ }
+
+ if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
+ log_debug("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EACCES;
+ goto finish;
+ }
+
+ log_info("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return r;
+#else
+ return -ENOSYS;
+#endif
+}
+
static int start_special(DBusConnection *bus, char **args) {
+ enum action a;
int r;
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")))
+ a = verb_to_action(args[0]);
+
+ if (arg_force >= 2 &&
+ (a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT))
+ halt_now(a);
+
+ if (arg_force >= 1 &&
+ (a == ACTION_HALT ||
+ a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT ||
+ a == ACTION_KEXEC ||
+ a == ACTION_EXIT))
return daemon_reload(bus, args);
- r = start_unit(bus, args);
+ /* first try logind, to allow authentication with polkit */
+ if (geteuid() != 0 &&
+ (a == ACTION_POWEROFF ||
+ a == ACTION_REBOOT)) {
+ r = reboot_with_logind(bus, a);
+ if (r >= 0)
+ return r;
+ }
+ r = start_unit(bus, args);
if (r >= 0)
- warn_wall(verb_to_action(args[0]));
+ warn_wall(a);
return r;
}
}
static int kill_unit(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
+ DBusMessage *m = NULL;
int r = 0;
DBusError error;
char **name;
arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
STRV_FOREACH(name, args+1) {
+ DBusMessage *reply;
if (!(m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
if (m)
dbus_message_unref(m);
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return r;
const char *load_state;
const char *active_state;
const char *sub_state;
+ const char *unit_file_state;
const char *description;
const char *following;
const char *default_control_group;
const char *load_error;
+ const char *result;
usec_t inactive_exit_timestamp;
+ usec_t inactive_exit_timestamp_monotonic;
usec_t active_enter_timestamp;
usec_t active_exit_timestamp;
usec_t inactive_enter_timestamp;
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);
+ if (streq_ptr(i->load_state, "error")) {
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
} else
on = off = "";
if (i->load_error)
printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
+ else if (i->path && i->unit_file_state)
+ printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
else if (i->path)
printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
else
ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
if (streq_ptr(i->active_state, "failed")) {
- on = ansi_highlight(true);
- off = ansi_highlight(false);
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
} else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
on = ansi_highlight_green(true);
off = ansi_highlight_green(false);
strna(i->active_state),
off);
+ if (!isempty(i->result) && !streq(i->result, "success"))
+ printf(" (Result: %s)", i->result);
+
timestamp = (streq_ptr(i->active_state, "active") ||
streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
(streq_ptr(i->active_state, "inactive") ||
good = is_clean_exit(p->code, p->status);
if (!good) {
- on = ansi_highlight(true);
- off = ansi_highlight(false);
+ on = ansi_highlight_red(true);
+ off = ansi_highlight_red(false);
} else
on = off = "";
printf(")%s\n", off);
- on = off = NULL;
-
if (i->main_pid == p->pid &&
i->start_timestamp == p->start_timestamp &&
i->exit_timestamp == p->start_timestamp)
if (i->running) {
char *t = NULL;
- get_process_name(i->main_pid, &t);
+ get_process_comm(i->main_pid, &t);
if (t) {
printf(" (%s)", t);
free(t);
printf(" Control: %u", (unsigned) i->control_pid);
- get_process_name(i->control_pid, &t);
+ get_process_comm(i->control_pid, &t);
if (t) {
printf(" (%s)", t);
free(t);
else
c = 0;
- show_cgroup_by_path(i->default_control_group, "\t\t ", c);
+ show_cgroup_by_path(i->default_control_group, "\t\t ", c, false);
}
}
+ if (i->id && arg_transport != TRANSPORT_SSH) {
+ printf("\n");
+ show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow);
+ }
+
if (i->need_daemon_reload)
printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
- ansi_highlight(true),
- ansi_highlight(false),
+ ansi_highlight_red(true),
+ ansi_highlight_red(false),
arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
}
i->what = s;
else if (streq(name, "Following"))
i->following = s;
+ else if (streq(name, "UnitFileState"))
+ i->unit_file_state = s;
+ else if (streq(name, "Result"))
+ i->result = s;
}
break;
i->inactive_enter_timestamp = (usec_t) u;
else if (streq(name, "InactiveExitTimestamp"))
i->inactive_exit_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveExitTimestampMonotonic"))
+ i->inactive_exit_timestamp_monotonic = (usec_t) u;
else if (streq(name, "ActiveExitTimestamp"))
i->active_exit_timestamp = (usec_t) u;
else if (streq(name, "ConditionTimestamp"))
} else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
const char *a = NULL, *b = NULL;
- if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) > 0)
+ if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0)
bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
if (arg_all || !isempty(a) || !isempty(b))
printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
+
+ return 0;
}
break;
return 0;
+ } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) {
+ DBusMessageIter sub, sub2;
+
+ dbus_message_iter_recurse(iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+ const char *controller, *attr, *value;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 &&
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) {
+
+ printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
+ controller,
+ attr,
+ value);
+ }
+
+ dbus_message_iter_next(&sub);
+ }
+
+ return 0;
+
} else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
DBusMessageIter sub;
}
static int reset_failed(DBusConnection *bus, char **args) {
- DBusMessage *m = NULL, *reply = NULL;
+ DBusMessage *m = NULL;
int r;
DBusError error;
char **name;
return daemon_reload(bus, args);
STRV_FOREACH(name, args+1) {
+ DBusMessage *reply;
if (!(m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
if (m)
dbus_message_unref(m);
- if (reply)
- dbus_message_unref(reply);
-
dbus_error_free(&error);
return r;
static int enable_sysv_units(char **args) {
int r = 0;
-#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX))
+#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA))
const char *verb = args[0];
unsigned f = 1, t = 1;
LookupPaths paths;
int r;
DBusError error;
- dbus_error_init(&error);
-
r = enable_sysv_units(args);
if (r < 0)
return r;
+ if (!args[1])
+ return 0;
+
+ dbus_error_init(&error);
+
if (!bus || avoid_bus()) {
if (streq(verb, "enable")) {
r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
goto finish;
}
- for (i = 0; i < n_changes; i++) {
- if (changes[i].type == UNIT_FILE_SYMLINK)
- log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
- else
- log_info("rm '%s'", changes[i].path);
+ if (!arg_quiet) {
+ for (i = 0; i < n_changes; i++) {
+ if (changes[i].type == UNIT_FILE_SYMLINK)
+ log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+ else
+ log_info("rm '%s'", changes[i].path);
+ }
}
} else {
goto finish;
}
- if (streq(type, "symlink"))
- log_info("ln -s '%s' '%s'", source, path);
- else
- log_info("rm '%s'", path);
+ if (!arg_quiet) {
+ if (streq(type, "symlink"))
+ log_info("ln -s '%s' '%s'", source, path);
+ else
+ log_info("rm '%s'", path);
+ }
dbus_message_iter_next(&sub);
}
" --no-wall Don't send wall message before halt/power-off/reboot\n"
" --no-reload When enabling/disabling unit files, don't reload daemon\n"
" configuration\n"
+ " --no-legend Do not print a legend (column headers and hints)\n"
" --no-pager Do not pipe output into a pager\n"
" --no-ask-password\n"
" Do not ask for system passwords\n"
" -f --force When enabling unit files, override existing symlinks\n"
" When shutting down, execute action immediately\n"
" --root=PATH Enable unit files in the specified root directory\n"
- " --runtime Enable unit files only temporarily until next reboot\n\n"
+ " --runtime Enable unit files only temporarily until next reboot\n"
+ " -n --lines=INTEGER Journal entries to show\n"
+ " --follow Follow journal\n"
+ " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
+ " verbose, export, json, cat)\n\n"
"Unit Commands:\n"
" list-units List loaded units\n"
" start [NAME...] Start (activate) one or more units\n"
ARG_SYSTEM,
ARG_GLOBAL,
ARG_NO_BLOCK,
+ ARG_NO_LEGEND,
ARG_NO_PAGER,
ARG_NO_WALL,
ARG_ORDER,
ARG_KILL_WHO,
ARG_NO_ASK_PASSWORD,
ARG_FAILED,
- ARG_RUNTIME
+ ARG_RUNTIME,
+ ARG_FOLLOW,
+ ARG_FORCE
};
static const struct option options[] = {
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "global", no_argument, NULL, ARG_GLOBAL },
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
+ { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
{ "quiet", no_argument, NULL, 'q' },
{ "order", no_argument, NULL, ARG_ORDER },
{ "require", no_argument, NULL, ARG_REQUIRE },
{ "root", required_argument, NULL, ARG_ROOT },
- { "force", no_argument, NULL, 'f' },
+ { "force", no_argument, NULL, ARG_FORCE },
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
{ "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
{ "kill-who", required_argument, NULL, ARG_KILL_WHO },
{ "host", required_argument, NULL, 'H' },
{ "privileged",no_argument, NULL, 'P' },
{ "runtime", no_argument, NULL, ARG_RUNTIME },
+ { "lines", required_argument, NULL, 'n' },
+ { "follow", no_argument, NULL, ARG_FOLLOW },
+ { "output", required_argument, NULL, 'o' },
{ NULL, 0, NULL, 0 }
};
assert(argc >= 0);
assert(argv);
- /* Only when running as systemctl we ask for passwords */
- arg_ask_password = true;
-
- while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:P", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
switch (c) {
arg_no_block = true;
break;
+ case ARG_NO_LEGEND:
+ arg_no_legend = true;
+ break;
+
case ARG_NO_PAGER:
arg_no_pager = true;
break;
arg_quiet = true;
break;
+ case ARG_FORCE:
+ arg_force ++;
+ break;
+
+ case ARG_FOLLOW:
+ arg_follow = true;
+ break;
+
case 'f':
- arg_force = true;
+ /* -f is short for both --follow and --force! */
+ arg_force ++;
+ arg_follow = true;
break;
case ARG_NO_RELOAD:
arg_runtime = true;
break;
+ case 'n':
+ if (safe_atou(optarg, &arg_lines) < 0) {
+ log_error("Failed to parse lines '%s'", optarg);
+ return -EINVAL;
+ }
+ break;
+
+ case 'o':
+ arg_output = output_mode_from_string(optarg);
+ if (arg_output < 0) {
+ log_error("Unknown output '%s'.", optarg);
+ return -EINVAL;
+ }
+ break;
+
case '?':
return -EINVAL;
return 0;
}
-static bool kexec_loaded(void) {
- bool loaded = false;
- char *s;
-
- if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
- if (s[0] == '1')
- loaded = true;
- free(s);
- }
- return loaded;
-}
-
static int shutdown_parse_argv(int argc, char *argv[]) {
enum {
{ "halt", no_argument, NULL, 'H' },
{ "poweroff", no_argument, NULL, 'P' },
{ "reboot", no_argument, NULL, 'r' },
+ { "kexec", no_argument, NULL, 'K' }, /* not documented extension */
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
{ NULL, 0, NULL, 0 }
};
arg_action = ACTION_REBOOT;
break;
+ case 'K':
+ arg_action = ACTION_KEXEC;
+ break;
+
case 'h':
if (arg_action != ACTION_HALT)
arg_action = ACTION_POWEROFF;
}
if (argc > optind) {
- if ((r = parse_time_spec(argv[optind], &arg_when)) < 0) {
+ r = parse_time_spec(argv[optind], &arg_when);
+ if (r < 0) {
log_error("Failed to parse time specification: %s", argv[optind]);
return r;
}
* enable/disable */
if (!streq(verbs[i].verb, "enable") &&
!streq(verbs[i].verb, "disable") &&
- !streq(verbs[i].verb, "is-enable") &&
+ !streq(verbs[i].verb, "is-enabled") &&
+ !streq(verbs[i].verb, "list-unit-files") &&
!streq(verbs[i].verb, "reenable") &&
!streq(verbs[i].verb, "preset") &&
!streq(verbs[i].verb, "mask") &&
return 0;
}
+ if (!bus) {
+ log_error("Failed to get D-Bus connection: %s",
+ dbus_error_is_set(error) ? error->message : "No connection to service manager.");
+ return -EIO;
+ }
+
+ } else {
+
if (!bus && !avoid_bus()) {
- log_error("Failed to get D-Bus connection: %s", error->message);
+ log_error("Failed to get D-Bus connection: %s",
+ dbus_error_is_set(error) ? error->message : "No connection to service manager.");
return -EIO;
}
}
}
static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
- int fd = -1;
+ int fd;
struct msghdr msghdr;
- struct iovec iovec;
+ struct iovec iovec[2];
union sockaddr_union sockaddr;
- struct shutdownd_command c;
+ struct sd_shutdown_command c;
+
+ fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
zero(c);
- c.elapse = t;
+ c.usec = t;
c.mode = mode;
c.dry_run = dry_run;
c.warn_wall = warn;
- if (message)
- strncpy(c.wall_message, message, sizeof(c.wall_message));
-
- if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0)
- return -errno;
-
zero(sockaddr);
sockaddr.sa.sa_family = AF_UNIX;
- sockaddr.un.sun_path[0] = 0;
strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
- zero(iovec);
- iovec.iov_base = (char*) &c;
- iovec.iov_len = sizeof(c);
-
zero(msghdr);
msghdr.msg_name = &sockaddr;
msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
+ zero(iovec);
+ iovec[0].iov_base = (char*) &c;
+ iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
+
+ if (isempty(message))
+ msghdr.msg_iovlen = 1;
+ else {
+ iovec[1].iov_base = (char*) message;
+ iovec[1].iov_len = strlen(message);
+ msghdr.msg_iovlen = 2;
+ }
+ msghdr.msg_iov = iovec;
if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
close_nointr_nofail(fd);
if (bus) {
/* First, try systemd via D-Bus. */
- if (daemon_reload(bus, NULL) > 0)
+ if (daemon_reload(bus, NULL) >= 0)
return 0;
}
return 0;
}
+static void halt_now(enum action a) {
+
+ /* Make sure C-A-D is handled by the kernel from this
+ * point on... */
+ reboot(RB_ENABLE_CAD);
+
+ switch (a) {
+
+ case ACTION_HALT:
+ log_info("Halting.");
+ reboot(RB_HALT_SYSTEM);
+ break;
+
+ case ACTION_POWEROFF:
+ log_info("Powering off.");
+ reboot(RB_POWER_OFF);
+ break;
+
+ case ACTION_REBOOT:
+ log_info("Rebooting.");
+ reboot(RB_AUTOBOOT);
+ break;
+
+ default:
+ assert_not_reached("Unknown halt action.");
+ }
+
+ assert_not_reached("Uh? This shouldn't happen.");
+}
+
static int halt_main(DBusConnection *bus) {
int r;
if (geteuid() != 0) {
+ /* Try logind if we are a normal user and no special
+ * mode applies. Maybe PolicyKit allows us to shutdown
+ * the machine. */
+
+ if (arg_when <= 0 &&
+ !arg_dry &&
+ !arg_immediate &&
+ (arg_action == ACTION_POWEROFF ||
+ arg_action == ACTION_REBOOT)) {
+ r = reboot_with_logind(bus, arg_action);
+ if (r >= 0)
+ return r;
+ }
+
log_error("Must be root.");
return -EPERM;
}
if (arg_when > 0) {
char *m;
- char date[FORMAT_TIMESTAMP_MAX];
m = strv_join(arg_wall, " ");
r = send_shutdownd(arg_when,
arg_action == ACTION_HALT ? 'H' :
arg_action == ACTION_POWEROFF ? 'P' :
+ arg_action == ACTION_KEXEC ? 'K' :
'r',
arg_dry,
!arg_no_wall,
if (r < 0)
log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
else {
+ char date[FORMAT_TIMESTAMP_MAX];
+
log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
format_timestamp(date, sizeof(date), arg_when));
return 0;
if (!arg_no_wtmp) {
if (sd_booted() > 0)
log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
- else if ((r = utmp_put_shutdown(0)) < 0)
- log_warning("Failed to write utmp record: %s", strerror(-r));
+ else {
+ r = utmp_put_shutdown();
+ if (r < 0)
+ log_warning("Failed to write utmp record: %s", strerror(-r));
+ }
}
if (!arg_no_sync)
if (arg_dry)
return 0;
- /* Make sure C-A-D is handled by the kernel from this
- * point on... */
- reboot(RB_ENABLE_CAD);
-
- switch (arg_action) {
-
- case ACTION_HALT:
- log_info("Halting.");
- reboot(RB_HALT_SYSTEM);
- break;
-
- case ACTION_POWEROFF:
- log_info("Powering off.");
- reboot(RB_POWER_OFF);
- break;
-
- case ACTION_REBOOT:
- log_info("Rebooting.");
- reboot(RB_AUTOBOOT);
- break;
-
- default:
- assert_not_reached("Unknown halt action.");
- }
-
+ halt_now(arg_action);
/* We should never reach this. */
return -ENOSYS;
}
log_parse_environment();
log_open();
- if ((r = parse_argv(argc, argv)) < 0)
+ r = parse_argv(argc, argv);
+ if (r < 0)
goto finish;
else if (r == 0) {
retval = EXIT_SUCCESS;
strv_free(arg_property);
pager_close();
- agent_close();
+ ask_password_agent_close();
+ polkit_agent_close();
return retval;
}