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