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