]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/delta/delta.c
update TODO
[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,
4c4e6431
LP
42 SHOW_OVERRIDEN = 1 << 3,
43 SHOW_UNCHANGED = 1 << 4,
4c4e6431
LP
44
45 SHOW_DEFAULTS =
866062b1
LP
46 (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDEN)
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
866062b1
LP
94static int notify_override_overriden(const char *top, const char *bottom) {
95 if (!(arg_flags & SHOW_OVERRIDEN))
807f4645
GN
96 return 0;
97
c8021373 98 printf(ANSI_HIGHLIGHT_ON "[OVERRIDEN]" 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
c8021373 106 printf(ANSI_HIGHLIGHT_ON "[UNCHANGED]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
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
866062b1
LP
134 notify_override_overriden(top, bottom);
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 (;;) {
179 struct dirent *de, buf;
180 int k;
181 char *p;
182
183 k = readdir_r(d, &buf, &de);
184 if (k != 0) {
185 r = -k;
186 goto finish;
187 }
188
189 if (!de)
190 break;
191
192 if (!dirent_is_file(de))
193 continue;
194
195 p = join(path, "/", de->d_name, NULL);
196 if (!p) {
197 r = -ENOMEM;
198 goto finish;
199 }
200
201 path_kill_slashes(p);
202
203 k = hashmap_put(top, path_get_file_name(p), p);
204 if (k >= 0) {
205 p = strdup(p);
206 if (!p) {
207 r = -ENOMEM;
208 goto finish;
209 }
210 } else if (k != -EEXIST) {
211 free(p);
212 r = k;
213 goto finish;
214 }
215
216 free(hashmap_remove(bottom, path_get_file_name(p)));
217 k = hashmap_put(bottom, path_get_file_name(p), p);
218 if (k < 0) {
219 free(p);
220 r = k;
221 goto finish;
222 }
223 }
224
225finish:
226 closedir(d);
227
228 return r;
229}
230
866062b1 231static int process_suffix(const char *prefixes, const char *suffix) {
7e8d5761
LP
232 const char *p;
233 char *f;
234 Hashmap *top, *bottom;
235 int r = 0, k;
236 Iterator i;
237 int n_found = 0;
238
239 assert(prefixes);
240 assert(suffix);
241
242 top = hashmap_new(string_hash_func, string_compare_func);
243 if (!top) {
244 r = -ENOMEM;
245 goto finish;
246 }
247
248 bottom = hashmap_new(string_hash_func, string_compare_func);
249 if (!bottom) {
250 r = -ENOMEM;
251 goto finish;
252 }
253
254 NULSTR_FOREACH(p, prefixes) {
255 char *t;
256
257 t = join(p, "/", suffix, NULL);
258 if (!t) {
259 r = -ENOMEM;
260 goto finish;
261 }
262
263 k = enumerate_dir(top, bottom, t);
264 if (k < 0)
265 r = k;
266
267 log_debug("Looking at %s", t);
268 free(t);
269 }
270
271 HASHMAP_FOREACH(f, top, i) {
272 char *o;
273
274 o = hashmap_get(bottom, path_get_file_name(f));
275 assert(o);
276
807f4645 277 if (path_equal(o, f)) {
866062b1 278 notify_override_unchanged(f);
7e8d5761 279 continue;
807f4645 280 }
7e8d5761 281
866062b1 282 k = found_override(f, o);
7e8d5761
LP
283 if (k < 0)
284 r = k;
285
286 n_found ++;
287 }
288
289finish:
290 if (top)
291 hashmap_free_free(top);
292 if (bottom)
293 hashmap_free_free(bottom);
294
295 return r < 0 ? r : n_found;
296}
297
866062b1 298static int process_suffix_chop(const char *prefixes, const char *suffix) {
7e8d5761
LP
299 const char *p;
300
301 assert(prefixes);
302 assert(suffix);
303
304 if (!path_is_absolute(suffix))
866062b1 305 return process_suffix(prefixes, suffix);
7e8d5761
LP
306
307 /* Strip prefix from the suffix */
308 NULSTR_FOREACH(p, prefixes) {
309 if (startswith(suffix, p)) {
310 suffix += strlen(p);;
311 suffix += strspn(suffix, "/");
866062b1 312 return process_suffix(prefixes, suffix);
7e8d5761
LP
313 }
314 }
315
316 log_error("Invalid suffix specification %s.", suffix);
317 return -EINVAL;
318}
319
320static void help(void) {
321
322 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
323 "Find overridden configuration files.\n\n"
324 " -h --help Show this help\n"
325 " --version Show package version\n"
807f4645
GN
326 " --no-pager Do not pipe output into a pager\n"
327 " --diff[=1|0] Show a diff when overriden files differ\n"
328 " -t --type=LIST... Only display a selected set of override types\n",
7e8d5761
LP
329 program_invocation_short_name);
330}
331
866062b1 332static int parse_flags(const char *flag_str, int flags) {
807f4645
GN
333 char *w, *state;
334 size_t l;
335
336 FOREACH_WORD(w, l, flag_str, state) {
337 if (strncmp("masked", w, l) == 0) {
338 flags |= SHOW_MASKED;
339 } else if (strncmp ("equivalent", w, l) == 0) {
c8021373 340 flags |= SHOW_EQUIVALENT;
807f4645 341 } else if (strncmp("redirected", w, l) == 0) {
c8021373 342 flags |= SHOW_REDIRECTED;
807f4645
GN
343 } else if (strncmp("override", w, l) == 0) {
344 flags |= SHOW_OVERRIDEN;
345 } else if (strncmp("unchanged", w, l) == 0) {
346 flags |= SHOW_UNCHANGED;
347 } else if (strncmp("default", w, l) == 0) {
348 flags |= SHOW_DEFAULTS;
866062b1
LP
349 else
350 return -EINVAL;
807f4645
GN
351 }
352 return flags;
353}
354
866062b1 355static int parse_argv(int argc, char *argv[]) {
7e8d5761
LP
356
357 enum {
358 ARG_NO_PAGER = 0x100,
807f4645 359 ARG_DIFF,
7e8d5761
LP
360 ARG_VERSION
361 };
362
363 static const struct option options[] = {
364 { "help", no_argument, NULL, 'h' },
365 { "version", no_argument, NULL, ARG_VERSION },
366 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
807f4645
GN
367 { "diff", optional_argument, NULL, ARG_DIFF },
368 { "type", required_argument, NULL, 't' },
7e8d5761
LP
369 { NULL, 0, NULL, 0 }
370 };
371
372 int c;
373
374 assert(argc >= 1);
375 assert(argv);
376
377 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
378
379 switch (c) {
380
381 case 'h':
382 help();
383 return 0;
384
385 case ARG_VERSION:
386 puts(PACKAGE_STRING);
387 puts(DISTRIBUTION);
388 puts(SYSTEMD_FEATURES);
389 return 0;
390
391 case ARG_NO_PAGER:
392 arg_no_pager = true;
393 break;
394
395 case '?':
396 return -EINVAL;
397
866062b1
LP
398 case 't': {
399 int f;
400 f = parse_flags(optarg, arg_flags);
401 if (f < 0) {
402 log_error("Failed to parse flags field.");
807f4645 403 return -EINVAL;
866062b1
LP
404 }
405 arg_flags = f;
807f4645 406 break;
866062b1 407 }
807f4645
GN
408
409 case ARG_DIFF:
866062b1
LP
410 if (!optarg)
411 arg_diff = 1;
412 else {
413 int b;
414
415 b = parse_boolean(optarg);
416 if (b < 0) {
417 log_error("Failed to parse diff boolean.");
418 return -EINVAL;
419 } else if (b)
420 arg_diff = 1;
807f4645 421 else
866062b1 422 arg_diff = 0;
807f4645
GN
423 }
424 break;
425
7e8d5761
LP
426 default:
427 log_error("Unknown option code %c", c);
428 return -EINVAL;
429 }
430 }
431
432 return 1;
433}
434
435int main(int argc, char *argv[]) {
436
437 const char prefixes[] =
438 "/etc\0"
439 "/run\0"
440 "/usr/local/lib\0"
441 "/usr/local/share\0"
442 "/usr/lib\0"
443 "/usr/share\0"
444#ifdef HAVE_SPLIT_USR
445 "/lib\0"
446#endif
447 ;
448
449 const char suffixes[] =
450 "sysctl.d\0"
451 "tmpfiles.d\0"
452 "modules-load.d\0"
3ea6e732 453 "binfmt.d\0"
7e8d5761
LP
454 "systemd/system\0"
455 "systemd/user\0"
3ea6e732
LP
456 "systemd/system.preset\0"
457 "systemd/user.preset\0"
7e8d5761
LP
458 "udev/rules.d\0"
459 "modprobe.d\0";
460
461 int r = 0, k;
462 int n_found = 0;
463
464 log_parse_environment();
465 log_open();
466
866062b1 467 r = parse_argv(argc, argv);
7e8d5761
LP
468 if (r <= 0)
469 goto finish;
470
866062b1
LP
471 if (arg_flags == 0)
472 arg_flags = SHOW_DEFAULTS;
473
474 if (arg_diff < 0)
475 arg_diff = !!(arg_flags & SHOW_OVERRIDEN);
476 else if (arg_diff)
477 arg_flags |= SHOW_OVERRIDEN;
807f4645 478
7e8d5761
LP
479 if (!arg_no_pager)
480 pager_open();
481
482 if (optind < argc) {
483 int i;
484
485 for (i = optind; i < argc; i++) {
866062b1 486 k = process_suffix_chop(prefixes, argv[i]);
7e8d5761
LP
487 if (k < 0)
488 r = k;
489 else
490 n_found += k;
491 }
492
493 } else {
494 const char *n;
495
496 NULSTR_FOREACH(n, suffixes) {
866062b1 497 k = process_suffix(prefixes, n);
7e8d5761
LP
498 if (k < 0)
499 r = k;
500 else
501 n_found += k;
502 }
503 }
504
505 if (r >= 0)
506 printf("\n%i overriden configuration files found.\n", n_found);
507
508finish:
509 pager_close();
510
511 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
512}