]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/delta/delta.c
delta: add missing files
[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;
36
37static int equivalent(const char *a, const char *b) {
38 char *x, *y;
39 int r;
40
41 x = canonicalize_file_name(a);
42 if (!x)
43 return -errno;
44
45 y = canonicalize_file_name(b);
46 if (!y) {
47 free(x);
48 return -errno;
49 }
50
51 r = path_equal(x, y);
52 free(x);
53 free(y);
54
55 return r;
56}
57
58static int found_override(const char *top, const char *bottom) {
59 char *dest;
60 int k;
61 pid_t pid;
62
63 assert(top);
64 assert(bottom);
65
66 if (null_or_empty_path(top) > 0) {
67 printf(ANSI_HIGHLIGHT_RED_ON "[MASK]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
68 goto finish;
69 }
70
71 k = readlink_malloc(top, &dest);
72 if (k >= 0) {
73 if (equivalent(dest, bottom) > 0)
74 printf(ANSI_HIGHLIGHT_GREEN_ON "[EQUIVALENT]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
75 else
76 printf(ANSI_HIGHLIGHT_ON "[REDIRECT]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
77
78 free(dest);
79 goto finish;
80 }
81
82 printf(ANSI_HIGHLIGHT_ON "[OVERRIDE]" ANSI_HIGHLIGHT_OFF " %s → %s\n", top, bottom);
83
84 putchar('\n');
85
86 fflush(stdout);
87
88 pid = fork();
89 if (pid < 0) {
90 log_error("Failed to fork off diff: %m");
91 return -errno;
92 } else if (pid == 0) {
93 execlp("diff", "diff", "-us", "--", bottom, top, NULL);
94 log_error("Failed to execute diff: %m");
95 _exit(1);
96 }
97
98 wait_for_terminate(pid, NULL);
99
100 putchar('\n');
101
102finish:
103
104 return 0;
105}
106
107static int enumerate_dir(Hashmap *top, Hashmap *bottom, const char *path) {
108 DIR *d;
109 int r = 0;
110
111 assert(top);
112 assert(bottom);
113 assert(path);
114
115 d = opendir(path);
116 if (!d) {
117 if (errno == ENOENT)
118 return 0;
119
120 log_error("Failed to enumerate %s: %m", path);
121 return -errno;
122 }
123
124 for (;;) {
125 struct dirent *de, buf;
126 int k;
127 char *p;
128
129 k = readdir_r(d, &buf, &de);
130 if (k != 0) {
131 r = -k;
132 goto finish;
133 }
134
135 if (!de)
136 break;
137
138 if (!dirent_is_file(de))
139 continue;
140
141 p = join(path, "/", de->d_name, NULL);
142 if (!p) {
143 r = -ENOMEM;
144 goto finish;
145 }
146
147 path_kill_slashes(p);
148
149 k = hashmap_put(top, path_get_file_name(p), p);
150 if (k >= 0) {
151 p = strdup(p);
152 if (!p) {
153 r = -ENOMEM;
154 goto finish;
155 }
156 } else if (k != -EEXIST) {
157 free(p);
158 r = k;
159 goto finish;
160 }
161
162 free(hashmap_remove(bottom, path_get_file_name(p)));
163 k = hashmap_put(bottom, path_get_file_name(p), p);
164 if (k < 0) {
165 free(p);
166 r = k;
167 goto finish;
168 }
169 }
170
171finish:
172 closedir(d);
173
174 return r;
175}
176
177static int process_suffix(const char *prefixes, const char *suffix) {
178 const char *p;
179 char *f;
180 Hashmap *top, *bottom;
181 int r = 0, k;
182 Iterator i;
183 int n_found = 0;
184
185 assert(prefixes);
186 assert(suffix);
187
188 top = hashmap_new(string_hash_func, string_compare_func);
189 if (!top) {
190 r = -ENOMEM;
191 goto finish;
192 }
193
194 bottom = hashmap_new(string_hash_func, string_compare_func);
195 if (!bottom) {
196 r = -ENOMEM;
197 goto finish;
198 }
199
200 NULSTR_FOREACH(p, prefixes) {
201 char *t;
202
203 t = join(p, "/", suffix, NULL);
204 if (!t) {
205 r = -ENOMEM;
206 goto finish;
207 }
208
209 k = enumerate_dir(top, bottom, t);
210 if (k < 0)
211 r = k;
212
213 log_debug("Looking at %s", t);
214 free(t);
215 }
216
217 HASHMAP_FOREACH(f, top, i) {
218 char *o;
219
220 o = hashmap_get(bottom, path_get_file_name(f));
221 assert(o);
222
223 if (path_equal(o, f))
224 continue;
225
226 k = found_override(f, o);
227 if (k < 0)
228 r = k;
229
230 n_found ++;
231 }
232
233finish:
234 if (top)
235 hashmap_free_free(top);
236 if (bottom)
237 hashmap_free_free(bottom);
238
239 return r < 0 ? r : n_found;
240}
241
242static int process_suffix_chop(const char *prefixes, const char *suffix) {
243 const char *p;
244
245 assert(prefixes);
246 assert(suffix);
247
248 if (!path_is_absolute(suffix))
249 return process_suffix(prefixes, suffix);
250
251 /* Strip prefix from the suffix */
252 NULSTR_FOREACH(p, prefixes) {
253 if (startswith(suffix, p)) {
254 suffix += strlen(p);;
255 suffix += strspn(suffix, "/");
256 return process_suffix(prefixes, suffix);
257 }
258 }
259
260 log_error("Invalid suffix specification %s.", suffix);
261 return -EINVAL;
262}
263
264static void help(void) {
265
266 printf("%s [OPTIONS...] [SUFFIX...]\n\n"
267 "Find overridden configuration files.\n\n"
268 " -h --help Show this help\n"
269 " --version Show package version\n"
270 " --no-pager Do not pipe output into a pager\n",
271 program_invocation_short_name);
272}
273
274static int parse_argv(int argc, char *argv[]) {
275
276 enum {
277 ARG_NO_PAGER = 0x100,
278 ARG_VERSION
279 };
280
281 static const struct option options[] = {
282 { "help", no_argument, NULL, 'h' },
283 { "version", no_argument, NULL, ARG_VERSION },
284 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
285 { NULL, 0, NULL, 0 }
286 };
287
288 int c;
289
290 assert(argc >= 1);
291 assert(argv);
292
293 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
294
295 switch (c) {
296
297 case 'h':
298 help();
299 return 0;
300
301 case ARG_VERSION:
302 puts(PACKAGE_STRING);
303 puts(DISTRIBUTION);
304 puts(SYSTEMD_FEATURES);
305 return 0;
306
307 case ARG_NO_PAGER:
308 arg_no_pager = true;
309 break;
310
311 case '?':
312 return -EINVAL;
313
314 default:
315 log_error("Unknown option code %c", c);
316 return -EINVAL;
317 }
318 }
319
320 return 1;
321}
322
323int main(int argc, char *argv[]) {
324
325 const char prefixes[] =
326 "/etc\0"
327 "/run\0"
328 "/usr/local/lib\0"
329 "/usr/local/share\0"
330 "/usr/lib\0"
331 "/usr/share\0"
332#ifdef HAVE_SPLIT_USR
333 "/lib\0"
334#endif
335 ;
336
337 const char suffixes[] =
338 "sysctl.d\0"
339 "tmpfiles.d\0"
340 "modules-load.d\0"
341 "systemd/system\0"
342 "systemd/user\0"
343 "binfmt.d\0"
344 "udev/rules.d\0"
345 "modprobe.d\0";
346
347 int r = 0, k;
348 int n_found = 0;
349
350 log_parse_environment();
351 log_open();
352
353 r = parse_argv(argc, argv);
354 if (r <= 0)
355 goto finish;
356
357 if (!arg_no_pager)
358 pager_open();
359
360 if (optind < argc) {
361 int i;
362
363 for (i = optind; i < argc; i++) {
364 k = process_suffix_chop(prefixes, argv[i]);
365 if (k < 0)
366 r = k;
367 else
368 n_found += k;
369 }
370
371 } else {
372 const char *n;
373
374 NULSTR_FOREACH(n, suffixes) {
375 k = process_suffix(prefixes, n);
376 if (k < 0)
377 r = k;
378 else
379 n_found += k;
380 }
381 }
382
383 if (r >= 0)
384 printf("\n%i overriden configuration files found.\n", n_found);
385
386finish:
387 pager_close();
388
389 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
390}