]> git.proxmox.com Git - systemd.git/blame - src/shared/path-lookup.c
Imported Upstream version 217
[systemd.git] / src / shared / path-lookup.c
CommitLineData
663996b3
MS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 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 <assert.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <unistd.h>
27#include <errno.h>
28
29#include "util.h"
30#include "mkdir.h"
31#include "strv.h"
32#include "path-util.h"
33#include "path-lookup.h"
34
663996b3
MS
35int user_config_home(char **config_home) {
36 const char *e;
37 char *r;
38
39 e = getenv("XDG_CONFIG_HOME");
40 if (e) {
41 r = strappend(e, "/systemd/user");
42 if (!r)
43 return -ENOMEM;
44
45 *config_home = r;
46 return 1;
47 } else {
48 const char *home;
49
50 home = getenv("HOME");
51 if (home) {
52 r = strappend(home, "/.config/systemd/user");
53 if (!r)
54 return -ENOMEM;
55
56 *config_home = r;
57 return 1;
58 }
59 }
60
61 return 0;
62}
63
5eef597e
MP
64int user_runtime_dir(char **runtime_dir) {
65 const char *e;
66 char *r;
67
68 e = getenv("XDG_RUNTIME_DIR");
69 if (e) {
70 r = strappend(e, "/systemd/user");
71 if (!r)
72 return -ENOMEM;
73
74 *runtime_dir = r;
75 return 1;
76 }
77
78 return 0;
79}
80
663996b3
MS
81static char** user_dirs(
82 const char *generator,
83 const char *generator_early,
84 const char *generator_late) {
85
86 const char * const config_unit_paths[] = {
87 USER_CONFIG_UNIT_PATH,
88 "/etc/systemd/user",
663996b3
MS
89 NULL
90 };
91
5eef597e
MP
92 const char * const runtime_unit_path = "/run/systemd/user";
93
663996b3
MS
94 const char * const data_unit_paths[] = {
95 "/usr/local/lib/systemd/user",
96 "/usr/local/share/systemd/user",
97 USER_DATA_UNIT_PATH,
98 "/usr/lib/systemd/user",
99 "/usr/share/systemd/user",
100 NULL
101 };
102
103 const char *home, *e;
5eef597e 104 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
60f067b4
JS
105 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
106 char **r = NULL;
663996b3
MS
107
108 /* Implement the mechanisms defined in
109 *
110 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
111 *
112 * We look in both the config and the data dirs because we
113 * want to encourage that distributors ship their unit files
114 * as data, and allow overriding as configuration.
115 */
116
117 if (user_config_home(&config_home) < 0)
118 goto fail;
119
5eef597e
MP
120 if (user_runtime_dir(&runtime_dir) < 0)
121 goto fail;
122
663996b3
MS
123 home = getenv("HOME");
124
125 e = getenv("XDG_CONFIG_DIRS");
126 if (e) {
127 config_dirs = strv_split(e, ":");
128 if (!config_dirs)
129 goto fail;
130 }
131
132 /* We don't treat /etc/xdg/systemd here as the spec
133 * suggests because we assume that that is a link to
134 * /etc/systemd/ anyway. */
135
136 e = getenv("XDG_DATA_HOME");
137 if (e) {
138 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
139 goto fail;
140
141 } else if (home) {
142 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
143 goto fail;
663996b3
MS
144 }
145
146 e = getenv("XDG_DATA_DIRS");
147 if (e)
148 data_dirs = strv_split(e, ":");
149 else
150 data_dirs = strv_new("/usr/local/share",
151 "/usr/share",
152 NULL);
153 if (!data_dirs)
154 goto fail;
155
156 /* Now merge everything we found. */
60f067b4
JS
157 if (generator_early)
158 if (strv_extend(&r, generator_early) < 0)
663996b3 159 goto fail;
663996b3 160
60f067b4
JS
161 if (config_home)
162 if (strv_extend(&r, config_home) < 0)
663996b3 163 goto fail;
663996b3 164
60f067b4
JS
165 if (!strv_isempty(config_dirs))
166 if (strv_extend_strv_concat(&r, config_dirs, "/systemd/user") < 0)
167 goto fail;
663996b3 168
60f067b4 169 if (strv_extend_strv(&r, (char**) config_unit_paths) < 0)
663996b3 170 goto fail;
663996b3 171
5eef597e
MP
172 if (runtime_dir)
173 if (strv_extend(&r, runtime_dir) < 0)
174 goto fail;
175
176 if (strv_extend(&r, runtime_unit_path) < 0)
177 goto fail;
178
60f067b4
JS
179 if (generator)
180 if (strv_extend(&r, generator) < 0)
663996b3 181 goto fail;
663996b3 182
60f067b4
JS
183 if (data_home)
184 if (strv_extend(&r, data_home) < 0)
663996b3 185 goto fail;
663996b3 186
60f067b4
JS
187 if (!strv_isempty(data_dirs))
188 if (strv_extend_strv_concat(&r, data_dirs, "/systemd/user") < 0)
663996b3 189 goto fail;
663996b3 190
60f067b4 191 if (strv_extend_strv(&r, (char**) data_unit_paths) < 0)
663996b3 192 goto fail;
663996b3 193
60f067b4
JS
194 if (generator_late)
195 if (strv_extend(&r, generator_late) < 0)
663996b3 196 goto fail;
663996b3
MS
197
198 if (!path_strv_make_absolute_cwd(r))
199 goto fail;
200
663996b3
MS
201 return r;
202
203fail:
204 strv_free(r);
60f067b4 205 return NULL;
663996b3
MS
206}
207
208int lookup_paths_init(
209 LookupPaths *p,
210 SystemdRunningAs running_as,
211 bool personal,
60f067b4 212 const char *root_dir,
663996b3
MS
213 const char *generator,
214 const char *generator_early,
215 const char *generator_late) {
216
217 const char *e;
5eef597e 218 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
663996b3
MS
219
220 assert(p);
221
222 /* First priority is whatever has been passed to us via env
223 * vars */
224 e = getenv("SYSTEMD_UNIT_PATH");
225 if (e) {
5eef597e
MP
226 if (endswith(e, ":")) {
227 e = strndupa(e, strlen(e) - 1);
228 append = true;
229 }
230
231 /* FIXME: empty components in other places should be
232 * rejected. */
233
663996b3
MS
234 p->unit_path = path_split_and_make_absolute(e);
235 if (!p->unit_path)
236 return -ENOMEM;
237 } else
238 p->unit_path = NULL;
239
5eef597e
MP
240 if (!p->unit_path || append) {
241 /* Let's figure something out. */
242
243 _cleanup_strv_free_ char **unit_path;
244 int r;
663996b3
MS
245
246 /* For the user units we include share/ in the search
5eef597e
MP
247 * path in order to comply with the XDG basedir spec.
248 * For the system stuff we avoid such nonsense. OTOH
249 * we include /lib in the search path for the system
250 * stuff but avoid it for user stuff. */
663996b3
MS
251
252 if (running_as == SYSTEMD_USER) {
663996b3 253 if (personal)
5eef597e 254 unit_path = user_dirs(generator, generator_early, generator_late);
663996b3 255 else
5eef597e 256 unit_path = strv_new(
663996b3 257 /* If you modify this you also want to modify
5eef597e
MP
258 * systemduserunitpath= in systemd.pc.in, and
259 * the arrays in user_dirs() above! */
663996b3 260 STRV_IFNOTNULL(generator_early),
5eef597e
MP
261 USER_CONFIG_UNIT_PATH,
262 "/etc/systemd/user",
263 "/run/systemd/user",
663996b3 264 STRV_IFNOTNULL(generator),
5eef597e
MP
265 "/usr/local/lib/systemd/user",
266 "/usr/local/share/systemd/user",
267 USER_DATA_UNIT_PATH,
268 "/usr/lib/systemd/user",
269 "/usr/share/systemd/user",
663996b3
MS
270 STRV_IFNOTNULL(generator_late),
271 NULL);
5eef597e
MP
272 } else
273 unit_path = strv_new(
274 /* If you modify this you also want to modify
275 * systemdsystemunitpath= in systemd.pc.in! */
276 STRV_IFNOTNULL(generator_early),
277 SYSTEM_CONFIG_UNIT_PATH,
278 "/etc/systemd/system",
279 "/run/systemd/system",
280 STRV_IFNOTNULL(generator),
281 "/usr/local/lib/systemd/system",
282 SYSTEM_DATA_UNIT_PATH,
283 "/usr/lib/systemd/system",
284#ifdef HAVE_SPLIT_USR
285 "/lib/systemd/system",
286#endif
287 STRV_IFNOTNULL(generator_late),
288 NULL);
663996b3 289
5eef597e
MP
290 if (!unit_path)
291 return -ENOMEM;
292
293 r = strv_extend_strv(&p->unit_path, unit_path);
294 if (r < 0)
295 return r;
663996b3
MS
296 }
297
e842803a 298 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
663996b3
MS
299 return -ENOMEM;
300
663996b3
MS
301 if (!strv_isempty(p->unit_path)) {
302 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
303 if (!t)
304 return -ENOMEM;
305 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
306 } else {
307 log_debug("Ignoring unit files.");
308 strv_free(p->unit_path);
309 p->unit_path = NULL;
310 }
311
312 if (running_as == SYSTEMD_SYSTEM) {
313#ifdef HAVE_SYSV_COMPAT
314 /* /etc/init.d/ compatibility does not matter to users */
315
316 e = getenv("SYSTEMD_SYSVINIT_PATH");
317 if (e) {
318 p->sysvinit_path = path_split_and_make_absolute(e);
319 if (!p->sysvinit_path)
320 return -ENOMEM;
321 } else
322 p->sysvinit_path = NULL;
323
324 if (strv_isempty(p->sysvinit_path)) {
325 strv_free(p->sysvinit_path);
326
327 p->sysvinit_path = strv_new(
328 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
329 NULL);
330 if (!p->sysvinit_path)
331 return -ENOMEM;
332 }
333
334 e = getenv("SYSTEMD_SYSVRCND_PATH");
335 if (e) {
336 p->sysvrcnd_path = path_split_and_make_absolute(e);
337 if (!p->sysvrcnd_path)
338 return -ENOMEM;
339 } else
340 p->sysvrcnd_path = NULL;
341
342 if (strv_isempty(p->sysvrcnd_path)) {
343 strv_free(p->sysvrcnd_path);
344
345 p->sysvrcnd_path = strv_new(
346 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
347 NULL);
348 if (!p->sysvrcnd_path)
349 return -ENOMEM;
350 }
351
e842803a 352 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
663996b3
MS
353 return -ENOMEM;
354
e842803a 355 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
663996b3
MS
356 return -ENOMEM;
357
663996b3
MS
358 if (!strv_isempty(p->sysvinit_path)) {
359 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
360 if (!t)
361 return -ENOMEM;
362 log_debug("Looking for SysV init scripts in:\n\t%s", t);
363 } else {
364 log_debug("Ignoring SysV init scripts.");
365 strv_free(p->sysvinit_path);
366 p->sysvinit_path = NULL;
367 }
368
369 if (!strv_isempty(p->sysvrcnd_path)) {
370 _cleanup_free_ char *t =
371 strv_join(p->sysvrcnd_path, "\n\t");
372 if (!t)
373 return -ENOMEM;
374
375 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
376 } else {
377 log_debug("Ignoring SysV rcN.d links.");
378 strv_free(p->sysvrcnd_path);
379 p->sysvrcnd_path = NULL;
380 }
381#else
382 log_debug("SysV init scripts and rcN.d links support disabled");
383#endif
384 }
385
386 return 0;
387}
388
389void lookup_paths_free(LookupPaths *p) {
390 assert(p);
391
392 strv_free(p->unit_path);
393 p->unit_path = NULL;
394
395#ifdef HAVE_SYSV_COMPAT
396 strv_free(p->sysvinit_path);
397 strv_free(p->sysvrcnd_path);
398 p->sysvinit_path = p->sysvrcnd_path = NULL;
399#endif
400}