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