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