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 %s\n",
107 ansi_highlight_red(), "[MASKED]", ansi_highlight_off(),
108 top, draw_special_char(DRAW_ARROW), bottom);
112 static int notify_override_equivalent(const char *top, const char *bottom) {
113 if (!(arg_flags & SHOW_EQUIVALENT))
116 printf("%s%s%s %s %s %s\n",
117 ansi_highlight_green(), "[EQUIVALENT]", ansi_highlight_off(),
118 top, draw_special_char(DRAW_ARROW), bottom);
122 static int notify_override_redirected(const char *top, const char *bottom) {
123 if (!(arg_flags & SHOW_REDIRECTED))
126 printf("%s%s%s %s %s %s\n",
127 ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(),
128 top, draw_special_char(DRAW_ARROW), bottom);
132 static int notify_override_overridden(const char *top, const char *bottom) {
133 if (!(arg_flags & SHOW_OVERRIDDEN))
136 printf("%s%s%s %s %s %s\n",
137 ansi_highlight(), "[OVERRIDDEN]", ansi_highlight_off(),
138 top, draw_special_char(DRAW_ARROW), bottom);
142 static int notify_override_extended(const char *top, const char *bottom) {
143 if (!(arg_flags & SHOW_EXTENDED))
146 printf("%s%s%s %s %s %s\n",
147 ansi_highlight(), "[EXTENDED]", ansi_highlight_off(),
148 top, draw_special_char(DRAW_ARROW), bottom);
152 static int notify_override_unchanged(const char *f) {
153 if (!(arg_flags & SHOW_UNCHANGED))
156 printf("[UNCHANGED] %s\n", f);
160 static int found_override(const char *top, const char *bottom) {
161 _cleanup_free_ char *dest = NULL;
168 if (null_or_empty_path(top) > 0)
169 return notify_override_masked(top, bottom);
171 k = readlink_malloc(top, &dest);
173 if (equivalent(dest, bottom) > 0)
174 return notify_override_equivalent(top, bottom);
176 return notify_override_redirected(top, bottom);
179 k = notify_override_overridden(top, bottom);
189 log_error("Failed to fork off diff: %m");
191 } else if (pid == 0) {
192 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
193 log_error("Failed to execute diff: %m");
197 r = wait_for_terminate(pid, NULL);
199 log_warning("Failed to wait for diff: %s", strerror(-r));
206 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
207 _cleanup_free_ char *unit = NULL;
208 _cleanup_free_ char *path = NULL;
209 _cleanup_strv_free_ char **list = NULL;
214 assert(!endswith(drop, "/"));
216 path = strjoin(toppath, "/", drop, NULL);
220 log_debug("Looking at %s", path);
226 c = strrchr(unit, '.');
231 r = get_files_in_directory(path, &list);
233 log_error("Failed to enumerate %s: %s", path, strerror(-r));
237 STRV_FOREACH(file, list) {
243 if (!endswith(*file, ".conf"))
246 p = strjoin(path, "/", *file, NULL);
249 d = p + strlen(toppath) + 1;
251 log_debug("Adding at top: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
252 k = hashmap_put(top, d, p);
257 d = p + strlen(toppath) + 1;
258 } else if (k != -EEXIST) {
263 log_debug("Adding at bottom: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
264 free(hashmap_remove(bottom, d));
265 k = hashmap_put(bottom, d, p);
271 h = hashmap_get(drops, unit);
273 h = hashmap_new(&string_hash_ops);
276 hashmap_put(drops, unit, h);
286 log_debug("Adding to drops: %s %s %s %s %s",
287 unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p);
288 k = hashmap_put(h, basename(p), p);
298 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
299 _cleanup_closedir_ DIR *d;
306 log_debug("Looking at %s", path);
313 log_error("Failed to open %s: %m", path);
327 dirent_ensure_type(d, de);
329 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
330 enumerate_dir_d(top, bottom, drops, path, de->d_name);
332 if (!dirent_is_file(de))
335 p = strjoin(path, "/", de->d_name, NULL);
339 log_debug("Adding at top: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
340 k = hashmap_put(top, basename(p), p);
345 } else if (k != -EEXIST) {
350 log_debug("Adding at bottom: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
351 free(hashmap_remove(bottom, basename(p)));
352 k = hashmap_put(bottom, basename(p), p);
360 static int process_suffix(const char *suffix, const char *onlyprefix) {
363 Hashmap *top, *bottom, *drops;
372 assert(!startswith(suffix, "/"));
373 assert(!strstr(suffix, "//"));
375 dropins = nulstr_contains(have_dropins, suffix);
377 top = hashmap_new(&string_hash_ops);
378 bottom = hashmap_new(&string_hash_ops);
379 drops = hashmap_new(&string_hash_ops);
380 if (!top || !bottom || !drops) {
385 NULSTR_FOREACH(p, prefixes) {
386 _cleanup_free_ char *t = NULL;
388 t = strjoin(p, "/", suffix, NULL);
394 k = enumerate_dir(top, bottom, drops, t, dropins);
399 HASHMAP_FOREACH_KEY(f, key, top, i) {
402 o = hashmap_get(bottom, key);
405 if (!onlyprefix || startswith(o, onlyprefix)) {
406 if (path_equal(o, f)) {
407 notify_override_unchanged(f);
409 k = found_override(f, o);
417 h = hashmap_get(drops, key);
419 HASHMAP_FOREACH(o, h, j)
420 if (!onlyprefix || startswith(o, onlyprefix))
421 n_found += notify_override_extended(f, o);
426 hashmap_free_free(top);
428 hashmap_free_free(bottom);
430 HASHMAP_FOREACH_KEY(h, key, drops, i){
431 hashmap_free_free(hashmap_remove(drops, key));
432 hashmap_remove(drops, key);
437 return r < 0 ? r : n_found;
440 static int process_suffixes(const char *onlyprefix) {
444 NULSTR_FOREACH(n, suffixes) {
445 r = process_suffix(n, onlyprefix);
454 static int process_suffix_chop(const char *arg) {
459 if (!path_is_absolute(arg))
460 return process_suffix(arg, NULL);
462 /* Strip prefix from the suffix */
463 NULSTR_FOREACH(p, prefixes) {
464 const char *suffix = startswith(arg, p);
466 suffix += strspn(suffix, "/");
468 return process_suffix(suffix, NULL);
470 return process_suffixes(arg);
474 log_error("Invalid suffix specification %s.", arg);
478 static void help(void) {
479 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
480 "Find overridden configuration files.\n\n"
481 " -h --help Show this help\n"
482 " --version Show package version\n"
483 " --no-pager Do not pipe output into a pager\n"
484 " --diff[=1|0] Show a diff when overridden files differ\n"
485 " -t --type=LIST... Only display a selected set of override types\n"
486 , program_invocation_short_name);
489 static int parse_flags(const char *flag_str, int flags) {
490 const char *word, *state;
493 FOREACH_WORD(word, l, flag_str, state) {
494 if (strneq("masked", word, l))
495 flags |= SHOW_MASKED;
496 else if (strneq ("equivalent", word, l))
497 flags |= SHOW_EQUIVALENT;
498 else if (strneq("redirected", word, l))
499 flags |= SHOW_REDIRECTED;
500 else if (strneq("overridden", word, l))
501 flags |= SHOW_OVERRIDDEN;
502 else if (strneq("unchanged", word, l))
503 flags |= SHOW_UNCHANGED;
504 else if (strneq("extended", word, l))
505 flags |= SHOW_EXTENDED;
506 else if (strneq("default", word, l))
507 flags |= SHOW_DEFAULTS;
514 static int parse_argv(int argc, char *argv[]) {
517 ARG_NO_PAGER = 0x100,
522 static const struct option options[] = {
523 { "help", no_argument, NULL, 'h' },
524 { "version", no_argument, NULL, ARG_VERSION },
525 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
526 { "diff", optional_argument, NULL, ARG_DIFF },
527 { "type", required_argument, NULL, 't' },
536 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0)
545 puts(PACKAGE_STRING);
546 puts(SYSTEMD_FEATURES);
555 f = parse_flags(optarg, arg_flags);
557 log_error("Failed to parse flags field.");
570 b = parse_boolean(optarg);
572 log_error("Failed to parse diff boolean.");
585 assert_not_reached("Unhandled option");
591 int main(int argc, char *argv[]) {
595 log_parse_environment();
598 r = parse_argv(argc, argv);
603 arg_flags = SHOW_DEFAULTS;
606 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
608 arg_flags |= SHOW_OVERRIDDEN;
610 pager_open_if_enabled();
615 for (i = optind; i < argc; i++) {
616 path_kill_slashes(argv[i]);
617 k = process_suffix_chop(argv[i]);
625 k = process_suffixes(NULL);
633 printf("%s%i overridden configuration files found.\n",
634 n_found ? "\n" : "", n_found);
639 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;