]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-lookup.c
core: add new .slice unit type for partitioning systems
[thirdparty/systemd.git] / src / shared / path-lookup.c
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
35 static const char* const systemd_running_as_table[_SYSTEMD_RUNNING_AS_MAX] = {
36 [SYSTEMD_SYSTEM] = "system",
37 [SYSTEMD_USER] = "user"
38 };
39
40 DEFINE_STRING_TABLE_LOOKUP(systemd_running_as, SystemdRunningAs);
41
42 int user_config_home(char **config_home) {
43 const char *e;
44 char *r;
45
46 e = getenv("XDG_CONFIG_HOME");
47 if (e) {
48 r = strappend(e, "/systemd/user");
49 if (!r)
50 return -ENOMEM;
51
52 *config_home = r;
53 return 1;
54 } else {
55 const char *home;
56
57 home = getenv("HOME");
58 if (home) {
59 r = strappend(home, "/.config/systemd/user");
60 if (!r)
61 return -ENOMEM;
62
63 *config_home = r;
64 return 1;
65 }
66 }
67
68 return 0;
69 }
70
71 static char** user_dirs(
72 const char *generator,
73 const char *generator_early,
74 const char *generator_late) {
75
76 const char * const config_unit_paths[] = {
77 USER_CONFIG_UNIT_PATH,
78 "/etc/systemd/user",
79 "/run/systemd/user",
80 NULL
81 };
82
83 const char * const data_unit_paths[] = {
84 "/usr/local/lib/systemd/user",
85 "/usr/local/share/systemd/user",
86 USER_DATA_UNIT_PATH,
87 "/usr/lib/systemd/user",
88 "/usr/share/systemd/user",
89 NULL
90 };
91
92 const char *home, *e;
93 char *config_home = NULL, *data_home = NULL;
94 char **config_dirs = NULL, **data_dirs = NULL;
95 char **r = NULL, **t;
96
97 /* Implement the mechanisms defined in
98 *
99 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
100 *
101 * We look in both the config and the data dirs because we
102 * want to encourage that distributors ship their unit files
103 * as data, and allow overriding as configuration.
104 */
105
106 if (user_config_home(&config_home) < 0)
107 goto fail;
108
109 home = getenv("HOME");
110
111 e = getenv("XDG_CONFIG_DIRS");
112 if (e) {
113 config_dirs = strv_split(e, ":");
114 if (!config_dirs)
115 goto fail;
116 }
117
118 /* We don't treat /etc/xdg/systemd here as the spec
119 * suggests because we assume that that is a link to
120 * /etc/systemd/ anyway. */
121
122 e = getenv("XDG_DATA_HOME");
123 if (e) {
124 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
125 goto fail;
126
127 } else if (home) {
128 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
129 goto fail;
130
131 /* There is really no need for two unit dirs in $HOME,
132 * except to be fully compliant with the XDG spec. We
133 * now try to link the two dirs, so that we can
134 * minimize disk seeks a little. Further down we'll
135 * then filter out this link, if it is actually is
136 * one. */
137
138 mkdir_parents_label(data_home, 0777);
139 (void) symlink("../../../.config/systemd/user", data_home);
140 }
141
142 e = getenv("XDG_DATA_DIRS");
143 if (e)
144 data_dirs = strv_split(e, ":");
145 else
146 data_dirs = strv_new("/usr/local/share",
147 "/usr/share",
148 NULL);
149 if (!data_dirs)
150 goto fail;
151
152 /* Now merge everything we found. */
153 if (generator_early) {
154 t = strv_append(r, generator_early);
155 if (!t)
156 goto fail;
157 strv_free(r);
158 r = t;
159 }
160
161 if (config_home) {
162 t = strv_append(r, config_home);
163 if (!t)
164 goto fail;
165 strv_free(r);
166 r = t;
167 }
168
169 if (!strv_isempty(config_dirs)) {
170 t = strv_merge_concat(r, config_dirs, "/systemd/user");
171 if (!t)
172 goto finish;
173 strv_free(r);
174 r = t;
175 }
176
177 t = strv_merge(r, (char**) config_unit_paths);
178 if (!t)
179 goto fail;
180 strv_free(r);
181 r = t;
182
183 if (generator) {
184 t = strv_append(r, generator);
185 if (!t)
186 goto fail;
187 strv_free(r);
188 r = t;
189 }
190
191 if (data_home) {
192 t = strv_append(r, data_home);
193 if (!t)
194 goto fail;
195 strv_free(r);
196 r = t;
197 }
198
199 if (!strv_isempty(data_dirs)) {
200 t = strv_merge_concat(r, data_dirs, "/systemd/user");
201 if (!t)
202 goto fail;
203 strv_free(r);
204 r = t;
205 }
206
207 t = strv_merge(r, (char**) data_unit_paths);
208 if (!t)
209 goto fail;
210 strv_free(r);
211 r = t;
212
213 if (generator_late) {
214 t = strv_append(r, generator_late);
215 if (!t)
216 goto fail;
217 strv_free(r);
218 r = t;
219 }
220
221 if (!path_strv_make_absolute_cwd(r))
222 goto fail;
223
224 finish:
225 free(config_home);
226 strv_free(config_dirs);
227 free(data_home);
228 strv_free(data_dirs);
229
230 return r;
231
232 fail:
233 strv_free(r);
234 r = NULL;
235 goto finish;
236 }
237
238 int lookup_paths_init(
239 LookupPaths *p,
240 SystemdRunningAs running_as,
241 bool personal,
242 const char *generator,
243 const char *generator_early,
244 const char *generator_late) {
245
246 const char *e;
247
248 assert(p);
249
250 /* First priority is whatever has been passed to us via env
251 * vars */
252 e = getenv("SYSTEMD_UNIT_PATH");
253 if (e) {
254 p->unit_path = path_split_and_make_absolute(e);
255 if (!p->unit_path)
256 return -ENOMEM;
257 } else
258 p->unit_path = NULL;
259
260 if (strv_isempty(p->unit_path)) {
261 /* Nothing is set, so let's figure something out. */
262 strv_free(p->unit_path);
263
264 /* For the user units we include share/ in the search
265 * path in order to comply with the XDG basedir
266 * spec. For the system stuff we avoid such
267 * nonsense. OTOH we include /lib in the search path
268 * for the system stuff but avoid it for user
269 * stuff. */
270
271 if (running_as == SYSTEMD_USER) {
272
273 if (personal)
274 p->unit_path = user_dirs(generator, generator_early, generator_late);
275 else
276 p->unit_path = strv_new(
277 /* If you modify this you also want to modify
278 * systemduserunitpath= in systemd.pc.in, and
279 * the arrays in user_dirs() above! */
280 STRV_IFNOTNULL(generator_early),
281 USER_CONFIG_UNIT_PATH,
282 "/etc/systemd/user",
283 "/run/systemd/user",
284 STRV_IFNOTNULL(generator),
285 "/usr/local/lib/systemd/user",
286 "/usr/local/share/systemd/user",
287 USER_DATA_UNIT_PATH,
288 "/usr/lib/systemd/user",
289 "/usr/share/systemd/user",
290 STRV_IFNOTNULL(generator_late),
291 NULL);
292
293 if (!p->unit_path)
294 return -ENOMEM;
295
296 } else {
297 p->unit_path = strv_new(
298 /* If you modify this you also want to modify
299 * systemdsystemunitpath= in systemd.pc.in! */
300 STRV_IFNOTNULL(generator_early),
301 SYSTEM_CONFIG_UNIT_PATH,
302 "/etc/systemd/system",
303 "/run/systemd/system",
304 STRV_IFNOTNULL(generator),
305 "/usr/local/lib/systemd/system",
306 SYSTEM_DATA_UNIT_PATH,
307 "/usr/lib/systemd/system",
308 #ifdef HAVE_SPLIT_USR
309 "/lib/systemd/system",
310 #endif
311 STRV_IFNOTNULL(generator_late),
312 NULL);
313
314 if (!p->unit_path)
315 return -ENOMEM;
316 }
317 }
318
319 if (!path_strv_canonicalize(p->unit_path))
320 return -ENOMEM;
321
322 strv_uniq(p->unit_path);
323
324 if (!strv_isempty(p->unit_path)) {
325 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
326 if (!t)
327 return -ENOMEM;
328 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
329 } else {
330 log_debug("Ignoring unit files.");
331 strv_free(p->unit_path);
332 p->unit_path = NULL;
333 }
334
335 if (running_as == SYSTEMD_SYSTEM) {
336 #ifdef HAVE_SYSV_COMPAT
337 /* /etc/init.d/ compatibility does not matter to users */
338
339 e = getenv("SYSTEMD_SYSVINIT_PATH");
340 if (e) {
341 p->sysvinit_path = path_split_and_make_absolute(e);
342 if (!p->sysvinit_path)
343 return -ENOMEM;
344 } else
345 p->sysvinit_path = NULL;
346
347 if (strv_isempty(p->sysvinit_path)) {
348 strv_free(p->sysvinit_path);
349
350 p->sysvinit_path = strv_new(
351 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
352 NULL);
353 if (!p->sysvinit_path)
354 return -ENOMEM;
355 }
356
357 e = getenv("SYSTEMD_SYSVRCND_PATH");
358 if (e) {
359 p->sysvrcnd_path = path_split_and_make_absolute(e);
360 if (!p->sysvrcnd_path)
361 return -ENOMEM;
362 } else
363 p->sysvrcnd_path = NULL;
364
365 if (strv_isempty(p->sysvrcnd_path)) {
366 strv_free(p->sysvrcnd_path);
367
368 p->sysvrcnd_path = strv_new(
369 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
370 NULL);
371 if (!p->sysvrcnd_path)
372 return -ENOMEM;
373 }
374
375 if (!path_strv_canonicalize(p->sysvinit_path))
376 return -ENOMEM;
377
378 if (!path_strv_canonicalize(p->sysvrcnd_path))
379 return -ENOMEM;
380
381 strv_uniq(p->sysvinit_path);
382 strv_uniq(p->sysvrcnd_path);
383
384 if (!strv_isempty(p->sysvinit_path)) {
385 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
386 if (!t)
387 return -ENOMEM;
388 log_debug("Looking for SysV init scripts in:\n\t%s", t);
389 } else {
390 log_debug("Ignoring SysV init scripts.");
391 strv_free(p->sysvinit_path);
392 p->sysvinit_path = NULL;
393 }
394
395 if (!strv_isempty(p->sysvrcnd_path)) {
396 _cleanup_free_ char *t =
397 strv_join(p->sysvrcnd_path, "\n\t");
398 if (!t)
399 return -ENOMEM;
400
401 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
402 } else {
403 log_debug("Ignoring SysV rcN.d links.");
404 strv_free(p->sysvrcnd_path);
405 p->sysvrcnd_path = NULL;
406 }
407 #else
408 log_debug("SysV init scripts and rcN.d links support disabled");
409 #endif
410 }
411
412 return 0;
413 }
414
415 void lookup_paths_free(LookupPaths *p) {
416 assert(p);
417
418 strv_free(p->unit_path);
419 p->unit_path = NULL;
420
421 #ifdef HAVE_SYSV_COMPAT
422 strv_free(p->sysvinit_path);
423 strv_free(p->sysvrcnd_path);
424 p->sysvinit_path = p->sysvrcnd_path = NULL;
425 #endif
426 }