]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core,install: generalize install error handling 31590/head
authorMike Yuan <me@yhndnzj.com>
Thu, 29 Feb 2024 12:58:17 +0000 (20:58 +0800)
committerMike Yuan <me@yhndnzj.com>
Wed, 6 Mar 2024 18:05:15 +0000 (02:05 +0800)
src/core/dbus-manager.c
src/shared/install.c
src/shared/install.h

index 00fd801cb3ffd046e8104aff0cfb44cc6118f27d..fbc5fce1282afe42e16a2d8e833b768b8820d3ac 100644 (file)
@@ -2304,85 +2304,36 @@ static int send_unit_files_changed(sd_bus *bus, void *userdata) {
         return sd_bus_send(bus, message, NULL);
 }
 
-/* Create an error reply, using the error information from changes[]
- * if possible, and fall back to generating an error from error code c.
- * The error message only describes the first error.
- */
 static int install_error(
                 sd_bus_error *error,
                 int c,
                 InstallChange *changes,
                 size_t n_changes) {
 
-        CLEANUP_ARRAY(changes, n_changes, install_changes_free);
+        int r;
 
-        for (size_t i = 0; i < n_changes; i++)
+        /* Create an error reply, using the error information from changes[] if possible, and fall back to
+         * generating an error from error code c. The error message only describes the first error. */
 
-                /* When making changes here, make sure to also change install_changes_dump() in install.c. */
+        assert(changes || n_changes == 0);
 
-                switch (changes[i].type) {
-                case 0 ... _INSTALL_CHANGE_TYPE_MAX: /* not errors */
-                        break;
+        CLEANUP_ARRAY(changes, n_changes, install_changes_free);
 
-                case -EEXIST:
-                        if (changes[i].source)
-                                return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
-                                                         "File %s already exists and is a symlink to %s.",
-                                                         changes[i].path, changes[i].source);
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
-                                                 "File %s already exists.",
-                                                 changes[i].path);
-
-                case -ERFKILL:
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED,
-                                                 "Unit file %s is masked.", changes[i].path);
-
-                case -EADDRNOTAVAIL:
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED,
-                                                 "Unit %s is transient or generated.", changes[i].path);
-
-                case -ETXTBSY:
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_BAD_PATH,
-                                                 "File %s is under the systemd unit hierarchy already.", changes[i].path);
-
-                case -EBADSLT:
-                        return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
-                                                 "Invalid specifier in %s.", changes[i].path);
-
-                case -EIDRM:
-                        return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
-                                                 "Destination unit %s is a non-template unit.", changes[i].path);
-
-                case -EUCLEAN:
-                        return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
-                                                 "\"%s\" is not a valid unit name.",
-                                                 changes[i].path);
-
-                case -ELOOP:
-                        return sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED,
-                                                 "Refusing to operate on alias name or linked unit file: %s",
-                                                 changes[i].path);
-
-                case -EXDEV:
-                        if (changes[i].source)
-                                return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
-                                                         "Cannot alias %s as %s.",
-                                                         changes[i].source, changes[i].path);
-                        return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
-                                                 "Invalid unit reference %s.", changes[i].path);
-
-                case -ENOENT:
-                        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT,
-                                                 "Unit file %s does not exist.", changes[i].path);
+        FOREACH_ARRAY(i, changes, n_changes) {
+                _cleanup_free_ char *err_message = NULL;
+                const char *bus_error;
+
+                if (i->type >= 0)
+                        continue;
 
-                case -EUNATCH:
-                        return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
-                                                 "Cannot resolve specifiers in %s.", changes[i].path);
+                r = install_change_dump_error(i, &err_message, &bus_error);
+                if (r == -ENOMEM)
+                        return r;
+                if (r < 0)
+                        return sd_bus_error_set_errnof(error, r, "File %s: %m", i->path);
 
-                default:
-                        assert(changes[i].type < 0); /* other errors */
-                        return sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path);
-                }
+                return sd_bus_error_set(error, bus_error, err_message);
+        }
 
         return c < 0 ? c : -EINVAL;
 }
@@ -2421,18 +2372,17 @@ static int reply_install_changes_and_free(
         if (r < 0)
                 return r;
 
-        for (size_t i = 0; i < n_changes; i++) {
-
-                if (changes[i].type < 0) {
+        FOREACH_ARRAY(i, changes, n_changes) {
+                if (i->type < 0) {
                         bad = true;
                         continue;
                 }
 
                 r = sd_bus_message_append(
                                 reply, "(sss)",
-                                install_change_type_to_string(changes[i].type),
-                                changes[i].path,
-                                changes[i].source);
+                                install_change_type_to_string(i->type),
+                                i->path,
+                                i->source);
                 if (r < 0)
                         return r;
 
index ce8adb2e16a7b1e6caeca87b0daee8ff795ad72a..e42f77aa28e451b368befbd6913bf9332b34de7c 100644 (file)
@@ -10,6 +10,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "bus-common-errors.h"
 #include "chase.h"
 #include "conf-files.h"
 #include "conf-parser.h"
@@ -332,122 +333,163 @@ void install_changes_free(InstallChange *changes, size_t n_changes) {
         free(changes);
 }
 
-void install_changes_dump(int r, const char *verb, const InstallChange *changes, size_t n_changes, bool quiet) {
-        int err = 0;
+static void install_change_dump_success(const InstallChange *change) {
+        assert(change);
+        assert(change->path);
 
-        assert(changes || n_changes == 0);
-        /* If verb is not specified, errors are not allowed! */
-        assert(verb || r >= 0);
+        switch (change->type) {
 
-        for (size_t i = 0; i < n_changes; i++) {
-                assert(changes[i].path);
-                /* This tries to tell the compiler that it's safe to use 'verb' in a string format if there
-                 * was an error, but the compiler doesn't care and fails anyway, so strna(verb) is used
-                 * too. */
-                assert(verb || changes[i].type >= 0);
-                verb = strna(verb);
+        case INSTALL_CHANGE_SYMLINK:
+                return log_info("Created symlink '%s' %s '%s'.",
+                                change->path, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), change->source);
 
-                /* When making changes here, make sure to also change install_error() in dbus-manager.c. */
+        case INSTALL_CHANGE_UNLINK:
+                return log_info("Removed '%s'.", change->path);
 
-                switch (changes[i].type) {
-                case INSTALL_CHANGE_SYMLINK:
-                        if (!quiet)
-                                log_info("Created symlink %s %s %s.",
-                                         changes[i].path,
-                                         special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
-                                         changes[i].source);
-                        break;
-                case INSTALL_CHANGE_UNLINK:
-                        if (!quiet)
-                                log_info("Removed \"%s\".", changes[i].path);
-                        break;
-                case INSTALL_CHANGE_IS_MASKED:
-                        if (!quiet)
-                                log_info("Unit %s is masked, ignoring.", changes[i].path);
-                        break;
-                case INSTALL_CHANGE_IS_MASKED_GENERATOR:
-                        if (!quiet)
-                                log_info("Unit %s is masked via a generator and cannot be unmasked.",
-                                         changes[i].path);
-                        break;
-                case INSTALL_CHANGE_IS_DANGLING:
-                        if (!quiet)
-                                log_info("Unit %s is an alias to a unit that is not present, ignoring.",
-                                         changes[i].path);
-                        break;
-                case INSTALL_CHANGE_DESTINATION_NOT_PRESENT:
-                        if (!quiet)
-                                log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
-                                            changes[i].source, changes[i].path);
-                        break;
-                case INSTALL_CHANGE_AUXILIARY_FAILED:
+        case INSTALL_CHANGE_IS_MASKED:
+                return log_info("Unit %s is masked, ignoring.", change->path);
+
+        case INSTALL_CHANGE_IS_MASKED_GENERATOR:
+                return log_info("Unit %s is masked via a generator and cannot be unmasked, skipping.", change->path);
+
+        case INSTALL_CHANGE_IS_DANGLING:
+                return log_info("Unit %s is an alias to a non-existent unit, ignoring.", change->path);
+
+        case INSTALL_CHANGE_DESTINATION_NOT_PRESENT:
+                return log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
+                                   change->source, change->path);
+
+        case INSTALL_CHANGE_AUXILIARY_FAILED:
+                return log_warning("Failed to enable auxiliary unit %s, ignoring.", change->path);
+
+        default:
+                assert_not_reached();
+        }
+}
+
+int install_change_dump_error(const InstallChange *change, char **ret_errmsg, const char **ret_bus_error) {
+        char *m;
+        const char *bus_error;
+
+        /* Returns 0:   known error and ret_errmsg formatted
+         *         < 0: non-recognizable error */
+
+        assert(change);
+        assert(change->path);
+        assert(change->type < 0);
+        assert(ret_errmsg);
+
+        switch (change->type) {
+
+        case -EEXIST:
+                m = strjoin("File '", change->path, "' already exists",
+                            change->source ? " and is a symlink to " : NULL, change->source);
+                bus_error = BUS_ERROR_UNIT_EXISTS;
+                break;
+
+        case -ERFKILL:
+                m = strjoin("Unit ", change->path, " is masked");
+                bus_error = BUS_ERROR_UNIT_MASKED;
+                break;
+
+        case -EADDRNOTAVAIL:
+                m = strjoin("Unit ", change->path, " is transient or generated");
+                bus_error = BUS_ERROR_UNIT_GENERATED;
+                break;
+
+        case -ETXTBSY:
+                m = strjoin("File '", change->path, "' is under the systemd unit hierarchy already");
+                bus_error = BUS_ERROR_UNIT_BAD_PATH;
+                break;
+
+        case -EBADSLT:
+                m = strjoin("Invalid specifier in unit ", change->path);
+                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
+                break;
+
+        case -EIDRM:
+                m = strjoin("Refusing to operate on template unit ", change->source,
+                            " when destination unit ", change->path, " is a non-template unit");
+                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
+                break;
+
+        case -EUCLEAN:
+                m = strjoin("Invalid unit name ", change->path);
+                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
+                break;
+
+        case -ELOOP:
+                m = strjoin("Refusing to operate on linked unit file ", change->path);
+                bus_error = BUS_ERROR_UNIT_LINKED;
+                break;
+
+        case -EXDEV:
+                if (change->source)
+                        m = strjoin("Cannot alias ", change->source, " as ", change->path);
+                else
+                        m = strjoin("Invalid unit reference ", change->path);
+                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
+                break;
+
+        case -ENOENT:
+                m = strjoin("Unit ", change->path, " does not exist");
+                bus_error = BUS_ERROR_NO_SUCH_UNIT;
+                break;
+
+        case -EUNATCH:
+                m = strjoin("Cannot resolve specifiers in unit ", change->path);
+                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
+                break;
+
+        default:
+                return change->type;
+        }
+        if (!m)
+                return -ENOMEM;
+
+        *ret_errmsg = m;
+        if (ret_bus_error)
+                *ret_bus_error = bus_error;
+
+        return 0;
+}
+
+void install_changes_dump(
+                int error,
+                const char *verb,
+                const InstallChange *changes,
+                size_t n_changes,
+                bool quiet) {
+
+        bool err_logged = false;
+        int r;
+
+        /* If verb is not specified, errors are not allowed! */
+        assert(verb || error >= 0);
+        assert(changes || n_changes == 0);
+
+        FOREACH_ARRAY(i, changes, n_changes)
+                if (i->type >= 0) {
                         if (!quiet)
-                                log_warning("Failed to enable auxiliary unit %s, ignoring.", changes[i].path);
-                        break;
-                case -EEXIST:
-                        if (changes[i].source)
-                                err = log_error_errno(changes[i].type,
-                                                      "Failed to %s unit, file \"%s\" already exists and is a symlink to \"%s\".",
-                                                      verb, changes[i].path, changes[i].source);
-                        else
-                                err = log_error_errno(changes[i].type,
-                                                      "Failed to %s unit, file \"%s\" already exists.",
-                                                      verb, changes[i].path);
-                        break;
-                case -ERFKILL:
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.",
-                                              verb, changes[i].path);
-                        break;
-                case -EADDRNOTAVAIL:
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.",
-                                              verb, changes[i].path);
-                        break;
-                case -ETXTBSY:
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, file %s is under the systemd unit hierarchy already.",
-                                              verb, changes[i].path);
-                        break;
-                case -EBADSLT:
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, invalid specifier in \"%s\".",
-                                              verb, changes[i].path);
-                        break;
-                case -EIDRM:
-                        err = log_error_errno(changes[i].type, "Failed to %s %s, destination unit %s is a non-template unit.",
-                                              verb, changes[i].source, changes[i].path);
-                        break;
-                case -EUCLEAN:
-                        err = log_error_errno(changes[i].type,
-                                              "Failed to %s unit, \"%s\" is not a valid unit name.",
-                                              verb, changes[i].path);
-                        break;
-                case -ELOOP:
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s.",
-                                              verb, changes[i].path);
-                        break;
-                case -EXDEV:
-                        if (changes[i].source)
-                                err = log_error_errno(changes[i].type, "Failed to %s unit, cannot alias %s as %s.",
-                                                      verb, changes[i].source, changes[i].path);
+                                install_change_dump_success(i);
+                } else {
+                        _cleanup_free_ char *err_message = NULL;
+
+                        assert(verb);
+
+                        r = install_change_dump_error(i, &err_message, /* ret_bus_error = */ NULL);
+                        if (r == -ENOMEM)
+                                return (void) log_oom();
+                        if (r < 0)
+                                log_error_errno(r, "Failed to %s unit %s: %m", verb, i->path);
                         else
-                                err = log_error_errno(changes[i].type, "Failed to %s unit, invalid unit reference \"%s\".",
-                                                      verb, changes[i].path);
-                        break;
-                case -ENOENT:
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, unit %s does not exist.",
-                                              verb, changes[i].path);
-                        break;
-                case -EUNATCH:
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, cannot resolve specifiers in \"%s\".",
-                                              verb, changes[i].path);
-                        break;
-                default:
-                        assert(changes[i].type < 0);
-                        err = log_error_errno(changes[i].type, "Failed to %s unit, file \"%s\": %m",
-                                              verb, changes[i].path);
+                                log_error_errno(i->type, "Failed to %s unit: %s", verb, err_message);
+
+                        err_logged = true;
                 }
-        }
 
-        if (r < 0 && err >= 0)
-                log_error_errno(r, "Failed to %s: %m.", verb);
+        if (error < 0 && !err_logged)
+                log_error_errno(error, "Failed to %s unit: %m.", verb);
 }
 
 /**
index 3e2ada45f495f5a697ebdd25d53675041c388183..a09557f69f244c1d6dfd407120c2015d2240b90b 100644 (file)
@@ -205,7 +205,14 @@ extern const struct hash_ops unit_file_list_hash_ops_free;
 
 InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, InstallChangeType type, const char *path, const char *source);
 void install_changes_free(InstallChange *changes, size_t n_changes);
-void install_changes_dump(int r, const char *verb, const InstallChange *changes, size_t n_changes, bool quiet);
+
+int install_change_dump_error(const InstallChange *change, char **ret_errmsg, const char **ret_bus_error);
+void install_changes_dump(
+                int error,
+                const char *verb,
+                const InstallChange *changes,
+                size_t n_changes,
+                bool quiet);
 
 int unit_file_verify_alias(
                 const InstallInfo *info,