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/>.
30 #include "path-util.h"
36 static const char prefixes[] =
48 static const char suffixes[] =
55 "systemd/system-preset\0"
56 "systemd/user-preset\0"
60 static const char have_dropins[] =
64 static bool arg_no_pager = false;
65 static int arg_diff = -1;
69 SHOW_EQUIVALENT = 1 << 1,
70 SHOW_REDIRECTED = 1 << 2,
71 SHOW_OVERRIDDEN = 1 << 3,
72 SHOW_UNCHANGED = 1 << 4,
73 SHOW_EXTENDED = 1 << 5,
76 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED)
79 static void pager_open_if_enabled(void) {
87 static int equivalent(const char *a, const char *b) {
88 _cleanup_free_ char *x = NULL, *y = NULL;
90 x = canonicalize_file_name(a);
94 y = canonicalize_file_name(b);
98 return path_equal(x, y);
101 static int notify_override_masked(const char *top, const char *bottom) {
102 if (!(arg_flags & SHOW_MASKED))
105 printf("%s%s%s %s %s %s\n",
106 ansi_highlight_red(), "[MASKED]", ansi_highlight_off(),
107 top, draw_special_char(DRAW_ARROW), 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 %s\n",
116 ansi_highlight_green(), "[EQUIVALENT]", ansi_highlight_off(),
117 top, draw_special_char(DRAW_ARROW), bottom);
121 static int notify_override_redirected(const char *top, const char *bottom) {
122 if (!(arg_flags & SHOW_REDIRECTED))
125 printf("%s%s%s %s %s %s\n",
126 ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(),
127 top, draw_special_char(DRAW_ARROW), bottom);
131 static int notify_override_overridden(const char *top, const char *bottom) {
132 if (!(arg_flags & SHOW_OVERRIDDEN))
135 printf("%s%s%s %s %s %s\n",
136 ansi_highlight(), "[OVERRIDDEN]", ansi_highlight_off(),
137 top, draw_special_char(DRAW_ARROW), bottom);
141 static int notify_override_extended(const char *top, const char *bottom) {
142 if (!(arg_flags & SHOW_EXTENDED))
145 printf("%s%s%s %s %s %s\n",
146 ansi_highlight(), "[EXTENDED]", ansi_highlight_off(),
147 top, draw_special_char(DRAW_ARROW), bottom);
151 static int notify_override_unchanged(const char *f) {
152 if (!(arg_flags & SHOW_UNCHANGED))
155 printf("[UNCHANGED] %s\n", f);
159 static int found_override(const char *top, const char *bottom) {
160 _cleanup_free_ char *dest = NULL;
167 if (null_or_empty_path(top) > 0)
168 return notify_override_masked(top, bottom);
170 k = readlink_malloc(top, &dest);
172 if (equivalent(dest, bottom) > 0)
173 return notify_override_equivalent(top, bottom);
175 return notify_override_redirected(top, bottom);
178 k = notify_override_overridden(top, bottom);
188 return log_error_errno(errno, "Failed to fork off diff: %m");
190 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
191 log_error_errno(errno, "Failed to execute diff: %m");
195 wait_for_terminate_and_warn("diff", pid, false);
201 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
202 _cleanup_free_ char *unit = NULL;
203 _cleanup_free_ char *path = NULL;
204 _cleanup_strv_free_ char **list = NULL;
209 assert(!endswith(drop, "/"));
211 path = strjoin(toppath, "/", drop, NULL);
215 log_debug("Looking at %s", path);
221 c = strrchr(unit, '.');
226 r = get_files_in_directory(path, &list);
228 return log_error_errno(r, "Failed to enumerate %s: %m", path);
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 %s", d, draw_special_char(DRAW_ARROW), 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 %s", d, draw_special_char(DRAW_ARROW), 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_ops);
269 hashmap_put(drops, unit, h);
279 log_debug("Adding to drops: %s %s %s %s %s",
280 unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p);
281 k = hashmap_put(h, basename(p), p);
291 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
292 _cleanup_closedir_ DIR *d;
299 log_debug("Looking at %s", path);
306 log_error_errno(errno, "Failed to open %s: %m", path);
320 dirent_ensure_type(d, de);
322 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
323 enumerate_dir_d(top, bottom, drops, path, de->d_name);
325 if (!dirent_is_file(de))
328 p = strjoin(path, "/", de->d_name, NULL);
332 log_debug("Adding at top: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
333 k = hashmap_put(top, basename(p), p);
338 } else if (k != -EEXIST) {
343 log_debug("Adding at bottom: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
344 free(hashmap_remove(bottom, basename(p)));
345 k = hashmap_put(bottom, basename(p), p);
353 static int process_suffix(const char *suffix, const char *onlyprefix) {
356 Hashmap *top, *bottom, *drops;
365 assert(!startswith(suffix, "/"));
366 assert(!strstr(suffix, "//"));
368 dropins = nulstr_contains(have_dropins, suffix);
370 top = hashmap_new(&string_hash_ops);
371 bottom = hashmap_new(&string_hash_ops);
372 drops = hashmap_new(&string_hash_ops);
373 if (!top || !bottom || !drops) {
378 NULSTR_FOREACH(p, prefixes) {
379 _cleanup_free_ char *t = NULL;
381 t = strjoin(p, "/", suffix, NULL);
387 k = enumerate_dir(top, bottom, drops, t, dropins);
392 HASHMAP_FOREACH_KEY(f, key, top, i) {
395 o = hashmap_get(bottom, key);
398 if (!onlyprefix || startswith(o, onlyprefix)) {
399 if (path_equal(o, f)) {
400 notify_override_unchanged(f);
402 k = found_override(f, o);
410 h = hashmap_get(drops, key);
412 HASHMAP_FOREACH(o, h, j)
413 if (!onlyprefix || startswith(o, onlyprefix))
414 n_found += notify_override_extended(f, o);
419 hashmap_free_free(top);
421 hashmap_free_free(bottom);
423 HASHMAP_FOREACH_KEY(h, key, drops, i){
424 hashmap_free_free(hashmap_remove(drops, key));
425 hashmap_remove(drops, key);
430 return r < 0 ? r : n_found;
433 static int process_suffixes(const char *onlyprefix) {
437 NULSTR_FOREACH(n, suffixes) {
438 r = process_suffix(n, onlyprefix);
447 static int process_suffix_chop(const char *arg) {
452 if (!path_is_absolute(arg))
453 return process_suffix(arg, NULL);
455 /* Strip prefix from the suffix */
456 NULSTR_FOREACH(p, prefixes) {
457 const char *suffix = startswith(arg, p);
459 suffix += strspn(suffix, "/");
461 return process_suffix(suffix, NULL);
463 return process_suffixes(arg);
467 log_error("Invalid suffix specification %s.", arg);
471 static void 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);
482 static int parse_flags(const char *flag_str, int flags) {
483 const char *word, *state;
486 FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) {
487 if (strneq("masked", word, l))
488 flags |= SHOW_MASKED;
489 else if (strneq ("equivalent", word, l))
490 flags |= SHOW_EQUIVALENT;
491 else if (strneq("redirected", word, l))
492 flags |= SHOW_REDIRECTED;
493 else if (strneq("overridden", word, l))
494 flags |= SHOW_OVERRIDDEN;
495 else if (strneq("unchanged", word, l))
496 flags |= SHOW_UNCHANGED;
497 else if (strneq("extended", word, l))
498 flags |= SHOW_EXTENDED;
499 else if (strneq("default", word, l))
500 flags |= SHOW_DEFAULTS;
507 static int parse_argv(int argc, char *argv[]) {
510 ARG_NO_PAGER = 0x100,
515 static const struct option options[] = {
516 { "help", no_argument, NULL, 'h' },
517 { "version", no_argument, NULL, ARG_VERSION },
518 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
519 { "diff", optional_argument, NULL, ARG_DIFF },
520 { "type", required_argument, NULL, 't' },
529 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0)
538 puts(PACKAGE_STRING);
539 puts(SYSTEMD_FEATURES);
548 f = parse_flags(optarg, arg_flags);
550 log_error("Failed to parse flags field.");
563 b = parse_boolean(optarg);
565 log_error("Failed to parse diff boolean.");
578 assert_not_reached("Unhandled option");
584 int main(int argc, char *argv[]) {
588 log_parse_environment();
591 r = parse_argv(argc, argv);
596 arg_flags = SHOW_DEFAULTS;
599 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
601 arg_flags |= SHOW_OVERRIDDEN;
603 pager_open_if_enabled();
608 for (i = optind; i < argc; i++) {
609 path_kill_slashes(argv[i]);
610 k = process_suffix_chop(argv[i]);
618 k = process_suffixes(NULL);
626 printf("%s%i overridden configuration files found.\n",
627 n_found ? "\n" : "", n_found);
632 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;