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