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 const char prefixes[] =
48 static const char suffixes[] =
55 "systemd/system-preset\0"
56 "systemd/user-preset\0"
60 static const char have_dropins[] =
64 static bool arg_no_pager = false;
65 static int arg_diff = -1;
69 SHOW_EQUIVALENT = 1 << 1,
70 SHOW_REDIRECTED = 1 << 2,
71 SHOW_OVERRIDDEN = 1 << 3,
72 SHOW_UNCHANGED = 1 << 4,
73 SHOW_EXTENDED = 1 << 5,
76 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED)
79 static void pager_open_if_enabled(void) {
87 static int equivalent(const char *a, const char *b) {
88 _cleanup_free_ char *x = NULL, *y = NULL;
90 x = canonicalize_file_name(a);
94 y = canonicalize_file_name(b);
98 return path_equal(x, y);
101 static int notify_override_masked(const char *top, const char *bottom) {
102 if (!(arg_flags & SHOW_MASKED))
105 printf("%s%s%s %s → %s\n",
106 ansi_highlight_red(), "[MASKED]", ansi_highlight_off(), top, bottom);
110 static int notify_override_equivalent(const char *top, const char *bottom) {
111 if (!(arg_flags & SHOW_EQUIVALENT))
114 printf("%s%s%s %s → %s\n",
115 ansi_highlight_green(), "[EQUIVALENT]", ansi_highlight_off(), top, bottom);
119 static int notify_override_redirected(const char *top, const char *bottom) {
120 if (!(arg_flags & SHOW_REDIRECTED))
123 printf("%s%s%s %s → %s\n",
124 ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(), top, bottom);
128 static int notify_override_overridden(const char *top, const char *bottom) {
129 if (!(arg_flags & SHOW_OVERRIDDEN))
132 printf("%s%s%s %s → %s\n",
133 ansi_highlight(), "[OVERRIDDEN]", ansi_highlight_off(), top, bottom);
137 static int notify_override_extended(const char *top, const char *bottom) {
138 if (!(arg_flags & SHOW_EXTENDED))
141 printf("%s%s%s %s → %s\n",
142 ansi_highlight(), "[EXTENDED]", ansi_highlight_off(), top, bottom);
146 static int notify_override_unchanged(const char *f) {
147 if (!(arg_flags & SHOW_UNCHANGED))
150 printf("[UNCHANGED] %s\n", f);
154 static int found_override(const char *top, const char *bottom) {
155 _cleanup_free_ char *dest = NULL;
162 if (null_or_empty_path(top) > 0)
163 return notify_override_masked(top, bottom);
165 k = readlink_malloc(top, &dest);
167 if (equivalent(dest, bottom) > 0)
168 return notify_override_equivalent(top, bottom);
170 return notify_override_redirected(top, bottom);
173 k = notify_override_overridden(top, bottom);
183 log_error("Failed to fork off diff: %m");
185 } else if (pid == 0) {
186 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
187 log_error("Failed to execute diff: %m");
191 wait_for_terminate(pid, NULL);
198 static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
199 _cleanup_free_ char *unit = NULL;
200 _cleanup_free_ char *path = NULL;
201 _cleanup_strv_free_ char **list = NULL;
206 assert(!endswith(drop, "/"));
208 path = strjoin(toppath, "/", drop, NULL);
212 log_debug("Looking at %s", path);
218 c = strrchr(unit, '.');
223 r = get_files_in_directory(path, &list);
225 log_error("Failed to enumerate %s: %s", path, strerror(-r));
229 STRV_FOREACH(file, list) {
235 if (!endswith(*file, ".conf"))
238 p = strjoin(path, "/", *file, NULL);
241 d = p + strlen(toppath) + 1;
243 log_debug("Adding at top: %s → %s", d, p);
244 k = hashmap_put(top, d, p);
249 d = p + strlen(toppath) + 1;
250 } else if (k != -EEXIST) {
255 log_debug("Adding at bottom: %s → %s", d, p);
256 free(hashmap_remove(bottom, d));
257 k = hashmap_put(bottom, d, p);
263 h = hashmap_get(drops, unit);
265 h = hashmap_new(string_hash_func, string_compare_func);
268 hashmap_put(drops, unit, h);
278 log_debug("Adding to drops: %s → %s → %s", unit, basename(p), p);
279 k = hashmap_put(h, basename(p), p);
289 static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
290 _cleanup_closedir_ DIR *d;
297 log_debug("Looking at %s", path);
304 log_error("Failed to open %s: %m", path);
318 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
319 enumerate_dir_d(top, bottom, drops, path, de->d_name);
321 if (!dirent_is_file(de))
324 p = strjoin(path, "/", de->d_name, NULL);
328 log_debug("Adding at top: %s → %s", basename(p), p);
329 k = hashmap_put(top, basename(p), p);
334 } else if (k != -EEXIST) {
339 log_debug("Adding at bottom: %s → %s", basename(p), p);
340 free(hashmap_remove(bottom, basename(p)));
341 k = hashmap_put(bottom, basename(p), p);
349 static int process_suffix(const char *suffix) {
352 Hashmap *top, *bottom, *drops;
361 assert(!startswith(suffix, "/"));
362 assert(!strstr(suffix, "//"));
364 dropins = nulstr_contains(have_dropins, suffix);
366 top = hashmap_new(string_hash_func, string_compare_func);
367 bottom = hashmap_new(string_hash_func, string_compare_func);
368 drops = hashmap_new(string_hash_func, string_compare_func);
369 if (!top || !bottom || !drops) {
374 NULSTR_FOREACH(p, prefixes) {
375 _cleanup_free_ char *t = NULL;
377 t = strjoin(p, "/", suffix, NULL);
383 k = enumerate_dir(top, bottom, drops, t, dropins);
388 HASHMAP_FOREACH_KEY(f, key, top, i) {
391 o = hashmap_get(bottom, key);
394 if (path_equal(o, f))
395 notify_override_unchanged(f);
397 k = found_override(f, o);
404 h = hashmap_get(drops, key);
406 HASHMAP_FOREACH(o, h, j)
407 n_found += notify_override_extended(f, o);
412 hashmap_free_free(top);
414 hashmap_free_free(bottom);
416 HASHMAP_FOREACH_KEY(h, key, drops, i){
417 hashmap_free_free(hashmap_remove(drops, key));
418 hashmap_remove(drops, key);
423 return r < 0 ? r : n_found;
426 static int process_suffix_chop(const char *suffix) {
431 if (!path_is_absolute(suffix))
432 return process_suffix(suffix);
434 /* Strip prefix from the suffix */
435 NULSTR_FOREACH(p, prefixes) {
436 if (startswith(suffix, p)) {
438 suffix += strspn(suffix, "/");
439 return process_suffix(suffix);
443 log_error("Invalid suffix specification %s.", suffix);
447 static int help(void) {
449 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
450 "Find overridden configuration files.\n\n"
451 " -h --help Show this help\n"
452 " --version Show package version\n"
453 " --no-pager Do not pipe output into a pager\n"
454 " --diff[=1|0] Show a diff when overridden files differ\n"
455 " -t --type=LIST... Only display a selected set of override types\n",
456 program_invocation_short_name);
461 static int parse_flags(const char *flag_str, int flags) {
465 FOREACH_WORD(w, l, flag_str, state) {
466 if (strneq("masked", w, l))
467 flags |= SHOW_MASKED;
468 else if (strneq ("equivalent", w, l))
469 flags |= SHOW_EQUIVALENT;
470 else if (strneq("redirected", w, l))
471 flags |= SHOW_REDIRECTED;
472 else if (strneq("overridden", w, l))
473 flags |= SHOW_OVERRIDDEN;
474 else if (strneq("unchanged", w, l))
475 flags |= SHOW_UNCHANGED;
476 else if (strneq("extended", w, l))
477 flags |= SHOW_EXTENDED;
478 else if (strneq("default", w, l))
479 flags |= SHOW_DEFAULTS;
486 static int parse_argv(int argc, char *argv[]) {
489 ARG_NO_PAGER = 0x100,
494 static const struct option options[] = {
495 { "help", no_argument, NULL, 'h' },
496 { "version", no_argument, NULL, ARG_VERSION },
497 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
498 { "diff", optional_argument, NULL, ARG_DIFF },
499 { "type", required_argument, NULL, 't' },
508 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) {
517 puts(PACKAGE_STRING);
518 puts(SYSTEMD_FEATURES);
527 f = parse_flags(optarg, arg_flags);
529 log_error("Failed to parse flags field.");
542 b = parse_boolean(optarg);
544 log_error("Failed to parse diff boolean.");
557 assert_not_reached("Unhandled option");
564 int main(int argc, char *argv[]) {
568 log_parse_environment();
571 r = parse_argv(argc, argv);
576 arg_flags = SHOW_DEFAULTS;
579 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
581 arg_flags |= SHOW_OVERRIDDEN;
583 pager_open_if_enabled();
588 for (i = optind; i < argc; i++) {
589 path_kill_slashes(argv[i]);
590 k = process_suffix_chop(argv[i]);
600 NULSTR_FOREACH(n, suffixes) {
601 k = process_suffix(n);
610 printf("%s%i overridden configuration files found.\n",
611 n_found ? "\n" : "", n_found);
616 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;