#include "conf-parser.h"
#include "sd-daemon.h"
#include "shutdownd.h"
+#include "exit-status.h"
static const char *arg_type = NULL;
static char **arg_property = NULL;
static bool arg_full = false;
static bool arg_force = false;
static bool arg_defaults = false;
+static bool arg_sysv_compat = false; /* this is undocumented, and
+ * exists simply to make
+ * implementation of SysV
+ * compatible shell glue
+ * easier */
static char **arg_wall = NULL;
static usec_t arg_when = 0;
static enum action {
if (isatty(STDOUT_FILENO)) {
- printf("\nLOAD = Load State, reflects whether the unit configuration was properly loaded.\n"
- "ACTIVE = Active State, the high-level unit activation state, i.e. generalization of the substate.\n"
- "SUB = Substate, the low-level unit activation state, possible values depend on unit type.\n"
- "JOB = Job, shows pending jobs for the unit.\n");
+ 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"
+ "JOB = Pending job for the unit.\n");
if (arg_all)
printf("\n%u units listed.\n", c);
if (!arg_quiet)
puts(state);
- if (streq(state, "active") || startswith(state, "reloading"))
+ if (streq(state, "active") || streq(state, "reloading"))
r = 0;
dbus_message_unref(m);
const char *path;
const char *default_control_group;
+ usec_t inactive_exit_timestamp;
+ usec_t active_enter_timestamp;
+ usec_t active_exit_timestamp;
+ usec_t inactive_enter_timestamp;
+
bool need_daemon_reload;
/* Service */
pid_t main_pid;
pid_t control_pid;
const char *status_text;
- bool running;
+ bool running:1;
+ bool is_sysv:1;
usec_t start_timestamp;
usec_t exit_timestamp;
static void print_status_info(UnitStatusInfo *i) {
ExecStatusInfo *p;
const char *on, *off, *ss;
+ usec_t timestamp;
+ char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
+ char since2[FORMAT_TIMESTAMP_MAX], *s2;
assert(i);
on = off = "";
if (ss)
- printf("\t Active: %s%s (%s)%s\n",
+ printf("\t Active: %s%s (%s)%s",
on,
strna(i->active_state),
ss,
off);
else
- printf("\t Active: %s%s%s\n",
+ printf("\t Active: %s%s%s",
on,
strna(i->active_state),
off);
+ timestamp = (streq_ptr(i->active_state, "active") ||
+ streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
+ (streq_ptr(i->active_state, "inactive") ||
+ streq_ptr(i->active_state, "maintenance")) ? i->inactive_enter_timestamp :
+ streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
+ i->active_exit_timestamp;
+
+ s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp);
+ s2 = format_timestamp(since2, sizeof(since2), timestamp);
+
+ if (s1)
+ printf(" since [%s; %s]\n", s2, s1);
+ else if (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)
continue;
t = strv_join(p->argv, " ");
- printf("\t Exited: %u (%s, code=%s, ", p->pid, strna(t), sigchld_code_to_string(p->code));
+ printf("\t Process: %u (%s, code=%s, ", p->pid, strna(t), sigchld_code_to_string(p->code));
free(t);
- if (p->code == CLD_EXITED)
+ if (p->code == CLD_EXITED) {
+ const char *c;
+
printf("status=%i", p->status);
- else
+
+ if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+ printf("/%s", c);
+
+ } else
printf("signal=%s", signal_to_string(p->status));
printf(")\n");
printf("\t");
if (i->main_pid > 0) {
- printf(" Main: %u", (unsigned) i->main_pid);
+ printf("Main PID: %u", (unsigned) i->main_pid);
if (i->running) {
char *t = NULL;
} else if (i->exit_code > 0) {
printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
- if (i->exit_code == CLD_EXITED)
+ if (i->exit_code == CLD_EXITED) {
+ const char *c;
+
printf("status=%i", i->exit_status);
- else
+
+ if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+ printf("/%s", c);
+
+ } else
printf("signal=%s", signal_to_string(i->exit_status));
printf(")");
}
i->description = s;
else if (streq(name, "FragmentPath"))
i->path = s;
- else if (streq(name, "SysVPath"))
+ else if (streq(name, "SysVPath")) {
+ i->is_sysv = true;
i->path = s;
- else if (streq(name, "DefaultControlGroup"))
+ } else if (streq(name, "DefaultControlGroup"))
i->default_control_group = s;
else if (streq(name, "StatusText"))
i->status_text = s;
i->start_timestamp = (usec_t) u;
else if (streq(name, "ExecMainExitTimestamp"))
i->exit_timestamp = (usec_t) u;
+ else if (streq(name, "ActiveEnterTimestamp"))
+ i->active_enter_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveEnterTimestamp"))
+ i->inactive_enter_timestamp = (usec_t) u;
+ else if (streq(name, "InactiveExitTimestamp"))
+ i->inactive_exit_timestamp = (usec_t) u;
+ else if (streq(name, "ActiveExitTimestamp"))
+ i->active_exit_timestamp = (usec_t) u;
break;
}
dbus_message_iter_next(&sub);
}
- if (!show_properties)
- print_status_info(&info);
+ r = 0;
+
+ if (!show_properties) {
+ if (arg_sysv_compat &&
+ !streq_ptr(info.active_state, "active") &&
+ !streq_ptr(info.active_state, "reloading")) {
+
+ /* If the SysV compatibility mode is on, we
+ * will refuse to run "status" on units that
+ * aren't active */
+ log_error("Unit not active.");
+ r = -EADDRNOTAVAIL;
+ } else
+ print_status_info(&info);
+ }
while ((p = info.exec)) {
LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
exec_status_info_free(p);
}
- r = 0;
-
finish:
if (m)
dbus_message_unref(m);
printf("Job %u removed.\n", id);
- } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Unit", "Changed") ||
- dbus_message_is_signal(message, "org.freedesktop.systemd1.Job", "Changed")) {
+ } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
const char *path, *interface, *property = "Id";
DBusMessageIter iter, sub;
path = dbus_message_get_path(message);
- interface = dbus_message_get_interface(message);
+
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse message: %s", error.message);
+ goto finish;
+ }
+
+ if (!streq(interface, "org.freedesktop.systemd1.Job") &&
+ !streq(interface, "org.freedesktop.systemd1.Unit"))
+ goto finish;
if (!(m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
dbus_bus_add_match(bus,
"type='signal',"
"sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Unit',"
- "member='Changed'",
- &error);
-
- if (dbus_error_is_set(&error)) {
- log_error("Failed to add match: %s", error.message);
- r = -EIO;
- goto finish;
- }
-
- dbus_bus_add_match(bus,
- "type='signal',"
- "sender='org.freedesktop.systemd1',"
- "interface='org.freedesktop.systemd1.Job',"
- "member='Changed'",
+ "interface='org.freedesktop.DBus.Properties',"
+ "member='PropertiesChanged'",
&error);
if (dbus_error_is_set(&error)) {
ARG_FULL,
ARG_FORCE,
ARG_NO_RELOAD,
- ARG_DEFAULTS
+ ARG_DEFAULTS,
+ ARG_SYSV_COMPAT
};
static const struct option options[] = {
{ "require", no_argument, NULL, ARG_REQUIRE },
{ "force", no_argument, NULL, ARG_FORCE },
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
- { "defaults", no_argument, NULL, ARG_DEFAULTS },
+ { "defaults", no_argument, NULL, ARG_DEFAULTS },
+ { "sysv-compat", no_argument, NULL, ARG_SYSV_COMPAT },
{ NULL, 0, NULL, 0 }
};
arg_defaults = true;
break;
+ case ARG_SYSV_COMPAT:
+ arg_sysv_compat = true;
+ break;
+
case '?':
return -EINVAL;