]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/path-lookup.c
1d95f7d1f83f5b4c64305e108366d7c430f8f45a
[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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser 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 "mkdir.h"
30 #include "strv.h"
31
32 #include "path-lookup.h"
33
34 int user_config_home(char **config_home) {
35 const char *e;
36
37 if ((e = getenv("XDG_CONFIG_HOME"))) {
38 if (asprintf(config_home, "%s/systemd/user", e) < 0)
39 return -ENOMEM;
40
41 return 1;
42 } else {
43 const char *home;
44
45 if ((home = getenv("HOME"))) {
46 if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
47 return -ENOMEM;
48
49 return 1;
50 }
51 }
52
53 return 0;
54 }
55
56 static char** user_dirs(void) {
57 const char * const config_unit_paths[] = {
58 USER_CONFIG_UNIT_PATH,
59 "/etc/systemd/user",
60 "/run/systemd/user",
61 NULL
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",
69 "/usr/share/systemd/user",
70 NULL
71 };
72
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
87 if (user_config_home(&config_home) < 0)
88 goto fail;
89
90 home = getenv("HOME");
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"))) {
101 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
102 goto fail;
103
104 } else if (home) {
105 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
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);
116 (void) symlink("../../../.config/systemd/user", data_home);
117 }
118
119 if ((e = getenv("XDG_DATA_DIRS")))
120 data_dirs = strv_split(e, ":");
121 else
122 data_dirs = strv_new("/usr/local/share",
123 "/usr/share",
124 NULL);
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
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 }
143
144 if (!(t = strv_merge(r, (char**) config_unit_paths)))
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
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 }
162
163 if (!(t = strv_merge(r, (char**) data_unit_paths)))
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
185 int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) {
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
202 if (running_as == MANAGER_USER) {
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! */
211 USER_CONFIG_UNIT_PATH,
212 "/etc/systemd/user",
213 "/run/systemd/user",
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)
222 return -ENOMEM;
223
224 } else
225 if (!(p->unit_path = strv_new(
226 /* If you modify this you also want to modify
227 * systemdsystemunitpath= in systemd.pc.in! */
228 SYSTEM_CONFIG_UNIT_PATH,
229 "/etc/systemd/system",
230 "/run/systemd/system",
231 "/usr/local/lib/systemd/system",
232 SYSTEM_DATA_UNIT_PATH,
233 "/usr/lib/systemd/system",
234 #ifdef HAVE_SPLIT_USR
235 "/lib/systemd/system",
236 #endif
237 NULL)))
238 return -ENOMEM;
239 }
240
241 if (p->unit_path)
242 if (!strv_path_canonicalize(p->unit_path))
243 return -ENOMEM;
244
245 strv_uniq(p->unit_path);
246 strv_path_remove_empty(p->unit_path);
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
260 if (running_as == MANAGER_SYSTEM) {
261 #ifdef HAVE_SYSV_COMPAT
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 }
289
290 if (p->sysvinit_path)
291 if (!strv_path_canonicalize(p->sysvinit_path))
292 return -ENOMEM;
293
294 if (p->sysvrcnd_path)
295 if (!strv_path_canonicalize(p->sysvrcnd_path))
296 return -ENOMEM;
297
298 strv_uniq(p->sysvinit_path);
299 strv_uniq(p->sysvrcnd_path);
300
301 strv_path_remove_empty(p->sysvinit_path);
302 strv_path_remove_empty(p->sysvrcnd_path);
303
304 if (!strv_isempty(p->sysvinit_path)) {
305
306 if (!(t = strv_join(p->sysvinit_path, "\n\t")))
307 return -ENOMEM;
308
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 }
316
317 if (!strv_isempty(p->sysvrcnd_path)) {
318
319 if (!(t = strv_join(p->sysvrcnd_path, "\n\t")))
320 return -ENOMEM;
321
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
332 }
333
334 return 0;
335 }
336
337 void lookup_paths_free(LookupPaths *p) {
338 assert(p);
339
340 strv_free(p->unit_path);
341 p->unit_path = NULL;
342
343 #ifdef HAVE_SYSV_COMPAT
344 strv_free(p->sysvinit_path);
345 strv_free(p->sysvrcnd_path);
346 p->sysvinit_path = p->sysvrcnd_path = NULL;
347 #endif
348 }