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