]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-lookup.c
core: rework logic to drop duplicate and non-existing items from search path
[thirdparty/systemd.git] / src / shared / path-lookup.c
CommitLineData
84e3543e
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
84e3543e
LP
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 14 Lesser General Public License for more details.
84e3543e 15
5430f7f2 16 You should have received a copy of the GNU Lesser General Public License
84e3543e
LP
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
cf0fbc49 20#include <errno.h>
84e3543e 21#include <stdio.h>
cf0fbc49 22#include <stdlib.h>
67445f4e 23#include <string.h>
84e3543e 24
b5efdb8a 25#include "alloc-util.h"
a8ffe6fb 26#include "install.h"
a8fbdf54
TA
27#include "log.h"
28#include "macro.h"
cd64fd56 29#include "mkdir.h"
07630cea 30#include "path-lookup.h"
cf0fbc49 31#include "path-util.h"
e4bb56c7 32#include "stat-util.h"
cf0fbc49
TA
33#include "string-util.h"
34#include "strv.h"
35#include "util.h"
84e3543e 36
af2d49f7 37int user_config_home(char **config_home) {
10e87ee7 38 const char *e;
26d04f86 39 char *r;
10e87ee7 40
07719a21
LP
41 e = getenv("XDG_CONFIG_HOME");
42 if (e) {
26d04f86
LP
43 r = strappend(e, "/systemd/user");
44 if (!r)
10e87ee7
LP
45 return -ENOMEM;
46
26d04f86 47 *config_home = r;
10e87ee7
LP
48 return 1;
49 } else {
50 const char *home;
51
07719a21
LP
52 home = getenv("HOME");
53 if (home) {
26d04f86
LP
54 r = strappend(home, "/.config/systemd/user");
55 if (!r)
10e87ee7
LP
56 return -ENOMEM;
57
26d04f86 58 *config_home = r;
10e87ee7
LP
59 return 1;
60 }
61 }
62
63 return 0;
64}
65
4d5dec23 66int user_runtime_dir(char **runtime_dir) {
718880ba
SA
67 const char *e;
68 char *r;
69
70 e = getenv("XDG_RUNTIME_DIR");
71 if (e) {
72 r = strappend(e, "/systemd/user");
73 if (!r)
74 return -ENOMEM;
75
4d5dec23 76 *runtime_dir = r;
718880ba
SA
77 return 1;
78 }
79
80 return 0;
81}
82
e801700e
ZJS
83static int user_data_home_dir(char **dir, const char *suffix) {
84 const char *e;
85 char *res;
86
87 /* We don't treat /etc/xdg/systemd here as the spec
88 * suggests because we assume that that is a link to
89 * /etc/systemd/ anyway. */
90
91 e = getenv("XDG_DATA_HOME");
92 if (e)
93 res = strappend(e, suffix);
94 else {
95 const char *home;
96
97 home = getenv("HOME");
98 if (home)
99 res = strjoin(home, "/.local/share", suffix, NULL);
100 else
101 return 0;
102 }
103 if (!res)
104 return -ENOMEM;
105
106 *dir = res;
463d0d15 107 return 1;
e801700e
ZJS
108}
109
07719a21 110static char** user_dirs(
a0f84a10
LP
111 const char *persistent_config,
112 const char *runtime_config,
07719a21
LP
113 const char *generator,
114 const char *generator_early,
39591351
LP
115 const char *generator_late,
116 const char *transient) {
07719a21 117
f437d5d2 118 const char * const config_unit_paths[] = {
f437d5d2 119 USER_CONFIG_UNIT_PATH,
4bf2bbb6
LP
120 "/etc/systemd/user",
121 NULL
f437d5d2
LP
122 };
123
124 const char * const data_unit_paths[] = {
125 "/usr/local/lib/systemd/user",
126 "/usr/local/share/systemd/user",
127 USER_DATA_UNIT_PATH,
128 "/usr/lib/systemd/user",
4bf2bbb6
LP
129 "/usr/share/systemd/user",
130 NULL
f437d5d2
LP
131 };
132
e801700e 133 const char *e;
4d5dec23 134 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
e3e45d4f 135 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
e801700e
ZJS
136 _cleanup_free_ char **res = NULL;
137 char **tmp;
138 int r;
84e3543e
LP
139
140 /* Implement the mechanisms defined in
141 *
142 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
143 *
144 * We look in both the config and the data dirs because we
145 * want to encourage that distributors ship their unit files
146 * as data, and allow overriding as configuration.
147 */
148
af2d49f7 149 if (user_config_home(&config_home) < 0)
e801700e 150 return NULL;
84e3543e 151
4d5dec23 152 if (user_runtime_dir(&runtime_dir) < 0)
e801700e 153 return NULL;
84e3543e 154
07719a21
LP
155 e = getenv("XDG_CONFIG_DIRS");
156 if (e) {
157 config_dirs = strv_split(e, ":");
158 if (!config_dirs)
e801700e 159 return NULL;
07719a21 160 }
84e3543e 161
e801700e
ZJS
162 r = user_data_home_dir(&data_home, "/systemd/user");
163 if (r < 0)
164 return NULL;
84e3543e 165
07719a21
LP
166 e = getenv("XDG_DATA_DIRS");
167 if (e)
84e3543e
LP
168 data_dirs = strv_split(e, ":");
169 else
ef3102bf 170 data_dirs = strv_new("/usr/local/share",
ef3102bf 171 "/usr/share",
ef3102bf 172 NULL);
84e3543e 173 if (!data_dirs)
e801700e 174 return NULL;
84e3543e
LP
175
176 /* Now merge everything we found. */
39591351
LP
177 if (transient)
178 if (strv_extend(&res, transient) < 0)
179 return NULL;
180
e3e45d4f 181 if (generator_early)
e801700e
ZJS
182 if (strv_extend(&res, generator_early) < 0)
183 return NULL;
07719a21 184
e3e45d4f 185 if (config_home)
e801700e
ZJS
186 if (strv_extend(&res, config_home) < 0)
187 return NULL;
84e3543e 188
aa08982d 189 if (!strv_isempty(config_dirs))
e801700e
ZJS
190 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
191 return NULL;
718880ba 192
a0f84a10
LP
193 if (strv_extend(&res, persistent_config) < 0)
194 return NULL;
195
e287086b 196 if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
e801700e 197 return NULL;
718880ba 198
aa08982d 199 if (runtime_dir)
e801700e
ZJS
200 if (strv_extend(&res, runtime_dir) < 0)
201 return NULL;
84e3543e 202
a0f84a10 203 if (strv_extend(&res, runtime_config) < 0)
e801700e 204 return NULL;
84e3543e 205
e3e45d4f 206 if (generator)
e801700e
ZJS
207 if (strv_extend(&res, generator) < 0)
208 return NULL;
07719a21 209
e3e45d4f 210 if (data_home)
e801700e
ZJS
211 if (strv_extend(&res, data_home) < 0)
212 return NULL;
84e3543e 213
e3e45d4f 214 if (!strv_isempty(data_dirs))
e801700e
ZJS
215 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
216 return NULL;
84e3543e 217
e287086b 218 if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
e801700e 219 return NULL;
84e3543e 220
e3e45d4f 221 if (generator_late)
e801700e
ZJS
222 if (strv_extend(&res, generator_late) < 0)
223 return NULL;
07719a21 224
0f474365 225 if (path_strv_make_absolute_cwd(res) < 0)
e801700e 226 return NULL;
84e3543e 227
e801700e
ZJS
228 tmp = res;
229 res = NULL;
230 return tmp;
231}
84e3543e 232
463d0d15
LP
233char **generator_paths(UnitFileScope scope) {
234
235 switch (scope) {
236
237 case UNIT_FILE_SYSTEM:
33e1e5a7
ZJS
238 return strv_new("/run/systemd/system-generators",
239 "/etc/systemd/system-generators",
e801700e
ZJS
240 "/usr/local/lib/systemd/system-generators",
241 SYSTEM_GENERATOR_PATH,
242 NULL);
463d0d15
LP
243
244 case UNIT_FILE_GLOBAL:
245 case UNIT_FILE_USER:
246 return strv_new("/run/systemd/user-generators",
247 "/etc/systemd/user-generators",
248 "/usr/local/lib/systemd/user-generators",
249 USER_GENERATOR_PATH,
250 NULL);
251
252 default:
253 assert_not_reached("Hmm, unexpected scope.");
254 }
84e3543e
LP
255}
256
a3c4eb07 257static int acquire_generator_dirs(
463d0d15 258 UnitFileScope scope,
a3c4eb07
LP
259 char **generator,
260 char **generator_early,
261 char **generator_late) {
262
263 _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
264 const char *prefix;
265
266 assert(generator);
267 assert(generator_early);
268 assert(generator_late);
269
463d0d15
LP
270 switch (scope) {
271
272 case UNIT_FILE_SYSTEM:
a3c4eb07 273 prefix = "/run/systemd/";
463d0d15 274 break;
a3c4eb07 275
463d0d15
LP
276 case UNIT_FILE_USER: {
277 const char *e;
a3c4eb07
LP
278
279 e = getenv("XDG_RUNTIME_DIR");
280 if (!e)
463d0d15 281 return -ENXIO;
a3c4eb07
LP
282
283 prefix = strjoina(e, "/systemd/", NULL);
463d0d15
LP
284 break;
285 }
286
287 case UNIT_FILE_GLOBAL:
288 return -EOPNOTSUPP;
289
290 default:
291 assert_not_reached("Hmm, unexpected scope value.");
a3c4eb07
LP
292 }
293
294 x = strappend(prefix, "generator");
295 if (!x)
296 return -ENOMEM;
297
298 y = strappend(prefix, "generator.early");
299 if (!y)
300 return -ENOMEM;
301
302 z = strappend(prefix, "generator.late");
303 if (!z)
304 return -ENOMEM;
305
306 *generator = x;
307 *generator_early = y;
308 *generator_late = z;
309
310 x = y = z = NULL;
311 return 0;
312}
313
39591351
LP
314static int acquire_transient_dir(UnitFileScope scope, char **ret) {
315 char *transient;
316
317 assert(ret);
318
319 switch (scope) {
320
321 case UNIT_FILE_SYSTEM:
322 transient = strdup("/run/systemd/transient");
323 break;
324
325 case UNIT_FILE_USER: {
326 const char *e;
327
328 e = getenv("XDG_RUNTIME_DIR");
329 if (!e)
330 return -ENXIO;
331
332 transient = strjoin(e, "/systemd/transient", NULL);
333 break;
334 }
335
336 case UNIT_FILE_GLOBAL:
337 return -EOPNOTSUPP;
338
339 default:
340 assert_not_reached("Hmm, unexpected scope value.");
341 }
342
343 if (!transient)
344 return -ENOMEM;
345
346 *ret = transient;
347 return 0;
348}
349
463d0d15 350static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
a0f84a10
LP
351 _cleanup_free_ char *a = NULL, *b = NULL;
352 int r;
353
354 assert(persistent);
355 assert(runtime);
356
463d0d15
LP
357 switch (scope) {
358
359 case UNIT_FILE_SYSTEM:
a0f84a10
LP
360 a = strdup(SYSTEM_CONFIG_UNIT_PATH);
361 b = strdup("/run/systemd/system");
463d0d15
LP
362 break;
363
364 case UNIT_FILE_GLOBAL:
365 a = strdup(USER_CONFIG_UNIT_PATH);
366 b = strdup("/run/systemd/user");
367 break;
a0f84a10 368
463d0d15 369 case UNIT_FILE_USER:
a0f84a10
LP
370 r = user_config_home(&a);
371 if (r < 0)
372 return r;
373
374 r = user_runtime_dir(runtime);
375 if (r < 0)
376 return r;
377
378 *persistent = a;
379 a = NULL;
380
381 return 0;
a0f84a10 382
463d0d15
LP
383 default:
384 assert_not_reached("Hmm, unexpected scope value.");
a0f84a10
LP
385 }
386
387 if (!a || !b)
388 return -ENOMEM;
389
390 *persistent = a;
391 *runtime = b;
392 a = b = NULL;
393
394 return 0;
395}
396
a3c4eb07
LP
397static int patch_root_prefix(char **p, const char *root_dir) {
398 char *c;
399
400 assert(p);
401
402 if (!*p)
403 return 0;
404
a3c4eb07
LP
405 c = prefix_root(root_dir, *p);
406 if (!c)
407 return -ENOMEM;
408
409 free(*p);
410 *p = c;
411
412 return 0;
413}
414
a1453343
LP
415static int patch_root_prefix_strv(char **l, const char *root_dir) {
416 char **i;
417 int r;
418
419 if (!root_dir)
420 return 0;
421
422 STRV_FOREACH(i, l) {
423 r = patch_root_prefix(i, root_dir);
424 if (r < 0)
425 return r;
426 }
427
428 return 0;
429}
430
07719a21
LP
431int lookup_paths_init(
432 LookupPaths *p,
463d0d15 433 UnitFileScope scope,
a3c4eb07 434 const char *root_dir) {
07719a21 435
e4bb56c7
LP
436 _cleanup_free_ char
437 *root = NULL,
39591351 438 *persistent_config = NULL, *runtime_config = NULL,
e4bb56c7 439 *generator = NULL, *generator_early = NULL, *generator_late = NULL,
39591351 440 *transient = NULL;
cf7d80a5 441 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
a3c4eb07
LP
442 char **l = NULL;
443 const char *e;
0f474365 444 int r;
84e3543e
LP
445
446 assert(p);
463d0d15
LP
447 assert(scope >= 0);
448 assert(scope < _UNIT_FILE_SCOPE_MAX);
a3c4eb07 449
e4bb56c7
LP
450 if (!isempty(root_dir) && !path_equal(root_dir, "/")) {
451 if (scope == UNIT_FILE_USER)
452 return -EINVAL;
453
454 r = is_dir(root_dir, true);
455 if (r < 0)
456 return r;
457 if (r == 0)
458 return -ENOTDIR;
459
460 root = strdup(root_dir);
461 if (!root)
462 return -ENOMEM;
463 }
464
463d0d15 465 r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
a0f84a10
LP
466 if (r < 0)
467 return r;
468
463d0d15
LP
469 r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late);
470 if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
a3c4eb07 471 return r;
84e3543e 472
39591351
LP
473 r = acquire_transient_dir(scope, &transient);
474 if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
475 return r;
476
cd64fd56 477 /* First priority is whatever has been passed to us via env vars */
07719a21
LP
478 e = getenv("SYSTEMD_UNIT_PATH");
479 if (e) {
a3c4eb07
LP
480 const char *k;
481
482 k = endswith(e, ":");
483 if (k) {
484 e = strndupa(e, k - e);
cf7d80a5
ZJS
485 append = true;
486 }
487
488 /* FIXME: empty components in other places should be
489 * rejected. */
490
a3c4eb07 491 r = path_split_and_make_absolute(e, &l);
0f474365
LP
492 if (r < 0)
493 return r;
07719a21 494 } else
a3c4eb07 495 l = NULL;
84e3543e 496
a3c4eb07 497 if (!l || append) {
cf7d80a5
ZJS
498 /* Let's figure something out. */
499
a3c4eb07 500 _cleanup_strv_free_ char **add = NULL;
84e3543e 501
07719a21 502 /* For the user units we include share/ in the search
cf7d80a5
ZJS
503 * path in order to comply with the XDG basedir spec.
504 * For the system stuff we avoid such nonsense. OTOH
505 * we include /lib in the search path for the system
506 * stuff but avoid it for user stuff. */
07719a21 507
463d0d15
LP
508 switch (scope) {
509
510 case UNIT_FILE_SYSTEM:
511 add = strv_new(
512 /* If you modify this you also want to modify
513 * systemdsystemunitpath= in systemd.pc.in! */
39591351 514 STRV_IFNOTNULL(transient),
463d0d15
LP
515 STRV_IFNOTNULL(generator_early),
516 persistent_config,
5f0a41da 517 SYSTEM_CONFIG_UNIT_PATH,
463d0d15
LP
518 "/etc/systemd/system",
519 runtime_config,
520 "/run/systemd/system",
521 STRV_IFNOTNULL(generator),
522 "/usr/local/lib/systemd/system",
523 SYSTEM_DATA_UNIT_PATH,
524 "/usr/lib/systemd/system",
525#ifdef HAVE_SPLIT_USR
526 "/lib/systemd/system",
527#endif
528 STRV_IFNOTNULL(generator_late),
529 NULL);
530 break;
531
532 case UNIT_FILE_GLOBAL:
533 add = strv_new(
07719a21 534 /* If you modify this you also want to modify
cf7d80a5
ZJS
535 * systemduserunitpath= in systemd.pc.in, and
536 * the arrays in user_dirs() above! */
39591351 537 STRV_IFNOTNULL(transient),
463d0d15 538 STRV_IFNOTNULL(generator_early),
a0f84a10 539 persistent_config,
5f0a41da 540 USER_CONFIG_UNIT_PATH,
cf7d80a5 541 "/etc/systemd/user",
a0f84a10 542 runtime_config,
cf7d80a5 543 "/run/systemd/user",
463d0d15 544 STRV_IFNOTNULL(generator),
cf7d80a5
ZJS
545 "/usr/local/lib/systemd/user",
546 "/usr/local/share/systemd/user",
547 USER_DATA_UNIT_PATH,
548 "/usr/lib/systemd/user",
549 "/usr/share/systemd/user",
463d0d15 550 STRV_IFNOTNULL(generator_late),
07719a21 551 NULL);
463d0d15
LP
552 break;
553
554 case UNIT_FILE_USER:
555 add = user_dirs(persistent_config, runtime_config,
39591351
LP
556 generator, generator_early, generator_late,
557 transient);
463d0d15
LP
558 break;
559
560 default:
561 assert_not_reached("Hmm, unexpected scope?");
562 }
07719a21 563
a3c4eb07 564 if (!add)
cf7d80a5
ZJS
565 return -ENOMEM;
566
a3c4eb07
LP
567 if (l) {
568 r = strv_extend_strv(&l, add, false);
569 if (r < 0)
570 return r;
571 } else {
572 l = add;
573 add = NULL;
574 }
84e3543e
LP
575 }
576
e4bb56c7 577 r = patch_root_prefix(&persistent_config, root);
a0f84a10
LP
578 if (r < 0)
579 return r;
e4bb56c7 580 r = patch_root_prefix(&runtime_config, root);
a0f84a10
LP
581 if (r < 0)
582 return r;
583
e4bb56c7 584 r = patch_root_prefix(&generator, root);
a3c4eb07
LP
585 if (r < 0)
586 return r;
e4bb56c7 587 r = patch_root_prefix(&generator_early, root);
a3c4eb07
LP
588 if (r < 0)
589 return r;
e4bb56c7 590 r = patch_root_prefix(&generator_late, root);
a3c4eb07
LP
591 if (r < 0)
592 return r;
593
39591351
LP
594 r = patch_root_prefix(&transient, root);
595 if (r < 0)
596 return r;
597
a1453343
LP
598 r = patch_root_prefix_strv(l, root);
599 if (r < 0)
07719a21 600 return -ENOMEM;
07459bb6 601
a1453343 602 p->search_path = strv_uniq(l);
a3c4eb07
LP
603 l = NULL;
604
a0f84a10
LP
605 p->persistent_config = persistent_config;
606 p->runtime_config = runtime_config;
607 persistent_config = runtime_config = NULL;
608
a3c4eb07
LP
609 p->generator = generator;
610 p->generator_early = generator_early;
611 p->generator_late = generator_late;
612 generator = generator_early = generator_late = NULL;
84e3543e 613
39591351
LP
614 p->transient = transient;
615 transient = NULL;
616
e4bb56c7
LP
617 p->root_dir = root;
618 root = NULL;
619
84e3543e
LP
620 return 0;
621}
622
623void lookup_paths_free(LookupPaths *p) {
a3c4eb07
LP
624 if (!p)
625 return;
84e3543e 626
a3c4eb07 627 p->search_path = strv_free(p->search_path);
a0f84a10
LP
628
629 p->persistent_config = mfree(p->persistent_config);
630 p->runtime_config = mfree(p->runtime_config);
631
a3c4eb07
LP
632 p->generator = mfree(p->generator);
633 p->generator_early = mfree(p->generator_early);
634 p->generator_late = mfree(p->generator_late);
e4bb56c7 635
39591351
LP
636 p->transient = mfree(p->transient);
637
e4bb56c7 638 p->root_dir = mfree(p->root_dir);
84e3543e 639}
cd64fd56 640
a1453343
LP
641int lookup_paths_reduce(LookupPaths *p) {
642 _cleanup_free_ struct stat *stats = NULL;
643 size_t n_stats = 0, allocated = 0;
644 unsigned c = 0;
645 int r;
646
647 assert(p);
648
649 /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
650 * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
651 * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
652 * account when following symlinks. When we have no root path set this restriction does not apply however. */
653
654 if (!p->search_path)
655 return 0;
656
657 while (p->search_path[c]) {
658 struct stat st;
659 unsigned k;
660
661 if (p->root_dir)
662 r = lstat(p->search_path[c], &st);
663 else
664 r = stat(p->search_path[c], &st);
665 if (r < 0) {
666 if (errno == ENOENT)
667 goto remove_item;
668
669 /* If something we don't grok happened, let's better leave it in. */
670 log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]);
671 c++;
672 continue;
673 }
674
675 for (k = 0; k < n_stats; k++) {
676 if (stats[k].st_dev == st.st_dev &&
677 stats[k].st_ino == st.st_ino)
678 break;
679 }
680
681 if (k < n_stats) /* Is there already an entry with the same device/inode? */
682 goto remove_item;
683
684 if (!GREEDY_REALLOC(stats, allocated, n_stats+1))
685 return -ENOMEM;
686
687 stats[n_stats++] = st;
688 c++;
689 continue;
690
691 remove_item:
692 free(p->search_path[c]);
693 memmove(p->search_path + c,
694 p->search_path + c + 1,
695 (strv_length(p->search_path + c + 1) + 1) * sizeof(char*));
696 }
697
698 if (strv_isempty(p->search_path)) {
699 log_debug("Ignoring unit files.");
700 p->search_path = strv_free(p->search_path);
701 } else {
702 _cleanup_free_ char *t;
703
704 t = strv_join(p->search_path, "\n\t");
705 if (!t)
706 return -ENOMEM;
707
708 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
709 }
710
711 return 0;
712}
713
cd64fd56
LP
714int lookup_paths_mkdir_generator(LookupPaths *p) {
715 int r, q;
716
717 assert(p);
718
719 r = mkdir_p_label(p->generator, 0755);
720
721 q = mkdir_p_label(p->generator_early, 0755);
722 if (q < 0 && r >= 0)
723 r = q;
724
725 q = mkdir_p_label(p->generator_late, 0755);
726 if (q < 0 && r >= 0)
727 r = q;
728
729 return r;
730}
731
732void lookup_paths_trim_generator(LookupPaths *p) {
733 assert(p);
734
735 /* Trim empty dirs */
736
737 if (p->generator)
738 (void) rmdir(p->generator);
739
740 if (p->generator_early)
741 (void) rmdir(p->generator_early);
742
743 if (p->generator_late)
744 (void) rmdir(p->generator_late);
745}