chiark / gitweb /
systemctl: properly initialize and free sd_bus_error in "systemctl cat"
[elogind.git] / src / systemctl / systemctl.c
index 3248f512bf305ddf0bec4498f0665a2edc99c8f4..3e6a6883f6ec2509b5532a7947f83d53bd8f6008 100644 (file)
@@ -1843,59 +1843,85 @@ static int enable_wait_for_jobs(sd_bus *bus) {
         return 0;
 }
 
+static int bus_process_wait(sd_bus *bus) {
+        int r;
+
+        for (;;) {
+                r = sd_bus_process(bus, NULL);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        return 0;
+                r = sd_bus_wait(bus, (uint64_t) -1);
+                if (r < 0)
+                        return r;
+        }
+}
+
+static int check_wait_response(WaitData *d) {
+        int r = 0;
+
+        assert(d->result);
+
+        if (!arg_quiet) {
+                if (streq(d->result, "timeout"))
+                        log_error("Job for %s timed out.", strna(d->name));
+                else if (streq(d->result, "canceled"))
+                        log_error("Job for %s canceled.", strna(d->name));
+                else if (streq(d->result, "dependency"))
+                        log_error("A dependency job for %s failed. See 'journalctl -xn' for details.", strna(d->name));
+                else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
+                        log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d->name), strna(d->name));
+        }
+
+        if (streq(d->result, "timeout"))
+                r = -ETIME;
+        else if (streq(d->result, "canceled"))
+                r = -ECANCELED;
+        else if (streq(d->result, "dependency"))
+                r = -EIO;
+        else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
+                r = -EIO;
+
+        return r;
+}
+
 static int wait_for_jobs(sd_bus *bus, Set *s) {
         WaitData d = { .set = s };
-        int r;
+        int r = 0, q;
 
         assert(bus);
         assert(s);
 
-        r = sd_bus_add_filter(bus, wait_filter, &d);
-        if (r < 0)
+        q = sd_bus_add_filter(bus, wait_filter, &d);
+        if (q < 0)
                 return log_oom();
 
         while (!set_isempty(s)) {
-                for (;;) {
-                        r = sd_bus_process(bus, NULL);
-                        if (r < 0)
-                                return r;
-                        if (r > 0)
-                                break;
-                        r = sd_bus_wait(bus, (uint64_t) -1);
-                        if (r < 0)
-                                return r;
-                }
-
-                if (!d.result)
-                        goto free_name;
+                q = bus_process_wait(bus);
+                if (q < 0)
+                        return q;
 
-                if (!arg_quiet) {
-                        if (streq(d.result, "timeout"))
-                                log_error("Job for %s timed out.", strna(d.name));
-                        else if (streq(d.result, "canceled"))
-                                log_error("Job for %s canceled.", strna(d.name));
-                        else if (streq(d.result, "dependency"))
-                                log_error("A dependency job for %s failed. See 'journalctl -xn' for details.", strna(d.name));
-                        else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
-                                log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d.name), strna(d.name));
+                if (d.result) {
+                        q = check_wait_response(&d);
+                        /* Return the first error as it is most likely to be
+                         * meaningful. */
+                        if (q < 0 && r == 0)
+                                r = q;
                 }
 
-                if (streq_ptr(d.result, "timeout"))
-                        r = -ETIME;
-                else if (streq_ptr(d.result, "canceled"))
-                        r = -ECANCELED;
-                else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped"))
-                        r = -EIO;
+                free(d.name);
+                d.name = NULL;
 
                 free(d.result);
                 d.result = NULL;
-
-        free_name:
-                free(d.name);
-                d.name = NULL;
         }
 
-        return sd_bus_remove_filter(bus, wait_filter, &d);
+        q = sd_bus_remove_filter(bus, wait_filter, &d);
+        if (q < 0 && r == 0)
+                r = q;
+
+        return r;
 }
 
 static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) {
@@ -3717,20 +3743,19 @@ static int show_all(
 }
 
 static int cat(sd_bus *bus, char **args) {
+        _cleanup_free_ char *unit = NULL, *n = NULL;
         int r = 0;
         char **name;
 
-        _cleanup_free_ char *unit = NULL, *n = NULL;
-
         assert(bus);
         assert(args);
 
         pager_open_if_enabled();
 
         STRV_FOREACH(name, args+1) {
-                _cleanup_free_ char *fragment_path = NULL;
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_strv_free_ char **dropin_paths = NULL;
-                sd_bus_error error;
+                _cleanup_free_ char *fragment_path = NULL;
                 char **path;
 
                 n = unit_name_mangle(*name);
@@ -3774,6 +3799,7 @@ static int cat(sd_bus *bus, char **args) {
                 if (!isempty(fragment_path)) {
                         fprintf(stdout, "# %s\n", fragment_path);
                         fflush(stdout);
+
                         r = sendfile_full(STDOUT_FILENO, fragment_path);
                         if (r < 0) {
                                 log_warning("Failed to cat %s: %s", fragment_path, strerror(-r));
@@ -3786,6 +3812,7 @@ static int cat(sd_bus *bus, char **args) {
                                 isempty(fragment_path) && path == dropin_paths ? "" : "\n",
                                 *path);
                         fflush(stdout);
+
                         r = sendfile_full(STDOUT_FILENO, *path);
                         if (r < 0) {
                                 log_warning("Failed to cat %s: %s", *path, strerror(-r));
@@ -4740,7 +4767,7 @@ static int unit_is_enabled(sd_bus *bus, char **args) {
                                         "GetUnitFileState",
                                         &error,
                                         &reply,
-                                        "s", name);
+                                        "s", *name);
                         if (r < 0) {
                                 log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
                                 return r;
@@ -4808,31 +4835,31 @@ static int systemctl_help(void) {
                "     --root=PATH      Enable unit files in the specified root directory\n"
                "  -n --lines=INTEGER  Number of journal entries to show\n"
                "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
-               "                      verbose, export, json, json-pretty, json-sse, cat)\n\n"
+               "                      verbose, export, json, json-pretty, json-sse, cat)\n"
+               "     --plain          Print unit dependencies as a list instead of a tree\n\n"
                "Unit Commands:\n"
                "  list-units [PATTERN...]         List loaded units\n"
                "  list-sockets [PATTERN...]       List loaded sockets ordered by address\n"
                "  list-timers [PATTERN...]        List loaded timers ordered by next elapse\n"
-               "  start [NAME...]                 Start (activate) one or more units\n"
-               "  stop [NAME...]                  Stop (deactivate) one or more units\n"
-               "  reload [NAME...]                Reload one or more units\n"
-               "  restart [NAME...]               Start or restart one or more units\n"
-               "  try-restart [NAME...]           Restart one or more units if active\n"
-               "  reload-or-restart [NAME...]     Reload one or more units if possible,\n"
+               "  start NAME...                   Start (activate) one or more units\n"
+               "  stop NAME...                    Stop (deactivate) one or more units\n"
+               "  reload NAME...                  Reload one or more units\n"
+               "  restart NAME...                 Start or restart one or more units\n"
+               "  try-restart NAME...             Restart one or more units if active\n"
+               "  reload-or-restart NAME...       Reload one or more units if possible,\n"
                "                                  otherwise start or restart\n"
-               "  reload-or-try-restart [NAME...] Reload one or more units if possible,\n"
+               "  reload-or-try-restart NAME...   Reload one or more units if 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"
-               "  is-failed [NAME...]             Check whether units are failed\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"
+               "  is-failed NAME...               Check whether units are failed\n"
                "  status [NAME...|PID...]         Show runtime status of one or more units\n"
                "  show [NAME...|JOB...]           Show properties of one or more\n"
                "                                  units/jobs or the manager\n"
-               "  cat [NAME...]                   Show files and drop-ins of one or more units\n"
-               "  set-property [NAME] [ASSIGNMENT...]\n"
-               "                                  Sets one or more properties of a unit\n"
-               "  help [NAME...|PID...]           Show manual for one or more units\n"
+               "  cat NAME...                     Show files and drop-ins of one or more units\n"
+               "  set-property NAME ASSIGNMENT... Sets one or more properties of a unit\n"
+               "  help NAME...|PID...             Show manual for one or more units\n"
                "  reset-failed [NAME...]          Reset failed state for all, one, or more\n"
                "                                  units\n"
                "  list-dependencies [NAME]        Recursively show units which are required\n"
@@ -4840,15 +4867,15 @@ static int systemctl_help(void) {
                "                                  unit is required or wanted\n\n"
                "Unit File Commands:\n"
                "  list-unit-files [PATTERN...]    List installed unit files\n"
-               "  enable [NAME...]                Enable one or more unit files\n"
-               "  disable [NAME...]               Disable one or more unit files\n"
-               "  reenable [NAME...]              Reenable one or more unit files\n"
-               "  preset [NAME...]                Enable/disable one or more unit files\n"
+               "  enable NAME...                  Enable one or more unit files\n"
+               "  disable NAME...                 Disable one or more unit files\n"
+               "  reenable NAME...                Reenable one or more unit files\n"
+               "  preset NAME...                  Enable/disable one or more unit files\n"
                "                                  based on preset configuration\n"
-               "  is-enabled [NAME...]            Check whether unit files are enabled\n\n"
-               "  mask [NAME...]                  Mask one or more units\n"
-               "  unmask [NAME...]                Unmask one or more units\n"
-               "  link [PATH...]                  Link one or more units files into\n"
+               "  is-enabled NAME...              Check whether unit files are enabled\n\n"
+               "  mask NAME...                    Mask one or more units\n"
+               "  unmask NAME...                  Unmask one or more units\n"
+               "  link PATH...                    Link one or more units files into\n"
                "                                  the search path\n"
                "  get-default                     Get the name of the default target\n"
                "  set-default NAME                Set the default target\n\n"
@@ -4857,11 +4884,11 @@ static int systemctl_help(void) {
                "  cancel [JOB...]                 Cancel all, one, or more jobs\n\n"
                "Snapshot Commands:\n"
                "  snapshot [NAME]                 Create a snapshot\n"
-               "  delete [NAME...]                Remove one or more snapshots\n\n"
+               "  delete NAME...                  Remove one or more snapshots\n\n"
                "Environment Commands:\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\n"
+               "  set-environment NAME=VALUE...   Set one or more environment variables\n"
+               "  unset-environment NAME...       Unset one or more environment variables\n\n"
                "Manager Lifecycle Commands:\n"
                "  daemon-reload                   Reload systemd manager configuration\n"
                "  daemon-reexec                   Reexecute systemd manager\n\n"
@@ -4874,7 +4901,7 @@ static int systemctl_help(void) {
                "  reboot [ARG]                    Shut down and reboot the system\n"
                "  kexec                           Shut down and reboot the system with kexec\n"
                "  exit                            Request user instance exit\n"
-               "  switch-root [ROOT] [INIT]       Change to a different root file system\n"
+               "  switch-root ROOT [INIT]         Change to a different root file system\n"
                "  suspend                         Suspend the system\n"
                "  hibernate                       Hibernate the system\n"
                "  hybrid-sleep                    Hibernate and suspend the system\n",
@@ -5788,9 +5815,13 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 } argc_cmp;
                 const int argc;
                 int (* const dispatch)(sd_bus *bus, char **args);
+                const enum {
+                        NOBUS = 1,
+                        FORCE,
+                } bus;
         } verbs[] = {
                 { "list-units",            MORE,  0, list_units        },
-                { "list-unit-files",       MORE,  1, list_unit_files   },
+                { "list-unit-files",       MORE,  1, list_unit_files,  NOBUS },
                 { "list-sockets",          MORE,  1, list_sockets      },
                 { "list-timers",           MORE,  1, list_timers       },
                 { "list-jobs",             MORE,  1, list_jobs         },
@@ -5823,9 +5854,9 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "show-environment",      EQUAL, 1, show_environment  },
                 { "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     },
+                { "halt",                  EQUAL, 1, start_special,    FORCE },
+                { "poweroff",              EQUAL, 1, start_special,    FORCE },
+                { "reboot",                EQUAL, 1, start_special,    FORCE },
                 { "kexec",                 EQUAL, 1, start_special     },
                 { "suspend",               EQUAL, 1, start_special     },
                 { "hibernate",             EQUAL, 1, start_special     },
@@ -5835,53 +5866,50 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "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       },
-                { "is-enabled",            MORE,  2, unit_is_enabled   },
-                { "reenable",              MORE,  2, enable_unit       },
-                { "preset",                MORE,  2, enable_unit       },
-                { "mask",                  MORE,  2, enable_unit       },
-                { "unmask",                MORE,  2, enable_unit       },
-                { "link",                  MORE,  2, enable_unit       },
+                { "enable",                MORE,  2, enable_unit,      NOBUS },
+                { "disable",               MORE,  2, enable_unit,      NOBUS },
+                { "is-enabled",            MORE,  2, unit_is_enabled,  NOBUS },
+                { "reenable",              MORE,  2, enable_unit,      NOBUS },
+                { "preset",                MORE,  2, enable_unit,      NOBUS },
+                { "mask",                  MORE,  2, enable_unit,      NOBUS },
+                { "unmask",                MORE,  2, enable_unit,      NOBUS },
+                { "link",                  MORE,  2, enable_unit,      NOBUS },
                 { "switch-root",           MORE,  2, switch_root       },
                 { "list-dependencies",     LESS,  2, list_dependencies },
-                { "set-default",           EQUAL, 2, set_default       },
-                { "get-default",           EQUAL, 1, get_default       },
+                { "set-default",           EQUAL, 2, set_default,      NOBUS },
+                { "get-default",           EQUAL, 1, get_default,      NOBUS },
                 { "set-property",          MORE,  3, set_property      },
-        };
+                {}
+        }, *verb = verbs;
 
         int left;
-        unsigned i;
 
         assert(argc >= 0);
         assert(argv);
 
         left = argc - optind;
 
-        if (left <= 0)
-                /* Special rule: no arguments means "list-units" */
-                i = 0;
-        else {
+        /* Special rule: no arguments (left == 0) means "list-units" */
+        if (left > 0) {
                 if (streq(argv[optind], "help") && !argv[optind+1]) {
                         log_error("This command expects one or more "
                                   "unit names. Did you mean --help?");
                         return -EINVAL;
                 }
 
-                for (i = 0; i < ELEMENTSOF(verbs); i++)
-                        if (streq(argv[optind], verbs[i].verb))
-                                break;
+                for (; verb->verb; verb++)
+                        if (streq(argv[optind], verb->verb))
+                                goto found;
 
-                if (i >= ELEMENTSOF(verbs)) {
-                        log_error("Unknown operation '%s'.", argv[optind]);
-                        return -EINVAL;
-                }
+                log_error("Unknown operation '%s'.", argv[optind]);
+                return -EINVAL;
         }
+found:
 
-        switch (verbs[i].argc_cmp) {
+        switch (verb->argc_cmp) {
 
         case EQUAL:
-                if (left != verbs[i].argc) {
+                if (left != verb->argc) {
                         log_error("Invalid number of arguments.");
                         return -EINVAL;
                 }
@@ -5889,7 +5917,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 break;
 
         case MORE:
-                if (left < verbs[i].argc) {
+                if (left < verb->argc) {
                         log_error("Too few arguments.");
                         return -EINVAL;
                 }
@@ -5897,7 +5925,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 break;
 
         case LESS:
-                if (left > verbs[i].argc) {
+                if (left > verb->argc) {
                         log_error("Too many arguments.");
                         return -EINVAL;
                 }
@@ -5910,39 +5938,25 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
 
         /* Require a bus connection for all operations but
          * enable/disable */
-        if (!streq(verbs[i].verb, "enable") &&
-            !streq(verbs[i].verb, "disable") &&
-            !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") &&
-            !streq(verbs[i].verb, "unmask") &&
-            !streq(verbs[i].verb, "link") &&
-            !streq(verbs[i].verb, "set-default") &&
-            !streq(verbs[i].verb, "get-default")) {
+        if (verb->bus == NOBUS) {
+                if (!bus && !avoid_bus()) {
+                        log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
+                        return -EIO;
+                }
 
+        } else {
                 if (running_in_chroot() > 0) {
                         log_info("Running in chroot, ignoring request.");
                         return 0;
                 }
 
-                if (((!streq(verbs[i].verb, "reboot") &&
-                      !streq(verbs[i].verb, "halt") &&
-                      !streq(verbs[i].verb, "poweroff")) || arg_force <= 0) && !bus) {
-                        log_error("Failed to get D-Bus connection: %s", strerror (-bus_error));
-                        return -EIO;
-                }
-
-        } else {
-
-                if (!bus && !avoid_bus()) {
-                        log_error("Failed to get D-Bus connection: %s", strerror (-bus_error));
+                if ((verb->bus != FORCE || arg_force <= 0) && !bus) {
+                        log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
                         return -EIO;
                 }
         }
 
-        return verbs[i].dispatch(bus, argv + optind);
+        return verb->dispatch(bus, argv + optind);
 }
 
 static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {