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