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("%s%s%s %s → %s\n",
70 ansi_highlight_red(), "[MASKED]", ansi_highlight_off(), top, bottom);
74 static int notify_override_equivalent(const char *top, const char *bottom) {
75 if (!(arg_flags & SHOW_EQUIVALENT))
78 printf("%s%s%s %s → %s\n",
79 ansi_highlight_green(), "[EQUIVALENT]", ansi_highlight(), top, bottom);
83 static int notify_override_redirected(const char *top, const char *bottom) {
84 if (!(arg_flags & SHOW_REDIRECTED))
87 printf("%s%s%s %s → %s\n",
88 ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(), top, bottom);
92 static int notify_override_overridden(const char *top, const char *bottom) {
93 if (!(arg_flags & SHOW_OVERRIDDEN))
96 printf("%s%s%s %s → %s\n",
97 ansi_highlight(), "[OVERRIDDEN]", ansi_highlight_off(), top, bottom);
101 static int notify_override_extended(const char *top, const char *bottom) {
102 if (!(arg_flags & SHOW_EXTENDED))
105 printf("%s%s%s %s → %s\n",
106 ansi_highlight(), "[EXTENDED]", ansi_highlight_off(), top, bottom);
110 static int notify_override_unchanged(const char *f) {
111 if (!(arg_flags & SHOW_UNCHANGED))
114 printf("[UNCHANGED] %s\n", f);
118 static int found_override(const char *top, const char *bottom) {
119 _cleanup_free_ char *dest = NULL;
126 if (null_or_empty_path(top) > 0)
127 return notify_override_masked(top, bottom);
129 k = readlink_malloc(top, &dest);
131 if (equivalent(dest, bottom) > 0)
132 return notify_override_equivalent(top, bottom);
134 return notify_override_redirected(top, bottom);
137 k = notify_override_overridden(top, bottom);
147 log_error("Failed to fork off diff: %m");
149 } else if (pid == 0) {
150 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
151 log_error("Failed to execute diff: %m");
155 wait_for_terminate(pid, NULL);
162 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
163 _cleanup_free_ char *conf = NULL;
164 _cleanup_free_ char *path = NULL;
165 _cleanup_strv_free_ char **list = NULL;
170 path = strjoin(toppath, "/", drop, NULL);
174 path_kill_slashes(path);
180 c = strrchr(conf, '.');
185 r = get_files_in_directory(path, &list);
187 log_error("Failed to enumerate %s: %s", path, strerror(-r));
191 STRV_FOREACH(file, list) {
197 if (!endswith(*file, ".conf"))
200 p = strjoin(path, "/", *file, NULL);
204 path_kill_slashes(p);
219 k = hashmap_put(top, d, p);
227 } else if (k != -EEXIST) {
232 free(hashmap_remove(bottom, d));
233 k = hashmap_put(bottom, d, p);
239 h = hashmap_get(drops, conf);
241 h = hashmap_new(string_hash_func, string_compare_func);
244 hashmap_put(drops, conf, h);
254 k = hashmap_put(h, path_get_file_name(p), p);
264 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
265 _cleanup_closedir_ DIR *d;
277 log_error("Failed to enumerate %s: %m", path);
283 union dirent_storage buf;
287 k = readdir_r(d, &buf.de, &de);
294 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
295 enumerate_dir_d(top, bottom, drops, path, de->d_name);
297 if (!dirent_is_file(de))
300 p = strjoin(path, "/", de->d_name, NULL);
304 path_kill_slashes(p);
306 k = hashmap_put(top, path_get_file_name(p), p);
311 } else if (k != -EEXIST) {
316 free(hashmap_remove(bottom, path_get_file_name(p)));
317 k = hashmap_put(bottom, path_get_file_name(p), p);
327 static int process_suffix(const char *prefixes, const char *suffix, bool dropins) {
330 Hashmap *top, *bottom=NULL, *drops=NULL;
340 top = hashmap_new(string_hash_func, string_compare_func);
346 bottom = hashmap_new(string_hash_func, string_compare_func);
352 drops = hashmap_new(string_hash_func, string_compare_func);
358 NULSTR_FOREACH(p, prefixes) {
359 _cleanup_free_ char *t = NULL;
361 t = strjoin(p, "/", suffix, NULL);
367 k = enumerate_dir(top, bottom, drops, t, dropins);
371 log_debug("Looking at %s", t);
374 HASHMAP_FOREACH_KEY(f, key, top, i) {
377 o = hashmap_get(bottom, key);
380 if (path_equal(o, f))
381 notify_override_unchanged(f);
383 k = found_override(f, o);
390 h = hashmap_get(drops, key);
392 HASHMAP_FOREACH(o, h, j)
393 n_found += 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("%s%i overridden configuration files found.\n",
626 n_found ? "\n" : "", n_found);
631 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;