]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/delta/delta.c
build-sys: drop all distribution specfic checks
[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
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <assert.h>
24#include <string.h>
25#include <unistd.h>
26#include <getopt.h>
27
28#include "hashmap.h"
29#include "util.h"
30#include "path-util.h"
31#include "log.h"
32#include "pager.h"
33#include "build.h"
34
35static bool arg_no_pager = false;
866062b1 36static int arg_diff = -1;
7e8d5761 37
866062b1 38static enum {
4c4e6431 39 SHOW_MASKED = 1 << 0,
c8021373
LP
40 SHOW_EQUIVALENT = 1 << 1,
41 SHOW_REDIRECTED = 1 << 2,
386da858 42 SHOW_OVERRIDDEN = 1 << 3,
4c4e6431 43 SHOW_UNCHANGED = 1 << 4,
4c4e6431
LP
44
45 SHOW_DEFAULTS =
386da858 46 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN)
866062b1 47} arg_flags = 0;
4c4e6431 48
7e8d5761 49static int equivalent(const char *a, const char *b) {
e26970a8 50 _cleanup_free_ char *x = NULL, *y = NULL;
7e8d5761
LP
51
52 x = canonicalize_file_name(a);
53 if (!x)
54 return -errno;
55
56 y = canonicalize_file_name(b);
e26970a8 57 if (!y)
7e8d5761 58 return -errno;
7e8d5761 59
e26970a8 60 return path_equal(x, y);
7e8d5761
LP
61}
62
866062b1
LP
63static int notify_override_masked(const char *top, const char *bottom) {
64 if (!(arg_flags & SHOW_MASKED))
807f4645
GN
65 return 0;
66
c8021373 67 printf(ANSI_HIGHLIGHT_RED_ON "[MASKED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
807f4645
GN
68 return 1;
69}
70
866062b1
LP
71static int notify_override_equivalent(const char *top, const char *bottom) {
72 if (!(arg_flags & SHOW_EQUIVALENT))
807f4645
GN
73 return 0;
74
75 printf(ANSI_HIGHLIGHT_GREEN_ON "[EQUIVALENT]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
76 return 1;
77}
78
866062b1
LP
79static int notify_override_redirected(const char *top, const char *bottom) {
80 if (!(arg_flags & SHOW_REDIRECTED))
807f4645
GN
81 return 0;
82
c8021373 83 printf(ANSI_HIGHLIGHT_ON "[REDIRECTED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
807f4645
GN
84 return 1;
85}
86
386da858
NM
87static int notify_override_overridden(const char *top, const char *bottom) {
88 if (!(arg_flags & SHOW_OVERRIDDEN))
807f4645
GN
89 return 0;
90
386da858 91 printf(ANSI_HIGHLIGHT_ON "[OVERRIDDEN]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
807f4645
GN
92 return 1;
93}
94
866062b1
LP
95static int notify_override_unchanged(const char *f) {
96 if (!(arg_flags & SHOW_UNCHANGED))
807f4645
GN
97 return 0;
98
8e812a23 99 printf("[UNCHANGED] %s\n", f);
807f4645
GN
100 return 1;
101}
102
866062b1 103static int found_override(const char *top, const char *bottom) {
e26970a8 104 _cleanup_free_ char *dest = NULL;
7e8d5761
LP
105 int k;
106 pid_t pid;
107
108 assert(top);
109 assert(bottom);
110
111 if (null_or_empty_path(top) > 0) {
866062b1 112 notify_override_masked(top, bottom);
e26970a8 113 return 0;
7e8d5761
LP
114 }
115
116 k = readlink_malloc(top, &dest);
117 if (k >= 0) {
118 if (equivalent(dest, bottom) > 0)
866062b1 119 notify_override_equivalent(top, bottom);
7e8d5761 120 else
866062b1 121 notify_override_redirected(top, bottom);
7e8d5761 122
e26970a8 123 return 0;
7e8d5761
LP
124 }
125
386da858 126 notify_override_overridden(top, bottom);
866062b1 127 if (!arg_diff)
e26970a8 128 return 0;
7e8d5761
LP
129
130 putchar('\n');
131
132 fflush(stdout);
133
134 pid = fork();
135 if (pid < 0) {
136 log_error("Failed to fork off diff: %m");
137 return -errno;
138 } else if (pid == 0) {
139 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
140 log_error("Failed to execute diff: %m");
141 _exit(1);
142 }
143
144 wait_for_terminate(pid, NULL);
145
146 putchar('\n');
147
7e8d5761
LP
148 return 0;
149}
150
151static int enumerate_dir(Hashmap *top, Hashmap *bottom, const char *path) {
e26970a8 152 _cleanup_closedir_ DIR *d;
7e8d5761
LP
153
154 assert(top);
155 assert(bottom);
156 assert(path);
157
158 d = opendir(path);
159 if (!d) {
160 if (errno == ENOENT)
161 return 0;
162
163 log_error("Failed to enumerate %s: %m", path);
164 return -errno;
165 }
166
167 for (;;) {
7d5e9c0f
LP
168 struct dirent *de;
169 union dirent_storage buf;
7e8d5761
LP
170 int k;
171 char *p;
172
7d5e9c0f 173 k = readdir_r(d, &buf.de, &de);
e26970a8
TA
174 if (k != 0)
175 return -k;
7e8d5761
LP
176
177 if (!de)
178 break;
179
180 if (!dirent_is_file(de))
181 continue;
182
b7def684 183 p = strjoin(path, "/", de->d_name, NULL);
e26970a8
TA
184 if (!p)
185 return -ENOMEM;
7e8d5761
LP
186
187 path_kill_slashes(p);
188
189 k = hashmap_put(top, path_get_file_name(p), p);
190 if (k >= 0) {
191 p = strdup(p);
e26970a8
TA
192 if (!p)
193 return -ENOMEM;
7e8d5761
LP
194 } else if (k != -EEXIST) {
195 free(p);
e26970a8 196 return k;
7e8d5761
LP
197 }
198
199 free(hashmap_remove(bottom, path_get_file_name(p)));
200 k = hashmap_put(bottom, path_get_file_name(p), p);
201 if (k < 0) {
202 free(p);
e26970a8 203 return k;
7e8d5761
LP
204 }
205 }
206
e26970a8 207 return 0;
7e8d5761
LP
208}
209
866062b1 210static int process_suffix(const char *prefixes, const char *suffix) {
7e8d5761
LP
211 const char *p;
212 char *f;
d966a7b3 213 Hashmap *top, *bottom=NULL;
7e8d5761
LP
214 int r = 0, k;
215 Iterator i;
216 int n_found = 0;
217
218 assert(prefixes);
219 assert(suffix);
220
221 top = hashmap_new(string_hash_func, string_compare_func);
222 if (!top) {
223 r = -ENOMEM;
224 goto finish;
225 }
226
227 bottom = hashmap_new(string_hash_func, string_compare_func);
228 if (!bottom) {
229 r = -ENOMEM;
230 goto finish;
231 }
232
233 NULSTR_FOREACH(p, prefixes) {
e26970a8 234 _cleanup_free_ char *t = NULL;
7e8d5761 235
b7def684 236 t = strjoin(p, "/", suffix, NULL);
7e8d5761
LP
237 if (!t) {
238 r = -ENOMEM;
239 goto finish;
240 }
241
242 k = enumerate_dir(top, bottom, t);
243 if (k < 0)
244 r = k;
245
246 log_debug("Looking at %s", t);
7e8d5761
LP
247 }
248
249 HASHMAP_FOREACH(f, top, i) {
250 char *o;
251
252 o = hashmap_get(bottom, path_get_file_name(f));
253 assert(o);
254
807f4645 255 if (path_equal(o, f)) {
866062b1 256 notify_override_unchanged(f);
7e8d5761 257 continue;
807f4645 258 }
7e8d5761 259
866062b1 260 k = found_override(f, o);
7e8d5761
LP
261 if (k < 0)
262 r = k;
263
264 n_found ++;
265 }
266
267finish:
268 if (top)
269 hashmap_free_free(top);
270 if (bottom)
271 hashmap_free_free(bottom);
272
273 return r < 0 ? r : n_found;
274}
275
866062b1 276static int process_suffix_chop(const char *prefixes, const char *suffix) {
7e8d5761
LP
277 const char *p;
278
279 assert(prefixes);
280 assert(suffix);
281
282 if (!path_is_absolute(suffix))
866062b1 283 return process_suffix(prefixes, suffix);
7e8d5761
LP
284
285 /* Strip prefix from the suffix */
286 NULSTR_FOREACH(p, prefixes) {
287 if (startswith(suffix, p)) {
33b40551 288 suffix += strlen(p);
7e8d5761 289 suffix += strspn(suffix, "/");
866062b1 290 return process_suffix(prefixes, suffix);
7e8d5761
LP
291 }
292 }
293
294 log_error("Invalid suffix specification %s.", suffix);
295 return -EINVAL;
296}
297
298static void help(void) {
299
300 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
301 "Find overridden configuration files.\n\n"
302 " -h --help Show this help\n"
303 " --version Show package version\n"
807f4645 304 " --no-pager Do not pipe output into a pager\n"
386da858 305 " --diff[=1|0] Show a diff when overridden files differ\n"
807f4645 306 " -t --type=LIST... Only display a selected set of override types\n",
7e8d5761
LP
307 program_invocation_short_name);
308}
309
866062b1 310static int parse_flags(const char *flag_str, int flags) {
807f4645
GN
311 char *w, *state;
312 size_t l;
313
314 FOREACH_WORD(w, l, flag_str, state) {
c0ef53aa 315 if (strncmp("masked", w, l) == 0)
807f4645 316 flags |= SHOW_MASKED;
c0ef53aa 317 else if (strncmp ("equivalent", w, l) == 0)
c8021373 318 flags |= SHOW_EQUIVALENT;
c0ef53aa 319 else if (strncmp("redirected", w, l) == 0)
c8021373 320 flags |= SHOW_REDIRECTED;
386da858
NM
321 else if (strncmp("overridden", w, l) == 0)
322 flags |= SHOW_OVERRIDDEN;
c0ef53aa 323 else if (strncmp("unchanged", w, l) == 0)
807f4645 324 flags |= SHOW_UNCHANGED;
c0ef53aa 325 else if (strncmp("default", w, l) == 0)
807f4645 326 flags |= SHOW_DEFAULTS;
866062b1
LP
327 else
328 return -EINVAL;
807f4645
GN
329 }
330 return flags;
331}
332
866062b1 333static int parse_argv(int argc, char *argv[]) {
7e8d5761
LP
334
335 enum {
336 ARG_NO_PAGER = 0x100,
807f4645 337 ARG_DIFF,
7e8d5761
LP
338 ARG_VERSION
339 };
340
341 static const struct option options[] = {
342 { "help", no_argument, NULL, 'h' },
343 { "version", no_argument, NULL, ARG_VERSION },
344 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
807f4645
GN
345 { "diff", optional_argument, NULL, ARG_DIFF },
346 { "type", required_argument, NULL, 't' },
7e8d5761
LP
347 { NULL, 0, NULL, 0 }
348 };
349
350 int c;
351
352 assert(argc >= 1);
353 assert(argv);
354
377ec8bf 355 while ((c = getopt_long(argc, argv, "ht:", options, NULL)) >= 0) {
7e8d5761
LP
356
357 switch (c) {
358
359 case 'h':
360 help();
361 return 0;
362
363 case ARG_VERSION:
364 puts(PACKAGE_STRING);
7e8d5761
LP
365 puts(SYSTEMD_FEATURES);
366 return 0;
367
368 case ARG_NO_PAGER:
369 arg_no_pager = true;
370 break;
371
372 case '?':
373 return -EINVAL;
374
866062b1
LP
375 case 't': {
376 int f;
377 f = parse_flags(optarg, arg_flags);
378 if (f < 0) {
379 log_error("Failed to parse flags field.");
807f4645 380 return -EINVAL;
866062b1
LP
381 }
382 arg_flags = f;
807f4645 383 break;
866062b1 384 }
807f4645
GN
385
386 case ARG_DIFF:
866062b1
LP
387 if (!optarg)
388 arg_diff = 1;
389 else {
390 int b;
391
392 b = parse_boolean(optarg);
393 if (b < 0) {
394 log_error("Failed to parse diff boolean.");
395 return -EINVAL;
396 } else if (b)
397 arg_diff = 1;
807f4645 398 else
866062b1 399 arg_diff = 0;
807f4645
GN
400 }
401 break;
402
7e8d5761
LP
403 default:
404 log_error("Unknown option code %c", c);
405 return -EINVAL;
406 }
407 }
408
409 return 1;
410}
411
412int main(int argc, char *argv[]) {
413
414 const char prefixes[] =
415 "/etc\0"
416 "/run\0"
417 "/usr/local/lib\0"
418 "/usr/local/share\0"
419 "/usr/lib\0"
420 "/usr/share\0"
421#ifdef HAVE_SPLIT_USR
422 "/lib\0"
423#endif
424 ;
425
426 const char suffixes[] =
427 "sysctl.d\0"
428 "tmpfiles.d\0"
429 "modules-load.d\0"
3ea6e732 430 "binfmt.d\0"
7e8d5761
LP
431 "systemd/system\0"
432 "systemd/user\0"
a7480dba
LP
433 "systemd/system-preset\0"
434 "systemd/user-preset\0"
7e8d5761
LP
435 "udev/rules.d\0"
436 "modprobe.d\0";
437
438 int r = 0, k;
439 int n_found = 0;
440
441 log_parse_environment();
442 log_open();
443
866062b1 444 r = parse_argv(argc, argv);
7e8d5761
LP
445 if (r <= 0)
446 goto finish;
447
866062b1
LP
448 if (arg_flags == 0)
449 arg_flags = SHOW_DEFAULTS;
450
451 if (arg_diff < 0)
386da858 452 arg_diff = !!(arg_flags & SHOW_OVERRIDDEN);
866062b1 453 else if (arg_diff)
386da858 454 arg_flags |= SHOW_OVERRIDDEN;
807f4645 455
7e8d5761
LP
456 if (!arg_no_pager)
457 pager_open();
458
459 if (optind < argc) {
460 int i;
461
462 for (i = optind; i < argc; i++) {
866062b1 463 k = process_suffix_chop(prefixes, argv[i]);
7e8d5761
LP
464 if (k < 0)
465 r = k;
466 else
467 n_found += k;
468 }
469
470 } else {
471 const char *n;
472
473 NULSTR_FOREACH(n, suffixes) {
866062b1 474 k = process_suffix(prefixes, n);
7e8d5761
LP
475 if (k < 0)
476 r = k;
477 else
478 n_found += k;
479 }
480 }
481
482 if (r >= 0)
386da858 483 printf("\n%i overridden configuration files found.\n", n_found);
7e8d5761
LP
484
485finish:
486 pager_close();
487
488 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
489}