]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-lookup.c
core: drop SysV paths from path-lookup logic
[thirdparty/systemd.git] / src / shared / path-lookup.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "alloc-util.h"
26 #include "install.h"
27 #include "log.h"
28 #include "macro.h"
29 #include "path-lookup.h"
30 #include "path-util.h"
31 #include "string-util.h"
32 #include "strv.h"
33 #include "util.h"
34
35 int user_config_home(char **config_home) {
36 const char *e;
37 char *r;
38
39 e = getenv("XDG_CONFIG_HOME");
40 if (e) {
41 r = strappend(e, "/systemd/user");
42 if (!r)
43 return -ENOMEM;
44
45 *config_home = r;
46 return 1;
47 } else {
48 const char *home;
49
50 home = getenv("HOME");
51 if (home) {
52 r = strappend(home, "/.config/systemd/user");
53 if (!r)
54 return -ENOMEM;
55
56 *config_home = r;
57 return 1;
58 }
59 }
60
61 return 0;
62 }
63
64 int user_runtime_dir(char **runtime_dir) {
65 const char *e;
66 char *r;
67
68 e = getenv("XDG_RUNTIME_DIR");
69 if (e) {
70 r = strappend(e, "/systemd/user");
71 if (!r)
72 return -ENOMEM;
73
74 *runtime_dir = r;
75 return 1;
76 }
77
78 return 0;
79 }
80
81 static int user_data_home_dir(char **dir, const char *suffix) {
82 const char *e;
83 char *res;
84
85 /* We don't treat /etc/xdg/systemd here as the spec
86 * suggests because we assume that that is a link to
87 * /etc/systemd/ anyway. */
88
89 e = getenv("XDG_DATA_HOME");
90 if (e)
91 res = strappend(e, suffix);
92 else {
93 const char *home;
94
95 home = getenv("HOME");
96 if (home)
97 res = strjoin(home, "/.local/share", suffix, NULL);
98 else
99 return 0;
100 }
101 if (!res)
102 return -ENOMEM;
103
104 *dir = res;
105 return 0;
106 }
107
108 static char** user_dirs(
109 const char *generator,
110 const char *generator_early,
111 const char *generator_late) {
112
113 const char * const config_unit_paths[] = {
114 USER_CONFIG_UNIT_PATH,
115 "/etc/systemd/user",
116 NULL
117 };
118
119 const char * const runtime_unit_path = "/run/systemd/user";
120
121 const char * const data_unit_paths[] = {
122 "/usr/local/lib/systemd/user",
123 "/usr/local/share/systemd/user",
124 USER_DATA_UNIT_PATH,
125 "/usr/lib/systemd/user",
126 "/usr/share/systemd/user",
127 NULL
128 };
129
130 const char *e;
131 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
132 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
133 _cleanup_free_ char **res = NULL;
134 char **tmp;
135 int r;
136
137 /* Implement the mechanisms defined in
138 *
139 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
140 *
141 * We look in both the config and the data dirs because we
142 * want to encourage that distributors ship their unit files
143 * as data, and allow overriding as configuration.
144 */
145
146 if (user_config_home(&config_home) < 0)
147 return NULL;
148
149 if (user_runtime_dir(&runtime_dir) < 0)
150 return NULL;
151
152 e = getenv("XDG_CONFIG_DIRS");
153 if (e) {
154 config_dirs = strv_split(e, ":");
155 if (!config_dirs)
156 return NULL;
157 }
158
159 r = user_data_home_dir(&data_home, "/systemd/user");
160 if (r < 0)
161 return NULL;
162
163 e = getenv("XDG_DATA_DIRS");
164 if (e)
165 data_dirs = strv_split(e, ":");
166 else
167 data_dirs = strv_new("/usr/local/share",
168 "/usr/share",
169 NULL);
170 if (!data_dirs)
171 return NULL;
172
173 /* Now merge everything we found. */
174 if (generator_early)
175 if (strv_extend(&res, generator_early) < 0)
176 return NULL;
177
178 if (config_home)
179 if (strv_extend(&res, config_home) < 0)
180 return NULL;
181
182 if (!strv_isempty(config_dirs))
183 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
184 return NULL;
185
186 if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
187 return NULL;
188
189 if (runtime_dir)
190 if (strv_extend(&res, runtime_dir) < 0)
191 return NULL;
192
193 if (strv_extend(&res, runtime_unit_path) < 0)
194 return NULL;
195
196 if (generator)
197 if (strv_extend(&res, generator) < 0)
198 return NULL;
199
200 if (data_home)
201 if (strv_extend(&res, data_home) < 0)
202 return NULL;
203
204 if (!strv_isempty(data_dirs))
205 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
206 return NULL;
207
208 if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
209 return NULL;
210
211 if (generator_late)
212 if (strv_extend(&res, generator_late) < 0)
213 return NULL;
214
215 if (path_strv_make_absolute_cwd(res) < 0)
216 return NULL;
217
218 tmp = res;
219 res = NULL;
220 return tmp;
221 }
222
223 char **generator_paths(ManagerRunningAs running_as) {
224 if (running_as == MANAGER_USER)
225 return strv_new("/run/systemd/user-generators",
226 "/etc/systemd/user-generators",
227 "/usr/local/lib/systemd/user-generators",
228 USER_GENERATOR_PATH,
229 NULL);
230 else
231 return strv_new("/run/systemd/system-generators",
232 "/etc/systemd/system-generators",
233 "/usr/local/lib/systemd/system-generators",
234 SYSTEM_GENERATOR_PATH,
235 NULL);
236 }
237
238 int lookup_paths_init(
239 LookupPaths *p,
240 ManagerRunningAs running_as,
241 bool personal,
242 const char *root_dir,
243 const char *generator,
244 const char *generator_early,
245 const char *generator_late) {
246
247 const char *e;
248 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
249 int r;
250
251 assert(p);
252
253 /* First priority is whatever has been passed to us via env
254 * vars */
255 e = getenv("SYSTEMD_UNIT_PATH");
256 if (e) {
257 if (endswith(e, ":")) {
258 e = strndupa(e, strlen(e) - 1);
259 append = true;
260 }
261
262 /* FIXME: empty components in other places should be
263 * rejected. */
264
265 r = path_split_and_make_absolute(e, &p->unit_path);
266 if (r < 0)
267 return r;
268 } else
269 p->unit_path = NULL;
270
271 if (!p->unit_path || append) {
272 /* Let's figure something out. */
273
274 _cleanup_strv_free_ char **unit_path;
275
276 /* For the user units we include share/ in the search
277 * path in order to comply with the XDG basedir spec.
278 * For the system stuff we avoid such nonsense. OTOH
279 * we include /lib in the search path for the system
280 * stuff but avoid it for user stuff. */
281
282 if (running_as == MANAGER_USER) {
283 if (personal)
284 unit_path = user_dirs(generator, generator_early, generator_late);
285 else
286 unit_path = strv_new(
287 /* If you modify this you also want to modify
288 * systemduserunitpath= in systemd.pc.in, and
289 * the arrays in user_dirs() above! */
290 STRV_IFNOTNULL(generator_early),
291 USER_CONFIG_UNIT_PATH,
292 "/etc/systemd/user",
293 "/run/systemd/user",
294 STRV_IFNOTNULL(generator),
295 "/usr/local/lib/systemd/user",
296 "/usr/local/share/systemd/user",
297 USER_DATA_UNIT_PATH,
298 "/usr/lib/systemd/user",
299 "/usr/share/systemd/user",
300 STRV_IFNOTNULL(generator_late),
301 NULL);
302 } else
303 unit_path = strv_new(
304 /* If you modify this you also want to modify
305 * systemdsystemunitpath= in systemd.pc.in! */
306 STRV_IFNOTNULL(generator_early),
307 SYSTEM_CONFIG_UNIT_PATH,
308 "/etc/systemd/system",
309 "/run/systemd/system",
310 STRV_IFNOTNULL(generator),
311 "/usr/local/lib/systemd/system",
312 SYSTEM_DATA_UNIT_PATH,
313 "/usr/lib/systemd/system",
314 #ifdef HAVE_SPLIT_USR
315 "/lib/systemd/system",
316 #endif
317 STRV_IFNOTNULL(generator_late),
318 NULL);
319
320 if (!unit_path)
321 return -ENOMEM;
322
323 r = strv_extend_strv(&p->unit_path, unit_path, false);
324 if (r < 0)
325 return r;
326 }
327
328 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
329 return -ENOMEM;
330
331 if (!strv_isempty(p->unit_path)) {
332 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
333 if (!t)
334 return -ENOMEM;
335 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
336 } else {
337 log_debug("Ignoring unit files.");
338 p->unit_path = strv_free(p->unit_path);
339 }
340
341
342 return 0;
343 }
344
345 void lookup_paths_free(LookupPaths *p) {
346 assert(p);
347
348 p->unit_path = strv_free(p->unit_path);
349 }
350
351 int lookup_paths_init_from_scope(LookupPaths *paths,
352 UnitFileScope scope,
353 const char *root_dir) {
354 assert(paths);
355 assert(scope >= 0);
356 assert(scope < _UNIT_FILE_SCOPE_MAX);
357
358 zero(*paths);
359
360 return lookup_paths_init(paths,
361 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
362 scope == UNIT_FILE_USER,
363 root_dir,
364 NULL, NULL, NULL);
365 }