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