1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 #include "path-util.h"
36 static bool arg_no_pager = false;
37 static int arg_diff = -1;
41 SHOW_EQUIVALENT = 1 << 1,
42 SHOW_REDIRECTED = 1 << 2,
43 SHOW_OVERRIDDEN = 1 << 3,
44 SHOW_UNCHANGED = 1 << 4,
45 SHOW_EXTENDED = 1 << 5,
48 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED)
51 static int equivalent(const char *a, const char *b) {
52 _cleanup_free_ char *x = NULL, *y = NULL;
54 x = canonicalize_file_name(a);
58 y = canonicalize_file_name(b);
62 return path_equal(x, y);
65 static int notify_override_masked(const char *top, const char *bottom) {
66 if (!(arg_flags & SHOW_MASKED))
69 printf(ANSI_HIGHLIGHT_RED_ON "[MASKED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
73 static int notify_override_equivalent(const char *top, const char *bottom) {
74 if (!(arg_flags & SHOW_EQUIVALENT))
77 printf(ANSI_HIGHLIGHT_GREEN_ON "[EQUIVALENT]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
81 static int notify_override_redirected(const char *top, const char *bottom) {
82 if (!(arg_flags & SHOW_REDIRECTED))
85 printf(ANSI_HIGHLIGHT_ON "[REDIRECTED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
89 static int notify_override_overridden(const char *top, const char *bottom) {
90 if (!(arg_flags & SHOW_OVERRIDDEN))
93 printf(ANSI_HIGHLIGHT_ON "[OVERRIDDEN]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
97 static int notify_override_extended(const char *top, const char *bottom) {
98 if (!(arg_flags & SHOW_EXTENDED))
101 printf(ANSI_HIGHLIGHT_ON "[EXTENDED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
105 static int notify_override_unchanged(const char *f) {
106 if (!(arg_flags & SHOW_UNCHANGED))
109 printf("[UNCHANGED] %s\n", f);
113 static int found_override(const char *top, const char *bottom) {
114 _cleanup_free_ char *dest = NULL;
121 if (null_or_empty_path(top) > 0)
122 return notify_override_masked(top, bottom);
124 k = readlink_malloc(top, &dest);
126 if (equivalent(dest, bottom) > 0)
127 return notify_override_equivalent(top, bottom);
129 return notify_override_redirected(top, bottom);
132 k = notify_override_overridden(top, bottom);
142 log_error("Failed to fork off diff: %m");
144 } else if (pid == 0) {
145 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
146 log_error("Failed to execute diff: %m");
150 wait_for_terminate(pid, NULL);
157 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
158 _cleanup_free_ char *conf = NULL;
159 _cleanup_free_ char *path = NULL;
160 _cleanup_strv_free_ char **list = NULL;
165 path = strjoin(toppath, "/", drop, NULL);
169 path_kill_slashes(path);
175 c = strrchr(conf, '.');
180 r = get_files_in_directory(path, &list);
182 log_error("Failed to enumerate %s: %s", path, strerror(-r));
186 STRV_FOREACH(file, list) {
192 if (!endswith(*file, ".conf"))
195 p = strjoin(path, "/", *file, NULL);
199 path_kill_slashes(p);
214 k = hashmap_put(top, d, p);
222 } else if (k != -EEXIST) {
227 free(hashmap_remove(bottom, d));
228 k = hashmap_put(bottom, d, p);
234 h = hashmap_get(drops, conf);
236 h = hashmap_new(string_hash_func, string_compare_func);
239 hashmap_put(drops, conf, h);
249 k = hashmap_put(h, path_get_file_name(p), p);
259 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
260 _cleanup_closedir_ DIR *d;
272 log_error("Failed to enumerate %s: %m", path);
278 union dirent_storage buf;
282 k = readdir_r(d, &buf.de, &de);
289 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
290 enumerate_dir_d(top, bottom, drops, path, de->d_name);
292 if (!dirent_is_file(de))
295 p = strjoin(path, "/", de->d_name, NULL);
299 path_kill_slashes(p);
301 k = hashmap_put(top, path_get_file_name(p), p);
306 } else if (k != -EEXIST) {
311 free(hashmap_remove(bottom, path_get_file_name(p)));
312 k = hashmap_put(bottom, path_get_file_name(p), p);
322 static int process_suffix(const char *prefixes, const char *suffix, bool dropins) {
325 Hashmap *top, *bottom=NULL, *drops=NULL;
335 top = hashmap_new(string_hash_func, string_compare_func);
341 bottom = hashmap_new(string_hash_func, string_compare_func);
347 drops = hashmap_new(string_hash_func, string_compare_func);
353 NULSTR_FOREACH(p, prefixes) {
354 _cleanup_free_ char *t = NULL;
356 t = strjoin(p, "/", suffix, NULL);
362 k = enumerate_dir(top, bottom, drops, t, dropins);
366 log_debug("Looking at %s", t);
369 HASHMAP_FOREACH_KEY(f, key, top, i) {
372 o = hashmap_get(bottom, key);
375 if (path_equal(o, f))
376 notify_override_unchanged(f);
378 k = found_override(f, o);
385 h = hashmap_get(drops, key);
387 HASHMAP_FOREACH(o, h, j)
388 n_found += notify_override_extended(f, o);
393 hashmap_free_free(top);
395 hashmap_free_free(bottom);
397 HASHMAP_FOREACH_KEY(h, key, drops, i){
398 hashmap_free_free(hashmap_remove(drops, key));
399 hashmap_remove(drops, key);
404 return r < 0 ? r : n_found;
407 static int process_suffix_chop(const char *prefixes, const char *suffix, const char *have_dropins) {
413 if (!path_is_absolute(suffix))
414 return process_suffix(prefixes, suffix, nulstr_contains(have_dropins, suffix));
416 /* Strip prefix from the suffix */
417 NULSTR_FOREACH(p, prefixes) {
418 if (startswith(suffix, p)) {
420 suffix += strspn(suffix, "/");
421 return process_suffix(prefixes, suffix, nulstr_contains(have_dropins, suffix));
425 log_error("Invalid suffix specification %s.", suffix);
429 static void help(void) {
431 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
432 "Find overridden configuration files.\n\n"
433 " -h --help Show this help\n"
434 " --version Show package version\n"
435 " --no-pager Do not pipe output into a pager\n"
436 " --diff[=1|0] Show a diff when overridden files differ\n"
437 " -t --type=LIST... Only display a selected set of override types\n",
438 program_invocation_short_name);
441 static int parse_flags(const char *flag_str, int flags) {
445 FOREACH_WORD(w, l, flag_str, state) {
446 if (strneq("masked", w, l))
447 flags |= SHOW_MASKED;
448 else if (strneq ("equivalent", w, l))
449 flags |= SHOW_EQUIVALENT;
450 else if (strneq("redirected", w, l))
451 flags |= SHOW_REDIRECTED;
452 else if (strneq("overridden", w, l))
453 flags |= SHOW_OVERRIDDEN;
454 else if (strneq("unchanged", w, l))
455 flags |= SHOW_UNCHANGED;
456 else if (strneq("extended", w, l))
457 flags |= SHOW_EXTENDED;
458 else if (strneq("default", w, l))
459 flags |= SHOW_DEFAULTS;
466 static int parse_argv(int argc, char *argv[]) {
469 ARG_NO_PAGER = 0x100,
474 static const struct option options[] = {
475 { "help", no_argument, NULL, 'h' },
476 { "version", no_argument, NULL, ARG_VERSION },
477 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
478 { "diff", optional_argument, NULL, ARG_DIFF },
479 { "type", required_argument, NULL, 't' },
488 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) {
497 puts(PACKAGE_STRING);
498 puts(SYSTEMD_FEATURES);
510 f = parse_flags(optarg, arg_flags);
512 log_error("Failed to parse flags field.");
525 b = parse_boolean(optarg);
527 log_error("Failed to parse diff boolean.");
537 log_error("Unknown option code %c", c);
545 int main(int argc, char *argv[]) {
547 const char prefixes[] =
554 #ifdef HAVE_SPLIT_USR
559 const char suffixes[] =
566 "systemd/system-preset\0"
567 "systemd/user-preset\0"
571 const char have_dropins[] =
578 log_parse_environment();
581 r = parse_argv(argc, argv);
586 arg_flags = SHOW_DEFAULTS;
589 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
591 arg_flags |= SHOW_OVERRIDDEN;
599 for (i = optind; i < argc; i++) {
600 k = process_suffix_chop(prefixes, argv[i], have_dropins);
610 NULSTR_FOREACH(n, suffixes) {
611 k = process_suffix(prefixes, n, nulstr_contains(have_dropins, n));
620 printf("%s%i overridden configuration files found.\n",
621 n_found ? "\n" : "", n_found);
626 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;