chiark / gitweb /
systemctl: fix return code in chroot
[elogind.git] / src / systemctl / systemctl.c
index 0e564a542820aa1b29992bc3ffcbf99ef68b5f44..b4b58c05b0a77192b569ee91d8fb16833eb0b167 100644 (file)
@@ -123,7 +123,6 @@ static enum transport {
         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;
 
@@ -132,26 +131,8 @@ 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;
-
-        /* Note that this is invoked relatively early, before we start
-         * the pager. That means the value we return reflects whether
-         * we originally were started on a tty, not if we currently
-         * are. But this is intended, since we want colour and so on
-         * when run in our own pager. */
-
-        if (_unlikely_(t < 0))
-                t = isatty(STDOUT_FILENO) > 0;
-
-        return t;
-}
-
 static void pager_open_if_enabled(void) {
 
-        /* Cache result before we open the pager */
-        on_tty();
-
         if (arg_no_pager)
                 return;
 
@@ -186,6 +167,14 @@ static void polkit_agent_open_if_enabled(void) {
 }
 #endif
 
+static const char *ansi_highlight(bool b) {
+
+        if (!on_tty())
+                return "";
+
+        return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
+}
+
 static const char *ansi_highlight_red(bool b) {
 
         if (!on_tty())
@@ -372,15 +361,6 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
         } 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 (!arg_full && arg_no_pager)
-                        printf("%.*s\n", desc_len, "DESCRIPTION");
-                else
-                        printf("%s\n", "DESCRIPTION");
-        }
-
         for (u = unit_infos; u < unit_infos + c; u++) {
                 char *e;
                 const char *on_loaded, *off_loaded;
@@ -389,6 +369,15 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
                 if (!output_show_unit(u))
                         continue;
 
+                if (!n_shown && !arg_no_legend) {
+                        printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD",
+                               active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
+                        if (!arg_full && arg_no_pager)
+                                printf("%.*s\n", desc_len, "DESCRIPTION");
+                        else
+                                printf("%s\n", "DESCRIPTION");
+                }
+
                 n_shown++;
 
                 if (streq(u->load_state, "error")) {
@@ -420,17 +409,28 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
         }
 
         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"
-                       "JOB    = Pending job for the unit.\n");
+                const char *on, *off;
+
+                if (n_shown) {
+                        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\n");
+                        on = ansi_highlight(true);
+                        off = ansi_highlight(false);
+                } else {
+                        on = ansi_highlight_red(true);
+                        off = ansi_highlight_red(false);
+                }
 
                 if (arg_all)
-                        printf("\n%u loaded units listed.\n"
-                               "To show all installed unit files use 'systemctl list-unit-files'.\n", n_shown);
+                        printf("%s%u loaded units listed.%s\n"
+                               "To show all installed unit files use 'systemctl list-unit-files'.\n",
+                               on, n_shown, off);
                 else
-                        printf("\n%u loaded units listed. Pass --all to see loaded but inactive units, too.\n"
-                               "To show all installed unit files use 'systemctl list-unit-files'.\n", n_shown);
+                        printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
+                               "To show all installed unit files use 'systemctl list-unit-files'.\n",
+                               on, n_shown, off);
         }
 }
 
@@ -1322,7 +1322,10 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
                         return -ECONNREFUSED;
                 }
 
-                if (!arg_quiet && d.result) {
+                if (!d.result)
+                        goto free_name;
+
+                if (!arg_quiet) {
                         if (streq(d.result, "timeout"))
                                 log_error("Job for %s timed out.", strna(d.name));
                         else if (streq(d.result, "canceled"))
@@ -1343,11 +1346,12 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
                 free(d.result);
                 d.result = NULL;
 
+        free_name:
                 free(d.name);
                 d.name = NULL;
         }
 
-        /* This is slightly dirty, since we don't undo the filter registration. */
+        dbus_connection_remove_filter(bus, wait_filter, &d);
         return r;
 }
 
@@ -1441,22 +1445,26 @@ static void check_triggering_units(
                 DBusConnection *bus,
                 const char *unit_name) {
 
-        DBusMessage *reply = NULL;
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
         DBusMessageIter iter, sub;
         char *service_trigger = NULL;
         const char *interface = "org.freedesktop.systemd1.Unit",
                    *triggered_by_property = "TriggeredBy";
 
-        char *unit_path = NULL, *n = NULL;
+        char _cleanup_free_ *unit_path = NULL, *n = NULL;
         bool print_warning_label = true;
         int r;
 
         n = unit_name_mangle(unit_name);
-        unit_path = unit_dbus_path_from_name(n ? n : unit_name);
-        free(n);
+        if (!n) {
+                log_oom();
+                return;
+        }
+
+        unit_path = unit_dbus_path_from_name(n);
         if (!unit_path) {
-                log_error("Could not allocate dbus path.");
-                goto finish;
+                log_oom();
+                return;
         }
 
         r = bus_method_call_with_reply (
@@ -1471,13 +1479,12 @@ static void check_triggering_units(
                         DBUS_TYPE_STRING, &triggered_by_property,
                         DBUS_TYPE_INVALID);
         if (r)
-                goto finish;
+                return;
 
         if (!dbus_message_iter_init(reply, &iter) ||
             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
                 log_error("Failed to parse reply.");
-                goto finish;
-
+                return;
         }
 
         dbus_message_iter_recurse(&iter, &sub);
@@ -1488,14 +1495,14 @@ static void check_triggering_units(
 
                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
                         log_error("Failed to parse reply.");
-                        goto finish;
+                        return;
                 }
 
                 dbus_message_iter_get_basic(&sub, &service_trigger);
 
                 r = check_one_unit(bus, service_trigger, true);
                 if (r < 0)
-                        goto finish;
+                        return;
                 if (r == 0) {
                         if (print_warning_label) {
                                 log_warning("Warning: Stopping %s, but it can still be activated by:", unit_name);
@@ -1506,11 +1513,6 @@ static void check_triggering_units(
 
                 dbus_message_iter_next(&sub);
         }
-finish:
-        if (reply)
-                dbus_message_unref(reply);
-
-        free(unit_path);
 }
 
 static int start_unit_one(
@@ -1521,7 +1523,7 @@ static int start_unit_one(
                 DBusError *error,
                 Set *s) {
 
-        DBusMessage *reply = NULL;
+        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
         const char *path;
         int r;
         _cleanup_free_ char *n, *p = NULL;
@@ -1554,15 +1556,14 @@ static int start_unit_one(
                 else
                         log_error("Failed to issue method call: %s", bus_error_message(error));
 
-                goto finish;
+                return r;
         }
 
         if (!dbus_message_get_args(reply, error,
                                    DBUS_TYPE_OBJECT_PATH, &path,
                                    DBUS_TYPE_INVALID)) {
                 log_error("Failed to parse reply: %s", bus_error_message(error));
-                r = -EIO;
-                goto finish;
+                return -EIO;
         }
 
         if (need_daemon_reload(bus, n))
@@ -1571,32 +1572,19 @@ static int start_unit_one(
 
         if (s) {
                 p = strdup(path);
-                if (!p) {
-                        r = log_oom();
-                        goto finish;
-                }
+                if (!p)
+                        return log_oom();
 
                 r = set_put(s, p);
                 if (r < 0) {
                         log_error("Failed to add path to set.");
-                        goto finish;
+                        return r;
                 }
 
                 p = NULL;
         }
 
-        /* When stopping a unit warn if it can still be triggered by
-         * another active unit (socket, path, timer) */
-        if (!arg_quiet && streq(method, "StopUnit"))
-                check_triggering_units(bus, name);
-
-        r = 0;
-
-finish:
-        if (reply)
-                dbus_message_unref(reply);
-
-        return r;
+        return 0;
 }
 
 static enum action verb_to_action(const char *verb) {
@@ -1730,6 +1718,16 @@ static int start_unit(DBusConnection *bus, char **args) {
                         ret = r;
                         goto finish;
                 }
+
+                /* When stopping units, warn if they can still be triggered by
+                 * another active unit (socket, path, timer) */
+                if (!arg_quiet && streq(method, "StopUnit")) {
+                        if (one_name)
+                                check_triggering_units(bus, one_name);
+                        else
+                                STRV_FOREACH(name, args+1)
+                                        check_triggering_units(bus, *name);
+                }
         }
 
 finish:
@@ -2276,14 +2274,18 @@ static void print_status_info(UnitStatusInfo *i) {
         if (i->id && arg_transport != TRANSPORT_SSH) {
                 int flags =
                         arg_all * OUTPUT_SHOW_ALL |
-                        arg_follow * OUTPUT_FOLLOW |
-                        !arg_quiet * OUTPUT_WARN_CUTOFF |
-                        on_tty() * OUTPUT_COLOR;
+                        (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
+                        on_tty() * OUTPUT_COLOR |
+                        !arg_quiet * OUTPUT_WARN_CUTOFF;
 
                 printf("\n");
-                show_journal_by_unit(i->id, arg_output, 0,
+                show_journal_by_unit(stdout,
+                                     i->id,
+                                     arg_output,
+                                     0,
                                      i->inactive_exit_timestamp_monotonic,
-                                     arg_lines, flags);
+                                     arg_lines,
+                                     flags);
         }
 
         if (i->need_daemon_reload)
@@ -3327,12 +3329,13 @@ static int switch_root(DBusConnection *bus, char **args) {
 }
 
 static int set_environment(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
+        _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
-        int r;
         const char *method;
-        DBusMessageIter iter, sub;
-        char **name;
+        DBusMessageIter iter;
+        int r;
+
+        assert(bus);
 
         dbus_error_init(&error);
 
@@ -3340,38 +3343,22 @@ static int set_environment(DBusConnection *bus, char **args) {
                 ? "SetEnvironment"
                 : "UnsetEnvironment";
 
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              method))) {
-
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        method);
+        if (!m)
+                return log_oom();
 
         dbus_message_iter_init_append(m, &iter);
 
-        if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        STRV_FOREACH(name, args+1)
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-        if (!dbus_message_iter_close_container(&iter, &sub)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
+        r = bus_append_strv_iter(&iter, args + 1);
+        if (r < 0)
+                return log_oom();
 
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+        if (!reply) {
                 log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
@@ -3380,14 +3367,7 @@ static int set_environment(DBusConnection *bus, char **args) {
         r = 0;
 
 finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
         dbus_error_free(&error);
-
         return r;
 }
 
@@ -3573,7 +3553,15 @@ static int mangle_names(char **original_names, char ***mangled_names) {
 
         i = l;
         STRV_FOREACH(name, original_names) {
-                *i = unit_name_mangle(*name);
+
+                /* When enabling units qualified path names are OK,
+                 * too, hence allow them explicitly. */
+
+                if (is_path(*name))
+                        *i = strdup(*name);
+                else
+                        *i = unit_name_mangle(*name);
+
                 if (!*i) {
                         strv_free(l);
                         return log_oom();
@@ -3642,6 +3630,7 @@ static int enable_unit(DBusConnection *bus, char **args) {
                         }
                 }
 
+                r = 0;
         } else {
                 const char *method;
                 bool send_force = true, expect_carries_install_info = false;
@@ -3916,9 +3905,8 @@ static int systemctl_help(void) {
                "     --root=PATH      Enable unit files in the specified root directory\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, json-pretty, cat)\n\n"
+               "                      verbose, export, json, json-pretty, json-sse, cat)\n\n"
                "Unit Commands:\n"
                "  list-units                      List loaded units\n"
                "  start [NAME...]                 Start (activate) one or more units\n"
@@ -4072,7 +4060,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_NO_ASK_PASSWORD,
                 ARG_FAILED,
                 ARG_RUNTIME,
-                ARG_FOLLOW,
                 ARG_FORCE
         };
 
@@ -4106,7 +4093,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "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             }
         };
@@ -4226,14 +4212,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_force ++;
                         break;
 
-                case ARG_FOLLOW:
-                        arg_follow = true;
-                        break;
-
                 case 'f':
-                        /* -f is short for both --follow and --force! */
                         arg_force ++;
-                        arg_follow = true;
                         break;
 
                 case ARG_NO_RELOAD: