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"
35 static bool arg_no_pager = false;
36 static int arg_diff = -1;
40 SHOW_EQUIVALENT = 1 << 1,
41 SHOW_REDIRECTED = 1 << 2,
42 SHOW_OVERRIDDEN = 1 << 3,
43 SHOW_UNCHANGED = 1 << 4,
46 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN)
49 static int equivalent(const char *a, const char *b) {
50 _cleanup_free_ char *x = NULL, *y = NULL;
52 x = canonicalize_file_name(a);
56 y = canonicalize_file_name(b);
60 return path_equal(x, y);
63 static int notify_override_masked(const char *top, const char *bottom) {
64 if (!(arg_flags & SHOW_MASKED))
67 printf(ANSI_HIGHLIGHT_RED_ON "[MASKED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
71 static int notify_override_equivalent(const char *top, const char *bottom) {
72 if (!(arg_flags & SHOW_EQUIVALENT))
75 printf(ANSI_HIGHLIGHT_GREEN_ON "[EQUIVALENT]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
79 static int notify_override_redirected(const char *top, const char *bottom) {
80 if (!(arg_flags & SHOW_REDIRECTED))
83 printf(ANSI_HIGHLIGHT_ON "[REDIRECTED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
87 static int notify_override_overridden(const char *top, const char *bottom) {
88 if (!(arg_flags & SHOW_OVERRIDDEN))
91 printf(ANSI_HIGHLIGHT_ON "[OVERRIDDEN]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
95 static int notify_override_unchanged(const char *f) {
96 if (!(arg_flags & SHOW_UNCHANGED))
99 printf("[UNCHANGED] %s\n", f);
103 static int found_override(const char *top, const char *bottom) {
104 _cleanup_free_ char *dest = NULL;
111 if (null_or_empty_path(top) > 0) {
112 notify_override_masked(top, bottom);
116 k = readlink_malloc(top, &dest);
118 if (equivalent(dest, bottom) > 0)
119 notify_override_equivalent(top, bottom);
121 notify_override_redirected(top, bottom);
126 notify_override_overridden(top, bottom);
136 log_error("Failed to fork off diff: %m");
138 } else if (pid == 0) {
139 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
140 log_error("Failed to execute diff: %m");
144 wait_for_terminate(pid, NULL);
151 static int enumerate_dir(Hashmap *top, Hashmap *bottom, const char *path) {
152 _cleanup_closedir_ DIR *d;
163 log_error("Failed to enumerate %s: %m", path);
169 union dirent_storage buf;
173 k = readdir_r(d, &buf.de, &de);
180 if (!dirent_is_file(de))
183 p = strjoin(path, "/", de->d_name, NULL);
187 path_kill_slashes(p);
189 k = hashmap_put(top, path_get_file_name(p), p);
194 } else if (k != -EEXIST) {
199 free(hashmap_remove(bottom, path_get_file_name(p)));
200 k = hashmap_put(bottom, path_get_file_name(p), p);
210 static int process_suffix(const char *prefixes, const char *suffix) {
213 Hashmap *top, *bottom=NULL;
221 top = hashmap_new(string_hash_func, string_compare_func);
227 bottom = hashmap_new(string_hash_func, string_compare_func);
233 NULSTR_FOREACH(p, prefixes) {
234 _cleanup_free_ char *t = NULL;
236 t = strjoin(p, "/", suffix, NULL);
242 k = enumerate_dir(top, bottom, t);
246 log_debug("Looking at %s", t);
249 HASHMAP_FOREACH(f, top, i) {
252 o = hashmap_get(bottom, path_get_file_name(f));
255 if (path_equal(o, f)) {
256 notify_override_unchanged(f);
260 k = found_override(f, o);
269 hashmap_free_free(top);
271 hashmap_free_free(bottom);
273 return r < 0 ? r : n_found;
276 static int process_suffix_chop(const char *prefixes, const char *suffix) {
282 if (!path_is_absolute(suffix))
283 return process_suffix(prefixes, suffix);
285 /* Strip prefix from the suffix */
286 NULSTR_FOREACH(p, prefixes) {
287 if (startswith(suffix, p)) {
289 suffix += strspn(suffix, "/");
290 return process_suffix(prefixes, suffix);
294 log_error("Invalid suffix specification %s.", suffix);
298 static void help(void) {
300 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
301 "Find overridden configuration files.\n\n"
302 " -h --help Show this help\n"
303 " --version Show package version\n"
304 " --no-pager Do not pipe output into a pager\n"
305 " --diff[=1|0] Show a diff when overridden files differ\n"
306 " -t --type=LIST... Only display a selected set of override types\n",
307 program_invocation_short_name);
310 static int parse_flags(const char *flag_str, int flags) {
314 FOREACH_WORD(w, l, flag_str, state) {
315 if (strneq("masked", w, l))
316 flags |= SHOW_MASKED;
317 else if (strneq ("equivalent", w, l))
318 flags |= SHOW_EQUIVALENT;
319 else if (strneq("redirected", w, l))
320 flags |= SHOW_REDIRECTED;
321 else if (strneq("overridden", w, l))
322 flags |= SHOW_OVERRIDDEN;
323 else if (strneq("unchanged", w, l))
324 flags |= SHOW_UNCHANGED;
325 else if (strneq("default", w, l))
326 flags |= SHOW_DEFAULTS;
333 static int parse_argv(int argc, char *argv[]) {
336 ARG_NO_PAGER = 0x100,
341 static const struct option options[] = {
342 { "help", no_argument, NULL, 'h' },
343 { "version", no_argument, NULL, ARG_VERSION },
344 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
345 { "diff", optional_argument, NULL, ARG_DIFF },
346 { "type", required_argument, NULL, 't' },
355 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) {
364 puts(PACKAGE_STRING);
365 puts(SYSTEMD_FEATURES);
377 f = parse_flags(optarg, arg_flags);
379 log_error("Failed to parse flags field.");
392 b = parse_boolean(optarg);
394 log_error("Failed to parse diff boolean.");
404 log_error("Unknown option code %c", c);
412 int main(int argc, char *argv[]) {
414 const char prefixes[] =
421 #ifdef HAVE_SPLIT_USR
426 const char suffixes[] =
433 "systemd/system-preset\0"
434 "systemd/user-preset\0"
441 log_parse_environment();
444 r = parse_argv(argc, argv);
449 arg_flags = SHOW_DEFAULTS;
452 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
454 arg_flags |= SHOW_OVERRIDDEN;
462 for (i = optind; i < argc; i++) {
463 k = process_suffix_chop(prefixes, argv[i]);
473 NULSTR_FOREACH(n, suffixes) {
474 k = process_suffix(prefixes, n);
483 printf("\n%i overridden configuration files found.\n", n_found);
488 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;