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