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