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, basename(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, basename(p), p);
311 } else if (k != -EEXIST) {
316 free(hashmap_remove(bottom, basename(p)));
317 k = hashmap_put(bottom, basename(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 int 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);
448 static int parse_flags(const char *flag_str, int flags) {
452 FOREACH_WORD(w, l, flag_str, state) {
453 if (strneq("masked", w, l))
454 flags |= SHOW_MASKED;
455 else if (strneq ("equivalent", w, l))
456 flags |= SHOW_EQUIVALENT;
457 else if (strneq("redirected", w, l))
458 flags |= SHOW_REDIRECTED;
459 else if (strneq("overridden", w, l))
460 flags |= SHOW_OVERRIDDEN;
461 else if (strneq("unchanged", w, l))
462 flags |= SHOW_UNCHANGED;
463 else if (strneq("extended", w, l))
464 flags |= SHOW_EXTENDED;
465 else if (strneq("default", w, l))
466 flags |= SHOW_DEFAULTS;
473 static int parse_argv(int argc, char *argv[]) {
476 ARG_NO_PAGER = 0x100,
481 static const struct option options[] = {
482 { "help", no_argument, NULL, 'h' },
483 { "version", no_argument, NULL, ARG_VERSION },
484 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
485 { "diff", optional_argument, NULL, ARG_DIFF },
486 { "type", required_argument, NULL, 't' },
495 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) {
504 puts(PACKAGE_STRING);
505 puts(SYSTEMD_FEATURES);
514 f = parse_flags(optarg, arg_flags);
516 log_error("Failed to parse flags field.");
529 b = parse_boolean(optarg);
531 log_error("Failed to parse diff boolean.");
544 assert_not_reached("Unhandled option");
551 int main(int argc, char *argv[]) {
553 const char prefixes[] =
560 #ifdef HAVE_SPLIT_USR
565 const char suffixes[] =
572 "systemd/system-preset\0"
573 "systemd/user-preset\0"
577 const char have_dropins[] =
584 log_parse_environment();
587 r = parse_argv(argc, argv);
592 arg_flags = SHOW_DEFAULTS;
595 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
597 arg_flags |= SHOW_OVERRIDDEN;
605 for (i = optind; i < argc; i++) {
606 k = process_suffix_chop(prefixes, argv[i], have_dropins);
616 NULSTR_FOREACH(n, suffixes) {
617 k = process_suffix(prefixes, n, nulstr_contains(have_dropins, n));
626 printf("%s%i overridden configuration files found.\n",
627 n_found ? "\n" : "", n_found);
632 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;