]> git.proxmox.com Git - systemd.git/blame - src/delta/delta.c
Imported Upstream version 218
[systemd.git] / src / delta / delta.c
CommitLineData
663996b3
MS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
60f067b4 7 Copyright 2013 Zbigniew Jędrzejewski-Szmek
663996b3
MS
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
23#include <errno.h>
24#include <assert.h>
25#include <string.h>
26#include <unistd.h>
27#include <getopt.h>
28
29#include "hashmap.h"
30#include "util.h"
31#include "path-util.h"
32#include "log.h"
33#include "pager.h"
34#include "build.h"
14228c0d 35#include "strv.h"
663996b3 36
60f067b4
JS
37static const char prefixes[] =
38 "/etc\0"
39 "/run\0"
40 "/usr/local/lib\0"
41 "/usr/local/share\0"
42 "/usr/lib\0"
43 "/usr/share\0"
44#ifdef HAVE_SPLIT_USR
45 "/lib\0"
46#endif
47 ;
48
49static const char suffixes[] =
50 "sysctl.d\0"
51 "tmpfiles.d\0"
52 "modules-load.d\0"
53 "binfmt.d\0"
54 "systemd/system\0"
55 "systemd/user\0"
56 "systemd/system-preset\0"
57 "systemd/user-preset\0"
58 "udev/rules.d\0"
59 "modprobe.d\0";
60
61static const char have_dropins[] =
62 "systemd/system\0"
63 "systemd/user\0";
64
663996b3
MS
65static bool arg_no_pager = false;
66static int arg_diff = -1;
67
68static enum {
69 SHOW_MASKED = 1 << 0,
70 SHOW_EQUIVALENT = 1 << 1,
71 SHOW_REDIRECTED = 1 << 2,
72 SHOW_OVERRIDDEN = 1 << 3,
73 SHOW_UNCHANGED = 1 << 4,
14228c0d 74 SHOW_EXTENDED = 1 << 5,
663996b3
MS
75
76 SHOW_DEFAULTS =
14228c0d 77 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED)
663996b3
MS
78} arg_flags = 0;
79
60f067b4
JS
80static void pager_open_if_enabled(void) {
81
82 if (arg_no_pager)
83 return;
84
85 pager_open(false);
86}
87
663996b3
MS
88static int equivalent(const char *a, const char *b) {
89 _cleanup_free_ char *x = NULL, *y = NULL;
90
91 x = canonicalize_file_name(a);
92 if (!x)
93 return -errno;
94
95 y = canonicalize_file_name(b);
96 if (!y)
97 return -errno;
98
99 return path_equal(x, y);
100}
101
102static int notify_override_masked(const char *top, const char *bottom) {
103 if (!(arg_flags & SHOW_MASKED))
104 return 0;
105
60f067b4
JS
106 printf("%s%s%s %s %s %s\n",
107 ansi_highlight_red(), "[MASKED]", ansi_highlight_off(),
108 top, draw_special_char(DRAW_ARROW), bottom);
663996b3
MS
109 return 1;
110}
111
112static int notify_override_equivalent(const char *top, const char *bottom) {
113 if (!(arg_flags & SHOW_EQUIVALENT))
114 return 0;
115
60f067b4
JS
116 printf("%s%s%s %s %s %s\n",
117 ansi_highlight_green(), "[EQUIVALENT]", ansi_highlight_off(),
118 top, draw_special_char(DRAW_ARROW), bottom);
663996b3
MS
119 return 1;
120}
121
122static int notify_override_redirected(const char *top, const char *bottom) {
123 if (!(arg_flags & SHOW_REDIRECTED))
124 return 0;
125
60f067b4
JS
126 printf("%s%s%s %s %s %s\n",
127 ansi_highlight(), "[REDIRECTED]", ansi_highlight_off(),
128 top, draw_special_char(DRAW_ARROW), bottom);
663996b3
MS
129 return 1;
130}
131
132static int notify_override_overridden(const char *top, const char *bottom) {
133 if (!(arg_flags & SHOW_OVERRIDDEN))
134 return 0;
135
60f067b4
JS
136 printf("%s%s%s %s %s %s\n",
137 ansi_highlight(), "[OVERRIDDEN]", ansi_highlight_off(),
138 top, draw_special_char(DRAW_ARROW), bottom);
14228c0d
MB
139 return 1;
140}
141
142static int notify_override_extended(const char *top, const char *bottom) {
143 if (!(arg_flags & SHOW_EXTENDED))
144 return 0;
145
60f067b4
JS
146 printf("%s%s%s %s %s %s\n",
147 ansi_highlight(), "[EXTENDED]", ansi_highlight_off(),
148 top, draw_special_char(DRAW_ARROW), bottom);
663996b3
MS
149 return 1;
150}
151
152static int notify_override_unchanged(const char *f) {
153 if (!(arg_flags & SHOW_UNCHANGED))
154 return 0;
155
156 printf("[UNCHANGED] %s\n", f);
157 return 1;
158}
159
160static int found_override(const char *top, const char *bottom) {
161 _cleanup_free_ char *dest = NULL;
162 int k;
163 pid_t pid;
164
165 assert(top);
166 assert(bottom);
167
14228c0d
MB
168 if (null_or_empty_path(top) > 0)
169 return notify_override_masked(top, bottom);
663996b3
MS
170
171 k = readlink_malloc(top, &dest);
172 if (k >= 0) {
173 if (equivalent(dest, bottom) > 0)
14228c0d 174 return notify_override_equivalent(top, bottom);
663996b3 175 else
14228c0d 176 return notify_override_redirected(top, bottom);
663996b3
MS
177 }
178
14228c0d 179 k = notify_override_overridden(top, bottom);
663996b3 180 if (!arg_diff)
14228c0d 181 return k;
663996b3
MS
182
183 putchar('\n');
184
185 fflush(stdout);
186
187 pid = fork();
f47781d8
MP
188 if (pid < 0)
189 return log_error_errno(errno, "Failed to fork off diff: %m");
190 else if (pid == 0) {
663996b3 191 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
f47781d8 192 log_error_errno(errno, "Failed to execute diff: %m");
663996b3
MS
193 _exit(1);
194 }
195
f47781d8 196 wait_for_terminate_and_warn("diff", pid, false);
663996b3
MS
197 putchar('\n');
198
14228c0d
MB
199 return k;
200}
201
202static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) {
60f067b4 203 _cleanup_free_ char *unit = NULL;
14228c0d
MB
204 _cleanup_free_ char *path = NULL;
205 _cleanup_strv_free_ char **list = NULL;
206 char **file;
207 char *c;
208 int r;
209
60f067b4
JS
210 assert(!endswith(drop, "/"));
211
14228c0d
MB
212 path = strjoin(toppath, "/", drop, NULL);
213 if (!path)
214 return -ENOMEM;
215
60f067b4 216 log_debug("Looking at %s", path);
14228c0d 217
60f067b4
JS
218 unit = strdup(drop);
219 if (!unit)
14228c0d
MB
220 return -ENOMEM;
221
60f067b4 222 c = strrchr(unit, '.');
14228c0d
MB
223 if (!c)
224 return -EINVAL;
225 *c = 0;
226
227 r = get_files_in_directory(path, &list);
f47781d8
MP
228 if (r < 0)
229 return log_error_errno(r, "Failed to enumerate %s: %m", path);
14228c0d
MB
230
231 STRV_FOREACH(file, list) {
232 Hashmap *h;
233 int k;
234 char *p;
235 char *d;
236
237 if (!endswith(*file, ".conf"))
238 continue;
239
240 p = strjoin(path, "/", *file, NULL);
241 if (!p)
242 return -ENOMEM;
60f067b4 243 d = p + strlen(toppath) + 1;
14228c0d 244
60f067b4 245 log_debug("Adding at top: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
14228c0d
MB
246 k = hashmap_put(top, d, p);
247 if (k >= 0) {
248 p = strdup(p);
249 if (!p)
250 return -ENOMEM;
60f067b4 251 d = p + strlen(toppath) + 1;
14228c0d
MB
252 } else if (k != -EEXIST) {
253 free(p);
254 return k;
255 }
256
60f067b4 257 log_debug("Adding at bottom: %s %s %s", d, draw_special_char(DRAW_ARROW), p);
14228c0d
MB
258 free(hashmap_remove(bottom, d));
259 k = hashmap_put(bottom, d, p);
260 if (k < 0) {
261 free(p);
262 return k;
263 }
264
60f067b4 265 h = hashmap_get(drops, unit);
14228c0d 266 if (!h) {
5eef597e 267 h = hashmap_new(&string_hash_ops);
14228c0d
MB
268 if (!h)
269 return -ENOMEM;
60f067b4
JS
270 hashmap_put(drops, unit, h);
271 unit = strdup(unit);
272 if (!unit)
14228c0d
MB
273 return -ENOMEM;
274 }
275
276 p = strdup(p);
277 if (!p)
278 return -ENOMEM;
279
60f067b4
JS
280 log_debug("Adding to drops: %s %s %s %s %s",
281 unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p);
282 k = hashmap_put(h, basename(p), p);
14228c0d
MB
283 if (k < 0) {
284 free(p);
285 if (k != -EEXIST)
286 return k;
287 }
288 }
663996b3
MS
289 return 0;
290}
291
14228c0d 292static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) {
663996b3
MS
293 _cleanup_closedir_ DIR *d;
294
295 assert(top);
296 assert(bottom);
14228c0d 297 assert(drops);
663996b3
MS
298 assert(path);
299
60f067b4
JS
300 log_debug("Looking at %s", path);
301
663996b3
MS
302 d = opendir(path);
303 if (!d) {
304 if (errno == ENOENT)
305 return 0;
306
f47781d8 307 log_error_errno(errno, "Failed to open %s: %m", path);
663996b3
MS
308 return -errno;
309 }
310
311 for (;;) {
312 struct dirent *de;
663996b3
MS
313 int k;
314 char *p;
315
60f067b4
JS
316 errno = 0;
317 de = readdir(d);
663996b3 318 if (!de)
60f067b4
JS
319 return -errno;
320
321 dirent_ensure_type(d, de);
663996b3 322
14228c0d
MB
323 if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d"))
324 enumerate_dir_d(top, bottom, drops, path, de->d_name);
325
663996b3
MS
326 if (!dirent_is_file(de))
327 continue;
328
329 p = strjoin(path, "/", de->d_name, NULL);
330 if (!p)
331 return -ENOMEM;
332
60f067b4
JS
333 log_debug("Adding at top: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
334 k = hashmap_put(top, basename(p), p);
663996b3
MS
335 if (k >= 0) {
336 p = strdup(p);
337 if (!p)
338 return -ENOMEM;
339 } else if (k != -EEXIST) {
340 free(p);
341 return k;
342 }
343
60f067b4
JS
344 log_debug("Adding at bottom: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p);
345 free(hashmap_remove(bottom, basename(p)));
346 k = hashmap_put(bottom, basename(p), p);
663996b3
MS
347 if (k < 0) {
348 free(p);
349 return k;
350 }
351 }
663996b3
MS
352}
353
60f067b4 354static int process_suffix(const char *suffix, const char *onlyprefix) {
663996b3
MS
355 const char *p;
356 char *f;
60f067b4 357 Hashmap *top, *bottom, *drops;
14228c0d
MB
358 Hashmap *h;
359 char *key;
663996b3 360 int r = 0, k;
14228c0d 361 Iterator i, j;
663996b3 362 int n_found = 0;
60f067b4 363 bool dropins;
663996b3 364
663996b3 365 assert(suffix);
60f067b4
JS
366 assert(!startswith(suffix, "/"));
367 assert(!strstr(suffix, "//"));
663996b3 368
60f067b4 369 dropins = nulstr_contains(have_dropins, suffix);
663996b3 370
5eef597e
MP
371 top = hashmap_new(&string_hash_ops);
372 bottom = hashmap_new(&string_hash_ops);
373 drops = hashmap_new(&string_hash_ops);
60f067b4 374 if (!top || !bottom || !drops) {
14228c0d
MB
375 r = -ENOMEM;
376 goto finish;
377 }
378
663996b3
MS
379 NULSTR_FOREACH(p, prefixes) {
380 _cleanup_free_ char *t = NULL;
381
382 t = strjoin(p, "/", suffix, NULL);
383 if (!t) {
384 r = -ENOMEM;
385 goto finish;
386 }
387
14228c0d 388 k = enumerate_dir(top, bottom, drops, t, dropins);
60f067b4 389 if (r == 0)
663996b3 390 r = k;
663996b3
MS
391 }
392
14228c0d 393 HASHMAP_FOREACH_KEY(f, key, top, i) {
663996b3
MS
394 char *o;
395
14228c0d 396 o = hashmap_get(bottom, key);
663996b3
MS
397 assert(o);
398
60f067b4
JS
399 if (!onlyprefix || startswith(o, onlyprefix)) {
400 if (path_equal(o, f)) {
401 notify_override_unchanged(f);
402 } else {
403 k = found_override(f, o);
404 if (k < 0)
405 r = k;
406 else
407 n_found += k;
408 }
663996b3
MS
409 }
410
14228c0d
MB
411 h = hashmap_get(drops, key);
412 if (h)
413 HASHMAP_FOREACH(o, h, j)
60f067b4
JS
414 if (!onlyprefix || startswith(o, onlyprefix))
415 n_found += notify_override_extended(f, o);
663996b3
MS
416 }
417
418finish:
419 if (top)
420 hashmap_free_free(top);
421 if (bottom)
422 hashmap_free_free(bottom);
14228c0d
MB
423 if (drops) {
424 HASHMAP_FOREACH_KEY(h, key, drops, i){
425 hashmap_free_free(hashmap_remove(drops, key));
426 hashmap_remove(drops, key);
427 free(key);
428 }
429 hashmap_free(drops);
430 }
663996b3
MS
431 return r < 0 ? r : n_found;
432}
433
60f067b4
JS
434static int process_suffixes(const char *onlyprefix) {
435 const char *n;
436 int n_found = 0, r;
437
438 NULSTR_FOREACH(n, suffixes) {
439 r = process_suffix(n, onlyprefix);
440 if (r < 0)
441 return r;
442 else
443 n_found += r;
444 }
445 return n_found;
446}
447
448static int process_suffix_chop(const char *arg) {
663996b3
MS
449 const char *p;
450
60f067b4 451 assert(arg);
663996b3 452
60f067b4
JS
453 if (!path_is_absolute(arg))
454 return process_suffix(arg, NULL);
663996b3
MS
455
456 /* Strip prefix from the suffix */
457 NULSTR_FOREACH(p, prefixes) {
60f067b4
JS
458 const char *suffix = startswith(arg, p);
459 if (suffix) {
663996b3 460 suffix += strspn(suffix, "/");
60f067b4
JS
461 if (*suffix)
462 return process_suffix(suffix, NULL);
463 else
464 return process_suffixes(arg);
663996b3
MS
465 }
466 }
467
60f067b4 468 log_error("Invalid suffix specification %s.", arg);
663996b3
MS
469 return -EINVAL;
470}
471
5eef597e 472static void help(void) {
663996b3
MS
473 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
474 "Find overridden configuration files.\n\n"
475 " -h --help Show this help\n"
476 " --version Show package version\n"
477 " --no-pager Do not pipe output into a pager\n"
478 " --diff[=1|0] Show a diff when overridden files differ\n"
5eef597e
MP
479 " -t --type=LIST... Only display a selected set of override types\n"
480 , program_invocation_short_name);
663996b3
MS
481}
482
483static int parse_flags(const char *flag_str, int flags) {
5eef597e 484 const char *word, *state;
663996b3
MS
485 size_t l;
486
f47781d8 487 FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) {
5eef597e 488 if (strneq("masked", word, l))
663996b3 489 flags |= SHOW_MASKED;
5eef597e 490 else if (strneq ("equivalent", word, l))
663996b3 491 flags |= SHOW_EQUIVALENT;
5eef597e 492 else if (strneq("redirected", word, l))
663996b3 493 flags |= SHOW_REDIRECTED;
5eef597e 494 else if (strneq("overridden", word, l))
663996b3 495 flags |= SHOW_OVERRIDDEN;
5eef597e 496 else if (strneq("unchanged", word, l))
663996b3 497 flags |= SHOW_UNCHANGED;
5eef597e 498 else if (strneq("extended", word, l))
14228c0d 499 flags |= SHOW_EXTENDED;
5eef597e 500 else if (strneq("default", word, l))
663996b3
MS
501 flags |= SHOW_DEFAULTS;
502 else
503 return -EINVAL;
504 }
505 return flags;
506}
507
508static int parse_argv(int argc, char *argv[]) {
509
510 enum {
511 ARG_NO_PAGER = 0x100,
512 ARG_DIFF,
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 },
520 { "diff", optional_argument, NULL, ARG_DIFF },
521 { "type", required_argument, NULL, 't' },
60f067b4 522 {}
663996b3
MS
523 };
524
525 int c;
526
527 assert(argc >= 1);
528 assert(argv);
529
5eef597e 530 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0)
663996b3
MS
531
532 switch (c) {
533
534 case 'h':
535 help();
536 return 0;
537
538 case ARG_VERSION:
539 puts(PACKAGE_STRING);
540 puts(SYSTEMD_FEATURES);
541 return 0;
542
543 case ARG_NO_PAGER:
544 arg_no_pager = true;
545 break;
546
663996b3
MS
547 case 't': {
548 int f;
549 f = parse_flags(optarg, arg_flags);
550 if (f < 0) {
551 log_error("Failed to parse flags field.");
552 return -EINVAL;
553 }
554 arg_flags = f;
555 break;
556 }
557
558 case ARG_DIFF:
559 if (!optarg)
560 arg_diff = 1;
561 else {
562 int b;
563
564 b = parse_boolean(optarg);
565 if (b < 0) {
566 log_error("Failed to parse diff boolean.");
567 return -EINVAL;
568 } else if (b)
569 arg_diff = 1;
570 else
571 arg_diff = 0;
572 }
573 break;
574
60f067b4 575 case '?':
663996b3 576 return -EINVAL;
60f067b4
JS
577
578 default:
579 assert_not_reached("Unhandled option");
663996b3 580 }
663996b3
MS
581
582 return 1;
583}
584
585int main(int argc, char *argv[]) {
663996b3
MS
586 int r = 0, k;
587 int n_found = 0;
588
589 log_parse_environment();
590 log_open();
591
592 r = parse_argv(argc, argv);
593 if (r <= 0)
594 goto finish;
595
596 if (arg_flags == 0)
597 arg_flags = SHOW_DEFAULTS;
598
599 if (arg_diff < 0)
600 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
601 else if (arg_diff)
602 arg_flags |= SHOW_OVERRIDDEN;
603
60f067b4 604 pager_open_if_enabled();
663996b3
MS
605
606 if (optind < argc) {
607 int i;
608
609 for (i = optind; i < argc; i++) {
60f067b4
JS
610 path_kill_slashes(argv[i]);
611 k = process_suffix_chop(argv[i]);
663996b3
MS
612 if (k < 0)
613 r = k;
614 else
615 n_found += k;
616 }
617
618 } else {
60f067b4
JS
619 k = process_suffixes(NULL);
620 if (k < 0)
621 r = k;
622 else
623 n_found += k;
663996b3
MS
624 }
625
626 if (r >= 0)
14228c0d
MB
627 printf("%s%i overridden configuration files found.\n",
628 n_found ? "\n" : "", n_found);
663996b3
MS
629
630finish:
631 pager_close();
632
633 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
634}