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