]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/delta/delta.c
tree-wide: drop license boilerplate
[thirdparty/systemd.git] / src / delta / delta.c
index 8bf678c28fb793062f53f57d6a06dd295e79aa46..aab1dbfad210fa4b8b42229c3c909be57dcfd25e 100644 (file)
@@ -1,23 +1,9 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
   Copyright 2012 Lennart Poettering
   Copyright 2013 Zbigniew JÄ™drzejewski-Szmek
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include <errno.h>
@@ -51,7 +37,7 @@ static const char prefixes[] =
         "/usr/local/share\0"
         "/usr/lib\0"
         "/usr/share\0"
-#ifdef HAVE_SPLIT_USR
+#if HAVE_SPLIT_USR
         "/lib\0"
 #endif
         ;
@@ -87,24 +73,17 @@ static enum {
         (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED)
 } arg_flags = 0;
 
-static void pager_open_if_enabled(void) {
-
-        if (arg_no_pager)
-                return;
-
-        pager_open(false);
-}
-
 static int equivalent(const char *a, const char *b) {
         _cleanup_free_ char *x = NULL, *y = NULL;
+        int r;
 
-        x = canonicalize_file_name(a);
-        if (!x)
-                return -errno;
+        r = chase_symlinks(a, NULL, CHASE_TRAIL_SLASH, &x);
+        if (r < 0)
+                return r;
 
-        y = canonicalize_file_name(b);
-        if (!y)
-                return -errno;
+        r = chase_symlinks(b, NULL, CHASE_TRAIL_SLASH, &y);
+        if (r < 0)
+                return r;
 
         return path_equal(x, y);
 }
@@ -115,7 +94,7 @@ static int notify_override_masked(const char *top, const char *bottom) {
 
         printf("%s%s%s     %s %s %s\n",
                ansi_highlight_red(), "[MASKED]", ansi_normal(),
-               top, draw_special_char(DRAW_ARROW), bottom);
+               top, special_glyph(ARROW), bottom);
         return 1;
 }
 
@@ -125,7 +104,7 @@ static int notify_override_equivalent(const char *top, const char *bottom) {
 
         printf("%s%s%s %s %s %s\n",
                ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(),
-               top, draw_special_char(DRAW_ARROW), bottom);
+               top, special_glyph(ARROW), bottom);
         return 1;
 }
 
@@ -135,7 +114,7 @@ static int notify_override_redirected(const char *top, const char *bottom) {
 
         printf("%s%s%s %s %s %s\n",
                ansi_highlight(), "[REDIRECTED]", ansi_normal(),
-               top, draw_special_char(DRAW_ARROW), bottom);
+               top, special_glyph(ARROW), bottom);
         return 1;
 }
 
@@ -145,7 +124,7 @@ static int notify_override_overridden(const char *top, const char *bottom) {
 
         printf("%s%s%s %s %s %s\n",
                ansi_highlight(), "[OVERRIDDEN]", ansi_normal(),
-               top, draw_special_char(DRAW_ARROW), bottom);
+               top, special_glyph(ARROW), bottom);
         return 1;
 }
 
@@ -155,7 +134,7 @@ static int notify_override_extended(const char *top, const char *bottom) {
 
         printf("%s%s%s   %s %s %s\n",
                ansi_highlight(), "[EXTENDED]", ansi_normal(),
-               top, draw_special_char(DRAW_ARROW), bottom);
+               top, special_glyph(ARROW), bottom);
         return 1;
 }
 
@@ -169,8 +148,8 @@ static int notify_override_unchanged(const char *f) {
 
 static int found_override(const char *top, const char *bottom) {
         _cleanup_free_ char *dest = NULL;
-        int k;
         pid_t pid;
+        int r;
 
         assert(top);
         assert(bottom);
@@ -178,43 +157,44 @@ static int found_override(const char *top, const char *bottom) {
         if (null_or_empty_path(top) > 0)
                 return notify_override_masked(top, bottom);
 
-        k = readlink_malloc(top, &dest);
-        if (k >= 0) {
+        r = readlink_malloc(top, &dest);
+        if (r >= 0) {
                 if (equivalent(dest, bottom) > 0)
                         return notify_override_equivalent(top, bottom);
                 else
                         return notify_override_redirected(top, bottom);
         }
 
-        k = notify_override_overridden(top, bottom);
+        r = notify_override_overridden(top, bottom);
         if (!arg_diff)
-                return k;
+                return r;
 
         putchar('\n');
 
         fflush(stdout);
 
-        pid = fork();
-        if (pid < 0)
-                return log_error_errno(errno, "Failed to fork off diff: %m");
-        else if (pid == 0) {
-
-                (void) reset_all_signal_handlers();
-                (void) reset_signal_mask();
-                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
+        r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
                 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
+                log_open();
                 log_error_errno(errno, "Failed to execute diff: %m");
                 _exit(EXIT_FAILURE);
         }
 
-        wait_for_terminate_and_warn("diff", pid, false);
+        (void) wait_for_terminate_and_check("diff", pid, WAIT_LOG_ABNORMAL);
         putchar('\n');
 
-        return k;
+        return r;
 }
 
-static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
+static int enumerate_dir_d(
+                OrderedHashmap *top,
+                OrderedHashmap *bottom,
+                OrderedHashmap *drops,
+                const char *toppath, const char *drop) {
+
         _cleanup_free_ char *unit = NULL;
         _cleanup_free_ char *path = NULL;
         _cleanup_strv_free_ char **list = NULL;
@@ -224,7 +204,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
 
         assert(!endswith(drop, "/"));
 
-        path = strjoin(toppath, "/", drop, NULL);
+        path = strjoin(toppath, "/", drop);
         if (!path)
                 return -ENOMEM;
 
@@ -243,8 +223,10 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate %s: %m", path);
 
+        strv_sort(list);
+
         STRV_FOREACH(file, list) {
-                Hashmap *h;
+                OrderedHashmap *h;
                 int k;
                 char *p;
                 char *d;
@@ -252,13 +234,13 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
                 if (!endswith(*file, ".conf"))
                         continue;
 
-                p = strjoin(path, "/", *file, NULL);
+                p = strjoin(path, "/", *file);
                 if (!p)
                         return -ENOMEM;
                 d = p + strlen(toppath) + 1;
 
-                log_debug("Adding at top: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
-                k = hashmap_put(top, d, p);
+                log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p);
+                k = ordered_hashmap_put(top, d, p);
                 if (k >= 0) {
                         p = strdup(p);
                         if (!p)
@@ -269,20 +251,20 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
                         return k;
                 }
 
-                log_debug("Adding at bottom: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
-                free(hashmap_remove(bottom, d));
-                k = hashmap_put(bottom, d, p);
+                log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p);
+                free(ordered_hashmap_remove(bottom, d));
+                k = ordered_hashmap_put(bottom, d, p);
                 if (k < 0) {
                         free(p);
                         return k;
                 }
 
-                h = hashmap_get(drops, unit);
+                h = ordered_hashmap_get(drops, unit);
                 if (!h) {
-                        h = hashmap_new(&string_hash_ops);
+                        h = ordered_hashmap_new(&string_hash_ops);
                         if (!h)
                                 return -ENOMEM;
-                        hashmap_put(drops, unit, h);
+                        ordered_hashmap_put(drops, unit, h);
                         unit = strdup(unit);
                         if (!unit)
                                 return -ENOMEM;
@@ -293,8 +275,8 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
                         return -ENOMEM;
 
                 log_debug("Adding to drops: %s %s %s %s %s",
-                          unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p);
-                k = hashmap_put(h, basename(p), p);
+                          unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p);
+                k = ordered_hashmap_put(h, basename(p), p);
                 if (k < 0) {
                         free(p);
                         if (k != -EEXIST)
@@ -304,8 +286,18 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const
         return 0;
 }
 
-static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
-        _cleanup_closedir_ DIR *d;
+static int enumerate_dir(
+                OrderedHashmap *top,
+                OrderedHashmap *bottom,
+                OrderedHashmap *drops,
+                const char *path, bool dropins) {
+
+        _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+        _cleanup_strv_free_ char **files = NULL, **dirs = NULL;
+        size_t n_files = 0, allocated_files = 0, n_dirs = 0, allocated_dirs = 0;
+        char **t;
+        int r;
 
         assert(top);
         assert(bottom);
@@ -322,54 +314,96 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch
                 return log_error_errno(errno, "Failed to open %s: %m", path);
         }
 
-        for (;;) {
-                struct dirent *de;
-                int k;
-                char *p;
-
-                errno = 0;
-                de = readdir(d);
-                if (!de)
-                        return -errno;
-
+        FOREACH_DIRENT_ALL(de, d, return -errno) {
                 dirent_ensure_type(d, de);
 
-                if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
-                        enumerate_dir_d(top, bottom, drops, path, de->d_name);
+                if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d")) {
+                        if (!GREEDY_REALLOC0(dirs, allocated_dirs, n_dirs + 2))
+                                return -ENOMEM;
+
+                        dirs[n_dirs] = strdup(de->d_name);
+                        if (!dirs[n_dirs])
+                                return -ENOMEM;
+                        n_dirs ++;
+                }
 
                 if (!dirent_is_file(de))
                         continue;
 
-                p = strjoin(path, "/", de->d_name, NULL);
+                if (!GREEDY_REALLOC0(files, allocated_files, n_files + 2))
+                        return -ENOMEM;
+
+                files[n_files] = strdup(de->d_name);
+                if (!files[n_files])
+                        return -ENOMEM;
+                n_files ++;
+        }
+
+        strv_sort(dirs);
+        strv_sort(files);
+
+        STRV_FOREACH(t, dirs) {
+                r = enumerate_dir_d(top, bottom, drops, path, *t);
+                if (r < 0)
+                        return r;
+        }
+
+        STRV_FOREACH(t, files) {
+                _cleanup_free_ char *p = NULL;
+
+                p = strjoin(path, "/", *t);
                 if (!p)
                         return -ENOMEM;
 
-                log_debug("Adding at top: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
-                k = hashmap_put(top, basename(p), p);
-                if (k >= 0) {
+                log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p);
+                r = ordered_hashmap_put(top, basename(p), p);
+                if (r >= 0) {
                         p = strdup(p);
                         if (!p)
                                 return -ENOMEM;
-                } else if (k != -EEXIST) {
-                        free(p);
-                        return k;
-                }
+                } else if (r != -EEXIST)
+                        return r;
 
-                log_debug("Adding at bottom: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
-                free(hashmap_remove(bottom, basename(p)));
-                k = hashmap_put(bottom, basename(p), p);
-                if (k < 0) {
-                        free(p);
-                        return k;
+                log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(ARROW), p);
+                free(ordered_hashmap_remove(bottom, basename(p)));
+                r = ordered_hashmap_put(bottom, basename(p), p);
+                if (r < 0)
+                        return r;
+                p = NULL;
+        }
+
+        return 0;
+}
+
+static bool should_skip_path(const char *prefix, const char *suffix) {
+#if HAVE_SPLIT_USR
+        _cleanup_free_ char *target = NULL;
+        const char *p;
+        char *dirname;
+
+        dirname = strjoina(prefix, "/", suffix);
+
+        if (chase_symlinks(dirname, NULL, 0, &target) < 0)
+                return false;
+
+        NULSTR_FOREACH(p, prefixes) {
+                if (path_startswith(dirname, p))
+                        continue;
+
+                if (path_equal(target, strjoina(p, "/", suffix))) {
+                        log_debug("%s redirects to %s, skipping.", dirname, target);
+                        return true;
                 }
         }
+#endif
+        return false;
 }
 
 static int process_suffix(const char *suffix, const char *onlyprefix) {
         const char *p;
         char *f;
-        Hashmap *top, *bottom, *drops;
-        Hashmap *h;
+        OrderedHashmap *top, *bottom, *drops;
+        OrderedHashmap *h;
         char *key;
         int r = 0, k;
         Iterator i, j;
@@ -382,9 +416,9 @@ static int process_suffix(const char *suffix, const char *onlyprefix) {
 
         dropins = nulstr_contains(have_dropins, suffix);
 
-        top = hashmap_new(&string_hash_ops);
-        bottom = hashmap_new(&string_hash_ops);
-        drops = hashmap_new(&string_hash_ops);
+        top = ordered_hashmap_new(&string_hash_ops);
+        bottom = ordered_hashmap_new(&string_hash_ops);
+        drops = ordered_hashmap_new(&string_hash_ops);
         if (!top || !bottom || !drops) {
                 r = -ENOMEM;
                 goto finish;
@@ -393,7 +427,10 @@ static int process_suffix(const char *suffix, const char *onlyprefix) {
         NULSTR_FOREACH(p, prefixes) {
                 _cleanup_free_ char *t = NULL;
 
-                t = strjoin(p, "/", suffix, NULL);
+                if (should_skip_path(p, suffix))
+                        continue;
+
+                t = strjoin(p, "/", suffix);
                 if (!t) {
                         r = -ENOMEM;
                         goto finish;
@@ -404,10 +441,10 @@ static int process_suffix(const char *suffix, const char *onlyprefix) {
                         r = k;
         }
 
-        HASHMAP_FOREACH_KEY(f, key, top, i) {
+        ORDERED_HASHMAP_FOREACH_KEY(f, key, top, i) {
                 char *o;
 
-                o = hashmap_get(bottom, key);
+                o = ordered_hashmap_get(bottom, key);
                 assert(o);
 
                 if (!onlyprefix || startswith(o, onlyprefix)) {
@@ -422,26 +459,24 @@ static int process_suffix(const char *suffix, const char *onlyprefix) {
                         }
                 }
 
-                h = hashmap_get(drops, key);
+                h = ordered_hashmap_get(drops, key);
                 if (h)
-                        HASHMAP_FOREACH(o, h, j)
+                        ORDERED_HASHMAP_FOREACH(o, h, j)
                                 if (!onlyprefix || startswith(o, onlyprefix))
                                         n_found += notify_override_extended(f, o);
         }
 
 finish:
-        if (top)
-                hashmap_free_free(top);
-        if (bottom)
-                hashmap_free_free(bottom);
-        if (drops) {
-                HASHMAP_FOREACH_KEY(h, key, drops, i){
-                        hashmap_free_free(hashmap_remove(drops, key));
-                        hashmap_remove(drops, key);
-                        free(key);
-                }
-                hashmap_free(drops);
+        ordered_hashmap_free_free(top);
+        ordered_hashmap_free_free(bottom);
+
+        ORDERED_HASHMAP_FOREACH_KEY(h, key, drops, i) {
+                ordered_hashmap_free_free(ordered_hashmap_remove(drops, key));
+                ordered_hashmap_remove(drops, key);
+                free(key);
         }
+        ordered_hashmap_free(drops);
+
         return r < 0 ? r : n_found;
 }
 
@@ -453,9 +488,10 @@ static int process_suffixes(const char *onlyprefix) {
                 r = process_suffix(n, onlyprefix);
                 if (r < 0)
                         return r;
-                else
-                        n_found += r;
+
+                n_found += r;
         }
+
         return n_found;
 }
 
@@ -469,11 +505,13 @@ static int process_suffix_chop(const char *arg) {
 
         /* Strip prefix from the suffix */
         NULSTR_FOREACH(p, prefixes) {
-                const char *suffix = startswith(arg, p);
+                const char *suffix;
+
+                suffix = startswith(arg, p);
                 if (suffix) {
                         suffix += strspn(suffix, "/");
                         if (*suffix)
-                                return process_suffix(suffix, NULL);
+                                return process_suffix(suffix, p);
                         else
                                 return process_suffixes(arg);
                 }
@@ -577,10 +615,9 @@ static int parse_argv(int argc, char *argv[]) {
                                 if (b < 0) {
                                         log_error("Failed to parse diff boolean.");
                                         return -EINVAL;
-                                } else if (b)
-                                        arg_diff = 1;
-                                else
-                                        arg_diff = 0;
+                                }
+
+                                arg_diff = b;
                         }
                         break;
 
@@ -595,8 +632,7 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 int main(int argc, char *argv[]) {
-        int r = 0, k;
-        int n_found = 0;
+        int r, k, n_found = 0;
 
         log_parse_environment();
         log_open();
@@ -613,13 +649,14 @@ int main(int argc, char *argv[]) {
         else if (arg_diff)
                 arg_flags |= SHOW_OVERRIDDEN;
 
-        pager_open_if_enabled();
+        (void) pager_open(arg_no_pager, false);
 
         if (optind < argc) {
                 int i;
 
                 for (i = optind; i < argc; i++) {
                         path_kill_slashes(argv[i]);
+
                         k = process_suffix_chop(argv[i]);
                         if (k < 0)
                                 r = k;
@@ -636,8 +673,7 @@ int main(int argc, char *argv[]) {
         }
 
         if (r >= 0)
-                printf("%s%i overridden configuration files found.\n",
-                       n_found ? "\n" : "", n_found);
+                printf("%s%i overridden configuration files found.\n", n_found ? "\n" : "", n_found);
 
 finish:
         pager_close();