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