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_errno(errno, "Failed to fork off diff: %m");
191 } else if (pid == 0) {
192 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
193 log_error_errno(errno, "Failed to execute diff: %m");
197 wait_for_terminate_and_warn("diff", pid);
203 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
204 _cleanup_free_ char *unit = NULL;
205 _cleanup_free_ char *path = NULL;
206 _cleanup_strv_free_ char **list = NULL;
211 assert(!endswith(drop, "/"));
213 path = strjoin(toppath, "/", drop, NULL);
217 log_debug("Looking at %s", path);
223 c = strrchr(unit, '.');
228 r = get_files_in_directory(path, &list);
230 return log_error_errno(r, "Failed to enumerate %s: %m", path);
232 STRV_FOREACH(file, list) {
238 if (!endswith(*file, ".conf"))
241 p = strjoin(path, "/", *file, NULL);
244 d = p + strlen(toppath) + 1;
246 log_debug("Adding at top: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
247 k = hashmap_put(top, d, p);
252 d = p + strlen(toppath) + 1;
253 } else if (k != -EEXIST) {
258 log_debug("Adding at bottom: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
259 free(hashmap_remove(bottom, d));
260 k = hashmap_put(bottom, d, p);
266 h = hashmap_get(drops, unit);
268 h = hashmap_new(&string_hash_ops);
271 hashmap_put(drops, unit, h);
281 log_debug("Adding to drops: %s %s %s %s %s",
282 unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p);
283 k = hashmap_put(h, basename(p), p);
293 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
294 _cleanup_closedir_ DIR *d;
301 log_debug("Looking at %s", path);
308 log_error_errno(errno, "Failed to open %s: %m", path);
322 dirent_ensure_type(d, de);
324 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
325 enumerate_dir_d(top, bottom, drops, path, de->d_name);
327 if (!dirent_is_file(de))
330 p = strjoin(path, "/", de->d_name, NULL);
334 log_debug("Adding at top: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
335 k = hashmap_put(top, basename(p), p);
340 } else if (k != -EEXIST) {
345 log_debug("Adding at bottom: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
346 free(hashmap_remove(bottom, basename(p)));
347 k = hashmap_put(bottom, basename(p), p);
355 static int process_suffix(const char *suffix, const char *onlyprefix) {
358 Hashmap *top, *bottom, *drops;
367 assert(!startswith(suffix, "/"));
368 assert(!strstr(suffix, "//"));
370 dropins = nulstr_contains(have_dropins, suffix);
372 top = hashmap_new(&string_hash_ops);
373 bottom = hashmap_new(&string_hash_ops);
374 drops = hashmap_new(&string_hash_ops);
375 if (!top || !bottom || !drops) {
380 NULSTR_FOREACH(p, prefixes) {
381 _cleanup_free_ char *t = NULL;
383 t = strjoin(p, "/", suffix, NULL);
389 k = enumerate_dir(top, bottom, drops, t, dropins);
394 HASHMAP_FOREACH_KEY(f, key, top, i) {
397 o = hashmap_get(bottom, key);
400 if (!onlyprefix || startswith(o, onlyprefix)) {
401 if (path_equal(o, f)) {
402 notify_override_unchanged(f);
404 k = found_override(f, o);
412 h = hashmap_get(drops, key);
414 HASHMAP_FOREACH(o, h, j)
415 if (!onlyprefix || startswith(o, onlyprefix))
416 n_found += notify_override_extended(f, o);
421 hashmap_free_free(top);
423 hashmap_free_free(bottom);
425 HASHMAP_FOREACH_KEY(h, key, drops, i){
426 hashmap_free_free(hashmap_remove(drops, key));
427 hashmap_remove(drops, key);
432 return r < 0 ? r : n_found;
435 static int process_suffixes(const char *onlyprefix) {
439 NULSTR_FOREACH(n, suffixes) {
440 r = process_suffix(n, onlyprefix);
449 static int process_suffix_chop(const char *arg) {
454 if (!path_is_absolute(arg))
455 return process_suffix(arg, NULL);
457 /* Strip prefix from the suffix */
458 NULSTR_FOREACH(p, prefixes) {
459 const char *suffix = startswith(arg, p);
461 suffix += strspn(suffix, "/");
463 return process_suffix(suffix, NULL);
465 return process_suffixes(arg);
469 log_error("Invalid suffix specification %s.", arg);
473 static void help(void) {
474 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
475 "Find overridden configuration files.\n\n"
476 " -h --help Show this help\n"
477 " --version Show package version\n"
478 " --no-pager Do not pipe output into a pager\n"
479 " --diff[=1|0] Show a diff when overridden files differ\n"
480 " -t --type=LIST... Only display a selected set of override types\n"
481 , program_invocation_short_name);
484 static int parse_flags(const char *flag_str, int flags) {
485 const char *word, *state;
488 FOREACH_WORD(word, l, flag_str, state) {
489 if (strneq("masked", word, l))
490 flags |= SHOW_MASKED;
491 else if (strneq ("equivalent", word, l))
492 flags |= SHOW_EQUIVALENT;
493 else if (strneq("redirected", word, l))
494 flags |= SHOW_REDIRECTED;
495 else if (strneq("overridden", word, l))
496 flags |= SHOW_OVERRIDDEN;
497 else if (strneq("unchanged", word, l))
498 flags |= SHOW_UNCHANGED;
499 else if (strneq("extended", word, l))
500 flags |= SHOW_EXTENDED;
501 else if (strneq("default", word, 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");
586 int main(int argc, char *argv[]) {
590 log_parse_environment();
593 r = parse_argv(argc, argv);
598 arg_flags = SHOW_DEFAULTS;
601 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
603 arg_flags |= SHOW_OVERRIDDEN;
605 pager_open_if_enabled();
610 for (i = optind; i < argc; i++) {
611 path_kill_slashes(argv[i]);
612 k = process_suffix_chop(argv[i]);
620 k = process_suffixes(NULL);
628 printf("%s%i overridden configuration files found.\n",
629 n_found ? "\n" : "", n_found);
634 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;