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