chiark / gitweb /
systemctl: show whether we follow somebody in 'status' output
[elogind.git] / src / systemctl.c
index ea09c3cf36df25bcc6e17ef98a20d575654bcf36..51f5bba84c4bc2f93ac7c5055fffdeddc57a0ccd 100644 (file)
@@ -31,6 +31,7 @@
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <stddef.h>
 
 #include <dbus/dbus.h>
 
@@ -52,6 +53,8 @@
 #include "shutdownd.h"
 #include "exit-status.h"
 #include "bus-errors.h"
+#include "build.h"
+#include "unit-name.h"
 
 static const char *arg_type = NULL;
 static char **arg_property = NULL;
@@ -250,12 +253,114 @@ static int compare_unit_info(const void *a, const void *b) {
         return strcasecmp(u->id, v->id);
 }
 
+static bool output_show_job(const struct unit_info *u) {
+        const char *dot;
+
+        return (!arg_type || ((dot = strrchr(u->id, '.')) &&
+                              streq(dot+1, arg_type))) &&
+                (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
+}
+
+static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
+        unsigned active_len, sub_len, job_len;
+        const struct unit_info *u;
+
+        active_len = sizeof("ACTIVE")-1;
+        sub_len = sizeof("SUB")-1;
+        job_len = sizeof("JOB")-1;
+
+        for (u = unit_infos; u < unit_infos + c; u++) {
+                if (!output_show_job(u))
+                        continue;
+
+                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",
+                       active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
+                if (columns() >= 80+12 || arg_full)
+                        printf(" %s\n", "DESCRIPTION");
+                else
+                        printf("\n");
+        }
+
+        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;
+
+                if (!output_show_job(u))
+                        continue;
+
+                if (!streq(u->load_state, "loaded") &&
+                    !streq(u->load_state, "banned")) {
+                        on_loaded = ansi_highlight(true);
+                        off_loaded = ansi_highlight(false);
+                } else
+                        on_loaded = off_loaded = "";
+
+                if (streq(u->active_state, "failed")) {
+                        on_active = ansi_highlight(true);
+                        off_active = ansi_highlight(false);
+                } else
+                        on_active = off_active = "";
+
+                e = arg_full ? NULL : ellipsize(u->id, 25, 33);
+
+                printf("%-25s %s%-6s%s %s%-*s %-*s%s%n",
+                       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);
+                else
+                        b = 1 + job_len;
+
+                if (a + b + 1 < columns()) {
+                        if (u->job_id == 0)
+                                printf(" %-*s", job_len, "");
+
+                        if (arg_full)
+                                printf(" %s", u->description);
+                        else
+                                printf(" %.*s", columns() - a - b - 1, u->description);
+                }
+
+                fputs("\n", stdout);
+        }
+
+        if (on_tty()) {
+                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);
+                else
+                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", c);
+        }
+}
+
 static int list_units(DBusConnection *bus, char **args, unsigned n) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessageIter iter, sub, sub2;
-        unsigned c = 0, k, n_units = 0;
+        unsigned c = 0, n_units = 0;
         struct unit_info *unit_infos = NULL;
 
         dbus_error_init(&error);
@@ -335,82 +440,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
         }
 
         qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
-
-        if (isatty(STDOUT_FILENO)) {
-                if (columns() >= 80+12 || arg_full)
-                        printf("%-25s %-6s %-12s %-18s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
-                else
-                        printf("%-25s %-6s %-12s %-18s %-15s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB");
-        }
-
-        for (k = 0; k < c; k++) {
-                const char *dot;
-                struct unit_info *u = unit_infos+k;
-
-                if ((!arg_type || ((dot = strrchr(u->id, '.')) &&
-                                   streq(dot+1, arg_type))) &&
-                    (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0)) {
-                        char *e;
-                        int a = 0, b = 0;
-                        const char *on_loaded, *off_loaded;
-                        const char *on_active, *off_active;
-
-                        if (!streq(u->load_state, "loaded")) {
-                                on_loaded = ansi_highlight(true);
-                                off_loaded = ansi_highlight(false);
-                        } else
-                                on_loaded = off_loaded = "";
-
-                        if (streq(u->active_state, "failed")) {
-                                on_active = ansi_highlight(true);
-                                off_active = ansi_highlight(false);
-                        } else
-                                on_active = off_active = "";
-
-                        e = arg_full ? NULL : ellipsize(u->id, 25, 33);
-
-                        printf("%-25s %s%-6s%s %s%-12s %-18s%s%n",
-                               e ? e : u->id,
-                               on_loaded, u->load_state, off_loaded,
-                               on_active, u->active_state, 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(" %-15s%n", u->job_type, &b);
-                        else
-                                b = 1 + 15;
-
-                        if (a + b + 1 < columns()) {
-                                if (u->job_id == 0)
-                                        printf("                ");
-
-                                if (arg_full)
-                                        printf(" %s", u->description);
-                                else
-                                        printf(" %.*s", columns() - a - b - 1, u->description);
-                        }
-
-                        fputs("\n", stdout);
-                }
-        }
-
-        if (isatty(STDOUT_FILENO)) {
-
-                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);
-                else
-                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", c);
-        }
+        output_units_list(unit_infos, c);
 
         r = 0;
 
@@ -1230,11 +1260,11 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                         streq(args[0], "stop")                  ? "StopUnit" :
                         streq(args[0], "reload")                ? "ReloadUnit" :
                         streq(args[0], "restart")               ? "RestartUnit" :
-                        streq(args[0], "try-restart")           ? "TryRestartUnit" :
+                        streq(args[0], "try-restart")           ||
+                        streq(args[0], "condrestart")           ? "TryRestartUnit" :
                         streq(args[0], "reload-or-restart")     ? "ReloadOrRestartUnit" :
                         streq(args[0], "reload-or-try-restart") ||
-                        streq(args[0], "force-reload")          ||
-                        streq(args[0], "condrestart")           ? "ReloadOrTryRestartUnit" :
+                        streq(args[0], "force-reload")          ? "ReloadOrTryRestartUnit" :
                                                                   "StartUnit";
 
                 mode =
@@ -1253,7 +1283,11 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 method = "StartUnit";
 
                 mode = (arg_action == ACTION_EMERGENCY ||
-                        arg_action == ACTION_RESCUE) ? "isolate" : "replace";
+                        arg_action == ACTION_RESCUE ||
+                        arg_action == ACTION_RUNLEVEL2 ||
+                        arg_action == ACTION_RUNLEVEL3 ||
+                        arg_action == ACTION_RUNLEVEL4 ||
+                        arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
 
                 one_name = table[arg_action];
         }
@@ -1534,6 +1568,7 @@ typedef struct UnitStatusInfo {
         const char *sub_state;
 
         const char *description;
+        const char *following;
 
         const char *path;
         const char *default_control_group;
@@ -1550,7 +1585,9 @@ typedef struct UnitStatusInfo {
         pid_t control_pid;
         const char *status_text;
         bool running:1;
+#ifdef HAVE_SYSV_COMPAT
         bool is_sysv:1;
+#endif
 
         usec_t start_timestamp;
         usec_t exit_timestamp;
@@ -1593,7 +1630,11 @@ static void print_status_info(UnitStatusInfo *i) {
 
         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
@@ -1670,7 +1711,11 @@ static void print_status_info(UnitStatusInfo *i) {
 
                         printf("status=%i", p->status);
 
+#ifdef HAVE_SYSV_COMPAT
                         if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+                        if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
+#endif
                                 printf("/%s", c);
 
                 } else
@@ -1708,7 +1753,11 @@ static void print_status_info(UnitStatusInfo *i) {
 
                                         printf("status=%i", i->exit_status);
 
+#ifdef HAVE_SYSV_COMPAT
                                         if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+                                        if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
+#endif
                                                 printf("/%s", c);
 
                                 } else
@@ -1780,10 +1829,13 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->description = s;
                         else if (streq(name, "FragmentPath"))
                                 i->path = s;
+#ifdef HAVE_SYSV_COMPAT
                         else if (streq(name, "SysVPath")) {
                                 i->is_sysv = true;
                                 i->path = s;
-                        } else if (streq(name, "DefaultControlGroup"))
+                        }
+#endif
+                        else if (streq(name, "DefaultControlGroup"))
                                 i->default_control_group = s;
                         else if (streq(name, "StatusText"))
                                 i->status_text = s;
@@ -1793,6 +1845,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->where = s;
                         else if (streq(name, "What"))
                                 i->what = s;
+                        else if (streq(name, "Following"))
+                                i->following = s;
                 }
 
                 break;
@@ -1972,6 +2026,14 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                 return 0;
         }
 
+        case DBUS_TYPE_DOUBLE: {
+                double d;
+                dbus_message_iter_get_basic(iter, &d);
+
+                printf("%s=%g\n", name, d);
+                return 0;
+        }
+
         case DBUS_TYPE_STRUCT: {
                 DBusMessageIter sub;
                 dbus_message_iter_recurse(iter, &sub);
@@ -3185,28 +3247,16 @@ static void install_info_hashmap_free(Hashmap *m) {
         hashmap_free(m);
 }
 
-static bool unit_name_valid(const char *name) {
-
-        /* This is a minimal version of unit_name_valid() from
-         * unit-name.c */
-
-        if (!*name)
-                return false;
-
-        if (ignore_file(name))
-                return false;
-
-        return true;
-}
-
 static int install_info_add(const char *name) {
         InstallInfo *i;
         int r;
 
         assert(will_install);
 
-        if (!unit_name_valid(name))
+        if (!unit_name_is_valid_no_type(name, true)) {
+                log_warning("Unit name %s is not a valid unit name.", name);
                 return -EINVAL;
+        }
 
         if (hashmap_get(have_installed, name) ||
             hashmap_get(will_install, name))
@@ -3258,11 +3308,13 @@ static int config_parse_also(
                 if (!(n = strndup(w, l)))
                         return -ENOMEM;
 
-                r = install_info_add(n);
-                free(n);
-
-                if (r < 0)
+                if ((r = install_info_add(n)) < 0) {
+                        log_warning("Cannot install unit %s: %s", n, strerror(-r));
+                        free(n);
                         return r;
+                }
+
+                free(n);
         }
 
         return 0;
@@ -3587,12 +3639,6 @@ static int install_info_symlink_alias(const char *verb, InstallInfo *i, const ch
 
         STRV_FOREACH(s, i->aliases) {
 
-                if (!unit_name_valid(*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");
@@ -3606,7 +3652,6 @@ static int install_info_symlink_alias(const char *verb, InstallInfo *i, const ch
                 if (streq(verb, "disable"))
                         rmdir_parents(alias_path, config_path);
         }
-
         r = 0;
 
 finish:
@@ -3625,7 +3670,7 @@ static int install_info_symlink_wants(const char *verb, InstallInfo *i, const ch
         assert(config_path);
 
         STRV_FOREACH(s, i->wanted_by) {
-                if (!unit_name_valid(*s)) {
+                if (!unit_name_is_valid_no_type(*s, true)) {
                         log_error("Invalid name %s.", *s);
                         r = -EINVAL;
                         goto finish;
@@ -3788,8 +3833,10 @@ static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
         for (j = 1; j < n; j++)
-                if ((r = install_info_add(args[j])) < 0)
+                if ((r = install_info_add(args[j])) < 0) {
+                        log_warning("Cannot install unit %s: %s", args[j], strerror(-r));
                         goto finish;
+                }
 
         while ((i = hashmap_first(will_install))) {
                 int q;
@@ -3845,6 +3892,7 @@ 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"
@@ -3979,6 +4027,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
         enum {
                 ARG_FAIL = 0x100,
+                ARG_VERSION,
                 ARG_SESSION,
                 ARG_SYSTEM,
                 ARG_GLOBAL,
@@ -3994,6 +4043,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
         static const struct option options[] = {
                 { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
                 { "type",      required_argument, NULL, 't'           },
                 { "property",  required_argument, NULL, 'p'           },
                 { "all",       no_argument,       NULL, 'a'           },
@@ -4026,6 +4076,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         systemctl_help();
                         return 0;
 
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(DISTRIBUTION);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
                 case 't':
                         arg_type = optarg;
                         break;
@@ -4624,6 +4680,7 @@ finish:
                 dbus_message_unref(reply);
 
         if (bus) {
+                dbus_connection_flush(bus);
                 dbus_connection_close(bus);
                 dbus_connection_unref(bus);
         }
@@ -4821,7 +4878,7 @@ static int send_shutdownd(usec_t t, char mode, bool warn, const char *message) {
 
         zero(msghdr);
         msghdr.msg_name = &sockaddr;
-        msghdr.msg_namelen = sizeof(sa_family_t) + 1 + sizeof("/org/freedesktop/systemd1/shutdownd") - 1;
+        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + 1 + sizeof("/org/freedesktop/systemd1/shutdownd") - 1;
 
         msghdr.msg_iov = &iovec;
         msghdr.msg_iovlen = 1;
@@ -5039,6 +5096,7 @@ int main(int argc, char*argv[]) {
 finish:
 
         if (bus) {
+                dbus_connection_flush(bus);
                 dbus_connection_close(bus);
                 dbus_connection_unref(bus);
         }