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 notify_override_masked(top, bottom);
126 k = readlink_malloc(top, &dest);
128 if (equivalent(dest, bottom) > 0)
129 notify_override_equivalent(top, bottom);
131 notify_override_redirected(top, bottom);
136 notify_override_overridden(top, bottom);
146 log_error("Failed to fork off diff: %m");
148 } else if (pid == 0) {
149 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
150 log_error("Failed to execute diff: %m");
154 wait_for_terminate(pid, NULL);
161 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
162 _cleanup_free_ char *conf = NULL;
163 _cleanup_free_ char *path = NULL;
164 _cleanup_strv_free_ char **list = NULL;
169 path = strjoin(toppath, "/", drop, NULL);
173 path_kill_slashes(path);
179 c = strrchr(conf, '.');
184 r = get_files_in_directory(path, &list);
186 log_error("Failed to enumerate %s: %s", path, strerror(-r));
190 STRV_FOREACH(file, list) {
196 if (!endswith(*file, ".conf"))
199 p = strjoin(path, "/", *file, NULL);
203 path_kill_slashes(p);
218 k = hashmap_put(top, d, p);
226 } else if (k != -EEXIST) {
231 free(hashmap_remove(bottom, d));
232 k = hashmap_put(bottom, d, p);
238 h = hashmap_get(drops, conf);
240 h = hashmap_new(string_hash_func, string_compare_func);
243 hashmap_put(drops, conf, h);
253 k = hashmap_put(h, path_get_file_name(p), p);
263 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
264 _cleanup_closedir_ DIR *d;
276 log_error("Failed to enumerate %s: %m", path);
282 union dirent_storage buf;
286 k = readdir_r(d, &buf.de, &de);
293 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
294 enumerate_dir_d(top, bottom, drops, path, de->d_name);
296 if (!dirent_is_file(de))
299 p = strjoin(path, "/", de->d_name, NULL);
303 path_kill_slashes(p);
305 k = hashmap_put(top, path_get_file_name(p), p);
310 } else if (k != -EEXIST) {
315 free(hashmap_remove(bottom, path_get_file_name(p)));
316 k = hashmap_put(bottom, path_get_file_name(p), p);
326 static int process_suffix(const char *prefixes, const char *suffix, bool dropins) {
329 Hashmap *top, *bottom=NULL, *drops=NULL;
339 top = hashmap_new(string_hash_func, string_compare_func);
345 bottom = hashmap_new(string_hash_func, string_compare_func);
351 drops = hashmap_new(string_hash_func, string_compare_func);
357 NULSTR_FOREACH(p, prefixes) {
358 _cleanup_free_ char *t = NULL;
360 t = strjoin(p, "/", suffix, NULL);
366 k = enumerate_dir(top, bottom, drops, t, dropins);
370 log_debug("Looking at %s", t);
373 HASHMAP_FOREACH_KEY(f, key, top, i) {
376 o = hashmap_get(bottom, key);
379 if (path_equal(o, f))
380 notify_override_unchanged(f);
382 k = found_override(f, o);
388 h = hashmap_get(drops, key);
390 HASHMAP_FOREACH(o, h, j) {
391 notify_override_extended(f, o);
398 hashmap_free_free(top);
400 hashmap_free_free(bottom);
402 HASHMAP_FOREACH_KEY(h, key, drops, i){
403 hashmap_free_free(hashmap_remove(drops, key));
404 hashmap_remove(drops, key);
409 return r < 0 ? r : n_found;
412 static int process_suffix_chop(const char *prefixes, const char *suffix, const char *have_dropins) {
418 if (!path_is_absolute(suffix))
419 return process_suffix(prefixes, suffix, nulstr_contains(have_dropins, suffix));
421 /* Strip prefix from the suffix */
422 NULSTR_FOREACH(p, prefixes) {
423 if (startswith(suffix, p)) {
425 suffix += strspn(suffix, "/");
426 return process_suffix(prefixes, suffix, nulstr_contains(have_dropins, suffix));
430 log_error("Invalid suffix specification %s.", suffix);
434 static void help(void) {
436 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
437 "Find overridden configuration files.\n\n"
438 " -h --help Show this help\n"
439 " --version Show package version\n"
440 " --no-pager Do not pipe output into a pager\n"
441 " --diff[=1|0] Show a diff when overridden files differ\n"
442 " -t --type=LIST... Only display a selected set of override types\n",
443 program_invocation_short_name);
446 static int parse_flags(const char *flag_str, int flags) {
450 FOREACH_WORD(w, l, flag_str, state) {
451 if (strneq("masked", w, l))
452 flags |= SHOW_MASKED;
453 else if (strneq ("equivalent", w, l))
454 flags |= SHOW_EQUIVALENT;
455 else if (strneq("redirected", w, l))
456 flags |= SHOW_REDIRECTED;
457 else if (strneq("overridden", w, l))
458 flags |= SHOW_OVERRIDDEN;
459 else if (strneq("unchanged", w, l))
460 flags |= SHOW_UNCHANGED;
461 else if (strneq("extended", w, l))
462 flags |= SHOW_EXTENDED;
463 else if (strneq("default", w, l))
464 flags |= SHOW_DEFAULTS;
471 static int parse_argv(int argc, char *argv[]) {
474 ARG_NO_PAGER = 0x100,
479 static const struct option options[] = {
480 { "help", no_argument, NULL, 'h' },
481 { "version", no_argument, NULL, ARG_VERSION },
482 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
483 { "diff", optional_argument, NULL, ARG_DIFF },
484 { "type", required_argument, NULL, 't' },
493 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) {
502 puts(PACKAGE_STRING);
503 puts(SYSTEMD_FEATURES);
515 f = parse_flags(optarg, arg_flags);
517 log_error("Failed to parse flags field.");
530 b = parse_boolean(optarg);
532 log_error("Failed to parse diff boolean.");
542 log_error("Unknown option code %c", c);
550 int main(int argc, char *argv[]) {
552 const char prefixes[] =
559 #ifdef HAVE_SPLIT_USR
564 const char suffixes[] =
571 "systemd/system-preset\0"
572 "systemd/user-preset\0"
576 const char have_dropins[] =
583 log_parse_environment();
586 r = parse_argv(argc, argv);
591 arg_flags = SHOW_DEFAULTS;
594 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
596 arg_flags |= SHOW_OVERRIDDEN;
604 for (i = optind; i < argc; i++) {
605 k = process_suffix_chop(prefixes, argv[i], have_dropins);
615 NULSTR_FOREACH(n, suffixes) {
616 k = process_suffix(prefixes, n, nulstr_contains(have_dropins, n));
625 printf("\n%i overridden configuration files found.\n", n_found);
630 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;