]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-path/sd-path.c
man: improve documentation about using resource-control options
[thirdparty/systemd.git] / src / libsystemd / sd-path / sd-path.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
9a00f57a 2
07630cea
LP
3#include "sd-path.h"
4
b5efdb8a 5#include "alloc-util.h"
9a00f57a 6#include "architecture.h"
3ffd4af2 7#include "fd-util.h"
0d39fa9c 8#include "fileio.h"
fccb4486 9#include "fs-util.h"
08af3cc5 10#include "nulstr-util.h"
9c5bb203 11#include "path-lookup.h"
9a00f57a 12#include "path-util.h"
07630cea 13#include "string-util.h"
9a00f57a 14#include "strv.h"
b1d4f8e1 15#include "user-util.h"
9a00f57a
LP
16
17static int from_environment(const char *envname, const char *fallback, const char **ret) {
18 assert(ret);
19
20 if (envname) {
21 const char *e;
22
23 e = secure_getenv(envname);
24 if (e && path_is_absolute(e)) {
25 *ret = e;
26 return 0;
27 }
28 }
29
30 if (fallback) {
31 *ret = fallback;
32 return 0;
33 }
34
35 return -ENXIO;
36}
37
38static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
39 _cleanup_free_ char *h = NULL;
9a00f57a
LP
40 int r;
41
42 assert(suffix);
43 assert(buffer);
44 assert(ret);
45
46 if (envname) {
47 const char *e = NULL;
48
49 e = secure_getenv(envname);
50 if (e && path_is_absolute(e)) {
51 *ret = e;
52 return 0;
53 }
54 }
55
56 r = get_home_dir(&h);
57 if (r < 0)
58 return r;
59
4ede9802 60 if (!path_extend(&h, suffix))
9a00f57a
LP
61 return -ENOMEM;
62
4ede9802
LP
63 *buffer = h;
64 *ret = TAKE_PTR(h);
9a00f57a
LP
65 return 0;
66}
67
68static int from_user_dir(const char *field, char **buffer, const char **ret) {
69 _cleanup_fclose_ FILE *f = NULL;
70 _cleanup_free_ char *b = NULL;
b6b0cfaa
EGP
71 _cleanup_free_ const char *fn = NULL;
72 const char *c = NULL;
9a00f57a
LP
73 int r;
74
75 assert(field);
76 assert(buffer);
77 assert(ret);
78
b6b0cfaa 79 r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c);
9a00f57a
LP
80 if (r < 0)
81 return r;
82
b910cc72 83 fn = path_join(c, "user-dirs.dirs");
b6b0cfaa
EGP
84 if (!fn)
85 return -ENOMEM;
86
9a00f57a
LP
87 f = fopen(fn, "re");
88 if (!f) {
89 if (errno == ENOENT)
90 goto fallback;
91
92 return -errno;
93 }
94
0ff6ff2b 95 /* This is an awful parse, but it follows closely what xdg-user-dirs does upstream */
710bf2ae
LP
96 for (;;) {
97 _cleanup_free_ char *line = NULL;
0ff6ff2b 98 char *p, *e;
9a00f57a 99
0ff6ff2b 100 r = read_stripped_line(f, LONG_LINE_MAX, &line);
710bf2ae
LP
101 if (r < 0)
102 return r;
103 if (r == 0)
104 break;
105
0ff6ff2b
LP
106 p = startswith(line, field);
107 if (!p)
9a00f57a
LP
108 continue;
109
9a00f57a
LP
110 p += strspn(p, WHITESPACE);
111
112 if (*p != '=')
113 continue;
114 p++;
115
116 p += strspn(p, WHITESPACE);
117
118 if (*p != '"')
119 continue;
120 p++;
121
122 e = strrchr(p, '"');
123 if (!e)
124 continue;
125 *e = 0;
126
127 /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
128 if (startswith(p, "$HOME/")) {
129 _cleanup_free_ char *h = NULL;
9a00f57a
LP
130
131 r = get_home_dir(&h);
132 if (r < 0)
133 return r;
134
4ede9802 135 if (!path_extend(&h, p+5))
9a00f57a
LP
136 return -ENOMEM;
137
4ede9802
LP
138 *buffer = h;
139 *ret = TAKE_PTR(h);
9a00f57a
LP
140 return 0;
141 } else if (streq(p, "$HOME")) {
142
143 r = get_home_dir(buffer);
144 if (r < 0)
145 return r;
146
147 *ret = *buffer;
148 return 0;
149 } else if (path_is_absolute(p)) {
150 char *copy;
151
152 copy = strdup(p);
153 if (!copy)
154 return -ENOMEM;
155
156 *buffer = copy;
157 *ret = copy;
158 return 0;
159 }
160 }
161
162fallback:
163 /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
164 if (streq(field, "XDG_DESKTOP_DIR")) {
165 _cleanup_free_ char *h = NULL;
9a00f57a
LP
166
167 r = get_home_dir(&h);
168 if (r < 0)
169 return r;
170
4ede9802 171 if (!path_extend(&h, "Desktop"))
9a00f57a
LP
172 return -ENOMEM;
173
4ede9802
LP
174 *buffer = h;
175 *ret = TAKE_PTR(h);
9a00f57a 176 } else {
9a00f57a
LP
177 r = get_home_dir(buffer);
178 if (r < 0)
179 return r;
180
181 *ret = *buffer;
182 }
183
184 return 0;
185}
186
187static int get_path(uint64_t type, char **buffer, const char **ret) {
188 int r;
189
190 assert(buffer);
191 assert(ret);
192
193 switch (type) {
194
195 case SD_PATH_TEMPORARY:
fccb4486 196 return tmp_dir(ret);
9a00f57a
LP
197
198 case SD_PATH_TEMPORARY_LARGE:
fccb4486 199 return var_tmp_dir(ret);
9a00f57a
LP
200
201 case SD_PATH_SYSTEM_BINARIES:
202 *ret = "/usr/bin";
203 return 0;
204
205 case SD_PATH_SYSTEM_INCLUDE:
206 *ret = "/usr/include";
207 return 0;
208
209 case SD_PATH_SYSTEM_LIBRARY_PRIVATE:
210 *ret = "/usr/lib";
211 return 0;
212
213 case SD_PATH_SYSTEM_LIBRARY_ARCH:
214 *ret = LIBDIR;
215 return 0;
216
217 case SD_PATH_SYSTEM_SHARED:
218 *ret = "/usr/share";
219 return 0;
220
221 case SD_PATH_SYSTEM_CONFIGURATION_FACTORY:
222 *ret = "/usr/share/factory/etc";
223 return 0;
224
225 case SD_PATH_SYSTEM_STATE_FACTORY:
226 *ret = "/usr/share/factory/var";
227 return 0;
228
229 case SD_PATH_SYSTEM_CONFIGURATION:
230 *ret = "/etc";
231 return 0;
232
233 case SD_PATH_SYSTEM_RUNTIME:
234 *ret = "/run";
235 return 0;
236
237 case SD_PATH_SYSTEM_RUNTIME_LOGS:
238 *ret = "/run/log";
239 return 0;
240
241 case SD_PATH_SYSTEM_STATE_PRIVATE:
242 *ret = "/var/lib";
243 return 0;
244
245 case SD_PATH_SYSTEM_STATE_LOGS:
246 *ret = "/var/log";
247 return 0;
248
249 case SD_PATH_SYSTEM_STATE_CACHE:
250 *ret = "/var/cache";
251 return 0;
252
253 case SD_PATH_SYSTEM_STATE_SPOOL:
254 *ret = "/var/spool";
255 return 0;
256
257 case SD_PATH_USER_BINARIES:
258 return from_home_dir(NULL, ".local/bin", buffer, ret);
259
260 case SD_PATH_USER_LIBRARY_PRIVATE:
261 return from_home_dir(NULL, ".local/lib", buffer, ret);
262
263 case SD_PATH_USER_LIBRARY_ARCH:
613e3a26 264 return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret);
9a00f57a
LP
265
266 case SD_PATH_USER_SHARED:
267 return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret);
268
269 case SD_PATH_USER_CONFIGURATION:
270 return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret);
271
272 case SD_PATH_USER_RUNTIME:
273 return from_environment("XDG_RUNTIME_DIR", NULL, ret);
274
275 case SD_PATH_USER_STATE_CACHE:
276 return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret);
277
9a653235
LP
278 case SD_PATH_USER_STATE_PRIVATE:
279 return from_home_dir("XDG_STATE_HOME", ".local/state", buffer, ret);
280
9a00f57a
LP
281 case SD_PATH_USER:
282 r = get_home_dir(buffer);
283 if (r < 0)
284 return r;
285
286 *ret = *buffer;
287 return 0;
288
289 case SD_PATH_USER_DOCUMENTS:
290 return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
291
292 case SD_PATH_USER_MUSIC:
293 return from_user_dir("XDG_MUSIC_DIR", buffer, ret);
294
295 case SD_PATH_USER_PICTURES:
296 return from_user_dir("XDG_PICTURES_DIR", buffer, ret);
297
298 case SD_PATH_USER_VIDEOS:
299 return from_user_dir("XDG_VIDEOS_DIR", buffer, ret);
300
301 case SD_PATH_USER_DOWNLOAD:
302 return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
303
304 case SD_PATH_USER_PUBLIC:
305 return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
306
307 case SD_PATH_USER_TEMPLATES:
308 return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
309
310 case SD_PATH_USER_DESKTOP:
311 return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
9c5bb203 312
ccdf03b9 313 case SD_PATH_SYSTEMD_UTIL:
b0d3095f 314 *ret = PREFIX_NOSLASH "/lib/systemd";
9c5bb203
ZJS
315 return 0;
316
ccdf03b9 317 case SD_PATH_SYSTEMD_SYSTEM_UNIT:
835cf75a 318 *ret = SYSTEM_DATA_UNIT_DIR;
9c5bb203
ZJS
319 return 0;
320
ccdf03b9 321 case SD_PATH_SYSTEMD_SYSTEM_PRESET:
b0d3095f 322 *ret = PREFIX_NOSLASH "/lib/systemd/system-preset";
9c5bb203
ZJS
323 return 0;
324
ccdf03b9 325 case SD_PATH_SYSTEMD_USER_UNIT:
96d33e4a 326 *ret = USER_DATA_UNIT_DIR;
9c5bb203
ZJS
327 return 0;
328
ccdf03b9 329 case SD_PATH_SYSTEMD_USER_PRESET:
b0d3095f 330 *ret = PREFIX_NOSLASH "/lib/systemd/user-preset";
9c5bb203
ZJS
331 return 0;
332
ccdf03b9 333 case SD_PATH_SYSTEMD_SYSTEM_CONF:
96d33e4a 334 *ret = SYSTEM_CONFIG_UNIT_DIR;
9c5bb203
ZJS
335 return 0;
336
ccdf03b9 337 case SD_PATH_SYSTEMD_USER_CONF:
96d33e4a 338 *ret = USER_CONFIG_UNIT_DIR;
9c5bb203
ZJS
339 return 0;
340
ccdf03b9 341 case SD_PATH_SYSTEMD_SYSTEM_GENERATOR:
96d33e4a 342 *ret = SYSTEM_GENERATOR_DIR;
9c5bb203
ZJS
343 return 0;
344
ccdf03b9 345 case SD_PATH_SYSTEMD_USER_GENERATOR:
96d33e4a 346 *ret = USER_GENERATOR_DIR;
9c5bb203
ZJS
347 return 0;
348
ccdf03b9 349 case SD_PATH_SYSTEMD_SLEEP:
b0d3095f 350 *ret = PREFIX_NOSLASH "/lib/systemd/system-sleep";
9c5bb203
ZJS
351 return 0;
352
ccdf03b9 353 case SD_PATH_SYSTEMD_SHUTDOWN:
b0d3095f 354 *ret = PREFIX_NOSLASH "/lib/systemd/system-shutdown";
9c5bb203
ZJS
355 return 0;
356
ccdf03b9 357 case SD_PATH_TMPFILES:
9c5bb203
ZJS
358 *ret = "/usr/lib/tmpfiles.d";
359 return 0;
360
ccdf03b9 361 case SD_PATH_SYSUSERS:
b0d3095f 362 *ret = PREFIX_NOSLASH "/lib/sysusers.d";
9c5bb203
ZJS
363 return 0;
364
ccdf03b9 365 case SD_PATH_SYSCTL:
b0d3095f 366 *ret = PREFIX_NOSLASH "/lib/sysctl.d";
9c5bb203
ZJS
367 return 0;
368
ccdf03b9 369 case SD_PATH_BINFMT:
b0d3095f 370 *ret = PREFIX_NOSLASH "/lib/binfmt.d";
9c5bb203
ZJS
371 return 0;
372
ccdf03b9 373 case SD_PATH_MODULES_LOAD:
b0d3095f 374 *ret = PREFIX_NOSLASH "/lib/modules-load.d";
9c5bb203
ZJS
375 return 0;
376
ccdf03b9 377 case SD_PATH_CATALOG:
9c5bb203
ZJS
378 *ret = "/usr/lib/systemd/catalog";
379 return 0;
71006538
DT
380
381 case SD_PATH_SYSTEMD_SYSTEM_ENVIRONMENT_GENERATOR:
382 *ret = SYSTEM_ENV_GENERATOR_DIR;
383 return 0;
384
385 case SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR:
386 *ret = USER_ENV_GENERATOR_DIR;
387 return 0;
9a00f57a
LP
388 }
389
15411c0c 390 return -EOPNOTSUPP;
9a00f57a
LP
391}
392
ce7eb6aa 393static int get_path_alloc(uint64_t type, const char *suffix, char **path) {
a13de89d 394 _cleanup_free_ char *buffer = NULL;
ce7eb6aa 395 char *buffer2 = NULL;
9a00f57a
LP
396 const char *ret;
397 int r;
398
ce7eb6aa 399 assert(path);
9a00f57a 400
ce7eb6aa
ZJS
401 r = get_path(type, &buffer, &ret);
402 if (r < 0)
403 return r;
9a00f57a 404
ce7eb6aa
ZJS
405 if (suffix) {
406 suffix += strspn(suffix, "/");
407 buffer2 = path_join(ret, suffix);
408 if (!buffer2)
409 return -ENOMEM;
410 } else if (!buffer) {
411 buffer = strdup(ret);
9a00f57a
LP
412 if (!buffer)
413 return -ENOMEM;
9a00f57a
LP
414 }
415
ce7eb6aa
ZJS
416 *path = buffer2 ?: TAKE_PTR(buffer);
417 return 0;
418}
419
420_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
421 int r;
422
423 assert_return(path, -EINVAL);
424
425 r = get_path_alloc(type, suffix, path);
426 if (r != -EOPNOTSUPP)
9a00f57a
LP
427 return r;
428
ce7eb6aa
ZJS
429 /* Fall back to sd_path_lookup_strv */
430 _cleanup_strv_free_ char **l = NULL;
431 char *buffer;
9a00f57a 432
ce7eb6aa
ZJS
433 r = sd_path_lookup_strv(type, suffix, &l);
434 if (r < 0)
435 return r;
9a00f57a 436
ce7eb6aa
ZJS
437 buffer = strv_join(l, ":");
438 if (!buffer)
9a00f57a
LP
439 return -ENOMEM;
440
ce7eb6aa 441 *path = buffer;
9a00f57a
LP
442 return 0;
443}
444
445static int search_from_environment(
c1fa6427 446 char ***ret,
9a00f57a
LP
447 const char *env_home,
448 const char *home_suffix,
449 const char *env_search,
450 bool env_search_sufficient,
451 const char *first, ...) {
452
623550af 453 _cleanup_strv_free_ char **l = NULL;
9a00f57a
LP
454 const char *e;
455 char *h = NULL;
9a00f57a
LP
456 int r;
457
c1fa6427 458 assert(ret);
9a00f57a
LP
459
460 if (env_search) {
461 e = secure_getenv(env_search);
462 if (e) {
463 l = strv_split(e, ":");
464 if (!l)
465 return -ENOMEM;
466
467 if (env_search_sufficient) {
c1fa6427 468 *ret = TAKE_PTR(l);
9a00f57a
LP
469 return 0;
470 }
471 }
472 }
473
474 if (!l && first) {
475 va_list ap;
476
477 va_start(ap, first);
478 l = strv_new_ap(first, ap);
479 va_end(ap);
480
481 if (!l)
482 return -ENOMEM;
483 }
484
485 if (env_home) {
486 e = secure_getenv(env_home);
487 if (e && path_is_absolute(e)) {
488 h = strdup(e);
623550af 489 if (!h)
9a00f57a 490 return -ENOMEM;
9a00f57a
LP
491 }
492 }
493
494 if (!h && home_suffix) {
495 e = secure_getenv("HOME");
496 if (e && path_is_absolute(e)) {
657ee2d8 497 h = path_join(e, home_suffix);
623550af 498 if (!h)
9a00f57a 499 return -ENOMEM;
9a00f57a
LP
500 }
501 }
502
503 if (h) {
504 r = strv_consume_prepend(&l, h);
623550af 505 if (r < 0)
9a00f57a 506 return -ENOMEM;
9a00f57a
LP
507 }
508
c1fa6427 509 *ret = TAKE_PTR(l);
9a00f57a
LP
510 return 0;
511}
512
671f0f8d
ZJS
513#if HAVE_SPLIT_BIN
514# define ARRAY_SBIN_BIN(x) x "sbin", x "bin"
515#else
516# define ARRAY_SBIN_BIN(x) x "bin"
517#endif
518
c1fa6427 519static int get_search(uint64_t type, char ***ret) {
9c5bb203 520 int r;
9a00f57a 521
c1fa6427 522 assert(ret);
9a00f57a 523
79893116 524 switch (type) {
9a00f57a
LP
525
526 case SD_PATH_SEARCH_BINARIES:
c1fa6427 527 return search_from_environment(ret,
9a00f57a
LP
528 NULL,
529 ".local/bin",
530 "PATH",
531 true,
671f0f8d
ZJS
532 ARRAY_SBIN_BIN("/usr/local/"),
533 ARRAY_SBIN_BIN("/usr/"),
9a00f57a
LP
534 NULL);
535
536 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
c1fa6427 537 return search_from_environment(ret,
9a00f57a
LP
538 NULL,
539 ".local/lib",
540 NULL,
541 false,
542 "/usr/local/lib",
543 "/usr/lib",
9a00f57a
LP
544 NULL);
545
546 case SD_PATH_SEARCH_LIBRARY_ARCH:
c1fa6427 547 return search_from_environment(ret,
9a00f57a 548 NULL,
613e3a26 549 ".local/lib/" LIB_ARCH_TUPLE,
9a00f57a
LP
550 "LD_LIBRARY_PATH",
551 true,
552 LIBDIR,
9a00f57a
LP
553 NULL);
554
555 case SD_PATH_SEARCH_SHARED:
c1fa6427 556 return search_from_environment(ret,
9a00f57a
LP
557 "XDG_DATA_HOME",
558 ".local/share",
559 "XDG_DATA_DIRS",
560 false,
561 "/usr/local/share",
562 "/usr/share",
563 NULL);
564
565 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
c1fa6427 566 return search_from_environment(ret,
9a00f57a
LP
567 NULL,
568 NULL,
569 NULL,
570 false,
571 "/usr/local/share/factory/etc",
572 "/usr/share/factory/etc",
573 NULL);
574
575 case SD_PATH_SEARCH_STATE_FACTORY:
c1fa6427 576 return search_from_environment(ret,
9a00f57a
LP
577 NULL,
578 NULL,
579 NULL,
580 false,
581 "/usr/local/share/factory/var",
582 "/usr/share/factory/var",
583 NULL);
584
585 case SD_PATH_SEARCH_CONFIGURATION:
c1fa6427 586 return search_from_environment(ret,
9a00f57a
LP
587 "XDG_CONFIG_HOME",
588 ".config",
589 "XDG_CONFIG_DIRS",
590 false,
591 "/etc",
592 NULL);
e12d446b 593
0f36a4c8
ZJS
594 case SD_PATH_SEARCH_BINARIES_DEFAULT: {
595 char **t = strv_split(default_PATH(), ":");
596 if (!t)
597 return -ENOMEM;
598
c1fa6427 599 *ret = t;
0f36a4c8
ZJS
600 return 0;
601 }
e12d446b 602
ccdf03b9
LP
603 case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT:
604 case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: {
7dfc7139 605 _cleanup_(lookup_paths_done) LookupPaths lp = {};
4870133b
LP
606 RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ?
607 RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
9c5bb203
ZJS
608
609 r = lookup_paths_init(&lp, scope, 0, NULL);
610 if (r < 0)
611 return r;
e12d446b 612
c1fa6427 613 *ret = TAKE_PTR(lp.search_path);
9c5bb203 614 return 0;
a7addf32 615 }
9a00f57a 616
ccdf03b9
LP
617 case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
618 case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: {
4870133b
LP
619 RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ?
620 RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
9c5bb203 621 char **t;
9c5bb203
ZJS
622
623 t = generator_binary_paths(scope);
624 if (!t)
625 return -ENOMEM;
626
c1fa6427 627 *ret = t;
9c5bb203 628 return 0;
f1bb691a
ZJS
629 }
630
71006538
DT
631 case SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR:
632 case SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR: {
633 char **t;
634
4870133b
LP
635 t = env_generator_binary_paths(type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR ?
636 RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER);
71006538
DT
637 if (!t)
638 return -ENOMEM;
639
c1fa6427 640 *ret = t;
71006538
DT
641 return 0;
642 }
643
ccdf03b9 644 case SD_PATH_SYSTEMD_SEARCH_NETWORK:
c1fa6427 645 return strv_from_nulstr(ret, NETWORK_DIRS_NULSTR);
f1bb691a
ZJS
646
647 }
9c5bb203 648
15411c0c 649 return -EOPNOTSUPP;
9a00f57a
LP
650}
651
51327bcc 652_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) {
a605e46f 653 _cleanup_strv_free_ char **l = NULL, **n = NULL;
9a00f57a
LP
654 int r;
655
656 assert_return(paths, -EINVAL);
657
ce7eb6aa
ZJS
658 r = get_search(type, &l);
659 if (r == -EOPNOTSUPP) {
660 _cleanup_free_ char *t = NULL;
9a00f57a 661
ce7eb6aa 662 r = get_path_alloc(type, suffix, &t);
9a00f57a
LP
663 if (r < 0)
664 return r;
665
666 l = new(char*, 2);
ce7eb6aa 667 if (!l)
9a00f57a 668 return -ENOMEM;
ce7eb6aa 669 l[0] = TAKE_PTR(t);
9a00f57a
LP
670 l[1] = NULL;
671
a605e46f 672 *paths = TAKE_PTR(l);
9a00f57a 673 return 0;
9a00f57a 674
ce7eb6aa 675 } else if (r < 0)
9a00f57a
LP
676 return r;
677
678 if (!suffix) {
a605e46f 679 *paths = TAKE_PTR(l);
9a00f57a
LP
680 return 0;
681 }
682
683 n = new(char*, strv_length(l)+1);
a605e46f 684 if (!n)
9a00f57a 685 return -ENOMEM;
9a00f57a 686
de010b0b 687 char **j = n;
9a00f57a 688 STRV_FOREACH(i, l) {
657ee2d8 689 *j = path_join(*i, suffix);
a605e46f 690 if (!*j)
9a00f57a 691 return -ENOMEM;
9a00f57a
LP
692
693 j++;
694 }
9a00f57a 695 *j = NULL;
ce7eb6aa 696
a605e46f 697 *paths = TAKE_PTR(n);
9a00f57a
LP
698 return 0;
699}