1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
7 Copyright 2013 Zbigniew Jędrzejewski-Szmek
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "path-util.h"
37 static const char prefixes
[] =
49 static const char suffixes
[] =
56 "systemd/system-preset\0"
57 "systemd/user-preset\0"
61 static const char have_dropins
[] =
65 static bool arg_no_pager
= false;
66 static int arg_diff
= -1;
70 SHOW_EQUIVALENT
= 1 << 1,
71 SHOW_REDIRECTED
= 1 << 2,
72 SHOW_OVERRIDDEN
= 1 << 3,
73 SHOW_UNCHANGED
= 1 << 4,
74 SHOW_EXTENDED
= 1 << 5,
77 (SHOW_MASKED
| SHOW_EQUIVALENT
| SHOW_REDIRECTED
| SHOW_OVERRIDDEN
| SHOW_EXTENDED
)
80 static void pager_open_if_enabled(void) {
88 static int equivalent(const char *a
, const char *b
) {
89 _cleanup_free_
char *x
= NULL
, *y
= NULL
;
91 x
= canonicalize_file_name(a
);
95 y
= canonicalize_file_name(b
);
99 return path_equal(x
, y
);
102 static int notify_override_masked(const char *top
, const char *bottom
) {
103 if (!(arg_flags
& SHOW_MASKED
))
106 printf("%s%s%s %s %s %s\n",
107 ansi_highlight_red(), "[MASKED]", ansi_highlight_off(),
108 top
, draw_special_char(DRAW_ARROW
), bottom
);
112 static int notify_override_equivalent(const char *top
, const char *bottom
) {
113 if (!(arg_flags
& SHOW_EQUIVALENT
))
116 printf("%s%s%s %s %s %s\n",
117 ansi_highlight_green(), "[EQUIVALENT]", ansi_highlight_off(),
118 top
, draw_special_char(DRAW_ARROW
), bottom
);
122 static int notify_override_redirected(const char *top
, const char *bottom
) {
123 if (!(arg_flags
& SHOW_REDIRECTED
))
126 printf("%s%s%s %s %s %s\n",
127 ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(),
128 top
, draw_special_char(DRAW_ARROW
), bottom
);
132 static int notify_override_overridden(const char *top
, const char *bottom
) {
133 if (!(arg_flags
& SHOW_OVERRIDDEN
))
136 printf("%s%s%s %s %s %s\n",
137 ansi_highlight(), "[OVERRIDDEN]", ansi_highlight_off(),
138 top
, draw_special_char(DRAW_ARROW
), bottom
);
142 static int notify_override_extended(const char *top
, const char *bottom
) {
143 if (!(arg_flags
& SHOW_EXTENDED
))
146 printf("%s%s%s %s %s %s\n",
147 ansi_highlight(), "[EXTENDED]", ansi_highlight_off(),
148 top
, draw_special_char(DRAW_ARROW
), bottom
);
152 static int notify_override_unchanged(const char *f
) {
153 if (!(arg_flags
& SHOW_UNCHANGED
))
156 printf("[UNCHANGED] %s\n", f
);
160 static int found_override(const char *top
, const char *bottom
) {
161 _cleanup_free_
char *dest
= NULL
;
168 if (null_or_empty_path(top
) > 0)
169 return notify_override_masked(top
, bottom
);
171 k
= readlink_malloc(top
, &dest
);
173 if (equivalent(dest
, bottom
) > 0)
174 return notify_override_equivalent(top
, bottom
);
176 return notify_override_redirected(top
, bottom
);
179 k
= notify_override_overridden(top
, bottom
);
189 return log_error_errno(errno
, "Failed to fork off diff: %m");
191 execlp("diff", "diff", "-us", "--", bottom
, top
, NULL
);
192 log_error_errno(errno
, "Failed to execute diff: %m");
196 wait_for_terminate_and_warn("diff", pid
, false);
202 static int enumerate_dir_d(Hashmap
*top
, Hashmap
*bottom
, Hashmap
*drops
, const char *toppath
, const char *drop
) {
203 _cleanup_free_
char *unit
= NULL
;
204 _cleanup_free_
char *path
= NULL
;
205 _cleanup_strv_free_
char **list
= NULL
;
210 assert(!endswith(drop
, "/"));
212 path
= strjoin(toppath
, "/", drop
, NULL
);
216 log_debug("Looking at %s", path
);
222 c
= strrchr(unit
, '.');
227 r
= get_files_in_directory(path
, &list
);
229 return log_error_errno(r
, "Failed to enumerate %s: %m", path
);
231 STRV_FOREACH(file
, list
) {
237 if (!endswith(*file
, ".conf"))
240 p
= strjoin(path
, "/", *file
, NULL
);
243 d
= p
+ strlen(toppath
) + 1;
245 log_debug("Adding at top: %s %s %s", d
, draw_special_char(DRAW_ARROW
), p
);
246 k
= hashmap_put(top
, d
, p
);
251 d
= p
+ strlen(toppath
) + 1;
252 } else if (k
!= -EEXIST
) {
257 log_debug("Adding at bottom: %s %s %s", d
, draw_special_char(DRAW_ARROW
), p
);
258 free(hashmap_remove(bottom
, d
));
259 k
= hashmap_put(bottom
, d
, p
);
265 h
= hashmap_get(drops
, unit
);
267 h
= hashmap_new(&string_hash_ops
);
270 hashmap_put(drops
, unit
, h
);
280 log_debug("Adding to drops: %s %s %s %s %s",
281 unit
, draw_special_char(DRAW_ARROW
), basename(p
), draw_special_char(DRAW_ARROW
), p
);
282 k
= hashmap_put(h
, basename(p
), p
);
292 static int enumerate_dir(Hashmap
*top
, Hashmap
*bottom
, Hashmap
*drops
, const char *path
, bool dropins
) {
293 _cleanup_closedir_
DIR *d
;
300 log_debug("Looking at %s", path
);
307 log_error_errno(errno
, "Failed to open %s: %m", path
);
321 dirent_ensure_type(d
, de
);
323 if (dropins
&& de
->d_type
== DT_DIR
&& endswith(de
->d_name
, ".d"))
324 enumerate_dir_d(top
, bottom
, drops
, path
, de
->d_name
);
326 if (!dirent_is_file(de
))
329 p
= strjoin(path
, "/", de
->d_name
, NULL
);
333 log_debug("Adding at top: %s %s %s", basename(p
), draw_special_char(DRAW_ARROW
), p
);
334 k
= hashmap_put(top
, basename(p
), p
);
339 } else if (k
!= -EEXIST
) {
344 log_debug("Adding at bottom: %s %s %s", basename(p
), draw_special_char(DRAW_ARROW
), p
);
345 free(hashmap_remove(bottom
, basename(p
)));
346 k
= hashmap_put(bottom
, basename(p
), p
);
354 static int process_suffix(const char *suffix
, const char *onlyprefix
) {
357 Hashmap
*top
, *bottom
, *drops
;
366 assert(!startswith(suffix
, "/"));
367 assert(!strstr(suffix
, "//"));
369 dropins
= nulstr_contains(have_dropins
, suffix
);
371 top
= hashmap_new(&string_hash_ops
);
372 bottom
= hashmap_new(&string_hash_ops
);
373 drops
= hashmap_new(&string_hash_ops
);
374 if (!top
|| !bottom
|| !drops
) {
379 NULSTR_FOREACH(p
, prefixes
) {
380 _cleanup_free_
char *t
= NULL
;
382 t
= strjoin(p
, "/", suffix
, NULL
);
388 k
= enumerate_dir(top
, bottom
, drops
, t
, dropins
);
393 HASHMAP_FOREACH_KEY(f
, key
, top
, i
) {
396 o
= hashmap_get(bottom
, key
);
399 if (!onlyprefix
|| startswith(o
, onlyprefix
)) {
400 if (path_equal(o
, f
)) {
401 notify_override_unchanged(f
);
403 k
= found_override(f
, o
);
411 h
= hashmap_get(drops
, key
);
413 HASHMAP_FOREACH(o
, h
, j
)
414 if (!onlyprefix
|| startswith(o
, onlyprefix
))
415 n_found
+= notify_override_extended(f
, o
);
420 hashmap_free_free(top
);
422 hashmap_free_free(bottom
);
424 HASHMAP_FOREACH_KEY(h
, key
, drops
, i
){
425 hashmap_free_free(hashmap_remove(drops
, key
));
426 hashmap_remove(drops
, key
);
431 return r
< 0 ? r
: n_found
;
434 static int process_suffixes(const char *onlyprefix
) {
438 NULSTR_FOREACH(n
, suffixes
) {
439 r
= process_suffix(n
, onlyprefix
);
448 static int process_suffix_chop(const char *arg
) {
453 if (!path_is_absolute(arg
))
454 return process_suffix(arg
, NULL
);
456 /* Strip prefix from the suffix */
457 NULSTR_FOREACH(p
, prefixes
) {
458 const char *suffix
= startswith(arg
, p
);
460 suffix
+= strspn(suffix
, "/");
462 return process_suffix(suffix
, NULL
);
464 return process_suffixes(arg
);
468 log_error("Invalid suffix specification %s.", arg
);
472 static void help(void) {
473 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
474 "Find overridden configuration files.\n\n"
475 " -h --help Show this help\n"
476 " --version Show package version\n"
477 " --no-pager Do not pipe output into a pager\n"
478 " --diff[=1|0] Show a diff when overridden files differ\n"
479 " -t --type=LIST... Only display a selected set of override types\n"
480 , program_invocation_short_name
);
483 static int parse_flags(const char *flag_str
, int flags
) {
484 const char *word
, *state
;
487 FOREACH_WORD_SEPARATOR(word
, l
, flag_str
, ",", state
) {
488 if (strneq("masked", word
, l
))
489 flags
|= SHOW_MASKED
;
490 else if (strneq ("equivalent", word
, l
))
491 flags
|= SHOW_EQUIVALENT
;
492 else if (strneq("redirected", word
, l
))
493 flags
|= SHOW_REDIRECTED
;
494 else if (strneq("overridden", word
, l
))
495 flags
|= SHOW_OVERRIDDEN
;
496 else if (strneq("unchanged", word
, l
))
497 flags
|= SHOW_UNCHANGED
;
498 else if (strneq("extended", word
, l
))
499 flags
|= SHOW_EXTENDED
;
500 else if (strneq("default", word
, l
))
501 flags
|= SHOW_DEFAULTS
;
508 static int parse_argv(int argc
, char *argv
[]) {
511 ARG_NO_PAGER
= 0x100,
516 static const struct option options
[] = {
517 { "help", no_argument
, NULL
, 'h' },
518 { "version", no_argument
, NULL
, ARG_VERSION
},
519 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
520 { "diff", optional_argument
, NULL
, ARG_DIFF
},
521 { "type", required_argument
, NULL
, 't' },
530 while ((c
= getopt_long(argc
, argv
, "ht:", options
, NULL
)) >= 0)
539 puts(PACKAGE_STRING
);
540 puts(SYSTEMD_FEATURES
);
549 f
= parse_flags(optarg
, arg_flags
);
551 log_error("Failed to parse flags field.");
564 b
= parse_boolean(optarg
);
566 log_error("Failed to parse diff boolean.");
579 assert_not_reached("Unhandled option");
585 int main(int argc
, char *argv
[]) {
589 log_parse_environment();
592 r
= parse_argv(argc
, argv
);
597 arg_flags
= SHOW_DEFAULTS
;
600 arg_diff
= !!(arg_flags
& SHOW_OVERRIDDEN
);
602 arg_flags
|= SHOW_OVERRIDDEN
;
604 pager_open_if_enabled();
609 for (i
= optind
; i
< argc
; i
++) {
610 path_kill_slashes(argv
[i
]);
611 k
= process_suffix_chop(argv
[i
]);
619 k
= process_suffixes(NULL
);
627 printf("%s%i overridden configuration files found.\n",
628 n_found
? "\n" : "", n_found
);
633 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;