]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/delta/delta.c
shutdown: explicitly set a log target in shutdown.c
[thirdparty/systemd.git] / src / delta / delta.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
7e8d5761
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
6096dfd6 6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7e8d5761
LP
7
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.
12
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.
17
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/>.
20***/
21
22#include <errno.h>
7e8d5761 23#include <getopt.h>
3f6fd1ba 24#include <string.h>
ce30c8dc 25#include <sys/prctl.h>
3f6fd1ba 26#include <unistd.h>
7e8d5761 27
b5efdb8a 28#include "alloc-util.h"
a0956174 29#include "dirent-util.h"
3ffd4af2 30#include "fd-util.h"
f4f15635 31#include "fs-util.h"
7e8d5761 32#include "hashmap.h"
8752c575 33#include "locale-util.h"
7e8d5761
LP
34#include "log.h"
35#include "pager.h"
6bedfcbb 36#include "parse-util.h"
3f6fd1ba 37#include "path-util.h"
0b452006 38#include "process-util.h"
ce30c8dc 39#include "signal-util.h"
8fcde012 40#include "stat-util.h"
07630cea 41#include "string-util.h"
3f6fd1ba
LP
42#include "strv.h"
43#include "terminal-util.h"
44#include "util.h"
7e8d5761 45
f939e9a4
ZJS
46static const char prefixes[] =
47 "/etc\0"
48 "/run\0"
49 "/usr/local/lib\0"
50 "/usr/local/share\0"
51 "/usr/lib\0"
52 "/usr/share\0"
349cc4a5 53#if HAVE_SPLIT_USR
f939e9a4
ZJS
54 "/lib\0"
55#endif
56 ;
57
58static const char suffixes[] =
59 "sysctl.d\0"
60 "tmpfiles.d\0"
61 "modules-load.d\0"
62 "binfmt.d\0"
63 "systemd/system\0"
64 "systemd/user\0"
65 "systemd/system-preset\0"
66 "systemd/user-preset\0"
67 "udev/rules.d\0"
68 "modprobe.d\0";
69
70static const char have_dropins[] =
71 "systemd/system\0"
72 "systemd/user\0";
73
7e8d5761 74static bool arg_no_pager = false;
866062b1 75static int arg_diff = -1;
7e8d5761 76
866062b1 77static enum {
4c4e6431 78 SHOW_MASKED = 1 << 0,
c8021373
LP
79 SHOW_EQUIVALENT = 1 << 1,
80 SHOW_REDIRECTED = 1 << 2,
386da858 81 SHOW_OVERRIDDEN = 1 << 3,
4c4e6431 82 SHOW_UNCHANGED = 1 << 4,
0000ce05 83 SHOW_EXTENDED = 1 << 5,
4c4e6431
LP
84
85 SHOW_DEFAULTS =
0000ce05 86 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED)
866062b1 87} arg_flags = 0;
4c4e6431 88
7e8d5761 89static int equivalent(const char *a, const char *b) {
e26970a8 90 _cleanup_free_ char *x = NULL, *y = NULL;
e1873695 91 int r;
7e8d5761 92
c4f4fce7 93 r = chase_symlinks(a, NULL, 0, &x);
e1873695
LP
94 if (r < 0)
95 return r;
7e8d5761 96
c4f4fce7 97 r = chase_symlinks(b, NULL, 0, &y);
e1873695
LP
98 if (r < 0)
99 return r;
7e8d5761 100
e26970a8 101 return path_equal(x, y);
7e8d5761
LP
102}
103
866062b1
LP
104static int notify_override_masked(const char *top, const char *bottom) {
105 if (!(arg_flags & SHOW_MASKED))
807f4645
GN
106 return 0;
107
00a5cc3a 108 printf("%s%s%s %s %s %s\n",
1fc464f6 109 ansi_highlight_red(), "[MASKED]", ansi_normal(),
323b7dc9 110 top, special_glyph(ARROW), bottom);
807f4645
GN
111 return 1;
112}
113
866062b1
LP
114static int notify_override_equivalent(const char *top, const char *bottom) {
115 if (!(arg_flags & SHOW_EQUIVALENT))
807f4645
GN
116 return 0;
117
00a5cc3a 118 printf("%s%s%s %s %s %s\n",
1fc464f6 119 ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(),
323b7dc9 120 top, special_glyph(ARROW), bottom);
807f4645
GN
121 return 1;
122}
123
866062b1
LP
124static int notify_override_redirected(const char *top, const char *bottom) {
125 if (!(arg_flags & SHOW_REDIRECTED))
807f4645
GN
126 return 0;
127
9b6e0ce5 128 printf("%s%s%s %s %s %s\n",
1fc464f6 129 ansi_highlight(), "[REDIRECTED]", ansi_normal(),
323b7dc9 130 top, special_glyph(ARROW), bottom);
807f4645
GN
131 return 1;
132}
133
386da858
NM
134static int notify_override_overridden(const char *top, const char *bottom) {
135 if (!(arg_flags & SHOW_OVERRIDDEN))
807f4645
GN
136 return 0;
137
00a5cc3a 138 printf("%s%s%s %s %s %s\n",
1fc464f6 139 ansi_highlight(), "[OVERRIDDEN]", ansi_normal(),
323b7dc9 140 top, special_glyph(ARROW), bottom);
807f4645
GN
141 return 1;
142}
143
0000ce05
LN
144static int notify_override_extended(const char *top, const char *bottom) {
145 if (!(arg_flags & SHOW_EXTENDED))
146 return 0;
147
00a5cc3a 148 printf("%s%s%s %s %s %s\n",
1fc464f6 149 ansi_highlight(), "[EXTENDED]", ansi_normal(),
323b7dc9 150 top, special_glyph(ARROW), bottom);
0000ce05
LN
151 return 1;
152}
153
866062b1
LP
154static int notify_override_unchanged(const char *f) {
155 if (!(arg_flags & SHOW_UNCHANGED))
807f4645
GN
156 return 0;
157
8e812a23 158 printf("[UNCHANGED] %s\n", f);
807f4645
GN
159 return 1;
160}
161
866062b1 162static int found_override(const char *top, const char *bottom) {
e26970a8 163 _cleanup_free_ char *dest = NULL;
7e8d5761 164 pid_t pid;
4c253ed1 165 int r;
7e8d5761
LP
166
167 assert(top);
168 assert(bottom);
169
8fd57568
ZJS
170 if (null_or_empty_path(top) > 0)
171 return notify_override_masked(top, bottom);
7e8d5761 172
4c253ed1
LP
173 r = readlink_malloc(top, &dest);
174 if (r >= 0) {
7e8d5761 175 if (equivalent(dest, bottom) > 0)
8fd57568 176 return notify_override_equivalent(top, bottom);
7e8d5761 177 else
8fd57568 178 return notify_override_redirected(top, bottom);
7e8d5761
LP
179 }
180
4c253ed1 181 r = notify_override_overridden(top, bottom);
866062b1 182 if (!arg_diff)
4c253ed1 183 return r;
7e8d5761
LP
184
185 putchar('\n');
186
187 fflush(stdout);
188
b6e1fff1 189 r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
4c253ed1 190 if (r < 0)
b6e1fff1 191 return r;
4c253ed1 192 if (r == 0) {
7e8d5761 193 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
56f64d95 194 log_error_errno(errno, "Failed to execute diff: %m");
ce30c8dc 195 _exit(EXIT_FAILURE);
7e8d5761
LP
196 }
197
7d4904fe 198 (void) wait_for_terminate_and_check("diff", pid, WAIT_LOG_ABNORMAL);
7e8d5761
LP
199 putchar('\n');
200
4c253ed1 201 return r;
7e8d5761
LP
202}
203
9f5ebb8a
ZJS
204static int enumerate_dir_d(
205 OrderedHashmap *top,
206 OrderedHashmap *bottom,
207 OrderedHashmap *drops,
208 const char *toppath, const char *drop) {
209
f939e9a4 210 _cleanup_free_ char *unit = NULL;
0000ce05
LN
211 _cleanup_free_ char *path = NULL;
212 _cleanup_strv_free_ char **list = NULL;
213 char **file;
214 char *c;
215 int r;
216
f939e9a4
ZJS
217 assert(!endswith(drop, "/"));
218
605405c6 219 path = strjoin(toppath, "/", drop);
0000ce05
LN
220 if (!path)
221 return -ENOMEM;
222
f939e9a4 223 log_debug("Looking at %s", path);
0000ce05 224
f939e9a4
ZJS
225 unit = strdup(drop);
226 if (!unit)
0000ce05
LN
227 return -ENOMEM;
228
f939e9a4 229 c = strrchr(unit, '.');
0000ce05
LN
230 if (!c)
231 return -EINVAL;
232 *c = 0;
233
234 r = get_files_in_directory(path, &list);
23bbb0de
MS
235 if (r < 0)
236 return log_error_errno(r, "Failed to enumerate %s: %m", path);
0000ce05 237
9f5ebb8a
ZJS
238 strv_sort(list);
239
0000ce05 240 STRV_FOREACH(file, list) {
9f5ebb8a 241 OrderedHashmap *h;
0000ce05
LN
242 int k;
243 char *p;
244 char *d;
245
246 if (!endswith(*file, ".conf"))
247 continue;
248
605405c6 249 p = strjoin(path, "/", *file);
0000ce05
LN
250 if (!p)
251 return -ENOMEM;
f939e9a4 252 d = p + strlen(toppath) + 1;
0000ce05 253
323b7dc9 254 log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p);
9f5ebb8a 255 k = ordered_hashmap_put(top, d, p);
0000ce05
LN
256 if (k >= 0) {
257 p = strdup(p);
258 if (!p)
259 return -ENOMEM;
f939e9a4 260 d = p + strlen(toppath) + 1;
0000ce05
LN
261 } else if (k != -EEXIST) {
262 free(p);
263 return k;
264 }
265
323b7dc9 266 log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p);
9f5ebb8a
ZJS
267 free(ordered_hashmap_remove(bottom, d));
268 k = ordered_hashmap_put(bottom, d, p);
0000ce05
LN
269 if (k < 0) {
270 free(p);
271 return k;
272 }
273
9f5ebb8a 274 h = ordered_hashmap_get(drops, unit);
0000ce05 275 if (!h) {
9f5ebb8a 276 h = ordered_hashmap_new(&string_hash_ops);
0000ce05
LN
277 if (!h)
278 return -ENOMEM;
9f5ebb8a 279 ordered_hashmap_put(drops, unit, h);
f939e9a4
ZJS
280 unit = strdup(unit);
281 if (!unit)
0000ce05
LN
282 return -ENOMEM;
283 }
284
285 p = strdup(p);
286 if (!p)
287 return -ENOMEM;
288
00a5cc3a 289 log_debug("Adding to drops: %s %s %s %s %s",
323b7dc9 290 unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p);
9f5ebb8a 291 k = ordered_hashmap_put(h, basename(p), p);
0000ce05
LN
292 if (k < 0) {
293 free(p);
294 if (k != -EEXIST)
295 return k;
296 }
297 }
298 return 0;
299}
300
9f5ebb8a
ZJS
301static int enumerate_dir(
302 OrderedHashmap *top,
303 OrderedHashmap *bottom,
304 OrderedHashmap *drops,
305 const char *path, bool dropins) {
306
307 _cleanup_closedir_ DIR *d = NULL;
8fb3f009 308 struct dirent *de;
9f5ebb8a
ZJS
309 _cleanup_strv_free_ char **files = NULL, **dirs = NULL;
310 size_t n_files = 0, allocated_files = 0, n_dirs = 0, allocated_dirs = 0;
311 char **t;
312 int r;
7e8d5761
LP
313
314 assert(top);
315 assert(bottom);
0000ce05 316 assert(drops);
7e8d5761
LP
317 assert(path);
318
f939e9a4
ZJS
319 log_debug("Looking at %s", path);
320
7e8d5761
LP
321 d = opendir(path);
322 if (!d) {
323 if (errno == ENOENT)
324 return 0;
325
e1427b13 326 return log_error_errno(errno, "Failed to open %s: %m", path);
7e8d5761
LP
327 }
328
8fb3f009 329 FOREACH_DIRENT_ALL(de, d, return -errno) {
277f2f75
LN
330 dirent_ensure_type(d, de);
331
9f5ebb8a
ZJS
332 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d")) {
333 if (!GREEDY_REALLOC0(dirs, allocated_dirs, n_dirs + 2))
334 return -ENOMEM;
335
336 dirs[n_dirs] = strdup(de->d_name);
337 if (!dirs[n_dirs])
338 return -ENOMEM;
339 n_dirs ++;
340 }
0000ce05 341
7e8d5761
LP
342 if (!dirent_is_file(de))
343 continue;
344
9f5ebb8a
ZJS
345 if (!GREEDY_REALLOC0(files, allocated_files, n_files + 2))
346 return -ENOMEM;
347
348 files[n_files] = strdup(de->d_name);
349 if (!files[n_files])
350 return -ENOMEM;
351 n_files ++;
352 }
353
354 strv_sort(dirs);
355 strv_sort(files);
356
357 STRV_FOREACH(t, dirs) {
358 r = enumerate_dir_d(top, bottom, drops, path, *t);
359 if (r < 0)
360 return r;
361 }
362
363 STRV_FOREACH(t, files) {
364 _cleanup_free_ char *p = NULL;
365
366 p = strjoin(path, "/", *t);
e26970a8
TA
367 if (!p)
368 return -ENOMEM;
7e8d5761 369
323b7dc9 370 log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p);
9f5ebb8a
ZJS
371 r = ordered_hashmap_put(top, basename(p), p);
372 if (r >= 0) {
7e8d5761 373 p = strdup(p);
e26970a8
TA
374 if (!p)
375 return -ENOMEM;
9f5ebb8a
ZJS
376 } else if (r != -EEXIST)
377 return r;
7e8d5761 378
323b7dc9 379 log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(ARROW), p);
9f5ebb8a
ZJS
380 free(ordered_hashmap_remove(bottom, basename(p)));
381 r = ordered_hashmap_put(bottom, basename(p), p);
382 if (r < 0)
383 return r;
384 p = NULL;
7e8d5761 385 }
9f5ebb8a 386
8fb3f009 387 return 0;
7e8d5761
LP
388}
389
4cc893e7 390static bool should_skip_path(const char *prefix, const char *suffix) {
349cc4a5 391#if HAVE_SPLIT_USR
b05422a8 392 _cleanup_free_ char *target = NULL;
4cc893e7
FB
393 const char *p;
394 char *dirname;
b05422a8 395
4cc893e7 396 dirname = strjoina(prefix, "/", suffix);
b05422a8 397
4cc893e7
FB
398 if (chase_symlinks(dirname, NULL, 0, &target) < 0)
399 return false;
400
401 NULSTR_FOREACH(p, prefixes) {
402 if (path_startswith(dirname, p))
403 continue;
404
405 if (path_equal(target, strjoina(p, "/", suffix))) {
406 log_debug("%s redirects to %s, skipping.", dirname, target);
407 return true;
408 }
409 }
b05422a8 410#endif
4cc893e7 411 return false;
b05422a8
FS
412}
413
6096dfd6 414static int process_suffix(const char *suffix, const char *onlyprefix) {
7e8d5761
LP
415 const char *p;
416 char *f;
9f5ebb8a
ZJS
417 OrderedHashmap *top, *bottom, *drops;
418 OrderedHashmap *h;
0000ce05 419 char *key;
7e8d5761 420 int r = 0, k;
0000ce05 421 Iterator i, j;
7e8d5761 422 int n_found = 0;
f939e9a4 423 bool dropins;
7e8d5761 424
7e8d5761 425 assert(suffix);
f939e9a4
ZJS
426 assert(!startswith(suffix, "/"));
427 assert(!strstr(suffix, "//"));
7e8d5761 428
f939e9a4 429 dropins = nulstr_contains(have_dropins, suffix);
7e8d5761 430
9f5ebb8a
ZJS
431 top = ordered_hashmap_new(&string_hash_ops);
432 bottom = ordered_hashmap_new(&string_hash_ops);
433 drops = ordered_hashmap_new(&string_hash_ops);
f939e9a4 434 if (!top || !bottom || !drops) {
0000ce05
LN
435 r = -ENOMEM;
436 goto finish;
437 }
438
7e8d5761 439 NULSTR_FOREACH(p, prefixes) {
e26970a8 440 _cleanup_free_ char *t = NULL;
b05422a8 441
4cc893e7 442 if (should_skip_path(p, suffix))
b05422a8 443 continue;
7e8d5761 444
605405c6 445 t = strjoin(p, "/", suffix);
7e8d5761
LP
446 if (!t) {
447 r = -ENOMEM;
448 goto finish;
449 }
450
0000ce05 451 k = enumerate_dir(top, bottom, drops, t, dropins);
f939e9a4 452 if (r == 0)
7e8d5761 453 r = k;
7e8d5761
LP
454 }
455
9f5ebb8a 456 ORDERED_HASHMAP_FOREACH_KEY(f, key, top, i) {
7e8d5761
LP
457 char *o;
458
9f5ebb8a 459 o = ordered_hashmap_get(bottom, key);
7e8d5761
LP
460 assert(o);
461
6096dfd6
ZJS
462 if (!onlyprefix || startswith(o, onlyprefix)) {
463 if (path_equal(o, f)) {
464 notify_override_unchanged(f);
465 } else {
466 k = found_override(f, o);
467 if (k < 0)
468 r = k;
469 else
470 n_found += k;
471 }
807f4645 472 }
7e8d5761 473
9f5ebb8a 474 h = ordered_hashmap_get(drops, key);
0000ce05 475 if (h)
9f5ebb8a 476 ORDERED_HASHMAP_FOREACH(o, h, j)
6096dfd6
ZJS
477 if (!onlyprefix || startswith(o, onlyprefix))
478 n_found += notify_override_extended(f, o);
7e8d5761
LP
479 }
480
481finish:
9f5ebb8a
ZJS
482 ordered_hashmap_free_free(top);
483 ordered_hashmap_free_free(bottom);
82376245 484
9f5ebb8a
ZJS
485 ORDERED_HASHMAP_FOREACH_KEY(h, key, drops, i) {
486 ordered_hashmap_free_free(ordered_hashmap_remove(drops, key));
487 ordered_hashmap_remove(drops, key);
82376245 488 free(key);
0000ce05 489 }
9f5ebb8a 490 ordered_hashmap_free(drops);
82376245 491
7e8d5761
LP
492 return r < 0 ? r : n_found;
493}
494
6096dfd6
ZJS
495static int process_suffixes(const char *onlyprefix) {
496 const char *n;
497 int n_found = 0, r;
498
499 NULSTR_FOREACH(n, suffixes) {
500 r = process_suffix(n, onlyprefix);
501 if (r < 0)
502 return r;
82376245
LP
503
504 n_found += r;
6096dfd6 505 }
82376245 506
6096dfd6
ZJS
507 return n_found;
508}
509
510static int process_suffix_chop(const char *arg) {
7e8d5761
LP
511 const char *p;
512
6096dfd6 513 assert(arg);
7e8d5761 514
6096dfd6
ZJS
515 if (!path_is_absolute(arg))
516 return process_suffix(arg, NULL);
7e8d5761
LP
517
518 /* Strip prefix from the suffix */
519 NULSTR_FOREACH(p, prefixes) {
82376245
LP
520 const char *suffix;
521
522 suffix = startswith(arg, p);
6096dfd6 523 if (suffix) {
7e8d5761 524 suffix += strspn(suffix, "/");
6096dfd6 525 if (*suffix)
a9d041fa 526 return process_suffix(suffix, p);
6096dfd6
ZJS
527 else
528 return process_suffixes(arg);
7e8d5761
LP
529 }
530 }
531
6096dfd6 532 log_error("Invalid suffix specification %s.", arg);
7e8d5761
LP
533 return -EINVAL;
534}
535
601185b4 536static void help(void) {
7e8d5761
LP
537 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
538 "Find overridden configuration files.\n\n"
539 " -h --help Show this help\n"
540 " --version Show package version\n"
807f4645 541 " --no-pager Do not pipe output into a pager\n"
386da858 542 " --diff[=1|0] Show a diff when overridden files differ\n"
601185b4
ZJS
543 " -t --type=LIST... Only display a selected set of override types\n"
544 , program_invocation_short_name);
7e8d5761
LP
545}
546
866062b1 547static int parse_flags(const char *flag_str, int flags) {
a2a5291b 548 const char *word, *state;
807f4645
GN
549 size_t l;
550
d0a2e1c3 551 FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) {
a2a5291b 552 if (strneq("masked", word, l))
807f4645 553 flags |= SHOW_MASKED;
a2a5291b 554 else if (strneq ("equivalent", word, l))
c8021373 555 flags |= SHOW_EQUIVALENT;
a2a5291b 556 else if (strneq("redirected", word, l))
c8021373 557 flags |= SHOW_REDIRECTED;
a2a5291b 558 else if (strneq("overridden", word, l))
386da858 559 flags |= SHOW_OVERRIDDEN;
a2a5291b 560 else if (strneq("unchanged", word, l))
807f4645 561 flags |= SHOW_UNCHANGED;
a2a5291b 562 else if (strneq("extended", word, l))
0000ce05 563 flags |= SHOW_EXTENDED;
a2a5291b 564 else if (strneq("default", word, l))
807f4645 565 flags |= SHOW_DEFAULTS;
866062b1
LP
566 else
567 return -EINVAL;
807f4645
GN
568 }
569 return flags;
570}
571
866062b1 572static int parse_argv(int argc, char *argv[]) {
7e8d5761
LP
573
574 enum {
575 ARG_NO_PAGER = 0x100,
807f4645 576 ARG_DIFF,
7e8d5761
LP
577 ARG_VERSION
578 };
579
580 static const struct option options[] = {
581 { "help", no_argument, NULL, 'h' },
582 { "version", no_argument, NULL, ARG_VERSION },
583 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
807f4645
GN
584 { "diff", optional_argument, NULL, ARG_DIFF },
585 { "type", required_argument, NULL, 't' },
eb9da376 586 {}
7e8d5761
LP
587 };
588
589 int c;
590
591 assert(argc >= 1);
592 assert(argv);
593
601185b4 594 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0)
7e8d5761
LP
595
596 switch (c) {
597
598 case 'h':
599 help();
600 return 0;
601
602 case ARG_VERSION:
3f6fd1ba 603 return version();
7e8d5761
LP
604
605 case ARG_NO_PAGER:
606 arg_no_pager = true;
607 break;
608
866062b1
LP
609 case 't': {
610 int f;
611 f = parse_flags(optarg, arg_flags);
612 if (f < 0) {
613 log_error("Failed to parse flags field.");
807f4645 614 return -EINVAL;
866062b1
LP
615 }
616 arg_flags = f;
807f4645 617 break;
866062b1 618 }
807f4645
GN
619
620 case ARG_DIFF:
866062b1
LP
621 if (!optarg)
622 arg_diff = 1;
623 else {
624 int b;
625
626 b = parse_boolean(optarg);
627 if (b < 0) {
628 log_error("Failed to parse diff boolean.");
629 return -EINVAL;
82376245
LP
630 }
631
632 arg_diff = b;
807f4645
GN
633 }
634 break;
635
eb9da376 636 case '?':
7e8d5761 637 return -EINVAL;
eb9da376
LP
638
639 default:
640 assert_not_reached("Unhandled option");
7e8d5761 641 }
7e8d5761
LP
642
643 return 1;
644}
645
646int main(int argc, char *argv[]) {
82376245 647 int r, k, n_found = 0;
7e8d5761
LP
648
649 log_parse_environment();
650 log_open();
651
866062b1 652 r = parse_argv(argc, argv);
7e8d5761
LP
653 if (r <= 0)
654 goto finish;
655
866062b1
LP
656 if (arg_flags == 0)
657 arg_flags = SHOW_DEFAULTS;
658
659 if (arg_diff < 0)
386da858 660 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
866062b1 661 else if (arg_diff)
386da858 662 arg_flags |= SHOW_OVERRIDDEN;
807f4645 663
ea4b98e6 664 pager_open(arg_no_pager, false);
7e8d5761
LP
665
666 if (optind < argc) {
667 int i;
668
669 for (i = optind; i < argc; i++) {
f939e9a4 670 path_kill_slashes(argv[i]);
82376245 671
f939e9a4 672 k = process_suffix_chop(argv[i]);
7e8d5761
LP
673 if (k < 0)
674 r = k;
675 else
676 n_found += k;
677 }
678
679 } else {
6096dfd6
ZJS
680 k = process_suffixes(NULL);
681 if (k < 0)
682 r = k;
683 else
684 n_found += k;
7e8d5761
LP
685 }
686
687 if (r >= 0)
82376245 688 printf("%s%i overridden configuration files found.\n", n_found ? "\n" : "", n_found);
7e8d5761
LP
689
690finish:
691 pager_close();
692
693 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
694}