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