1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
7 Copyright 2013 Zbigniew Jędrzejewski-Szmek
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "path-util.h"
37 static const char prefixes[] =
49 static const char suffixes[] =
56 "systemd/system-preset\0"
57 "systemd/user-preset\0"
61 static const char have_dropins[] =
65 static bool arg_no_pager = false;
66 static int arg_diff = -1;
70 SHOW_EQUIVALENT = 1 << 1,
71 SHOW_REDIRECTED = 1 << 2,
72 SHOW_OVERRIDDEN = 1 << 3,
73 SHOW_UNCHANGED = 1 << 4,
74 SHOW_EXTENDED = 1 << 5,
77 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED)
80 static void pager_open_if_enabled(void) {
88 static int equivalent(const char *a, const char *b) {
89 _cleanup_free_ char *x = NULL, *y = NULL;
91 x = canonicalize_file_name(a);
95 y = canonicalize_file_name(b);
99 return path_equal(x, y);
102 static int notify_override_masked(const char *top, const char *bottom) {
103 if (!(arg_flags & SHOW_MASKED))
106 printf("%s%s%s %s → %s\n",
107 ansi_highlight_red(), "[MASKED]", ansi_highlight_off(), top, bottom);
111 static int notify_override_equivalent(const char *top, const char *bottom) {
112 if (!(arg_flags & SHOW_EQUIVALENT))
115 printf("%s%s%s %s → %s\n",
116 ansi_highlight_green(), "[EQUIVALENT]", ansi_highlight_off(), top, bottom);
120 static int notify_override_redirected(const char *top, const char *bottom) {
121 if (!(arg_flags & SHOW_REDIRECTED))
124 printf("%s%s%s %s → %s\n",
125 ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(), top, bottom);
129 static int notify_override_overridden(const char *top, const char *bottom) {
130 if (!(arg_flags & SHOW_OVERRIDDEN))
133 printf("%s%s%s %s → %s\n",
134 ansi_highlight(), "[OVERRIDDEN]", ansi_highlight_off(), top, bottom);
138 static int notify_override_extended(const char *top, const char *bottom) {
139 if (!(arg_flags & SHOW_EXTENDED))
142 printf("%s%s%s %s → %s\n",
143 ansi_highlight(), "[EXTENDED]", ansi_highlight_off(), top, bottom);
147 static int notify_override_unchanged(const char *f) {
148 if (!(arg_flags & SHOW_UNCHANGED))
151 printf("[UNCHANGED] %s\n", f);
155 static int found_override(const char *top, const char *bottom) {
156 _cleanup_free_ char *dest = NULL;
163 if (null_or_empty_path(top) > 0)
164 return notify_override_masked(top, bottom);
166 k = readlink_malloc(top, &dest);
168 if (equivalent(dest, bottom) > 0)
169 return notify_override_equivalent(top, bottom);
171 return notify_override_redirected(top, bottom);
174 k = notify_override_overridden(top, bottom);
184 log_error("Failed to fork off diff: %m");
186 } else if (pid == 0) {
187 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
188 log_error("Failed to execute diff: %m");
192 wait_for_terminate(pid, NULL);
199 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
200 _cleanup_free_ char *unit = NULL;
201 _cleanup_free_ char *path = NULL;
202 _cleanup_strv_free_ char **list = NULL;
207 assert(!endswith(drop, "/"));
209 path = strjoin(toppath, "/", drop, NULL);
213 log_debug("Looking at %s", path);
219 c = strrchr(unit, '.');
224 r = get_files_in_directory(path, &list);
226 log_error("Failed to enumerate %s: %s", path, strerror(-r));
230 STRV_FOREACH(file, list) {
236 if (!endswith(*file, ".conf"))
239 p = strjoin(path, "/", *file, NULL);
242 d = p + strlen(toppath) + 1;
244 log_debug("Adding at top: %s → %s", d, p);
245 k = hashmap_put(top, d, p);
250 d = p + strlen(toppath) + 1;
251 } else if (k != -EEXIST) {
256 log_debug("Adding at bottom: %s → %s", d, p);
257 free(hashmap_remove(bottom, d));
258 k = hashmap_put(bottom, d, p);
264 h = hashmap_get(drops, unit);
266 h = hashmap_new(string_hash_func, string_compare_func);
269 hashmap_put(drops, unit, h);
279 log_debug("Adding to drops: %s → %s → %s", unit, basename(p), p);
280 k = hashmap_put(h, basename(p), p);
290 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
291 _cleanup_closedir_ DIR *d;
298 log_debug("Looking at %s", path);
305 log_error("Failed to open %s: %m", path);
319 dirent_ensure_type(d, de);
321 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
322 enumerate_dir_d(top, bottom, drops, path, de->d_name);
324 if (!dirent_is_file(de))
327 p = strjoin(path, "/", de->d_name, NULL);
331 log_debug("Adding at top: %s → %s", basename(p), p);
332 k = hashmap_put(top, basename(p), p);
337 } else if (k != -EEXIST) {
342 log_debug("Adding at bottom: %s → %s", basename(p), p);
343 free(hashmap_remove(bottom, basename(p)));
344 k = hashmap_put(bottom, basename(p), p);
352 static int process_suffix(const char *suffix, const char *onlyprefix) {
355 Hashmap *top, *bottom, *drops;
364 assert(!startswith(suffix, "/"));
365 assert(!strstr(suffix, "//"));
367 dropins = nulstr_contains(have_dropins, suffix);
369 top = hashmap_new(string_hash_func, string_compare_func);
370 bottom = hashmap_new(string_hash_func, string_compare_func);
371 drops = hashmap_new(string_hash_func, string_compare_func);
372 if (!top || !bottom || !drops) {
377 NULSTR_FOREACH(p, prefixes) {
378 _cleanup_free_ char *t = NULL;
380 t = strjoin(p, "/", suffix, NULL);
386 k = enumerate_dir(top, bottom, drops, t, dropins);
391 HASHMAP_FOREACH_KEY(f, key, top, i) {
394 o = hashmap_get(bottom, key);
397 if (!onlyprefix || startswith(o, onlyprefix)) {
398 if (path_equal(o, f)) {
399 notify_override_unchanged(f);
401 k = found_override(f, o);
409 h = hashmap_get(drops, key);
411 HASHMAP_FOREACH(o, h, j)
412 if (!onlyprefix || startswith(o, onlyprefix))
413 n_found += notify_override_extended(f, o);
418 hashmap_free_free(top);
420 hashmap_free_free(bottom);
422 HASHMAP_FOREACH_KEY(h, key, drops, i){
423 hashmap_free_free(hashmap_remove(drops, key));
424 hashmap_remove(drops, key);
429 return r < 0 ? r : n_found;
432 static int process_suffixes(const char *onlyprefix) {
436 NULSTR_FOREACH(n, suffixes) {
437 r = process_suffix(n, onlyprefix);
446 static int process_suffix_chop(const char *arg) {
451 if (!path_is_absolute(arg))
452 return process_suffix(arg, NULL);
454 /* Strip prefix from the suffix */
455 NULSTR_FOREACH(p, prefixes) {
456 const char *suffix = startswith(arg, p);
458 suffix += strspn(suffix, "/");
460 return process_suffix(suffix, NULL);
462 return process_suffixes(arg);
466 log_error("Invalid suffix specification %s.", arg);
470 static int help(void) {
472 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
473 "Find overridden configuration files.\n\n"
474 " -h --help Show this help\n"
475 " --version Show package version\n"
476 " --no-pager Do not pipe output into a pager\n"
477 " --diff[=1|0] Show a diff when overridden files differ\n"
478 " -t --type=LIST... Only display a selected set of override types\n",
479 program_invocation_short_name);
484 static int parse_flags(const char *flag_str, int flags) {
488 FOREACH_WORD(w, l, flag_str, state) {
489 if (strneq("masked", w, l))
490 flags |= SHOW_MASKED;
491 else if (strneq ("equivalent", w, l))
492 flags |= SHOW_EQUIVALENT;
493 else if (strneq("redirected", w, l))
494 flags |= SHOW_REDIRECTED;
495 else if (strneq("overridden", w, l))
496 flags |= SHOW_OVERRIDDEN;
497 else if (strneq("unchanged", w, l))
498 flags |= SHOW_UNCHANGED;
499 else if (strneq("extended", w, l))
500 flags |= SHOW_EXTENDED;
501 else if (strneq("default", w, l))
502 flags |= SHOW_DEFAULTS;
509 static int parse_argv(int argc, char *argv[]) {
512 ARG_NO_PAGER = 0x100,
517 static const struct option options[] = {
518 { "help", no_argument, NULL, 'h' },
519 { "version", no_argument, NULL, ARG_VERSION },
520 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
521 { "diff", optional_argument, NULL, ARG_DIFF },
522 { "type", required_argument, NULL, 't' },
531 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) {
540 puts(PACKAGE_STRING);
541 puts(SYSTEMD_FEATURES);
550 f = parse_flags(optarg, arg_flags);
552 log_error("Failed to parse flags field.");
565 b = parse_boolean(optarg);
567 log_error("Failed to parse diff boolean.");
580 assert_not_reached("Unhandled option");
587 int main(int argc, char *argv[]) {
591 log_parse_environment();
594 r = parse_argv(argc, argv);
599 arg_flags = SHOW_DEFAULTS;
602 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
604 arg_flags |= SHOW_OVERRIDDEN;
606 pager_open_if_enabled();
611 for (i = optind; i < argc; i++) {
612 path_kill_slashes(argv[i]);
613 k = process_suffix_chop(argv[i]);
621 k = process_suffixes(NULL);
629 printf("%s%i overridden configuration files found.\n",
630 n_found ? "\n" : "", n_found);
635 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;