]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-path/sd-path.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / libsystemd / sd-path / sd-path.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2014 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
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
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
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include "sd-path.h"
21
22 #include "alloc-util.h"
23 #include "architecture.h"
24 #include "fd-util.h"
25 #include "fileio.h"
26 #include "missing.h"
27 #include "path-util.h"
28 #include "string-util.h"
29 #include "strv.h"
30 #include "user-util.h"
31 #include "util.h"
32
33 static int from_environment(const char *envname, const char *fallback, const char **ret) {
34 assert(ret);
35
36 if (envname) {
37 const char *e;
38
39 e = secure_getenv(envname);
40 if (e && path_is_absolute(e)) {
41 *ret = e;
42 return 0;
43 }
44 }
45
46 if (fallback) {
47 *ret = fallback;
48 return 0;
49 }
50
51 return -ENXIO;
52 }
53
54 static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
55 _cleanup_free_ char *h = NULL;
56 char *cc = NULL;
57 int r;
58
59 assert(suffix);
60 assert(buffer);
61 assert(ret);
62
63 if (envname) {
64 const char *e = NULL;
65
66 e = secure_getenv(envname);
67 if (e && path_is_absolute(e)) {
68 *ret = e;
69 return 0;
70 }
71 }
72
73 r = get_home_dir(&h);
74 if (r < 0)
75 return r;
76
77 if (endswith(h, "/"))
78 cc = strappend(h, suffix);
79 else
80 cc = strjoin(h, "/", suffix, NULL);
81 if (!cc)
82 return -ENOMEM;
83
84 *buffer = cc;
85 *ret = cc;
86 return 0;
87 }
88
89 static int from_user_dir(const char *field, char **buffer, const char **ret) {
90 _cleanup_fclose_ FILE *f = NULL;
91 _cleanup_free_ char *b = NULL;
92 const char *fn = NULL;
93 char line[LINE_MAX];
94 size_t n;
95 int r;
96
97 assert(field);
98 assert(buffer);
99 assert(ret);
100
101 r = from_home_dir(NULL, ".config/user-dirs.dirs", &b, &fn);
102 if (r < 0)
103 return r;
104
105 f = fopen(fn, "re");
106 if (!f) {
107 if (errno == ENOENT)
108 goto fallback;
109
110 return -errno;
111 }
112
113 /* This is an awful parse, but it follows closely what
114 * xdg-user-dirs does upstream */
115
116 n = strlen(field);
117 FOREACH_LINE(line, f, return -errno) {
118 char *l, *p, *e;
119
120 l = strstrip(line);
121
122 if (!strneq(l, field, n))
123 continue;
124
125 p = l + n;
126 p += strspn(p, WHITESPACE);
127
128 if (*p != '=')
129 continue;
130 p++;
131
132 p += strspn(p, WHITESPACE);
133
134 if (*p != '"')
135 continue;
136 p++;
137
138 e = strrchr(p, '"');
139 if (!e)
140 continue;
141 *e = 0;
142
143 /* Three syntaxes permitted: relative to $HOME, $HOME itself, and absolute path */
144 if (startswith(p, "$HOME/")) {
145 _cleanup_free_ char *h = NULL;
146 char *cc;
147
148 r = get_home_dir(&h);
149 if (r < 0)
150 return r;
151
152 cc = strappend(h, p+5);
153 if (!cc)
154 return -ENOMEM;
155
156 *buffer = cc;
157 *ret = cc;
158 return 0;
159 } else if (streq(p, "$HOME")) {
160
161 r = get_home_dir(buffer);
162 if (r < 0)
163 return r;
164
165 *ret = *buffer;
166 return 0;
167 } else if (path_is_absolute(p)) {
168 char *copy;
169
170 copy = strdup(p);
171 if (!copy)
172 return -ENOMEM;
173
174 *buffer = copy;
175 *ret = copy;
176 return 0;
177 }
178 }
179
180 fallback:
181 /* The desktop directory defaults to $HOME/Desktop, the others to $HOME */
182 if (streq(field, "XDG_DESKTOP_DIR")) {
183 _cleanup_free_ char *h = NULL;
184 char *cc;
185
186 r = get_home_dir(&h);
187 if (r < 0)
188 return r;
189
190 cc = strappend(h, "/Desktop");
191 if (!cc)
192 return -ENOMEM;
193
194 *buffer = cc;
195 *ret = cc;
196 } else {
197
198 r = get_home_dir(buffer);
199 if (r < 0)
200 return r;
201
202 *ret = *buffer;
203 }
204
205 return 0;
206 }
207
208 static int get_path(uint64_t type, char **buffer, const char **ret) {
209 int r;
210
211 assert(buffer);
212 assert(ret);
213
214 switch (type) {
215
216 case SD_PATH_TEMPORARY:
217 return from_environment("TMPDIR", "/tmp", ret);
218
219 case SD_PATH_TEMPORARY_LARGE:
220 return from_environment("TMPDIR", "/var/tmp", ret);
221
222 case SD_PATH_SYSTEM_BINARIES:
223 *ret = "/usr/bin";
224 return 0;
225
226 case SD_PATH_SYSTEM_INCLUDE:
227 *ret = "/usr/include";
228 return 0;
229
230 case SD_PATH_SYSTEM_LIBRARY_PRIVATE:
231 *ret = "/usr/lib";
232 return 0;
233
234 case SD_PATH_SYSTEM_LIBRARY_ARCH:
235 *ret = LIBDIR;
236 return 0;
237
238 case SD_PATH_SYSTEM_SHARED:
239 *ret = "/usr/share";
240 return 0;
241
242 case SD_PATH_SYSTEM_CONFIGURATION_FACTORY:
243 *ret = "/usr/share/factory/etc";
244 return 0;
245
246 case SD_PATH_SYSTEM_STATE_FACTORY:
247 *ret = "/usr/share/factory/var";
248 return 0;
249
250 case SD_PATH_SYSTEM_CONFIGURATION:
251 *ret = "/etc";
252 return 0;
253
254 case SD_PATH_SYSTEM_RUNTIME:
255 *ret = "/run";
256 return 0;
257
258 case SD_PATH_SYSTEM_RUNTIME_LOGS:
259 *ret = "/run/log";
260 return 0;
261
262 case SD_PATH_SYSTEM_STATE_PRIVATE:
263 *ret = "/var/lib";
264 return 0;
265
266 case SD_PATH_SYSTEM_STATE_LOGS:
267 *ret = "/var/log";
268 return 0;
269
270 case SD_PATH_SYSTEM_STATE_CACHE:
271 *ret = "/var/cache";
272 return 0;
273
274 case SD_PATH_SYSTEM_STATE_SPOOL:
275 *ret = "/var/spool";
276 return 0;
277
278 case SD_PATH_USER_BINARIES:
279 return from_home_dir(NULL, ".local/bin", buffer, ret);
280
281 case SD_PATH_USER_LIBRARY_PRIVATE:
282 return from_home_dir(NULL, ".local/lib", buffer, ret);
283
284 case SD_PATH_USER_LIBRARY_ARCH:
285 return from_home_dir(NULL, ".local/lib/" LIB_ARCH_TUPLE, buffer, ret);
286
287 case SD_PATH_USER_SHARED:
288 return from_home_dir("XDG_DATA_HOME", ".local/share", buffer, ret);
289
290 case SD_PATH_USER_CONFIGURATION:
291 return from_home_dir("XDG_CONFIG_HOME", ".config", buffer, ret);
292
293 case SD_PATH_USER_RUNTIME:
294 return from_environment("XDG_RUNTIME_DIR", NULL, ret);
295
296 case SD_PATH_USER_STATE_CACHE:
297 return from_home_dir("XDG_CACHE_HOME", ".cache", buffer, ret);
298
299 case SD_PATH_USER:
300 r = get_home_dir(buffer);
301 if (r < 0)
302 return r;
303
304 *ret = *buffer;
305 return 0;
306
307 case SD_PATH_USER_DOCUMENTS:
308 return from_user_dir("XDG_DOCUMENTS_DIR", buffer, ret);
309
310 case SD_PATH_USER_MUSIC:
311 return from_user_dir("XDG_MUSIC_DIR", buffer, ret);
312
313 case SD_PATH_USER_PICTURES:
314 return from_user_dir("XDG_PICTURES_DIR", buffer, ret);
315
316 case SD_PATH_USER_VIDEOS:
317 return from_user_dir("XDG_VIDEOS_DIR", buffer, ret);
318
319 case SD_PATH_USER_DOWNLOAD:
320 return from_user_dir("XDG_DOWNLOAD_DIR", buffer, ret);
321
322 case SD_PATH_USER_PUBLIC:
323 return from_user_dir("XDG_PUBLICSHARE_DIR", buffer, ret);
324
325 case SD_PATH_USER_TEMPLATES:
326 return from_user_dir("XDG_TEMPLATES_DIR", buffer, ret);
327
328 case SD_PATH_USER_DESKTOP:
329 return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
330 }
331
332 return -EOPNOTSUPP;
333 }
334
335 _public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
336 char *buffer = NULL, *cc;
337 const char *ret;
338 int r;
339
340 assert_return(path, -EINVAL);
341
342 if (IN_SET(type,
343 SD_PATH_SEARCH_BINARIES,
344 SD_PATH_SEARCH_LIBRARY_PRIVATE,
345 SD_PATH_SEARCH_LIBRARY_ARCH,
346 SD_PATH_SEARCH_SHARED,
347 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
348 SD_PATH_SEARCH_STATE_FACTORY,
349 SD_PATH_SEARCH_CONFIGURATION)) {
350
351 _cleanup_strv_free_ char **l = NULL;
352
353 r = sd_path_search(type, suffix, &l);
354 if (r < 0)
355 return r;
356
357 buffer = strv_join(l, ":");
358 if (!buffer)
359 return -ENOMEM;
360
361 *path = buffer;
362 return 0;
363 }
364
365 r = get_path(type, &buffer, &ret);
366 if (r < 0)
367 return r;
368
369 if (!suffix) {
370 if (!buffer) {
371 buffer = strdup(ret);
372 if (!buffer)
373 return -ENOMEM;
374 }
375
376 *path = buffer;
377 return 0;
378 }
379
380 suffix += strspn(suffix, "/");
381
382 if (endswith(ret, "/"))
383 cc = strappend(ret, suffix);
384 else
385 cc = strjoin(ret, "/", suffix, NULL);
386
387 free(buffer);
388
389 if (!cc)
390 return -ENOMEM;
391
392 *path = cc;
393 return 0;
394 }
395
396 static int search_from_environment(
397 char ***list,
398 const char *env_home,
399 const char *home_suffix,
400 const char *env_search,
401 bool env_search_sufficient,
402 const char *first, ...) {
403
404 const char *e;
405 char *h = NULL;
406 char **l = NULL;
407 int r;
408
409 assert(list);
410
411 if (env_search) {
412 e = secure_getenv(env_search);
413 if (e) {
414 l = strv_split(e, ":");
415 if (!l)
416 return -ENOMEM;
417
418 if (env_search_sufficient) {
419 *list = l;
420 return 0;
421 }
422 }
423 }
424
425 if (!l && first) {
426 va_list ap;
427
428 va_start(ap, first);
429 l = strv_new_ap(first, ap);
430 va_end(ap);
431
432 if (!l)
433 return -ENOMEM;
434 }
435
436 if (env_home) {
437 e = secure_getenv(env_home);
438 if (e && path_is_absolute(e)) {
439 h = strdup(e);
440 if (!h) {
441 strv_free(l);
442 return -ENOMEM;
443 }
444 }
445 }
446
447 if (!h && home_suffix) {
448 e = secure_getenv("HOME");
449 if (e && path_is_absolute(e)) {
450 if (endswith(e, "/"))
451 h = strappend(e, home_suffix);
452 else
453 h = strjoin(e, "/", home_suffix, NULL);
454
455 if (!h) {
456 strv_free(l);
457 return -ENOMEM;
458 }
459 }
460 }
461
462 if (h) {
463 r = strv_consume_prepend(&l, h);
464 if (r < 0) {
465 strv_free(l);
466 return -ENOMEM;
467 }
468 }
469
470 *list = l;
471 return 0;
472 }
473
474 static int get_search(uint64_t type, char ***list) {
475
476 assert(list);
477
478 switch(type) {
479
480 case SD_PATH_SEARCH_BINARIES:
481 return search_from_environment(list,
482 NULL,
483 ".local/bin",
484 "PATH",
485 true,
486 "/usr/local/sbin",
487 "/usr/local/bin",
488 "/usr/sbin",
489 "/usr/bin",
490 #ifdef HAVE_SPLIT_USR
491 "/sbin",
492 "/bin",
493 #endif
494 NULL);
495
496 case SD_PATH_SEARCH_LIBRARY_PRIVATE:
497 return search_from_environment(list,
498 NULL,
499 ".local/lib",
500 NULL,
501 false,
502 "/usr/local/lib",
503 "/usr/lib",
504 #ifdef HAVE_SPLIT_USR
505 "/lib",
506 #endif
507 NULL);
508
509 case SD_PATH_SEARCH_LIBRARY_ARCH:
510 return search_from_environment(list,
511 NULL,
512 ".local/lib/" LIB_ARCH_TUPLE,
513 "LD_LIBRARY_PATH",
514 true,
515 LIBDIR,
516 #ifdef HAVE_SPLIT_USR
517 ROOTLIBDIR,
518 #endif
519 NULL);
520
521 case SD_PATH_SEARCH_SHARED:
522 return search_from_environment(list,
523 "XDG_DATA_HOME",
524 ".local/share",
525 "XDG_DATA_DIRS",
526 false,
527 "/usr/local/share",
528 "/usr/share",
529 NULL);
530
531 case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
532 return search_from_environment(list,
533 NULL,
534 NULL,
535 NULL,
536 false,
537 "/usr/local/share/factory/etc",
538 "/usr/share/factory/etc",
539 NULL);
540
541 case SD_PATH_SEARCH_STATE_FACTORY:
542 return search_from_environment(list,
543 NULL,
544 NULL,
545 NULL,
546 false,
547 "/usr/local/share/factory/var",
548 "/usr/share/factory/var",
549 NULL);
550
551 case SD_PATH_SEARCH_CONFIGURATION:
552 return search_from_environment(list,
553 "XDG_CONFIG_HOME",
554 ".config",
555 "XDG_CONFIG_DIRS",
556 false,
557 "/etc",
558 NULL);
559 }
560
561 return -EOPNOTSUPP;
562 }
563
564 _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
565 char **l, **i, **j, **n;
566 int r;
567
568 assert_return(paths, -EINVAL);
569
570 if (!IN_SET(type,
571 SD_PATH_SEARCH_BINARIES,
572 SD_PATH_SEARCH_LIBRARY_PRIVATE,
573 SD_PATH_SEARCH_LIBRARY_ARCH,
574 SD_PATH_SEARCH_SHARED,
575 SD_PATH_SEARCH_CONFIGURATION_FACTORY,
576 SD_PATH_SEARCH_STATE_FACTORY,
577 SD_PATH_SEARCH_CONFIGURATION)) {
578
579 char *p;
580
581 r = sd_path_home(type, suffix, &p);
582 if (r < 0)
583 return r;
584
585 l = new(char*, 2);
586 if (!l) {
587 free(p);
588 return -ENOMEM;
589 }
590
591 l[0] = p;
592 l[1] = NULL;
593
594 *paths = l;
595 return 0;
596 }
597
598 r = get_search(type, &l);
599 if (r < 0)
600 return r;
601
602 if (!suffix) {
603 *paths = l;
604 return 0;
605 }
606
607 n = new(char*, strv_length(l)+1);
608 if (!n) {
609 strv_free(l);
610 return -ENOMEM;
611 }
612
613 j = n;
614 STRV_FOREACH(i, l) {
615
616 if (endswith(*i, "/"))
617 *j = strappend(*i, suffix);
618 else
619 *j = strjoin(*i, "/", suffix, NULL);
620
621 if (!*j) {
622 strv_free(l);
623 strv_free(n);
624 return -ENOMEM;
625 }
626
627 j++;
628 }
629
630 *j = NULL;
631 *paths = n;
632 return 0;
633 }