]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/delta/delta.c
ci: enable arm64 runner for build/unit jobs
[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);
0000ce05
LN
289 }
290 return 0;
291}
292
9f5ebb8a 293static int enumerate_dir(
45160228
YW
294 OrderedHashmap **top,
295 OrderedHashmap **bottom,
296 OrderedHashmap **drops,
9f5ebb8a
ZJS
297 const char *path, bool dropins) {
298
299 _cleanup_closedir_ DIR *d = NULL;
9f5ebb8a 300 _cleanup_strv_free_ char **files = NULL, **dirs = NULL;
319a4f4b 301 size_t n_files = 0, n_dirs = 0;
9f5ebb8a 302 int r;
7e8d5761
LP
303
304 assert(top);
305 assert(bottom);
0000ce05 306 assert(drops);
7e8d5761
LP
307 assert(path);
308
f939e9a4
ZJS
309 log_debug("Looking at %s", path);
310
7e8d5761
LP
311 d = opendir(path);
312 if (!d) {
313 if (errno == ENOENT)
314 return 0;
315
e1427b13 316 return log_error_errno(errno, "Failed to open %s: %m", path);
7e8d5761
LP
317 }
318
8fb3f009 319 FOREACH_DIRENT_ALL(de, d, return -errno) {
9f5ebb8a 320 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d")) {
319a4f4b 321 if (!GREEDY_REALLOC0(dirs, n_dirs + 2))
9f5ebb8a
ZJS
322 return -ENOMEM;
323
324 dirs[n_dirs] = strdup(de->d_name);
325 if (!dirs[n_dirs])
326 return -ENOMEM;
b3a9d980 327 n_dirs++;
9f5ebb8a 328 }
0000ce05 329
7e8d5761
LP
330 if (!dirent_is_file(de))
331 continue;
332
319a4f4b 333 if (!GREEDY_REALLOC0(files, n_files + 2))
9f5ebb8a
ZJS
334 return -ENOMEM;
335
336 files[n_files] = strdup(de->d_name);
337 if (!files[n_files])
338 return -ENOMEM;
b3a9d980 339 n_files++;
9f5ebb8a
ZJS
340 }
341
342 strv_sort(dirs);
343 strv_sort(files);
344
345 STRV_FOREACH(t, dirs) {
346 r = enumerate_dir_d(top, bottom, drops, path, *t);
347 if (r < 0)
348 return r;
349 }
350
351 STRV_FOREACH(t, files) {
59829bb3
YW
352 log_debug("Adding at top: %s %s %s/%s", *t, glyph(GLYPH_ARROW_RIGHT), path, *t);
353 r = path_put(top, path, *t, /* override = */ false);
354 if (r < 0)
9f5ebb8a 355 return r;
7e8d5761 356
59829bb3
YW
357 log_debug("Adding at bottom: %s %s %s/%s", *t, glyph(GLYPH_ARROW_RIGHT), path, *t);
358 r = path_put(bottom, path, *t, /* override = */ true);
9f5ebb8a
ZJS
359 if (r < 0)
360 return r;
7e8d5761 361 }
9f5ebb8a 362
8fb3f009 363 return 0;
7e8d5761
LP
364}
365
6096dfd6 366static int process_suffix(const char *suffix, const char *onlyprefix) {
45160228 367 int r, ret = 0;
7e8d5761 368
7e8d5761 369 assert(suffix);
f939e9a4
ZJS
370 assert(!startswith(suffix, "/"));
371 assert(!strstr(suffix, "//"));
7e8d5761 372
45160228 373 bool dropins = nulstr_contains(have_dropins, suffix);
0000ce05 374
45160228 375 _cleanup_ordered_hashmap_free_ OrderedHashmap *top = NULL, *bottom = NULL, *drops = NULL;
7e8d5761 376 NULSTR_FOREACH(p, prefixes) {
e26970a8 377 _cleanup_free_ char *t = NULL;
b05422a8 378
657ee2d8 379 t = path_join(p, suffix);
45160228
YW
380 if (!t)
381 return -ENOMEM;
7e8d5761 382
45160228 383 RET_GATHER(ret, enumerate_dir(&top, &bottom, &drops, t, dropins));
7e8d5761
LP
384 }
385
45160228
YW
386 int n_found = 0;
387 char *f, *key;
90e74a66 388 ORDERED_HASHMAP_FOREACH_KEY(f, key, top) {
7e8d5761
LP
389 char *o;
390
9f5ebb8a 391 o = ordered_hashmap_get(bottom, key);
7e8d5761
LP
392 assert(o);
393
6096dfd6
ZJS
394 if (!onlyprefix || startswith(o, onlyprefix)) {
395 if (path_equal(o, f)) {
396 notify_override_unchanged(f);
397 } else {
45160228
YW
398 r = found_override(f, o);
399 if (r < 0)
400 RET_GATHER(ret, r);
6096dfd6 401 else
45160228 402 n_found += r;
6096dfd6 403 }
807f4645 404 }
7e8d5761 405
45160228 406 OrderedHashmap *h = ordered_hashmap_get(drops, key);
0000ce05 407 if (h)
90e74a66 408 ORDERED_HASHMAP_FOREACH(o, h)
6096dfd6
ZJS
409 if (!onlyprefix || startswith(o, onlyprefix))
410 n_found += notify_override_extended(f, o);
7e8d5761
LP
411 }
412
45160228 413 return ret < 0 ? ret : n_found;
7e8d5761
LP
414}
415
6096dfd6 416static int process_suffixes(const char *onlyprefix) {
6096dfd6
ZJS
417 int n_found = 0, r;
418
419 NULSTR_FOREACH(n, suffixes) {
420 r = process_suffix(n, onlyprefix);
421 if (r < 0)
422 return r;
82376245
LP
423
424 n_found += r;
6096dfd6 425 }
82376245 426
6096dfd6
ZJS
427 return n_found;
428}
429
430static int process_suffix_chop(const char *arg) {
6096dfd6 431 assert(arg);
7e8d5761 432
6096dfd6
ZJS
433 if (!path_is_absolute(arg))
434 return process_suffix(arg, NULL);
7e8d5761
LP
435
436 /* Strip prefix from the suffix */
437 NULSTR_FOREACH(p, prefixes) {
82376245
LP
438 const char *suffix;
439
440 suffix = startswith(arg, p);
6096dfd6 441 if (suffix) {
7e8d5761 442 suffix += strspn(suffix, "/");
6096dfd6 443 if (*suffix)
a9d041fa 444 return process_suffix(suffix, p);
6096dfd6
ZJS
445 else
446 return process_suffixes(arg);
7e8d5761
LP
447 }
448 }
449
baaa35ad
ZJS
450 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
451 "Invalid suffix specification %s.", arg);
7e8d5761
LP
452}
453
37ec0fdd
LP
454static int help(void) {
455 _cleanup_free_ char *link = NULL;
456 int r;
457
458 r = terminal_urlify_man("systemd-delta", "1", &link);
459 if (r < 0)
460 return log_oom();
461
7e8d5761
LP
462 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
463 "Find overridden configuration files.\n\n"
464 " -h --help Show this help\n"
465 " --version Show package version\n"
807f4645 466 " --no-pager Do not pipe output into a pager\n"
386da858 467 " --diff[=1|0] Show a diff when overridden files differ\n"
601185b4 468 " -t --type=LIST... Only display a selected set of override types\n"
bc556335
DDM
469 "\nSee the %s for details.\n",
470 program_invocation_short_name,
471 link);
37ec0fdd
LP
472
473 return 0;
7e8d5761
LP
474}
475
866062b1 476static int parse_flags(const char *flag_str, int flags) {
cc24f0b8
ZJS
477 for (;;) {
478 _cleanup_free_ char *word = NULL;
479 int r;
807f4645 480
cc24f0b8
ZJS
481 r = extract_first_word(&flag_str, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
482 if (r < 0)
483 return r;
484 if (r == 0)
485 return flags;
486
487 if (streq(word, "masked"))
807f4645 488 flags |= SHOW_MASKED;
cc24f0b8 489 else if (streq(word, "equivalent"))
c8021373 490 flags |= SHOW_EQUIVALENT;
cc24f0b8 491 else if (streq(word, "redirected"))
c8021373 492 flags |= SHOW_REDIRECTED;
cc24f0b8 493 else if (streq(word, "overridden"))
386da858 494 flags |= SHOW_OVERRIDDEN;
cc24f0b8 495 else if (streq(word, "unchanged"))
807f4645 496 flags |= SHOW_UNCHANGED;
cc24f0b8 497 else if (streq(word, "extended"))
0000ce05 498 flags |= SHOW_EXTENDED;
cc24f0b8 499 else if (streq(word, "default"))
807f4645 500 flags |= SHOW_DEFAULTS;
866062b1
LP
501 else
502 return -EINVAL;
807f4645 503 }
807f4645
GN
504}
505
866062b1 506static int parse_argv(int argc, char *argv[]) {
7e8d5761
LP
507
508 enum {
509 ARG_NO_PAGER = 0x100,
807f4645 510 ARG_DIFF,
7e8d5761
LP
511 ARG_VERSION
512 };
513
514 static const struct option options[] = {
515 { "help", no_argument, NULL, 'h' },
516 { "version", no_argument, NULL, ARG_VERSION },
517 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
807f4645
GN
518 { "diff", optional_argument, NULL, ARG_DIFF },
519 { "type", required_argument, NULL, 't' },
eb9da376 520 {}
7e8d5761
LP
521 };
522
c3470872 523 int c, r;
7e8d5761
LP
524
525 assert(argc >= 1);
526 assert(argv);
527
601185b4 528 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0)
7e8d5761
LP
529
530 switch (c) {
531
532 case 'h':
37ec0fdd 533 return help();
7e8d5761
LP
534
535 case ARG_VERSION:
3f6fd1ba 536 return version();
7e8d5761
LP
537
538 case ARG_NO_PAGER:
0221d68a 539 arg_pager_flags |= PAGER_DISABLE;
7e8d5761
LP
540 break;
541
866062b1
LP
542 case 't': {
543 int f;
544 f = parse_flags(optarg, arg_flags);
baaa35ad
ZJS
545 if (f < 0)
546 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
547 "Failed to parse flags field.");
866062b1 548 arg_flags = f;
807f4645 549 break;
866062b1 550 }
807f4645
GN
551
552 case ARG_DIFF:
c3470872
ZJS
553 r = parse_boolean_argument("--diff", optarg, NULL);
554 if (r < 0)
555 return r;
556 arg_diff = r;
807f4645
GN
557 break;
558
eb9da376 559 case '?':
7e8d5761 560 return -EINVAL;
eb9da376
LP
561
562 default:
04499a70 563 assert_not_reached();
7e8d5761 564 }
7e8d5761
LP
565
566 return 1;
567}
568
7280b076 569static int run(int argc, char *argv[]) {
82376245 570 int r, k, n_found = 0;
7e8d5761 571
d2acb93d 572 log_setup();
7e8d5761 573
866062b1 574 r = parse_argv(argc, argv);
7e8d5761 575 if (r <= 0)
7280b076 576 return r;
7e8d5761 577
866062b1
LP
578 if (arg_flags == 0)
579 arg_flags = SHOW_DEFAULTS;
580
581 if (arg_diff < 0)
386da858 582 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
866062b1 583 else if (arg_diff)
386da858 584 arg_flags |= SHOW_OVERRIDDEN;
807f4645 585
384c2c32 586 pager_open(arg_pager_flags);
7e8d5761
LP
587
588 if (optind < argc) {
d01481ca 589 for (int i = optind; i < argc; i++) {
4ff361cc 590 path_simplify(argv[i]);
82376245 591
f939e9a4 592 k = process_suffix_chop(argv[i]);
7e8d5761
LP
593 if (k < 0)
594 r = k;
595 else
596 n_found += k;
597 }
598
599 } else {
6096dfd6
ZJS
600 k = process_suffixes(NULL);
601 if (k < 0)
602 r = k;
603 else
604 n_found += k;
7e8d5761
LP
605 }
606
607 if (r >= 0)
82376245 608 printf("%s%i overridden configuration files found.\n", n_found ? "\n" : "", n_found);
7280b076 609 return r;
7e8d5761 610}
7280b076
ZJS
611
612DEFINE_MAIN_FUNCTION(run);