2 This file is part of systemd.
4 Copyright 2012 Lennart Poettering
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/prctl.h>
27 #include "alloc-util.h"
28 #include "dirent-util.h"
32 #include "locale-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "process-util.h"
38 #include "signal-util.h"
39 #include "stat-util.h"
40 #include "string-util.h"
42 #include "terminal-util.h"
45 static const char prefixes
[] =
57 static const char suffixes
[] =
64 "systemd/system-preset\0"
65 "systemd/user-preset\0"
69 static const char have_dropins
[] =
73 static bool arg_no_pager
= false;
74 static int arg_diff
= -1;
78 SHOW_EQUIVALENT
= 1 << 1,
79 SHOW_REDIRECTED
= 1 << 2,
80 SHOW_OVERRIDDEN
= 1 << 3,
81 SHOW_UNCHANGED
= 1 << 4,
82 SHOW_EXTENDED
= 1 << 5,
85 (SHOW_MASKED
| SHOW_EQUIVALENT
| SHOW_REDIRECTED
| SHOW_OVERRIDDEN
| SHOW_EXTENDED
)
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_normal(),
108 top
, special_glyph(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_normal(),
118 top
, special_glyph(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_normal(),
128 top
, special_glyph(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_normal(),
138 top
, special_glyph(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_normal(),
148 top
, special_glyph(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");
192 (void) reset_all_signal_handlers();
193 (void) reset_signal_mask();
194 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
196 execlp("diff", "diff", "-us", "--", bottom
, top
, NULL
);
197 log_error_errno(errno
, "Failed to execute diff: %m");
201 wait_for_terminate_and_warn("diff", pid
, false);
207 static int enumerate_dir_d(Hashmap
*top
, Hashmap
*bottom
, Hashmap
*drops
, const char *toppath
, const char *drop
) {
208 _cleanup_free_
char *unit
= NULL
;
209 _cleanup_free_
char *path
= NULL
;
210 _cleanup_strv_free_
char **list
= NULL
;
215 assert(!endswith(drop
, "/"));
217 path
= strjoin(toppath
, "/", drop
, NULL
);
221 log_debug("Looking at %s", path
);
227 c
= strrchr(unit
, '.');
232 r
= get_files_in_directory(path
, &list
);
234 return log_error_errno(r
, "Failed to enumerate %s: %m", path
);
236 STRV_FOREACH(file
, list
) {
242 if (!endswith(*file
, ".conf"))
245 p
= strjoin(path
, "/", *file
, NULL
);
248 d
= p
+ strlen(toppath
) + 1;
250 log_debug("Adding at top: %s %s %s", d
, special_glyph(ARROW
), p
);
251 k
= hashmap_put(top
, d
, p
);
256 d
= p
+ strlen(toppath
) + 1;
257 } else if (k
!= -EEXIST
) {
262 log_debug("Adding at bottom: %s %s %s", d
, special_glyph(ARROW
), p
);
263 free(hashmap_remove(bottom
, d
));
264 k
= hashmap_put(bottom
, d
, p
);
270 h
= hashmap_get(drops
, unit
);
272 h
= hashmap_new(&string_hash_ops
);
275 hashmap_put(drops
, unit
, h
);
285 log_debug("Adding to drops: %s %s %s %s %s",
286 unit
, special_glyph(ARROW
), basename(p
), special_glyph(ARROW
), p
);
287 k
= hashmap_put(h
, basename(p
), p
);
297 static int enumerate_dir(Hashmap
*top
, Hashmap
*bottom
, Hashmap
*drops
, const char *path
, bool dropins
) {
298 _cleanup_closedir_
DIR *d
;
305 log_debug("Looking at %s", path
);
312 return log_error_errno(errno
, "Failed to open %s: %m", path
);
325 dirent_ensure_type(d
, de
);
327 if (dropins
&& de
->d_type
== DT_DIR
&& endswith(de
->d_name
, ".d"))
328 enumerate_dir_d(top
, bottom
, drops
, path
, de
->d_name
);
330 if (!dirent_is_file(de
))
333 p
= strjoin(path
, "/", de
->d_name
, NULL
);
337 log_debug("Adding at top: %s %s %s", basename(p
), special_glyph(ARROW
), p
);
338 k
= hashmap_put(top
, basename(p
), p
);
343 } else if (k
!= -EEXIST
) {
348 log_debug("Adding at bottom: %s %s %s", basename(p
), special_glyph(ARROW
), p
);
349 free(hashmap_remove(bottom
, basename(p
)));
350 k
= hashmap_put(bottom
, basename(p
), p
);
358 static int process_suffix(const char *suffix
, const char *onlyprefix
) {
361 Hashmap
*top
, *bottom
, *drops
;
370 assert(!startswith(suffix
, "/"));
371 assert(!strstr(suffix
, "//"));
373 dropins
= nulstr_contains(have_dropins
, suffix
);
375 top
= hashmap_new(&string_hash_ops
);
376 bottom
= hashmap_new(&string_hash_ops
);
377 drops
= hashmap_new(&string_hash_ops
);
378 if (!top
|| !bottom
|| !drops
) {
383 NULSTR_FOREACH(p
, prefixes
) {
384 _cleanup_free_
char *t
= NULL
;
386 t
= strjoin(p
, "/", suffix
, NULL
);
392 k
= enumerate_dir(top
, bottom
, drops
, t
, dropins
);
397 HASHMAP_FOREACH_KEY(f
, key
, top
, i
) {
400 o
= hashmap_get(bottom
, key
);
403 if (!onlyprefix
|| startswith(o
, onlyprefix
)) {
404 if (path_equal(o
, f
)) {
405 notify_override_unchanged(f
);
407 k
= found_override(f
, o
);
415 h
= hashmap_get(drops
, key
);
417 HASHMAP_FOREACH(o
, h
, j
)
418 if (!onlyprefix
|| startswith(o
, onlyprefix
))
419 n_found
+= notify_override_extended(f
, o
);
423 hashmap_free_free(top
);
424 hashmap_free_free(bottom
);
426 HASHMAP_FOREACH_KEY(h
, key
, drops
, i
) {
427 hashmap_free_free(hashmap_remove(drops
, key
));
428 hashmap_remove(drops
, key
);
433 return r
< 0 ? r
: n_found
;
436 static int process_suffixes(const char *onlyprefix
) {
440 NULSTR_FOREACH(n
, suffixes
) {
441 r
= process_suffix(n
, onlyprefix
);
451 static int process_suffix_chop(const char *arg
) {
456 if (!path_is_absolute(arg
))
457 return process_suffix(arg
, NULL
);
459 /* Strip prefix from the suffix */
460 NULSTR_FOREACH(p
, prefixes
) {
463 suffix
= startswith(arg
, p
);
465 suffix
+= strspn(suffix
, "/");
467 return process_suffix(suffix
, NULL
);
469 return process_suffixes(arg
);
473 log_error("Invalid suffix specification %s.", arg
);
477 static void help(void) {
478 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
479 "Find overridden configuration files.\n\n"
480 " -h --help Show this help\n"
481 " --version Show package version\n"
482 " --no-pager Do not pipe output into a pager\n"
483 " --diff[=1|0] Show a diff when overridden files differ\n"
484 " -t --type=LIST... Only display a selected set of override types\n"
485 , program_invocation_short_name
);
488 static int parse_flags(const char *flag_str
, int flags
) {
489 const char *word
, *state
;
492 FOREACH_WORD_SEPARATOR(word
, l
, flag_str
, ",", state
) {
493 if (strneq("masked", word
, l
))
494 flags
|= SHOW_MASKED
;
495 else if (strneq ("equivalent", word
, l
))
496 flags
|= SHOW_EQUIVALENT
;
497 else if (strneq("redirected", word
, l
))
498 flags
|= SHOW_REDIRECTED
;
499 else if (strneq("overridden", word
, l
))
500 flags
|= SHOW_OVERRIDDEN
;
501 else if (strneq("unchanged", word
, l
))
502 flags
|= SHOW_UNCHANGED
;
503 else if (strneq("extended", word
, l
))
504 flags
|= SHOW_EXTENDED
;
505 else if (strneq("default", word
, l
))
506 flags
|= SHOW_DEFAULTS
;
513 static int parse_argv(int argc
, char *argv
[]) {
516 ARG_NO_PAGER
= 0x100,
521 static const struct option options
[] = {
522 { "help", no_argument
, NULL
, 'h' },
523 { "version", no_argument
, NULL
, ARG_VERSION
},
524 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
525 { "diff", optional_argument
, NULL
, ARG_DIFF
},
526 { "type", required_argument
, NULL
, 't' },
535 while ((c
= getopt_long(argc
, argv
, "ht:", options
, NULL
)) >= 0)
552 f
= parse_flags(optarg
, arg_flags
);
554 log_error("Failed to parse flags field.");
567 b
= parse_boolean(optarg
);
569 log_error("Failed to parse diff boolean.");
581 assert_not_reached("Unhandled option");
587 int main(int argc
, char *argv
[]) {
588 int r
, k
, n_found
= 0;
590 log_parse_environment();
593 r
= parse_argv(argc
, argv
);
598 arg_flags
= SHOW_DEFAULTS
;
601 arg_diff
= !!(arg_flags
& SHOW_OVERRIDDEN
);
603 arg_flags
|= SHOW_OVERRIDDEN
;
605 pager_open(arg_no_pager
, false);
610 for (i
= optind
; i
< argc
; i
++) {
611 path_kill_slashes(argv
[i
]);
613 k
= process_suffix_chop(argv
[i
]);
621 k
= process_suffixes(NULL
);
629 printf("%s%i overridden configuration files found.\n", n_found
? "\n" : "", n_found
);
634 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;