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