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/>.
26 #include <sys/prctl.h>
29 #include "dirent-util.h"
33 #include "locale-util.h"
36 #include "parse-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "signal-util.h"
40 #include "stat-util.h"
41 #include "string-util.h"
43 #include "terminal-util.h"
46 static const char prefixes
[] =
58 static const char suffixes
[] =
65 "systemd/system-preset\0"
66 "systemd/user-preset\0"
70 static const char have_dropins
[] =
74 static bool arg_no_pager
= false;
75 static int arg_diff
= -1;
79 SHOW_EQUIVALENT
= 1 << 1,
80 SHOW_REDIRECTED
= 1 << 2,
81 SHOW_OVERRIDDEN
= 1 << 3,
82 SHOW_UNCHANGED
= 1 << 4,
83 SHOW_EXTENDED
= 1 << 5,
86 (SHOW_MASKED
| SHOW_EQUIVALENT
| SHOW_REDIRECTED
| SHOW_OVERRIDDEN
| SHOW_EXTENDED
)
89 static void pager_open_if_enabled(void) {
97 static int equivalent(const char *a
, const char *b
) {
98 _cleanup_free_
char *x
= NULL
, *y
= NULL
;
100 x
= canonicalize_file_name(a
);
104 y
= canonicalize_file_name(b
);
108 return path_equal(x
, y
);
111 static int notify_override_masked(const char *top
, const char *bottom
) {
112 if (!(arg_flags
& SHOW_MASKED
))
115 printf("%s%s%s %s %s %s\n",
116 ansi_highlight_red(), "[MASKED]", ansi_normal(),
117 top
, draw_special_char(DRAW_ARROW
), bottom
);
121 static int notify_override_equivalent(const char *top
, const char *bottom
) {
122 if (!(arg_flags
& SHOW_EQUIVALENT
))
125 printf("%s%s%s %s %s %s\n",
126 ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(),
127 top
, draw_special_char(DRAW_ARROW
), bottom
);
131 static int notify_override_redirected(const char *top
, const char *bottom
) {
132 if (!(arg_flags
& SHOW_REDIRECTED
))
135 printf("%s%s%s %s %s %s\n",
136 ansi_highlight(), "[REDIRECTED]", ansi_normal(),
137 top
, draw_special_char(DRAW_ARROW
), bottom
);
141 static int notify_override_overridden(const char *top
, const char *bottom
) {
142 if (!(arg_flags
& SHOW_OVERRIDDEN
))
145 printf("%s%s%s %s %s %s\n",
146 ansi_highlight(), "[OVERRIDDEN]", ansi_normal(),
147 top
, draw_special_char(DRAW_ARROW
), bottom
);
151 static int notify_override_extended(const char *top
, const char *bottom
) {
152 if (!(arg_flags
& SHOW_EXTENDED
))
155 printf("%s%s%s %s %s %s\n",
156 ansi_highlight(), "[EXTENDED]", ansi_normal(),
157 top
, draw_special_char(DRAW_ARROW
), bottom
);
161 static int notify_override_unchanged(const char *f
) {
162 if (!(arg_flags
& SHOW_UNCHANGED
))
165 printf("[UNCHANGED] %s\n", f
);
169 static int found_override(const char *top
, const char *bottom
) {
170 _cleanup_free_
char *dest
= NULL
;
177 if (null_or_empty_path(top
) > 0)
178 return notify_override_masked(top
, bottom
);
180 k
= readlink_malloc(top
, &dest
);
182 if (equivalent(dest
, bottom
) > 0)
183 return notify_override_equivalent(top
, bottom
);
185 return notify_override_redirected(top
, bottom
);
188 k
= notify_override_overridden(top
, bottom
);
198 return log_error_errno(errno
, "Failed to fork off diff: %m");
201 (void) reset_all_signal_handlers();
202 (void) reset_signal_mask();
203 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
205 execlp("diff", "diff", "-us", "--", bottom
, top
, NULL
);
206 log_error_errno(errno
, "Failed to execute diff: %m");
210 wait_for_terminate_and_warn("diff", pid
, false);
216 static int enumerate_dir_d(Hashmap
*top
, Hashmap
*bottom
, Hashmap
*drops
, const char *toppath
, const char *drop
) {
217 _cleanup_free_
char *unit
= NULL
;
218 _cleanup_free_
char *path
= NULL
;
219 _cleanup_strv_free_
char **list
= NULL
;
224 assert(!endswith(drop
, "/"));
226 path
= strjoin(toppath
, "/", drop
, NULL
);
230 log_debug("Looking at %s", path
);
236 c
= strrchr(unit
, '.');
241 r
= get_files_in_directory(path
, &list
);
243 return log_error_errno(r
, "Failed to enumerate %s: %m", path
);
245 STRV_FOREACH(file
, list
) {
251 if (!endswith(*file
, ".conf"))
254 p
= strjoin(path
, "/", *file
, NULL
);
257 d
= p
+ strlen(toppath
) + 1;
259 log_debug("Adding at top: %s %s %s", d
, draw_special_char(DRAW_ARROW
), p
);
260 k
= hashmap_put(top
, d
, p
);
265 d
= p
+ strlen(toppath
) + 1;
266 } else if (k
!= -EEXIST
) {
271 log_debug("Adding at bottom: %s %s %s", d
, draw_special_char(DRAW_ARROW
), p
);
272 free(hashmap_remove(bottom
, d
));
273 k
= hashmap_put(bottom
, d
, p
);
279 h
= hashmap_get(drops
, unit
);
281 h
= hashmap_new(&string_hash_ops
);
284 hashmap_put(drops
, unit
, h
);
294 log_debug("Adding to drops: %s %s %s %s %s",
295 unit
, draw_special_char(DRAW_ARROW
), basename(p
), draw_special_char(DRAW_ARROW
), p
);
296 k
= hashmap_put(h
, basename(p
), p
);
306 static int enumerate_dir(Hashmap
*top
, Hashmap
*bottom
, Hashmap
*drops
, const char *path
, bool dropins
) {
307 _cleanup_closedir_
DIR *d
;
314 log_debug("Looking at %s", path
);
321 log_error_errno(errno
, "Failed to open %s: %m", path
);
335 dirent_ensure_type(d
, de
);
337 if (dropins
&& de
->d_type
== DT_DIR
&& endswith(de
->d_name
, ".d"))
338 enumerate_dir_d(top
, bottom
, drops
, path
, de
->d_name
);
340 if (!dirent_is_file(de
))
343 p
= strjoin(path
, "/", de
->d_name
, NULL
);
347 log_debug("Adding at top: %s %s %s", basename(p
), draw_special_char(DRAW_ARROW
), p
);
348 k
= hashmap_put(top
, basename(p
), p
);
353 } else if (k
!= -EEXIST
) {
358 log_debug("Adding at bottom: %s %s %s", basename(p
), draw_special_char(DRAW_ARROW
), p
);
359 free(hashmap_remove(bottom
, basename(p
)));
360 k
= hashmap_put(bottom
, basename(p
), p
);
368 static int process_suffix(const char *suffix
, const char *onlyprefix
) {
371 Hashmap
*top
, *bottom
, *drops
;
380 assert(!startswith(suffix
, "/"));
381 assert(!strstr(suffix
, "//"));
383 dropins
= nulstr_contains(have_dropins
, suffix
);
385 top
= hashmap_new(&string_hash_ops
);
386 bottom
= hashmap_new(&string_hash_ops
);
387 drops
= hashmap_new(&string_hash_ops
);
388 if (!top
|| !bottom
|| !drops
) {
393 NULSTR_FOREACH(p
, prefixes
) {
394 _cleanup_free_
char *t
= NULL
;
396 t
= strjoin(p
, "/", suffix
, NULL
);
402 k
= enumerate_dir(top
, bottom
, drops
, t
, dropins
);
407 HASHMAP_FOREACH_KEY(f
, key
, top
, i
) {
410 o
= hashmap_get(bottom
, key
);
413 if (!onlyprefix
|| startswith(o
, onlyprefix
)) {
414 if (path_equal(o
, f
)) {
415 notify_override_unchanged(f
);
417 k
= found_override(f
, o
);
425 h
= hashmap_get(drops
, key
);
427 HASHMAP_FOREACH(o
, h
, j
)
428 if (!onlyprefix
|| startswith(o
, onlyprefix
))
429 n_found
+= notify_override_extended(f
, o
);
434 hashmap_free_free(top
);
436 hashmap_free_free(bottom
);
438 HASHMAP_FOREACH_KEY(h
, key
, drops
, i
){
439 hashmap_free_free(hashmap_remove(drops
, key
));
440 hashmap_remove(drops
, key
);
445 return r
< 0 ? r
: n_found
;
448 static int process_suffixes(const char *onlyprefix
) {
452 NULSTR_FOREACH(n
, suffixes
) {
453 r
= process_suffix(n
, onlyprefix
);
462 static int process_suffix_chop(const char *arg
) {
467 if (!path_is_absolute(arg
))
468 return process_suffix(arg
, NULL
);
470 /* Strip prefix from the suffix */
471 NULSTR_FOREACH(p
, prefixes
) {
472 const char *suffix
= startswith(arg
, p
);
474 suffix
+= strspn(suffix
, "/");
476 return process_suffix(suffix
, NULL
);
478 return process_suffixes(arg
);
482 log_error("Invalid suffix specification %s.", arg
);
486 static void help(void) {
487 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
488 "Find overridden configuration files.\n\n"
489 " -h --help Show this help\n"
490 " --version Show package version\n"
491 " --no-pager Do not pipe output into a pager\n"
492 " --diff[=1|0] Show a diff when overridden files differ\n"
493 " -t --type=LIST... Only display a selected set of override types\n"
494 , program_invocation_short_name
);
497 static int parse_flags(const char *flag_str
, int flags
) {
498 const char *word
, *state
;
501 FOREACH_WORD_SEPARATOR(word
, l
, flag_str
, ",", state
) {
502 if (strneq("masked", word
, l
))
503 flags
|= SHOW_MASKED
;
504 else if (strneq ("equivalent", word
, l
))
505 flags
|= SHOW_EQUIVALENT
;
506 else if (strneq("redirected", word
, l
))
507 flags
|= SHOW_REDIRECTED
;
508 else if (strneq("overridden", word
, l
))
509 flags
|= SHOW_OVERRIDDEN
;
510 else if (strneq("unchanged", word
, l
))
511 flags
|= SHOW_UNCHANGED
;
512 else if (strneq("extended", word
, l
))
513 flags
|= SHOW_EXTENDED
;
514 else if (strneq("default", word
, l
))
515 flags
|= SHOW_DEFAULTS
;
522 static int parse_argv(int argc
, char *argv
[]) {
525 ARG_NO_PAGER
= 0x100,
530 static const struct option options
[] = {
531 { "help", no_argument
, NULL
, 'h' },
532 { "version", no_argument
, NULL
, ARG_VERSION
},
533 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
534 { "diff", optional_argument
, NULL
, ARG_DIFF
},
535 { "type", required_argument
, NULL
, 't' },
544 while ((c
= getopt_long(argc
, argv
, "ht:", options
, NULL
)) >= 0)
561 f
= parse_flags(optarg
, arg_flags
);
563 log_error("Failed to parse flags field.");
576 b
= parse_boolean(optarg
);
578 log_error("Failed to parse diff boolean.");
591 assert_not_reached("Unhandled option");
597 int main(int argc
, char *argv
[]) {
601 log_parse_environment();
604 r
= parse_argv(argc
, argv
);
609 arg_flags
= SHOW_DEFAULTS
;
612 arg_diff
= !!(arg_flags
& SHOW_OVERRIDDEN
);
614 arg_flags
|= SHOW_OVERRIDDEN
;
616 pager_open_if_enabled();
621 for (i
= optind
; i
< argc
; i
++) {
622 path_kill_slashes(argv
[i
]);
623 k
= process_suffix_chop(argv
[i
]);
631 k
= process_suffixes(NULL
);
639 printf("%s%i overridden configuration files found.\n",
640 n_found
? "\n" : "", n_found
);
645 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;