Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <dirent.h>
4 : : #include <errno.h>
5 : : #include <fcntl.h>
6 : : #include <fnmatch.h>
7 : : #include <limits.h>
8 : : #include <stddef.h>
9 : : #include <stdio.h>
10 : : #include <stdlib.h>
11 : : #include <string.h>
12 : : #include <sys/stat.h>
13 : : #include <unistd.h>
14 : :
15 : : #include "alloc-util.h"
16 : : #include "conf-files.h"
17 : : #include "conf-parser.h"
18 : : #include "def.h"
19 : : #include "dirent-util.h"
20 : : #include "extract-word.h"
21 : : #include "fd-util.h"
22 : : #include "fileio.h"
23 : : #include "fs-util.h"
24 : : #include "hashmap.h"
25 : : #include "install-printf.h"
26 : : #include "install.h"
27 : : #include "locale-util.h"
28 : : #include "log.h"
29 : : #include "macro.h"
30 : : #include "mkdir.h"
31 : : #include "path-lookup.h"
32 : : #include "path-util.h"
33 : : #include "rm-rf.h"
34 : : #include "set.h"
35 : : #include "special.h"
36 : : #include "stat-util.h"
37 : : #include "string-table.h"
38 : : #include "string-util.h"
39 : : #include "strv.h"
40 : : #include "unit-file.h"
41 : :
42 : : #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
43 : :
44 : : typedef enum SearchFlags {
45 : : SEARCH_LOAD = 1 << 0,
46 : : SEARCH_FOLLOW_CONFIG_SYMLINKS = 1 << 1,
47 : : SEARCH_DROPIN = 1 << 2,
48 : : } SearchFlags;
49 : :
50 : : typedef struct {
51 : : OrderedHashmap *will_process;
52 : : OrderedHashmap *have_processed;
53 : : } InstallContext;
54 : :
55 : : typedef enum {
56 : : PRESET_UNKNOWN,
57 : : PRESET_ENABLE,
58 : : PRESET_DISABLE,
59 : : } PresetAction;
60 : :
61 : : typedef struct {
62 : : char *pattern;
63 : : PresetAction action;
64 : : char **instances;
65 : : } PresetRule;
66 : :
67 : : typedef struct {
68 : : PresetRule *rules;
69 : : size_t n_rules;
70 : : } Presets;
71 : :
72 : 2072 : static bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) {
73 [ - + ]: 2072 : assert(i);
74 : :
75 : 2072 : return !strv_isempty(i->aliases) ||
76 [ + + + + ]: 3200 : !strv_isempty(i->wanted_by) ||
77 [ + + ]: 1128 : !strv_isempty(i->required_by);
78 : : }
79 : :
80 : 1104 : static bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) {
81 [ - + ]: 1104 : assert(i);
82 : :
83 : 1104 : return !strv_isempty(i->also);
84 : : }
85 : :
86 : 56 : static void presets_freep(Presets *p) {
87 : : size_t i;
88 : :
89 [ - + ]: 56 : if (!p)
90 : 0 : return;
91 : :
92 [ + + ]: 128 : for (i = 0; i < p->n_rules; i++) {
93 : 72 : free(p->rules[i].pattern);
94 : 72 : strv_free(p->rules[i].instances);
95 : : }
96 : :
97 : 56 : free(p->rules);
98 : 56 : p->n_rules = 0;
99 : : }
100 : :
101 : : static const char *const unit_file_type_table[_UNIT_FILE_TYPE_MAX] = {
102 : : [UNIT_FILE_TYPE_REGULAR] = "regular",
103 : : [UNIT_FILE_TYPE_SYMLINK] = "symlink",
104 : : [UNIT_FILE_TYPE_MASKED] = "masked",
105 : : };
106 : :
107 [ + - - + ]: 8 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType);
108 : :
109 : 124 : static int in_search_path(const LookupPaths *p, const char *path) {
110 : 124 : _cleanup_free_ char *parent = NULL;
111 : : char **i;
112 : :
113 [ - + ]: 124 : assert(path);
114 : :
115 : 124 : parent = dirname_malloc(path);
116 [ - + ]: 124 : if (!parent)
117 : 0 : return -ENOMEM;
118 : :
119 [ + - + + ]: 1372 : STRV_FOREACH(i, p->search_path)
120 [ + + ]: 1356 : if (path_equal(parent, *i))
121 : 108 : return true;
122 : :
123 : 16 : return false;
124 : : }
125 : :
126 : 1168 : static const char* skip_root(const LookupPaths *p, const char *path) {
127 : : char *e;
128 : :
129 [ - + ]: 1168 : assert(p);
130 [ - + ]: 1168 : assert(path);
131 : :
132 [ + + ]: 1168 : if (!p->root_dir)
133 : 676 : return path;
134 : :
135 : 492 : e = path_startswith(path, p->root_dir);
136 [ + + ]: 492 : if (!e)
137 : 8 : return NULL;
138 : :
139 : : /* Make sure the returned path starts with a slash */
140 [ + - ]: 484 : if (e[0] != '/') {
141 [ + - - + ]: 484 : if (e == path || e[-1] != '/')
142 : 0 : return NULL;
143 : :
144 : 484 : e--;
145 : : }
146 : :
147 : 484 : return e;
148 : : }
149 : :
150 : 2776 : static int path_is_generator(const LookupPaths *p, const char *path) {
151 : 2776 : _cleanup_free_ char *parent = NULL;
152 : :
153 [ - + ]: 2776 : assert(p);
154 [ - + ]: 2776 : assert(path);
155 : :
156 : 2776 : parent = dirname_malloc(path);
157 [ - + ]: 2776 : if (!parent)
158 : 0 : return -ENOMEM;
159 : :
160 [ + - ]: 5524 : return path_equal_ptr(parent, p->generator) ||
161 [ + + + + ]: 5524 : path_equal_ptr(parent, p->generator_early) ||
162 : 2748 : path_equal_ptr(parent, p->generator_late);
163 : : }
164 : :
165 : 2736 : static int path_is_transient(const LookupPaths *p, const char *path) {
166 : 2736 : _cleanup_free_ char *parent = NULL;
167 : :
168 [ - + ]: 2736 : assert(p);
169 [ - + ]: 2736 : assert(path);
170 : :
171 : 2736 : parent = dirname_malloc(path);
172 [ - + ]: 2736 : if (!parent)
173 : 0 : return -ENOMEM;
174 : :
175 : 2736 : return path_equal_ptr(parent, p->transient);
176 : : }
177 : :
178 : 0 : static int path_is_control(const LookupPaths *p, const char *path) {
179 : 0 : _cleanup_free_ char *parent = NULL;
180 : :
181 [ # # ]: 0 : assert(p);
182 [ # # ]: 0 : assert(path);
183 : :
184 : 0 : parent = dirname_malloc(path);
185 [ # # ]: 0 : if (!parent)
186 : 0 : return -ENOMEM;
187 : :
188 [ # # # # ]: 0 : return path_equal_ptr(parent, p->persistent_control) ||
189 : 0 : path_equal_ptr(parent, p->runtime_control);
190 : : }
191 : :
192 : 48 : static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) {
193 : 48 : _cleanup_free_ char *parent = NULL;
194 : :
195 [ - + ]: 48 : assert(p);
196 [ - + ]: 48 : assert(path);
197 : :
198 : : /* Note that we do *not* have generic checks for /etc or /run in place, since with
199 : : * them we couldn't discern configuration from transient or generated units */
200 : :
201 [ + - ]: 48 : if (check_parent) {
202 : 48 : parent = dirname_malloc(path);
203 [ - + ]: 48 : if (!parent)
204 : 0 : return -ENOMEM;
205 : :
206 : 48 : path = parent;
207 : : }
208 : :
209 [ + + - + ]: 88 : return path_equal_ptr(path, p->persistent_config) ||
210 : 40 : path_equal_ptr(path, p->runtime_config);
211 : : }
212 : :
213 : 904 : static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) {
214 : 904 : _cleanup_free_ char *parent = NULL;
215 : : const char *rpath;
216 : :
217 [ - + ]: 904 : assert(p);
218 [ - + ]: 904 : assert(path);
219 : :
220 : : /* Everything in /run is considered runtime. On top of that we also add
221 : : * explicit checks for the various runtime directories, as safety net. */
222 : :
223 : 904 : rpath = skip_root(p, path);
224 [ + - + + ]: 904 : if (rpath && path_startswith(rpath, "/run"))
225 : 4 : return true;
226 : :
227 [ + + ]: 900 : if (check_parent) {
228 : 20 : parent = dirname_malloc(path);
229 [ - + ]: 20 : if (!parent)
230 : 0 : return -ENOMEM;
231 : :
232 : 20 : path = parent;
233 : : }
234 : :
235 [ + - ]: 1800 : return path_equal_ptr(path, p->runtime_config) ||
236 [ + - ]: 1800 : path_equal_ptr(path, p->generator) ||
237 [ + - ]: 1800 : path_equal_ptr(path, p->generator_early) ||
238 [ + - ]: 1800 : path_equal_ptr(path, p->generator_late) ||
239 [ + - - + ]: 2700 : path_equal_ptr(path, p->transient) ||
240 : 900 : path_equal_ptr(path, p->runtime_control);
241 : : }
242 : :
243 : 16 : static int path_is_vendor(const LookupPaths *p, const char *path) {
244 : : const char *rpath;
245 : :
246 [ - + ]: 16 : assert(p);
247 [ - + ]: 16 : assert(path);
248 : :
249 : 16 : rpath = skip_root(p, path);
250 [ - + ]: 16 : if (!rpath)
251 : 0 : return 0;
252 : :
253 [ + + ]: 16 : if (path_startswith(rpath, "/usr"))
254 : 12 : return true;
255 : :
256 : : #if HAVE_SPLIT_USR
257 : : if (path_startswith(rpath, "/lib"))
258 : : return true;
259 : : #endif
260 : :
261 : 4 : return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
262 : : }
263 : :
264 : 488 : int unit_file_changes_add(
265 : : UnitFileChange **changes,
266 : : size_t *n_changes,
267 : : UnitFileChangeType type,
268 : : const char *path,
269 : : const char *source) {
270 : :
271 : 488 : _cleanup_free_ char *p = NULL, *s = NULL;
272 : : UnitFileChange *c;
273 : :
274 [ - + ]: 488 : assert(path);
275 [ - + ]: 488 : assert(!changes == !n_changes);
276 : :
277 [ + + ]: 488 : if (!changes)
278 : 236 : return 0;
279 : :
280 : 252 : c = reallocarray(*changes, *n_changes + 1, sizeof(UnitFileChange));
281 [ - + ]: 252 : if (!c)
282 : 0 : return -ENOMEM;
283 : 252 : *changes = c;
284 : :
285 : 252 : p = strdup(path);
286 [ + + ]: 252 : if (source)
287 : 148 : s = strdup(source);
288 : :
289 [ + - + + : 252 : if (!p || (source && !s))
- + ]
290 : 0 : return -ENOMEM;
291 : :
292 : 252 : path_simplify(p, false);
293 [ + + ]: 252 : if (s)
294 : 148 : path_simplify(s, false);
295 : :
296 : 252 : c[*n_changes] = (UnitFileChange) { type, p, s };
297 : 252 : p = s = NULL;
298 : 252 : (*n_changes) ++;
299 : 252 : return 0;
300 : : }
301 : :
302 : 176 : void unit_file_changes_free(UnitFileChange *changes, size_t n_changes) {
303 : : size_t i;
304 : :
305 [ + + - + ]: 176 : assert(changes || n_changes == 0);
306 : :
307 [ + + ]: 428 : for (i = 0; i < n_changes; i++) {
308 : 252 : free(changes[i].path);
309 : 252 : free(changes[i].source);
310 : : }
311 : :
312 : 176 : free(changes);
313 : 176 : }
314 : :
315 : 0 : void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet) {
316 : : size_t i;
317 : 0 : bool logged = false;
318 : :
319 [ # # # # ]: 0 : assert(changes || n_changes == 0);
320 : : /* If verb is not specified, errors are not allowed! */
321 [ # # # # ]: 0 : assert(verb || r >= 0);
322 : :
323 [ # # ]: 0 : for (i = 0; i < n_changes; i++) {
324 [ # # # # ]: 0 : assert(verb || changes[i].type >= 0);
325 : :
326 [ # # # # : 0 : switch(changes[i].type) {
# # # # #
# ]
327 : 0 : case UNIT_FILE_SYMLINK:
328 [ # # ]: 0 : if (!quiet)
329 [ # # ]: 0 : log_info("Created symlink %s %s %s.",
330 : : changes[i].path,
331 : : special_glyph(SPECIAL_GLYPH_ARROW),
332 : : changes[i].source);
333 : 0 : break;
334 : 0 : case UNIT_FILE_UNLINK:
335 [ # # ]: 0 : if (!quiet)
336 [ # # ]: 0 : log_info("Removed %s.", changes[i].path);
337 : 0 : break;
338 : 0 : case UNIT_FILE_IS_MASKED:
339 [ # # ]: 0 : if (!quiet)
340 [ # # ]: 0 : log_info("Unit %s is masked, ignoring.", changes[i].path);
341 : 0 : break;
342 : 0 : case UNIT_FILE_IS_DANGLING:
343 [ # # ]: 0 : if (!quiet)
344 [ # # ]: 0 : log_info("Unit %s is an alias to a unit that is not present, ignoring.",
345 : : changes[i].path);
346 : 0 : break;
347 : 0 : case -EEXIST:
348 [ # # ]: 0 : if (changes[i].source)
349 [ # # ]: 0 : log_error_errno(changes[i].type,
350 : : "Failed to %s unit, file %s already exists and is a symlink to %s.",
351 : : verb, changes[i].path, changes[i].source);
352 : : else
353 [ # # ]: 0 : log_error_errno(changes[i].type,
354 : : "Failed to %s unit, file %s already exists.",
355 : : verb, changes[i].path);
356 : 0 : logged = true;
357 : 0 : break;
358 : 0 : case -ERFKILL:
359 [ # # ]: 0 : log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.",
360 : : verb, changes[i].path);
361 : 0 : logged = true;
362 : 0 : break;
363 : 0 : case -EADDRNOTAVAIL:
364 [ # # ]: 0 : log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.",
365 : : verb, changes[i].path);
366 : 0 : logged = true;
367 : 0 : break;
368 : 0 : case -ELOOP:
369 [ # # ]: 0 : log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s",
370 : : verb, changes[i].path);
371 : 0 : logged = true;
372 : 0 : break;
373 : :
374 : 0 : case -ENOENT:
375 [ # # ]: 0 : log_error_errno(changes[i].type, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path);
376 : 0 : logged = true;
377 : 0 : break;
378 : :
379 : 0 : default:
380 [ # # ]: 0 : assert(changes[i].type < 0);
381 [ # # ]: 0 : log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.",
382 : : verb, changes[i].path);
383 : 0 : logged = true;
384 : : }
385 : : }
386 : :
387 [ # # # # ]: 0 : if (r < 0 && !logged)
388 [ # # ]: 0 : log_error_errno(r, "Failed to %s: %m.", verb);
389 : 0 : }
390 : :
391 : : /**
392 : : * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
393 : : * wc should be the full path in the host file system.
394 : : */
395 : 8 : static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
396 [ - + ]: 8 : assert(path_is_absolute(wd));
397 : :
398 : : /* This will give incorrect results if the paths are relative and go outside
399 : : * of the chroot. False negatives are possible. */
400 : :
401 [ - + ]: 8 : if (!root)
402 : 0 : root = "/";
403 : :
404 [ + - + + : 56 : a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
+ - - + -
+ + + +
- ]
405 [ + - + + : 56 : b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
+ - - + -
+ + + +
- ]
406 : 8 : return path_equal_or_files_same(a, b, 0);
407 : : }
408 : :
409 : 156 : static int create_symlink(
410 : : const LookupPaths *paths,
411 : : const char *old_path,
412 : : const char *new_path,
413 : : bool force,
414 : : UnitFileChange **changes,
415 : : size_t *n_changes) {
416 : :
417 : 156 : _cleanup_free_ char *dest = NULL, *dirname = NULL;
418 : : const char *rp;
419 : : int r;
420 : :
421 [ - + ]: 156 : assert(old_path);
422 [ - + ]: 156 : assert(new_path);
423 : :
424 : 156 : rp = skip_root(paths, old_path);
425 [ + + ]: 156 : if (rp)
426 : 148 : old_path = rp;
427 : :
428 : : /* Actually create a symlink, and remember that we did. Is
429 : : * smart enough to check if there's already a valid symlink in
430 : : * place.
431 : : *
432 : : * Returns 1 if a symlink was created or already exists and points to
433 : : * the right place, or negative on error.
434 : : */
435 : :
436 : 156 : mkdir_parents_label(new_path, 0755);
437 : :
438 [ + + ]: 156 : if (symlink(old_path, new_path) >= 0) {
439 : 148 : unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
440 : 148 : return 1;
441 : : }
442 : :
443 [ - + ]: 8 : if (errno != EEXIST) {
444 : 0 : unit_file_changes_add(changes, n_changes, -errno, new_path, NULL);
445 : 0 : return -errno;
446 : : }
447 : :
448 : 8 : r = readlink_malloc(new_path, &dest);
449 [ - + ]: 8 : if (r < 0) {
450 : : /* translate EINVAL (non-symlink exists) to EEXIST */
451 [ # # ]: 0 : if (r == -EINVAL)
452 : 0 : r = -EEXIST;
453 : :
454 : 0 : unit_file_changes_add(changes, n_changes, r, new_path, NULL);
455 : 0 : return r;
456 : : }
457 : :
458 : 8 : dirname = dirname_malloc(new_path);
459 [ - + ]: 8 : if (!dirname)
460 : 0 : return -ENOMEM;
461 : :
462 [ + - ]: 8 : if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) {
463 [ + - ]: 8 : log_debug("Symlink %s → %s already exists", new_path, dest);
464 : 8 : return 1;
465 : : }
466 : :
467 [ # # ]: 0 : if (!force) {
468 : 0 : unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest);
469 : 0 : return -EEXIST;
470 : : }
471 : :
472 : 0 : r = symlink_atomic(old_path, new_path);
473 [ # # ]: 0 : if (r < 0) {
474 : 0 : unit_file_changes_add(changes, n_changes, r, new_path, NULL);
475 : 0 : return r;
476 : : }
477 : :
478 : 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
479 : 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
480 : :
481 : 0 : return 1;
482 : : }
483 : :
484 : 232 : static int mark_symlink_for_removal(
485 : : Set **remove_symlinks_to,
486 : : const char *p) {
487 : :
488 : : char *n;
489 : : int r;
490 : :
491 [ - + ]: 232 : assert(p);
492 : :
493 : 232 : r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops);
494 [ - + ]: 232 : if (r < 0)
495 : 0 : return r;
496 : :
497 : 232 : n = strdup(p);
498 [ - + ]: 232 : if (!n)
499 : 0 : return -ENOMEM;
500 : :
501 : 232 : path_simplify(n, false);
502 : :
503 : 232 : r = set_consume(*remove_symlinks_to, n);
504 [ - + ]: 232 : if (r == -EEXIST)
505 : 0 : return 0;
506 [ - + ]: 232 : if (r < 0)
507 : 0 : return r;
508 : :
509 : 232 : return 1;
510 : : }
511 : :
512 : 248 : static int remove_marked_symlinks_fd(
513 : : Set *remove_symlinks_to,
514 : : int fd,
515 : : const char *path,
516 : : const char *config_path,
517 : : const LookupPaths *lp,
518 : : bool dry_run,
519 : : bool *restart,
520 : : UnitFileChange **changes,
521 : : size_t *n_changes) {
522 : :
523 : 248 : _cleanup_closedir_ DIR *d = NULL;
524 : : struct dirent *de;
525 : 248 : int r = 0;
526 : :
527 [ - + ]: 248 : assert(remove_symlinks_to);
528 [ - + ]: 248 : assert(fd >= 0);
529 [ - + ]: 248 : assert(path);
530 [ - + ]: 248 : assert(config_path);
531 [ - + ]: 248 : assert(lp);
532 [ - + ]: 248 : assert(restart);
533 : :
534 : 248 : d = fdopendir(fd);
535 [ - + ]: 248 : if (!d) {
536 : 0 : safe_close(fd);
537 : 0 : return -errno;
538 : : }
539 : :
540 : 248 : rewinddir(d);
541 : :
542 [ + + - + : 1304 : FOREACH_DIRENT(de, d, return -errno) {
+ + ]
543 : :
544 : 560 : dirent_ensure_type(d, de);
545 : :
546 [ + + ]: 560 : if (de->d_type == DT_DIR) {
547 [ + - - ]: 128 : _cleanup_free_ char *p = NULL;
548 : : int nfd, q;
549 : :
550 : 128 : nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
551 [ - + ]: 128 : if (nfd < 0) {
552 [ # # ]: 0 : if (errno == ENOENT)
553 : 0 : continue;
554 : :
555 [ # # ]: 0 : if (r == 0)
556 : 0 : r = -errno;
557 : 0 : continue;
558 : : }
559 : :
560 : 128 : p = path_make_absolute(de->d_name, path);
561 [ - + ]: 128 : if (!p) {
562 : 0 : safe_close(nfd);
563 : 0 : return -ENOMEM;
564 : : }
565 : :
566 : : /* This will close nfd, regardless whether it succeeds or not */
567 : 128 : q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, dry_run, restart, changes, n_changes);
568 [ - + # # ]: 128 : if (q < 0 && r == 0)
569 : 0 : r = q;
570 : :
571 [ + - ]: 432 : } else if (de->d_type == DT_LNK) {
572 [ + + - + : 784 : _cleanup_free_ char *p = NULL, *dest = NULL;
+ - ]
573 : : const char *rp;
574 : : bool found;
575 : : int q;
576 : :
577 [ - + ]: 432 : if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
578 : 0 : continue;
579 : :
580 : 432 : p = path_make_absolute(de->d_name, path);
581 [ - + ]: 432 : if (!p)
582 : 0 : return -ENOMEM;
583 : 432 : path_simplify(p, false);
584 : :
585 : 432 : q = readlink_malloc(p, &dest);
586 [ - + ]: 432 : if (q == -ENOENT)
587 : 0 : continue;
588 [ - + ]: 432 : if (q < 0) {
589 [ # # ]: 0 : if (r == 0)
590 : 0 : r = q;
591 : 0 : continue;
592 : : }
593 : :
594 : : /* We remove all links pointing to a file or path that is marked, as well as all files sharing
595 : : * the same name as a file that is marked. */
596 : :
597 [ + + ]: 864 : found = set_contains(remove_symlinks_to, dest) ||
598 [ + - + + ]: 864 : set_contains(remove_symlinks_to, basename(dest)) ||
599 : 360 : set_contains(remove_symlinks_to, de->d_name);
600 : :
601 [ + + ]: 432 : if (!found)
602 : 352 : continue;
603 : :
604 [ + - ]: 80 : if (!dry_run) {
605 [ - + # # ]: 80 : if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
606 [ # # ]: 0 : if (r == 0)
607 : 0 : r = -errno;
608 : 0 : unit_file_changes_add(changes, n_changes, -errno, p, NULL);
609 : 0 : continue;
610 : : }
611 : :
612 : 80 : (void) rmdir_parents(p, config_path);
613 : : }
614 : :
615 : 80 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
616 : :
617 : : /* Now, remember the full path (but with the root prefix removed) of
618 : : * the symlink we just removed, and remove any symlinks to it, too. */
619 : :
620 : 80 : rp = skip_root(lp, p);
621 [ + - ]: 80 : q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
622 [ - + ]: 80 : if (q < 0)
623 : 0 : return q;
624 [ + - + - ]: 80 : if (q > 0 && !dry_run)
625 : 80 : *restart = true;
626 : : }
627 : : }
628 : :
629 : 248 : return r;
630 : : }
631 : :
632 : 96 : static int remove_marked_symlinks(
633 : : Set *remove_symlinks_to,
634 : : const char *config_path,
635 : : const LookupPaths *lp,
636 : : bool dry_run,
637 : : UnitFileChange **changes,
638 : : size_t *n_changes) {
639 : :
640 : 96 : _cleanup_close_ int fd = -1;
641 : : bool restart;
642 : 96 : int r = 0;
643 : :
644 [ - + ]: 96 : assert(config_path);
645 [ - + ]: 96 : assert(lp);
646 : :
647 [ + + ]: 96 : if (set_size(remove_symlinks_to) <= 0)
648 : 20 : return 0;
649 : :
650 : 76 : fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
651 [ - + ]: 76 : if (fd < 0)
652 [ # # ]: 0 : return errno == ENOENT ? 0 : -errno;
653 : :
654 : : do {
655 : : int q, cfd;
656 : 120 : restart = false;
657 : :
658 : 120 : cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
659 [ - + ]: 120 : if (cfd < 0)
660 : 0 : return -errno;
661 : :
662 : : /* This takes possession of cfd and closes it */
663 : 120 : q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, dry_run, &restart, changes, n_changes);
664 [ + - ]: 120 : if (r == 0)
665 : 120 : r = q;
666 [ + + ]: 120 : } while (restart);
667 : :
668 : 76 : return r;
669 : : }
670 : :
671 : 1248 : static int is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *name) {
672 : : int r;
673 : :
674 [ + + ]: 1248 : if (streq(name, i->name))
675 : 680 : return true;
676 : :
677 [ + + ]: 568 : if (strv_contains(i->aliases, name))
678 : 88 : return true;
679 : :
680 : : /* Look for template symlink matching DefaultInstance */
681 [ + + + - ]: 480 : if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
682 [ + + ]: 80 : _cleanup_free_ char *s = NULL;
683 : :
684 : 80 : r = unit_name_replace_instance(i->name, i->default_instance, &s);
685 [ - + ]: 80 : if (r < 0) {
686 [ # # ]: 0 : if (r != -EINVAL)
687 : 0 : return r;
688 : :
689 [ + + ]: 80 : } else if (streq(name, s))
690 : 16 : return true;
691 : : }
692 : :
693 : 464 : return false;
694 : : }
695 : :
696 : 227792 : static int find_symlinks_fd(
697 : : const char *root_dir,
698 : : const UnitFileInstallInfo *i,
699 : : bool match_aliases,
700 : : bool ignore_same_name,
701 : : int fd,
702 : : const char *path,
703 : : const char *config_path,
704 : : bool *same_name_link) {
705 : :
706 : 227792 : _cleanup_closedir_ DIR *d = NULL;
707 : : struct dirent *de;
708 : 227792 : int r = 0;
709 : :
710 [ - + ]: 227792 : assert(i);
711 [ - + ]: 227792 : assert(fd >= 0);
712 [ - + ]: 227792 : assert(path);
713 [ - + ]: 227792 : assert(config_path);
714 [ - + ]: 227792 : assert(same_name_link);
715 : :
716 : 227792 : d = fdopendir(fd);
717 [ - + ]: 227792 : if (!d) {
718 : 0 : safe_close(fd);
719 : 0 : return -errno;
720 : : }
721 : :
722 [ + + - + : 2987224 : FOREACH_DIRENT(de, d, return -errno) {
+ + ]
723 : :
724 : 2307840 : dirent_ensure_type(d, de);
725 : :
726 [ + + ]: 2307840 : if (de->d_type == DT_DIR) {
727 [ + - + ]: 204448 : _cleanup_free_ char *p = NULL;
728 : : int nfd, q;
729 : :
730 : 204448 : nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
731 [ - + ]: 204448 : if (nfd < 0) {
732 [ # # ]: 0 : if (errno == ENOENT)
733 : 0 : continue;
734 : :
735 [ # # ]: 0 : if (r == 0)
736 : 0 : r = -errno;
737 : 0 : continue;
738 : : }
739 : :
740 : 204448 : p = path_make_absolute(de->d_name, path);
741 [ - + ]: 204448 : if (!p) {
742 : 0 : safe_close(nfd);
743 : 0 : return -ENOMEM;
744 : : }
745 : :
746 : : /* This will close nfd, regardless whether it succeeds or not */
747 : 204448 : q = find_symlinks_fd(root_dir, i, match_aliases, ignore_same_name, nfd,
748 : : p, config_path, same_name_link);
749 [ + + ]: 204448 : if (q > 0)
750 : 972 : return 1;
751 [ + - ]: 203476 : if (r == 0)
752 : 203476 : r = q;
753 : :
754 [ + + ]: 2103392 : } else if (de->d_type == DT_LNK) {
755 [ + + - + : 635976 : _cleanup_free_ char *p = NULL, *dest = NULL;
+ - ]
756 : 634592 : bool found_path = false, found_dest, b = false;
757 : : int q;
758 : :
759 : : /* Acquire symlink name */
760 : 634592 : p = path_make_absolute(de->d_name, path);
761 [ - + ]: 634592 : if (!p)
762 : 0 : return -ENOMEM;
763 : :
764 : : /* Acquire symlink destination */
765 : 634592 : q = readlink_malloc(p, &dest);
766 [ - + ]: 634592 : if (q == -ENOENT)
767 : 0 : continue;
768 [ - + ]: 634592 : if (q < 0) {
769 [ # # ]: 0 : if (r == 0)
770 : 0 : r = q;
771 : 0 : continue;
772 : : }
773 : :
774 : : /* Make absolute */
775 [ + + ]: 634592 : if (!path_is_absolute(dest)) {
776 : : char *x;
777 : :
778 : 340592 : x = path_join(root_dir, dest);
779 [ - + ]: 340592 : if (!x)
780 : 0 : return -ENOMEM;
781 : :
782 : 340592 : free_and_replace(dest, x);
783 : : }
784 : :
785 [ - + ]: 634592 : assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
786 [ + + ]: 634592 : if (!ignore_same_name)
787 : : /* Check if the symlink itself matches what we are looking for.
788 : : *
789 : : * If ignore_same_name is specified, we are in one of the directories which
790 : : * have lower priority than the unit file, and even if a file or symlink with
791 : : * this name was found, we should ignore it. */
792 : 617356 : found_path = streq(de->d_name, i->name);
793 : :
794 : : /* Check if what the symlink points to matches what we are looking for */
795 : 634592 : found_dest = streq(basename(dest), i->name);
796 : :
797 [ + + + + ]: 634592 : if (found_path && found_dest) {
798 [ + - ]: 952 : _cleanup_free_ char *t = NULL;
799 : :
800 : : /* Filter out same name links in the main
801 : : * config path */
802 : 952 : t = path_make_absolute(i->name, config_path);
803 [ - + ]: 952 : if (!t)
804 : 0 : return -ENOMEM;
805 : :
806 : 952 : b = path_equal(t, p);
807 : : }
808 : :
809 [ + + ]: 634592 : if (b)
810 : 76 : *same_name_link = true;
811 [ + + + + ]: 634516 : else if (found_path || found_dest) {
812 [ + + ]: 1848 : if (!match_aliases)
813 : 600 : return 1;
814 : :
815 : : /* Check if symlink name is in the set of names used by [Install] */
816 : 1248 : q = is_symlink_with_known_name(i, de->d_name);
817 [ - + ]: 1248 : if (q < 0)
818 : 0 : return q;
819 [ + + ]: 1248 : if (q > 0)
820 : 784 : return 1;
821 : : }
822 : : }
823 : : }
824 : :
825 : 225436 : return r;
826 : : }
827 : :
828 : 52876 : static int find_symlinks(
829 : : const char *root_dir,
830 : : const UnitFileInstallInfo *i,
831 : : bool match_name,
832 : : bool ignore_same_name,
833 : : const char *config_path,
834 : : bool *same_name_link) {
835 : :
836 : : int fd;
837 : :
838 [ - + ]: 52876 : assert(i);
839 [ - + ]: 52876 : assert(config_path);
840 [ - + ]: 52876 : assert(same_name_link);
841 : :
842 : 52876 : fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
843 [ + + ]: 52876 : if (fd < 0) {
844 [ + - + - ]: 29532 : if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
845 : 29532 : return 0;
846 : 0 : return -errno;
847 : : }
848 : :
849 : : /* This takes possession of fd and closes it */
850 : 23344 : return find_symlinks_fd(root_dir, i, match_name, ignore_same_name, fd,
851 : : config_path, config_path, same_name_link);
852 : : }
853 : :
854 : 4712 : static int find_symlinks_in_scope(
855 : : UnitFileScope scope,
856 : : const LookupPaths *paths,
857 : : const UnitFileInstallInfo *i,
858 : : bool match_name,
859 : : UnitFileState *state) {
860 : :
861 : 4712 : bool same_name_link_runtime = false, same_name_link_config = false;
862 : 4712 : bool enabled_in_runtime = false, enabled_at_all = false;
863 : 4712 : bool ignore_same_name = false;
864 : : char **p;
865 : : int r;
866 : :
867 [ - + ]: 4712 : assert(paths);
868 [ - + ]: 4712 : assert(i);
869 : :
870 : : /* As we iterate over the list of search paths in paths->search_path, we may encounter "same name"
871 : : * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
872 : : * effectively masked, so we should ignore them. */
873 : :
874 [ + - + + ]: 57064 : STRV_FOREACH(p, paths->search_path) {
875 : 52876 : bool same_name_link = false;
876 : :
877 : 52876 : r = find_symlinks(paths->root_dir, i, match_name, ignore_same_name, *p, &same_name_link);
878 [ - + ]: 52876 : if (r < 0)
879 : 524 : return r;
880 [ + + ]: 52876 : if (r > 0) {
881 : : /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
882 : :
883 [ + + ]: 1384 : if (path_equal_ptr(*p, paths->persistent_config)) {
884 : : /* This is the best outcome, let's return it immediately. */
885 : 524 : *state = UNIT_FILE_ENABLED;
886 : 524 : return 1;
887 : : }
888 : :
889 : : /* look for global enablement of user units */
890 [ - + # # ]: 860 : if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) {
891 : 0 : *state = UNIT_FILE_ENABLED;
892 : 0 : return 1;
893 : : }
894 : :
895 : 860 : r = path_is_runtime(paths, *p, false);
896 [ - + ]: 860 : if (r < 0)
897 : 0 : return r;
898 [ + + ]: 860 : if (r > 0)
899 : 4 : enabled_in_runtime = true;
900 : : else
901 : 856 : enabled_at_all = true;
902 : :
903 [ + + ]: 51492 : } else if (same_name_link) {
904 [ + + ]: 32 : if (path_equal_ptr(*p, paths->persistent_config))
905 : 8 : same_name_link_config = true;
906 : : else {
907 : 24 : r = path_is_runtime(paths, *p, false);
908 [ - + ]: 24 : if (r < 0)
909 : 0 : return r;
910 [ - + ]: 24 : if (r > 0)
911 : 0 : same_name_link_runtime = true;
912 : : }
913 : : }
914 : :
915 : : /* Check if next iteration will be "below" the unit file (either a regular file
916 : : * or a symlink), and hence should be ignored */
917 [ + + + + ]: 52352 : if (!ignore_same_name && path_startswith(i->path, *p))
918 : 4156 : ignore_same_name = true;
919 : : }
920 : :
921 [ + + ]: 4188 : if (enabled_in_runtime) {
922 : 4 : *state = UNIT_FILE_ENABLED_RUNTIME;
923 : 4 : return 1;
924 : : }
925 : :
926 : : /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
927 : : * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
928 : : * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
929 : : * something, and hence are a much stronger concept. */
930 [ + + + + ]: 4184 : if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
931 : 4 : *state = UNIT_FILE_STATIC;
932 : 4 : return 1;
933 : : }
934 : :
935 : : /* Hmm, we didn't find it, but maybe we found the same name
936 : : * link? */
937 [ + + ]: 4180 : if (same_name_link_config) {
938 : 8 : *state = UNIT_FILE_LINKED;
939 : 8 : return 1;
940 : : }
941 [ - + ]: 4172 : if (same_name_link_runtime) {
942 : 0 : *state = UNIT_FILE_LINKED_RUNTIME;
943 : 0 : return 1;
944 : : }
945 : :
946 : 4172 : return 0;
947 : : }
948 : :
949 : 4100 : static void install_info_free(UnitFileInstallInfo *i) {
950 : :
951 [ - + ]: 4100 : if (!i)
952 : 0 : return;
953 : :
954 : 4100 : free(i->name);
955 : 4100 : free(i->path);
956 : 4100 : strv_free(i->aliases);
957 : 4100 : strv_free(i->wanted_by);
958 : 4100 : strv_free(i->required_by);
959 : 4100 : strv_free(i->also);
960 : 4100 : free(i->default_instance);
961 : 4100 : free(i->symlink_target);
962 : 4100 : free(i);
963 : : }
964 : :
965 : 3316 : static void install_context_done(InstallContext *c) {
966 [ - + ]: 3316 : assert(c);
967 : :
968 [ + + ]: 7120 : c->will_process = ordered_hashmap_free_with_destructor(c->will_process, install_info_free);
969 [ + + ]: 3612 : c->have_processed = ordered_hashmap_free_with_destructor(c->have_processed, install_info_free);
970 : 3316 : }
971 : :
972 : 4512 : static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
973 : : UnitFileInstallInfo *i;
974 : :
975 : 4512 : i = ordered_hashmap_get(c->have_processed, name);
976 [ - + ]: 4512 : if (i)
977 : 0 : return i;
978 : :
979 : 4512 : return ordered_hashmap_get(c->will_process, name);
980 : : }
981 : :
982 : 124 : static int install_info_may_process(
983 : : const UnitFileInstallInfo *i,
984 : : const LookupPaths *paths,
985 : : UnitFileChange **changes,
986 : : size_t *n_changes) {
987 [ - + ]: 124 : assert(i);
988 [ - + ]: 124 : assert(paths);
989 : :
990 : : /* Checks whether the loaded unit file is one we should process, or is masked,
991 : : * transient or generated and thus not subject to enable/disable operations. */
992 : :
993 [ + + ]: 124 : if (i->type == UNIT_FILE_TYPE_MASKED) {
994 : 4 : unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
995 : 4 : return -ERFKILL;
996 : : }
997 [ + - - + ]: 240 : if (path_is_generator(paths, i->path) ||
998 : 120 : path_is_transient(paths, i->path)) {
999 : 0 : unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
1000 : 0 : return -EADDRNOTAVAIL;
1001 : : }
1002 : :
1003 : 120 : return 0;
1004 : : }
1005 : :
1006 : : /**
1007 : : * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
1008 : : * hashmap, or retrieves the existing one if already present.
1009 : : *
1010 : : * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1011 : : */
1012 : 4136 : static int install_info_add(
1013 : : InstallContext *c,
1014 : : const char *name,
1015 : : const char *path,
1016 : : bool auxiliary,
1017 : : UnitFileInstallInfo **ret) {
1018 : :
1019 : 4136 : UnitFileInstallInfo *i = NULL;
1020 : : int r;
1021 : :
1022 [ - + ]: 4136 : assert(c);
1023 [ + + - + ]: 4136 : assert(name || path);
1024 : :
1025 [ + + ]: 4136 : if (!name)
1026 : 4 : name = basename(path);
1027 : :
1028 [ - + ]: 4136 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
1029 : 0 : return -EINVAL;
1030 : :
1031 : 4136 : i = install_info_find(c, name);
1032 [ + + ]: 4136 : if (i) {
1033 [ + + + - ]: 36 : i->auxiliary = i->auxiliary && auxiliary;
1034 : :
1035 [ + - ]: 36 : if (ret)
1036 : 36 : *ret = i;
1037 : 36 : return 0;
1038 : : }
1039 : :
1040 : 4100 : r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
1041 [ - + ]: 4100 : if (r < 0)
1042 : 0 : return r;
1043 : :
1044 : 4100 : i = new(UnitFileInstallInfo, 1);
1045 [ - + ]: 4100 : if (!i)
1046 : 0 : return -ENOMEM;
1047 : :
1048 : 4100 : *i = (UnitFileInstallInfo) {
1049 : : .type = _UNIT_FILE_TYPE_INVALID,
1050 : : .auxiliary = auxiliary,
1051 : : };
1052 : :
1053 : 4100 : i->name = strdup(name);
1054 [ - + ]: 4100 : if (!i->name) {
1055 : 0 : r = -ENOMEM;
1056 : 0 : goto fail;
1057 : : }
1058 : :
1059 [ + + ]: 4100 : if (path) {
1060 : 4 : i->path = strdup(path);
1061 [ - + ]: 4 : if (!i->path) {
1062 : 0 : r = -ENOMEM;
1063 : 0 : goto fail;
1064 : : }
1065 : : }
1066 : :
1067 : 4100 : r = ordered_hashmap_put(c->will_process, i->name, i);
1068 [ - + ]: 4100 : if (r < 0)
1069 : 0 : goto fail;
1070 : :
1071 [ + + ]: 4100 : if (ret)
1072 : 4060 : *ret = i;
1073 : :
1074 : 4100 : return 1;
1075 : :
1076 : 0 : fail:
1077 : 0 : install_info_free(i);
1078 : 0 : return r;
1079 : : }
1080 : :
1081 : 144 : static int config_parse_alias(
1082 : : const char *unit,
1083 : : const char *filename,
1084 : : unsigned line,
1085 : : const char *section,
1086 : : unsigned section_line,
1087 : : const char *lvalue,
1088 : : int ltype,
1089 : : const char *rvalue,
1090 : : void *data,
1091 : : void *userdata) {
1092 : :
1093 : : UnitType type;
1094 : :
1095 [ - + ]: 144 : assert(unit);
1096 [ - + ]: 144 : assert(filename);
1097 [ - + ]: 144 : assert(lvalue);
1098 [ - + ]: 144 : assert(rvalue);
1099 : :
1100 : 144 : type = unit_name_to_type(unit);
1101 [ - + ]: 144 : if (!unit_type_may_alias(type))
1102 [ # # ]: 0 : return log_syntax(unit, LOG_WARNING, filename, line, 0,
1103 : : "Alias= is not allowed for %s units, ignoring.",
1104 : : unit_type_to_string(type));
1105 : :
1106 : 144 : return config_parse_strv(unit, filename, line, section, section_line,
1107 : : lvalue, ltype, rvalue, data, userdata);
1108 : : }
1109 : :
1110 : 184 : static int config_parse_also(
1111 : : const char *unit,
1112 : : const char *filename,
1113 : : unsigned line,
1114 : : const char *section,
1115 : : unsigned section_line,
1116 : : const char *lvalue,
1117 : : int ltype,
1118 : : const char *rvalue,
1119 : : void *data,
1120 : : void *userdata) {
1121 : :
1122 : 184 : UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
1123 : 184 : InstallContext *c = data;
1124 : : int r;
1125 : :
1126 [ - + ]: 184 : assert(unit);
1127 [ - + ]: 184 : assert(filename);
1128 [ - + ]: 184 : assert(lvalue);
1129 [ - + ]: 184 : assert(rvalue);
1130 : :
1131 : 200 : for (;;) {
1132 [ + - + + : 568 : _cleanup_free_ char *word = NULL, *printed = NULL;
- + ]
1133 : :
1134 : 384 : r = extract_first_word(&rvalue, &word, NULL, 0);
1135 [ - + ]: 384 : if (r < 0)
1136 : 0 : return r;
1137 [ + + ]: 384 : if (r == 0)
1138 : 184 : break;
1139 : :
1140 : 200 : r = install_full_printf(info, word, &printed);
1141 [ - + ]: 200 : if (r < 0)
1142 : 0 : return r;
1143 : :
1144 [ - + ]: 200 : if (!unit_name_is_valid(printed, UNIT_NAME_ANY))
1145 : 0 : return -EINVAL;
1146 : :
1147 : 200 : r = install_info_add(c, printed, NULL, true, &alsoinfo);
1148 [ - + ]: 200 : if (r < 0)
1149 : 0 : return r;
1150 : :
1151 : 200 : r = strv_push(&info->also, printed);
1152 [ - + ]: 200 : if (r < 0)
1153 : 0 : return r;
1154 : :
1155 : 200 : printed = NULL;
1156 : : }
1157 : :
1158 : 184 : return 0;
1159 : : }
1160 : :
1161 : 296 : static int config_parse_default_instance(
1162 : : const char *unit,
1163 : : const char *filename,
1164 : : unsigned line,
1165 : : const char *section,
1166 : : unsigned section_line,
1167 : : const char *lvalue,
1168 : : int ltype,
1169 : : const char *rvalue,
1170 : : void *data,
1171 : : void *userdata) {
1172 : :
1173 : 296 : UnitFileInstallInfo *i = data;
1174 : 296 : _cleanup_free_ char *printed = NULL;
1175 : : int r;
1176 : :
1177 [ - + ]: 296 : assert(unit);
1178 [ - + ]: 296 : assert(filename);
1179 [ - + ]: 296 : assert(lvalue);
1180 [ - + ]: 296 : assert(rvalue);
1181 : :
1182 [ + + ]: 296 : if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE))
1183 : : /* When enabling an instance, we might be using a template unit file,
1184 : : * but we should ignore DefaultInstance silently. */
1185 : 200 : return 0;
1186 [ - + ]: 96 : if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
1187 [ # # ]: 0 : return log_syntax(unit, LOG_WARNING, filename, line, 0,
1188 : : "DefaultInstance= only makes sense for template units, ignoring.");
1189 : :
1190 : 96 : r = install_full_printf(i, rvalue, &printed);
1191 [ - + ]: 96 : if (r < 0)
1192 : 0 : return r;
1193 : :
1194 [ - + ]: 96 : if (!unit_instance_is_valid(printed))
1195 : 0 : return -EINVAL;
1196 : :
1197 : 96 : return free_and_replace(i->default_instance, printed);
1198 : : }
1199 : :
1200 : 45176 : static int unit_file_load(
1201 : : InstallContext *c,
1202 : : UnitFileInstallInfo *info,
1203 : : const char *path,
1204 : : const char *root_dir,
1205 : : SearchFlags flags) {
1206 : :
1207 : 180704 : const ConfigTableItem items[] = {
1208 : 45176 : { "Install", "Alias", config_parse_alias, 0, &info->aliases },
1209 : 45176 : { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
1210 : 45176 : { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
1211 : : { "Install", "DefaultInstance", config_parse_default_instance, 0, info },
1212 : : { "Install", "Also", config_parse_also, 0, c },
1213 : : {}
1214 : : };
1215 : :
1216 : : UnitType type;
1217 : 45176 : _cleanup_fclose_ FILE *f = NULL;
1218 : 45176 : _cleanup_close_ int fd = -1;
1219 : : struct stat st;
1220 : : int r;
1221 : :
1222 [ - + ]: 45176 : assert(info);
1223 [ - + ]: 45176 : assert(path);
1224 : :
1225 [ + + ]: 45176 : if (!(flags & SEARCH_DROPIN)) {
1226 : : /* Loading or checking for the main unit file… */
1227 : :
1228 : 45052 : type = unit_name_to_type(info->name);
1229 [ - + ]: 45052 : if (type < 0)
1230 : 0 : return -EINVAL;
1231 [ + + - + ]: 45052 : if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type))
1232 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1233 : : "Unit type %s cannot be templated.", unit_type_to_string(type));
1234 : :
1235 [ + + ]: 45052 : if (!(flags & SEARCH_LOAD)) {
1236 : 4624 : r = lstat(path, &st);
1237 [ + + ]: 4624 : if (r < 0)
1238 : 4200 : return -errno;
1239 : :
1240 [ - + ]: 424 : if (null_or_empty(&st))
1241 : 0 : info->type = UNIT_FILE_TYPE_MASKED;
1242 [ + + ]: 424 : else if (S_ISREG(st.st_mode))
1243 : 308 : info->type = UNIT_FILE_TYPE_REGULAR;
1244 [ + - ]: 116 : else if (S_ISLNK(st.st_mode))
1245 : 116 : return -ELOOP;
1246 [ # # ]: 0 : else if (S_ISDIR(st.st_mode))
1247 : 0 : return -EISDIR;
1248 : : else
1249 : 0 : return -ENOTTY;
1250 : :
1251 : 308 : return 0;
1252 : : }
1253 : :
1254 : 40428 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
1255 [ + + ]: 40428 : if (fd < 0)
1256 : 37596 : return -errno;
1257 : : } else {
1258 : : /* Operating on a drop-in file. If we aren't supposed to load the unit file drop-ins don't matter, let's hence shortcut this. */
1259 : :
1260 [ + + ]: 124 : if (!(flags & SEARCH_LOAD))
1261 : 4 : return 0;
1262 : :
1263 : 120 : fd = chase_symlinks_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
1264 [ - + ]: 120 : if (fd < 0)
1265 : 0 : return fd;
1266 : : }
1267 : :
1268 [ - + ]: 2952 : if (fstat(fd, &st) < 0)
1269 : 0 : return -errno;
1270 : :
1271 [ - + ]: 2952 : if (null_or_empty(&st)) {
1272 [ # # ]: 0 : if ((flags & SEARCH_DROPIN) == 0)
1273 : 0 : info->type = UNIT_FILE_TYPE_MASKED;
1274 : :
1275 : 0 : return 0;
1276 : : }
1277 : :
1278 : 2952 : r = stat_verify_regular(&st);
1279 [ - + ]: 2952 : if (r < 0)
1280 : 0 : return r;
1281 : :
1282 : 2952 : f = fdopen(fd, "r");
1283 [ - + ]: 2952 : if (!f)
1284 : 0 : return -errno;
1285 : 2952 : fd = -1;
1286 : :
1287 : : /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1288 [ - + ]: 2952 : assert(c);
1289 : :
1290 : 2952 : r = config_parse(info->name, path, f,
1291 : : NULL,
1292 : : config_item_table_lookup, items,
1293 : : CONFIG_PARSE_RELAXED|CONFIG_PARSE_ALLOW_INCLUDE, info);
1294 [ - + ]: 2952 : if (r < 0)
1295 [ # # ]: 0 : return log_debug_errno(r, "Failed to parse %s: %m", info->name);
1296 : :
1297 [ + + ]: 2952 : if ((flags & SEARCH_DROPIN) == 0)
1298 : 2832 : info->type = UNIT_FILE_TYPE_REGULAR;
1299 : :
1300 : : return
1301 : 5904 : (int) strv_length(info->aliases) +
1302 : 5904 : (int) strv_length(info->wanted_by) +
1303 : 2952 : (int) strv_length(info->required_by);
1304 : : }
1305 : :
1306 : 45176 : static int unit_file_load_or_readlink(
1307 : : InstallContext *c,
1308 : : UnitFileInstallInfo *info,
1309 : : const char *path,
1310 : : const char *root_dir,
1311 : : SearchFlags flags) {
1312 : :
1313 : 45176 : _cleanup_free_ char *target = NULL;
1314 : : int r;
1315 : :
1316 : 45176 : r = unit_file_load(c, info, path, root_dir, flags);
1317 [ + + - + ]: 45176 : if (r != -ELOOP || (flags & SEARCH_DROPIN))
1318 : 44532 : return r;
1319 : :
1320 : : /* This is a symlink, let's read it. */
1321 : :
1322 : 644 : r = readlink_malloc(path, &target);
1323 [ - + ]: 644 : if (r < 0)
1324 : 0 : return r;
1325 : :
1326 [ + + ]: 644 : if (path_equal(target, "/dev/null"))
1327 : 24 : info->type = UNIT_FILE_TYPE_MASKED;
1328 : : else {
1329 : : const char *bn;
1330 : : UnitType a, b;
1331 : :
1332 : 620 : bn = basename(target);
1333 : :
1334 [ + + ]: 620 : if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
1335 : :
1336 [ - + ]: 512 : if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
1337 : 0 : return -EINVAL;
1338 : :
1339 [ + + ]: 108 : } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1340 : :
1341 [ - + ]: 68 : if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
1342 : 0 : return -EINVAL;
1343 : :
1344 [ + - ]: 40 : } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
1345 : :
1346 [ - + ]: 40 : if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
1347 : 0 : return -EINVAL;
1348 : : } else
1349 : 0 : return -EINVAL;
1350 : :
1351 : : /* Enforce that the symlink destination does not
1352 : : * change the unit file type. */
1353 : :
1354 : 620 : a = unit_name_to_type(info->name);
1355 : 620 : b = unit_name_to_type(bn);
1356 [ + - + - : 620 : if (a < 0 || b < 0 || a != b)
- + ]
1357 : 0 : return -EINVAL;
1358 : :
1359 [ + + ]: 620 : if (path_is_absolute(target))
1360 : : /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
1361 : 208 : info->symlink_target = path_join(root_dir, target);
1362 : : else
1363 : : /* This is a relative path, take it relative to the dir the symlink is located in. */
1364 : 412 : info->symlink_target = file_in_same_dir(path, target);
1365 [ - + ]: 620 : if (!info->symlink_target)
1366 : 0 : return -ENOMEM;
1367 : :
1368 : 620 : info->type = UNIT_FILE_TYPE_SYMLINK;
1369 : : }
1370 : :
1371 : 644 : return 0;
1372 : : }
1373 : :
1374 : 4200 : static int unit_file_search(
1375 : : InstallContext *c,
1376 : : UnitFileInstallInfo *info,
1377 : : const LookupPaths *paths,
1378 : : SearchFlags flags) {
1379 : :
1380 : 4200 : const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
1381 : 4200 : _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
1382 : 4200 : _cleanup_free_ char *template = NULL;
1383 : 4200 : bool found_unit = false;
1384 : : int r, result;
1385 : : char **p;
1386 : :
1387 [ - + ]: 4200 : assert(info);
1388 [ - + ]: 4200 : assert(paths);
1389 : :
1390 : : /* Was this unit already loaded? */
1391 [ + + ]: 4200 : if (info->type != _UNIT_FILE_TYPE_INVALID)
1392 : 276 : return 0;
1393 : :
1394 [ + + ]: 3924 : if (info->path)
1395 : 4 : return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags);
1396 : :
1397 [ - + ]: 3920 : assert(info->name);
1398 : :
1399 [ + + ]: 3920 : if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1400 : 320 : r = unit_name_template(info->name, &template);
1401 [ - + ]: 320 : if (r < 0)
1402 : 0 : return r;
1403 : : }
1404 : :
1405 [ + - + + ]: 42036 : STRV_FOREACH(p, paths->search_path) {
1406 [ + - + ]: 41500 : _cleanup_free_ char *path = NULL;
1407 : :
1408 : 41500 : path = path_join(*p, info->name);
1409 [ - + ]: 41500 : if (!path)
1410 : 0 : return -ENOMEM;
1411 : :
1412 : 41500 : r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
1413 [ + + ]: 41500 : if (r >= 0) {
1414 : 3384 : info->path = TAKE_PTR(path);
1415 : 3384 : result = r;
1416 : 3384 : found_unit = true;
1417 : 3384 : break;
1418 [ + - - + ]: 38116 : } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1419 : 0 : return r;
1420 : : }
1421 : :
1422 [ + + + + ]: 3920 : if (!found_unit && template) {
1423 : :
1424 : : /* Unit file doesn't exist, however instance
1425 : : * enablement was requested. We will check if it is
1426 : : * possible to load template unit file. */
1427 : :
1428 [ + - + + ]: 3464 : STRV_FOREACH(p, paths->search_path) {
1429 [ + - + ]: 3448 : _cleanup_free_ char *path = NULL;
1430 : :
1431 : 3448 : path = path_join(*p, template);
1432 [ - + ]: 3448 : if (!path)
1433 : 0 : return -ENOMEM;
1434 : :
1435 : 3448 : r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
1436 [ + + ]: 3448 : if (r >= 0) {
1437 : 296 : info->path = TAKE_PTR(path);
1438 : 296 : result = r;
1439 : 296 : found_unit = true;
1440 : 296 : break;
1441 [ + - - + ]: 3152 : } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1442 : 0 : return r;
1443 : : }
1444 : : }
1445 : :
1446 [ + + ]: 3920 : if (!found_unit)
1447 [ + - + + ]: 240 : return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
1448 : : "Cannot find unit %s%s%s.",
1449 : : info->name, template ? " or " : "", strempty(template));
1450 : :
1451 [ + + ]: 3680 : if (info->type == UNIT_FILE_TYPE_MASKED)
1452 : 24 : return result;
1453 : :
1454 : : /* Search for drop-in directories */
1455 : :
1456 [ + + + - : 18280 : dropin_dir_name = strjoina(info->name, ".d");
- + - + +
+ + - ]
1457 [ + - + + ]: 47484 : STRV_FOREACH(p, paths->search_path) {
1458 : : char *path;
1459 : :
1460 : 43828 : path = path_join(*p, dropin_dir_name);
1461 [ - + ]: 43828 : if (!path)
1462 : 0 : return -ENOMEM;
1463 : :
1464 : 43828 : r = strv_consume(&dirs, path);
1465 [ - + ]: 43828 : if (r < 0)
1466 : 0 : return r;
1467 : : }
1468 : :
1469 [ + + ]: 3656 : if (template) {
1470 [ + + + - : 1520 : dropin_template_dir_name = strjoina(template, ".d");
- + - + +
+ + - ]
1471 [ + - + + ]: 3952 : STRV_FOREACH(p, paths->search_path) {
1472 : : char *path;
1473 : :
1474 : 3648 : path = path_join(*p, dropin_template_dir_name);
1475 [ - + ]: 3648 : if (!path)
1476 : 0 : return -ENOMEM;
1477 : :
1478 : 3648 : r = strv_consume(&dirs, path);
1479 [ - + ]: 3648 : if (r < 0)
1480 : 0 : return r;
1481 : : }
1482 : : }
1483 : :
1484 : : /* Load drop-in conf files */
1485 : :
1486 : 3656 : r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs);
1487 [ - + ]: 3656 : if (r < 0)
1488 [ # # ]: 0 : return log_debug_errno(r, "Failed to get list of conf files: %m");
1489 : :
1490 [ + - + + ]: 3780 : STRV_FOREACH(p, files) {
1491 : 124 : r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags | SEARCH_DROPIN);
1492 [ - + ]: 124 : if (r < 0)
1493 [ # # ]: 0 : return log_debug_errno(r, "Failed to load conf file %s: %m", *p);
1494 : : }
1495 : :
1496 : 3656 : return result;
1497 : : }
1498 : :
1499 : 656 : static int install_info_follow(
1500 : : InstallContext *c,
1501 : : UnitFileInstallInfo *i,
1502 : : const char *root_dir,
1503 : : SearchFlags flags,
1504 : : bool ignore_different_name) {
1505 : :
1506 [ - + ]: 656 : assert(c);
1507 [ - + ]: 656 : assert(i);
1508 : :
1509 [ - + ]: 656 : if (i->type != UNIT_FILE_TYPE_SYMLINK)
1510 : 0 : return -EINVAL;
1511 [ - + ]: 656 : if (!i->symlink_target)
1512 : 0 : return -EINVAL;
1513 : :
1514 : : /* If the basename doesn't match, the caller should add a
1515 : : * complete new entry for this. */
1516 : :
1517 [ + + + + ]: 656 : if (!ignore_different_name && !streq(basename(i->symlink_target), i->name))
1518 : 556 : return -EXDEV;
1519 : :
1520 : 100 : free_and_replace(i->path, i->symlink_target);
1521 : 100 : i->type = _UNIT_FILE_TYPE_INVALID;
1522 : :
1523 : 100 : return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
1524 : : }
1525 : :
1526 : : /**
1527 : : * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1528 : : * target, maybe more than once. Propagate the instance name if present.
1529 : : */
1530 : 3640 : static int install_info_traverse(
1531 : : UnitFileScope scope,
1532 : : InstallContext *c,
1533 : : const LookupPaths *paths,
1534 : : UnitFileInstallInfo *start,
1535 : : SearchFlags flags,
1536 : : UnitFileInstallInfo **ret) {
1537 : :
1538 : : UnitFileInstallInfo *i;
1539 : 3640 : unsigned k = 0;
1540 : : int r;
1541 : :
1542 [ - + ]: 3640 : assert(paths);
1543 [ - + ]: 3640 : assert(start);
1544 [ - + ]: 3640 : assert(c);
1545 : :
1546 : 3640 : r = unit_file_search(c, start, paths, flags);
1547 [ + + ]: 3640 : if (r < 0)
1548 : 240 : return r;
1549 : :
1550 : 3400 : i = start;
1551 [ + + ]: 4052 : while (i->type == UNIT_FILE_TYPE_SYMLINK) {
1552 : : /* Follow the symlink */
1553 : :
1554 [ - + ]: 652 : if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
1555 : 0 : return -ELOOP;
1556 : :
1557 [ + + ]: 652 : if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
1558 : 28 : r = path_is_config(paths, i->path, true);
1559 [ - + ]: 28 : if (r < 0)
1560 : 0 : return r;
1561 [ - + ]: 28 : if (r > 0)
1562 : 0 : return -ELOOP;
1563 : : }
1564 : :
1565 : 652 : r = install_info_follow(c, i, paths->root_dir, flags, false);
1566 [ + + ]: 652 : if (r == -EXDEV) {
1567 [ + - + ]: 556 : _cleanup_free_ char *buffer = NULL;
1568 : : const char *bn;
1569 : :
1570 : : /* Target has a different name, create a new
1571 : : * install info object for that, and continue
1572 : : * with that. */
1573 : :
1574 : 556 : bn = basename(i->symlink_target);
1575 : :
1576 [ + + ]: 556 : if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
1577 [ + - ]: 72 : unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
1578 : :
1579 [ + - + ]: 72 : _cleanup_free_ char *instance = NULL;
1580 : :
1581 : 72 : r = unit_name_to_instance(i->name, &instance);
1582 [ - + ]: 72 : if (r < 0)
1583 : 0 : return r;
1584 : :
1585 : 72 : r = unit_name_replace_instance(bn, instance, &buffer);
1586 [ - + ]: 72 : if (r < 0)
1587 : 0 : return r;
1588 : :
1589 [ + + ]: 72 : if (streq(buffer, i->name)) {
1590 : :
1591 : : /* We filled in the instance, and the target stayed the same? If so, then let's
1592 : : * honour the link as it is. */
1593 : :
1594 : 4 : r = install_info_follow(c, i, paths->root_dir, flags, true);
1595 [ - + ]: 4 : if (r < 0)
1596 : 0 : return r;
1597 : :
1598 : 4 : continue;
1599 : : }
1600 : :
1601 : 68 : bn = buffer;
1602 : : }
1603 : :
1604 : 552 : r = install_info_add(c, bn, NULL, false, &i);
1605 [ - + ]: 552 : if (r < 0)
1606 : 0 : return r;
1607 : :
1608 : : /* Try again, with the new target we found. */
1609 : 552 : r = unit_file_search(c, i, paths, flags);
1610 [ - + ]: 552 : if (r == -ENOENT)
1611 : : /* Translate error code to highlight this specific case */
1612 : 0 : return -ENOLINK;
1613 : : }
1614 : :
1615 [ - + ]: 648 : if (r < 0)
1616 : 0 : return r;
1617 : : }
1618 : :
1619 [ + + ]: 3400 : if (ret)
1620 : 3100 : *ret = i;
1621 : :
1622 : 3400 : return 0;
1623 : : }
1624 : :
1625 : : /**
1626 : : * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1627 : : * or the name (otherwise). root_dir is prepended to the path.
1628 : : */
1629 : 3344 : static int install_info_add_auto(
1630 : : InstallContext *c,
1631 : : const LookupPaths *paths,
1632 : : const char *name_or_path,
1633 : : UnitFileInstallInfo **ret) {
1634 : :
1635 [ - + ]: 3344 : assert(c);
1636 [ - + ]: 3344 : assert(name_or_path);
1637 : :
1638 [ + + ]: 3344 : if (path_is_absolute(name_or_path)) {
1639 : : const char *pp;
1640 : :
1641 [ + - - + : 4 : pp = prefix_roota(paths->root_dir, name_or_path);
- + - + -
+ + - - +
- + ]
1642 : :
1643 : 4 : return install_info_add(c, NULL, pp, false, ret);
1644 : : } else
1645 : 3340 : return install_info_add(c, name_or_path, NULL, false, ret);
1646 : : }
1647 : :
1648 : 3344 : static int install_info_discover(
1649 : : UnitFileScope scope,
1650 : : InstallContext *c,
1651 : : const LookupPaths *paths,
1652 : : const char *name,
1653 : : SearchFlags flags,
1654 : : UnitFileInstallInfo **ret,
1655 : : UnitFileChange **changes,
1656 : : size_t *n_changes) {
1657 : :
1658 : : UnitFileInstallInfo *i;
1659 : : int r;
1660 : :
1661 [ - + ]: 3344 : assert(c);
1662 [ - + ]: 3344 : assert(paths);
1663 [ - + ]: 3344 : assert(name);
1664 : :
1665 : 3344 : r = install_info_add_auto(c, paths, name, &i);
1666 [ + - ]: 3344 : if (r >= 0)
1667 : 3344 : r = install_info_traverse(scope, c, paths, i, flags, ret);
1668 : :
1669 [ + + ]: 3344 : if (r < 0)
1670 : 240 : unit_file_changes_add(changes, n_changes, r, name, NULL);
1671 : 3344 : return r;
1672 : : }
1673 : :
1674 : 124 : static int install_info_discover_and_check(
1675 : : UnitFileScope scope,
1676 : : InstallContext *c,
1677 : : const LookupPaths *paths,
1678 : : const char *name,
1679 : : SearchFlags flags,
1680 : : UnitFileInstallInfo **ret,
1681 : : UnitFileChange **changes,
1682 : : size_t *n_changes) {
1683 : :
1684 : : int r;
1685 : :
1686 : 124 : r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes);
1687 [ + + ]: 124 : if (r < 0)
1688 : 4 : return r;
1689 : :
1690 [ + - ]: 120 : return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes);
1691 : : }
1692 : :
1693 : 120 : static int install_info_symlink_alias(
1694 : : UnitFileInstallInfo *i,
1695 : : const LookupPaths *paths,
1696 : : const char *config_path,
1697 : : bool force,
1698 : : UnitFileChange **changes,
1699 : : size_t *n_changes) {
1700 : :
1701 : : char **s;
1702 : 120 : int r = 0, q;
1703 : :
1704 [ - + ]: 120 : assert(i);
1705 [ - + ]: 120 : assert(paths);
1706 [ - + ]: 120 : assert(config_path);
1707 : :
1708 [ - + # # ]: 120 : STRV_FOREACH(s, i->aliases) {
1709 [ # # # # ]: 0 : _cleanup_free_ char *alias_path = NULL, *dst = NULL;
1710 : :
1711 : 0 : q = install_full_printf(i, *s, &dst);
1712 [ # # ]: 0 : if (q < 0)
1713 : 0 : return q;
1714 : :
1715 : 0 : alias_path = path_make_absolute(dst, config_path);
1716 [ # # ]: 0 : if (!alias_path)
1717 : 0 : return -ENOMEM;
1718 : :
1719 : 0 : q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
1720 [ # # ]: 0 : if (r == 0)
1721 : 0 : r = q;
1722 : : }
1723 : :
1724 : 120 : return r;
1725 : : }
1726 : :
1727 : 240 : static int install_info_symlink_wants(
1728 : : UnitFileInstallInfo *i,
1729 : : const LookupPaths *paths,
1730 : : const char *config_path,
1731 : : char **list,
1732 : : const char *suffix,
1733 : : UnitFileChange **changes,
1734 : : size_t *n_changes) {
1735 : :
1736 : 240 : _cleanup_free_ char *buf = NULL;
1737 : : const char *n;
1738 : : char **s;
1739 : 240 : int r = 0, q;
1740 : :
1741 [ - + ]: 240 : assert(i);
1742 [ - + ]: 240 : assert(paths);
1743 [ - + ]: 240 : assert(config_path);
1744 : :
1745 [ + + ]: 240 : if (strv_isempty(list))
1746 : 128 : return 0;
1747 : :
1748 [ + + ]: 112 : if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
1749 : 8 : UnitFileInstallInfo instance = {
1750 : : .type = _UNIT_FILE_TYPE_INVALID,
1751 : : };
1752 [ + - ]: 8 : _cleanup_free_ char *path = NULL;
1753 : :
1754 : : /* If this is a template, and we have no instance, don't do anything */
1755 [ - + ]: 8 : if (!i->default_instance)
1756 : 0 : return 1;
1757 : :
1758 : 8 : r = unit_name_replace_instance(i->name, i->default_instance, &buf);
1759 [ - + ]: 8 : if (r < 0)
1760 : 0 : return r;
1761 : :
1762 : 8 : instance.name = buf;
1763 : 8 : r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS);
1764 [ - + ]: 8 : if (r < 0)
1765 : 0 : return r;
1766 : :
1767 : 8 : path = TAKE_PTR(instance.path);
1768 : :
1769 [ - + ]: 8 : if (instance.type == UNIT_FILE_TYPE_MASKED) {
1770 : 0 : unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL);
1771 : 0 : return -ERFKILL;
1772 : : }
1773 : :
1774 : 8 : n = buf;
1775 : : } else
1776 : 104 : n = i->name;
1777 : :
1778 [ + - + + ]: 244 : STRV_FOREACH(s, list) {
1779 [ + - - + : 132 : _cleanup_free_ char *path = NULL, *dst = NULL;
- - ]
1780 : :
1781 : 132 : q = install_full_printf(i, *s, &dst);
1782 [ - + ]: 132 : if (q < 0)
1783 : 0 : return q;
1784 : :
1785 [ - + ]: 132 : if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
1786 : 0 : r = -EINVAL;
1787 : 0 : continue;
1788 : : }
1789 : :
1790 : 132 : path = strjoin(config_path, "/", dst, suffix, n);
1791 [ - + ]: 132 : if (!path)
1792 : 0 : return -ENOMEM;
1793 : :
1794 : 132 : q = create_symlink(paths, i->path, path, true, changes, n_changes);
1795 [ + + ]: 132 : if (r == 0)
1796 : 112 : r = q;
1797 : : }
1798 : :
1799 : 112 : return r;
1800 : : }
1801 : :
1802 : 120 : static int install_info_symlink_link(
1803 : : UnitFileInstallInfo *i,
1804 : : const LookupPaths *paths,
1805 : : const char *config_path,
1806 : : bool force,
1807 : : UnitFileChange **changes,
1808 : : size_t *n_changes) {
1809 : :
1810 : 120 : _cleanup_free_ char *path = NULL;
1811 : : int r;
1812 : :
1813 [ - + ]: 120 : assert(i);
1814 [ - + ]: 120 : assert(paths);
1815 [ - + ]: 120 : assert(config_path);
1816 [ - + ]: 120 : assert(i->path);
1817 : :
1818 : 120 : r = in_search_path(paths, i->path);
1819 [ - + ]: 120 : if (r < 0)
1820 : 0 : return r;
1821 [ + + ]: 120 : if (r > 0)
1822 : 108 : return 0;
1823 : :
1824 : 12 : path = path_join(config_path, i->name);
1825 [ - + ]: 12 : if (!path)
1826 : 0 : return -ENOMEM;
1827 : :
1828 : 12 : return create_symlink(paths, i->path, path, force, changes, n_changes);
1829 : : }
1830 : :
1831 : 120 : static int install_info_apply(
1832 : : UnitFileInstallInfo *i,
1833 : : const LookupPaths *paths,
1834 : : const char *config_path,
1835 : : bool force,
1836 : : UnitFileChange **changes,
1837 : : size_t *n_changes) {
1838 : :
1839 : : int r, q;
1840 : :
1841 [ - + ]: 120 : assert(i);
1842 [ - + ]: 120 : assert(paths);
1843 [ - + ]: 120 : assert(config_path);
1844 : :
1845 [ - + ]: 120 : if (i->type != UNIT_FILE_TYPE_REGULAR)
1846 : 0 : return 0;
1847 : :
1848 : 120 : r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
1849 : :
1850 : 120 : q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);
1851 [ + - ]: 120 : if (r == 0)
1852 : 120 : r = q;
1853 : :
1854 : 120 : q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);
1855 [ + + ]: 120 : if (r == 0)
1856 : 8 : r = q;
1857 : :
1858 : 120 : q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1859 : : /* Do not count links to the unit file towards the "carries_install_info" count */
1860 [ + + - + ]: 120 : if (r == 0 && q < 0)
1861 : 0 : r = q;
1862 : :
1863 : 120 : return r;
1864 : : }
1865 : :
1866 : 108 : static int install_context_apply(
1867 : : UnitFileScope scope,
1868 : : InstallContext *c,
1869 : : const LookupPaths *paths,
1870 : : const char *config_path,
1871 : : bool force,
1872 : : SearchFlags flags,
1873 : : UnitFileChange **changes,
1874 : : size_t *n_changes) {
1875 : :
1876 : : UnitFileInstallInfo *i;
1877 : : int r;
1878 : :
1879 [ - + ]: 108 : assert(c);
1880 [ - + ]: 108 : assert(paths);
1881 [ - + ]: 108 : assert(config_path);
1882 : :
1883 [ + + ]: 108 : if (ordered_hashmap_isempty(c->will_process))
1884 : 8 : return 0;
1885 : :
1886 : 100 : r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
1887 [ - + ]: 100 : if (r < 0)
1888 : 0 : return r;
1889 : :
1890 : 100 : r = 0;
1891 [ + + ]: 248 : while ((i = ordered_hashmap_first(c->will_process))) {
1892 : : int q;
1893 : :
1894 : 148 : q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1895 [ - + ]: 148 : if (q < 0)
1896 : 0 : return q;
1897 : :
1898 : 148 : q = install_info_traverse(scope, c, paths, i, flags, NULL);
1899 [ - + ]: 148 : if (q < 0) {
1900 : 0 : unit_file_changes_add(changes, n_changes, r, i->name, NULL);
1901 : 0 : return q;
1902 : : }
1903 : :
1904 : : /* We can attempt to process a masked unit when a different unit
1905 : : * that we were processing specifies it in Also=. */
1906 [ - + ]: 148 : if (i->type == UNIT_FILE_TYPE_MASKED) {
1907 : 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
1908 [ # # ]: 0 : if (r >= 0)
1909 : : /* Assume that something *could* have been enabled here,
1910 : : * avoid "empty [Install] section" warning. */
1911 : 0 : r += 1;
1912 : 0 : continue;
1913 : : }
1914 : :
1915 [ + + ]: 148 : if (i->type != UNIT_FILE_TYPE_REGULAR)
1916 : 28 : continue;
1917 : :
1918 : 120 : q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1919 [ + - ]: 120 : if (r >= 0) {
1920 [ - + ]: 120 : if (q < 0)
1921 : 0 : r = q;
1922 : : else
1923 : 120 : r += q;
1924 : : }
1925 : : }
1926 : :
1927 : 100 : return r;
1928 : : }
1929 : :
1930 : 68 : static int install_context_mark_for_removal(
1931 : : UnitFileScope scope,
1932 : : InstallContext *c,
1933 : : const LookupPaths *paths,
1934 : : Set **remove_symlinks_to,
1935 : : const char *config_path,
1936 : : UnitFileChange **changes,
1937 : : size_t *n_changes) {
1938 : :
1939 : : UnitFileInstallInfo *i;
1940 : : int r;
1941 : :
1942 [ - + ]: 68 : assert(c);
1943 [ - + ]: 68 : assert(paths);
1944 [ - + ]: 68 : assert(config_path);
1945 : :
1946 : : /* Marks all items for removal */
1947 : :
1948 [ + + ]: 68 : if (ordered_hashmap_isempty(c->will_process))
1949 : 12 : return 0;
1950 : :
1951 : 56 : r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
1952 [ - + ]: 56 : if (r < 0)
1953 : 0 : return r;
1954 : :
1955 [ + + ]: 204 : while ((i = ordered_hashmap_first(c->will_process))) {
1956 : :
1957 : 148 : r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1958 [ - + ]: 148 : if (r < 0)
1959 : 0 : return r;
1960 : :
1961 : 148 : r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
1962 [ - + ]: 148 : if (r == -ENOLINK) {
1963 [ # # ]: 0 : log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
1964 [ # # ]: 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL);
1965 [ - + ]: 148 : } else if (r == -ENOENT) {
1966 : :
1967 [ # # ]: 0 : if (i->auxiliary) /* some unit specified in Also= or similar is missing */
1968 [ # # ]: 0 : log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
1969 : : else {
1970 [ # # ]: 0 : log_debug_errno(r, "Unit %s not found, removing name.", i->name);
1971 [ # # ]: 0 : unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1972 : : }
1973 : :
1974 [ - + ]: 148 : } else if (r < 0) {
1975 [ # # ]: 0 : log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
1976 [ # # ]: 0 : unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1977 [ - + ]: 148 : } else if (i->type == UNIT_FILE_TYPE_MASKED) {
1978 [ # # ]: 0 : log_debug("Unit file %s is masked, ignoring.", i->name);
1979 [ # # ]: 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path ?: i->name, NULL);
1980 : 0 : continue;
1981 [ + + ]: 148 : } else if (i->type != UNIT_FILE_TYPE_REGULAR) {
1982 [ + - + - ]: 8 : log_debug("Unit %s has type %s, ignoring.", i->name, unit_file_type_to_string(i->type) ?: "invalid");
1983 : 8 : continue;
1984 : : }
1985 : :
1986 : 140 : r = mark_symlink_for_removal(remove_symlinks_to, i->name);
1987 [ - + ]: 140 : if (r < 0)
1988 : 0 : return r;
1989 : : }
1990 : :
1991 : 56 : return 0;
1992 : : }
1993 : :
1994 : 4 : int unit_file_mask(
1995 : : UnitFileScope scope,
1996 : : UnitFileFlags flags,
1997 : : const char *root_dir,
1998 : : char **files,
1999 : : UnitFileChange **changes,
2000 : : size_t *n_changes) {
2001 : :
2002 : 4 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2003 : : const char *config_path;
2004 : : char **i;
2005 : : int r;
2006 : :
2007 [ - + ]: 4 : assert(scope >= 0);
2008 [ - + ]: 4 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2009 : :
2010 : 4 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2011 [ - + ]: 4 : if (r < 0)
2012 : 0 : return r;
2013 : :
2014 [ - + ]: 4 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2015 [ - + ]: 4 : if (!config_path)
2016 : 0 : return -ENXIO;
2017 : :
2018 [ + - + + ]: 8 : STRV_FOREACH(i, files) {
2019 [ + - - ]: 4 : _cleanup_free_ char *path = NULL;
2020 : : int q;
2021 : :
2022 [ - + ]: 4 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
2023 [ # # ]: 0 : if (r == 0)
2024 : 0 : r = -EINVAL;
2025 : 0 : continue;
2026 : : }
2027 : :
2028 : 4 : path = path_make_absolute(*i, config_path);
2029 [ - + ]: 4 : if (!path)
2030 : 0 : return -ENOMEM;
2031 : :
2032 : 4 : q = create_symlink(&paths, "/dev/null", path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
2033 [ - + # # ]: 4 : if (q < 0 && r >= 0)
2034 : 0 : r = q;
2035 : : }
2036 : :
2037 : 4 : return r;
2038 : : }
2039 : :
2040 : 4 : int unit_file_unmask(
2041 : : UnitFileScope scope,
2042 : : UnitFileFlags flags,
2043 : : const char *root_dir,
2044 : : char **files,
2045 : : UnitFileChange **changes,
2046 : : size_t *n_changes) {
2047 : :
2048 : 4 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2049 : 4 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2050 : 4 : _cleanup_strv_free_ char **todo = NULL;
2051 : 4 : size_t n_todo = 0, n_allocated = 0;
2052 : : const char *config_path;
2053 : : char **i;
2054 : : bool dry_run;
2055 : : int r, q;
2056 : :
2057 [ - + ]: 4 : assert(scope >= 0);
2058 [ - + ]: 4 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2059 : :
2060 : 4 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2061 [ - + ]: 4 : if (r < 0)
2062 : 0 : return r;
2063 : :
2064 [ - + ]: 4 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2065 [ - + ]: 4 : if (!config_path)
2066 : 0 : return -ENXIO;
2067 : :
2068 : 4 : dry_run = !!(flags & UNIT_FILE_DRY_RUN);
2069 : :
2070 [ + - + + ]: 8 : STRV_FOREACH(i, files) {
2071 [ + - - ]: 4 : _cleanup_free_ char *path = NULL;
2072 : :
2073 [ - + ]: 4 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2074 : 0 : return -EINVAL;
2075 : :
2076 : 4 : path = path_make_absolute(*i, config_path);
2077 [ - + ]: 4 : if (!path)
2078 : 0 : return -ENOMEM;
2079 : :
2080 : 4 : r = null_or_empty_path(path);
2081 [ - + ]: 4 : if (r == -ENOENT)
2082 : 0 : continue;
2083 [ - + ]: 4 : if (r < 0)
2084 : 0 : return r;
2085 [ - + ]: 4 : if (r == 0)
2086 : 0 : continue;
2087 : :
2088 [ - + ]: 4 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2089 : 0 : return -ENOMEM;
2090 : :
2091 : 4 : todo[n_todo] = strdup(*i);
2092 [ - + ]: 4 : if (!todo[n_todo])
2093 : 0 : return -ENOMEM;
2094 : :
2095 : 4 : n_todo++;
2096 : : }
2097 : :
2098 : 4 : strv_uniq(todo);
2099 : :
2100 : 4 : r = 0;
2101 [ + - + + ]: 8 : STRV_FOREACH(i, todo) {
2102 [ + - - ]: 4 : _cleanup_free_ char *path = NULL;
2103 : : const char *rp;
2104 : :
2105 : 4 : path = path_make_absolute(*i, config_path);
2106 [ - + ]: 4 : if (!path)
2107 : 0 : return -ENOMEM;
2108 : :
2109 [ + - - + ]: 4 : if (!dry_run && unlink(path) < 0) {
2110 [ # # ]: 0 : if (errno != ENOENT) {
2111 [ # # ]: 0 : if (r >= 0)
2112 : 0 : r = -errno;
2113 : 0 : unit_file_changes_add(changes, n_changes, -errno, path, NULL);
2114 : : }
2115 : :
2116 : 0 : continue;
2117 : : }
2118 : :
2119 : 4 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
2120 : :
2121 : 4 : rp = skip_root(&paths, path);
2122 [ + - ]: 4 : q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
2123 [ - + ]: 4 : if (q < 0)
2124 : 0 : return q;
2125 : : }
2126 : :
2127 : 4 : q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
2128 [ + - ]: 4 : if (r >= 0)
2129 : 4 : r = q;
2130 : :
2131 : 4 : return r;
2132 : : }
2133 : :
2134 : 4 : int unit_file_link(
2135 : : UnitFileScope scope,
2136 : : UnitFileFlags flags,
2137 : : const char *root_dir,
2138 : : char **files,
2139 : : UnitFileChange **changes,
2140 : : size_t *n_changes) {
2141 : :
2142 : 4 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2143 : 4 : _cleanup_strv_free_ char **todo = NULL;
2144 : 4 : size_t n_todo = 0, n_allocated = 0;
2145 : : const char *config_path;
2146 : : char **i;
2147 : : int r, q;
2148 : :
2149 [ - + ]: 4 : assert(scope >= 0);
2150 [ - + ]: 4 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2151 : :
2152 : 4 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2153 [ - + ]: 4 : if (r < 0)
2154 : 0 : return r;
2155 : :
2156 [ - + ]: 4 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2157 [ - + ]: 4 : if (!config_path)
2158 : 0 : return -ENXIO;
2159 : :
2160 [ + - + + ]: 8 : STRV_FOREACH(i, files) {
2161 [ + - - ]: 4 : _cleanup_free_ char *full = NULL;
2162 : : struct stat st;
2163 : : char *fn;
2164 : :
2165 [ - + ]: 4 : if (!path_is_absolute(*i))
2166 : 0 : return -EINVAL;
2167 : :
2168 : 4 : fn = basename(*i);
2169 [ - + ]: 4 : if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
2170 : 0 : return -EINVAL;
2171 : :
2172 : 4 : full = path_join(paths.root_dir, *i);
2173 [ - + ]: 4 : if (!full)
2174 : 0 : return -ENOMEM;
2175 : :
2176 [ - + ]: 4 : if (lstat(full, &st) < 0)
2177 : 0 : return -errno;
2178 : 4 : r = stat_verify_regular(&st);
2179 [ - + ]: 4 : if (r < 0)
2180 : 0 : return r;
2181 : :
2182 : 4 : q = in_search_path(&paths, *i);
2183 [ - + ]: 4 : if (q < 0)
2184 : 0 : return q;
2185 [ - + ]: 4 : if (q > 0)
2186 : 0 : continue;
2187 : :
2188 [ - + ]: 4 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2189 : 0 : return -ENOMEM;
2190 : :
2191 : 4 : todo[n_todo] = strdup(*i);
2192 [ - + ]: 4 : if (!todo[n_todo])
2193 : 0 : return -ENOMEM;
2194 : :
2195 : 4 : n_todo++;
2196 : : }
2197 : :
2198 : 4 : strv_uniq(todo);
2199 : :
2200 : 4 : r = 0;
2201 [ + - + + ]: 8 : STRV_FOREACH(i, todo) {
2202 [ + - ]: 4 : _cleanup_free_ char *new_path = NULL;
2203 : :
2204 : 4 : new_path = path_make_absolute(basename(*i), config_path);
2205 [ - + ]: 4 : if (!new_path)
2206 : 0 : return -ENOMEM;
2207 : :
2208 : 4 : q = create_symlink(&paths, *i, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
2209 [ - + # # ]: 4 : if (q < 0 && r >= 0)
2210 : 0 : r = q;
2211 : : }
2212 : :
2213 : 4 : return r;
2214 : : }
2215 : :
2216 : 4 : static int path_shall_revert(const LookupPaths *paths, const char *path) {
2217 : : int r;
2218 : :
2219 [ - + ]: 4 : assert(paths);
2220 [ - + ]: 4 : assert(path);
2221 : :
2222 : : /* Checks whether the path is one where the drop-in directories shall be removed. */
2223 : :
2224 : 4 : r = path_is_config(paths, path, true);
2225 [ + - ]: 4 : if (r != 0)
2226 : 4 : return r;
2227 : :
2228 : 0 : r = path_is_control(paths, path);
2229 [ # # ]: 0 : if (r != 0)
2230 : 0 : return r;
2231 : :
2232 : 0 : return path_is_transient(paths, path);
2233 : : }
2234 : :
2235 : 12 : int unit_file_revert(
2236 : : UnitFileScope scope,
2237 : : const char *root_dir,
2238 : : char **files,
2239 : : UnitFileChange **changes,
2240 : : size_t *n_changes) {
2241 : :
2242 : 12 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2243 : 12 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2244 : 12 : _cleanup_strv_free_ char **todo = NULL;
2245 : 12 : size_t n_todo = 0, n_allocated = 0;
2246 : : char **i;
2247 : : int r, q;
2248 : :
2249 : : /* Puts a unit file back into vendor state. This means:
2250 : : *
2251 : : * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and
2252 : : * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated").
2253 : : *
2254 : : * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
2255 : : * "config", but not in "transient" or "control" or even "generated").
2256 : : *
2257 : : * We remove all that in both the runtime and the persistent directories, if that applies.
2258 : : */
2259 : :
2260 : 12 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2261 [ - + ]: 12 : if (r < 0)
2262 : 0 : return r;
2263 : :
2264 [ + - + + ]: 24 : STRV_FOREACH(i, files) {
2265 : 12 : bool has_vendor = false;
2266 : : char **p;
2267 : :
2268 [ - + ]: 12 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2269 : 0 : return -EINVAL;
2270 : :
2271 [ + - + + ]: 156 : STRV_FOREACH(p, paths.search_path) {
2272 [ + - + - ]: 144 : _cleanup_free_ char *path = NULL, *dropin = NULL;
2273 : : struct stat st;
2274 : :
2275 : 144 : path = path_make_absolute(*i, *p);
2276 [ - + ]: 144 : if (!path)
2277 : 0 : return -ENOMEM;
2278 : :
2279 : 144 : r = lstat(path, &st);
2280 [ + + ]: 144 : if (r < 0) {
2281 [ - + ]: 128 : if (errno != ENOENT)
2282 : 0 : return -errno;
2283 [ + - ]: 16 : } else if (S_ISREG(st.st_mode)) {
2284 : : /* Check if there's a vendor version */
2285 : 16 : r = path_is_vendor(&paths, path);
2286 [ - + ]: 16 : if (r < 0)
2287 : 0 : return r;
2288 [ + + ]: 16 : if (r > 0)
2289 : 12 : has_vendor = true;
2290 : : }
2291 : :
2292 : 144 : dropin = strjoin(path, ".d");
2293 [ - + ]: 144 : if (!dropin)
2294 : 0 : return -ENOMEM;
2295 : :
2296 : 144 : r = lstat(dropin, &st);
2297 [ + + ]: 144 : if (r < 0) {
2298 [ - + ]: 140 : if (errno != ENOENT)
2299 : 0 : return -errno;
2300 [ + - ]: 4 : } else if (S_ISDIR(st.st_mode)) {
2301 : : /* Remove the drop-ins */
2302 : 4 : r = path_shall_revert(&paths, dropin);
2303 [ - + ]: 4 : if (r < 0)
2304 : 0 : return r;
2305 [ + - ]: 4 : if (r > 0) {
2306 [ - + ]: 4 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2307 : 0 : return -ENOMEM;
2308 : :
2309 : 4 : todo[n_todo++] = TAKE_PTR(dropin);
2310 : : }
2311 : : }
2312 : : }
2313 : :
2314 [ - + ]: 12 : if (!has_vendor)
2315 : 0 : continue;
2316 : :
2317 : : /* OK, there's a vendor version, hence drop all configuration versions */
2318 [ + - + + ]: 156 : STRV_FOREACH(p, paths.search_path) {
2319 [ + - ]: 144 : _cleanup_free_ char *path = NULL;
2320 : : struct stat st;
2321 : :
2322 : 144 : path = path_make_absolute(*i, *p);
2323 [ - + ]: 144 : if (!path)
2324 : 0 : return -ENOMEM;
2325 : :
2326 : 144 : r = lstat(path, &st);
2327 [ + + ]: 144 : if (r < 0) {
2328 [ - + ]: 128 : if (errno != ENOENT)
2329 : 0 : return -errno;
2330 [ - + # # ]: 16 : } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
2331 : 16 : r = path_is_config(&paths, path, true);
2332 [ - + ]: 16 : if (r < 0)
2333 : 0 : return r;
2334 [ + + ]: 16 : if (r > 0) {
2335 [ - + ]: 4 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2336 : 0 : return -ENOMEM;
2337 : :
2338 : 4 : todo[n_todo++] = TAKE_PTR(path);
2339 : : }
2340 : : }
2341 : : }
2342 : : }
2343 : :
2344 : 12 : strv_uniq(todo);
2345 : :
2346 : 12 : r = 0;
2347 [ + + + + ]: 20 : STRV_FOREACH(i, todo) {
2348 [ + - - ]: 8 : _cleanup_strv_free_ char **fs = NULL;
2349 : : const char *rp;
2350 : : char **j;
2351 : :
2352 : 8 : (void) get_files_in_directory(*i, &fs);
2353 : :
2354 : 8 : q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
2355 [ - + # # : 8 : if (q < 0 && q != -ENOENT && r >= 0) {
# # ]
2356 : 0 : r = q;
2357 : 0 : continue;
2358 : : }
2359 : :
2360 [ + + + + ]: 12 : STRV_FOREACH(j, fs) {
2361 [ + - ]: 4 : _cleanup_free_ char *t = NULL;
2362 : :
2363 : 4 : t = path_join(*i, *j);
2364 [ - + ]: 4 : if (!t)
2365 : 0 : return -ENOMEM;
2366 : :
2367 : 4 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL);
2368 : : }
2369 : :
2370 : 8 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL);
2371 : :
2372 : 8 : rp = skip_root(&paths, *i);
2373 [ + - ]: 8 : q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
2374 [ - + ]: 8 : if (q < 0)
2375 : 0 : return q;
2376 : : }
2377 : :
2378 : 12 : q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, false, changes, n_changes);
2379 [ + - ]: 12 : if (r >= 0)
2380 : 12 : r = q;
2381 : :
2382 : 12 : q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, false, changes, n_changes);
2383 [ + - ]: 12 : if (r >= 0)
2384 : 12 : r = q;
2385 : :
2386 : 12 : return r;
2387 : : }
2388 : :
2389 : 4 : int unit_file_add_dependency(
2390 : : UnitFileScope scope,
2391 : : UnitFileFlags flags,
2392 : : const char *root_dir,
2393 : : char **files,
2394 : : const char *target,
2395 : : UnitDependency dep,
2396 : : UnitFileChange **changes,
2397 : : size_t *n_changes) {
2398 : :
2399 : 4 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2400 : 4 : _cleanup_(install_context_done) InstallContext c = {};
2401 : : UnitFileInstallInfo *i, *target_info;
2402 : : const char *config_path;
2403 : : char **f;
2404 : : int r;
2405 : :
2406 [ - + ]: 4 : assert(scope >= 0);
2407 [ - + ]: 4 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2408 [ - + ]: 4 : assert(target);
2409 : :
2410 [ + - - + ]: 4 : if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
2411 : 0 : return -EINVAL;
2412 : :
2413 [ - + ]: 4 : if (!unit_name_is_valid(target, UNIT_NAME_ANY))
2414 : 0 : return -EINVAL;
2415 : :
2416 : 4 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2417 [ - + ]: 4 : if (r < 0)
2418 : 0 : return r;
2419 : :
2420 [ - + ]: 4 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2421 [ - + ]: 4 : if (!config_path)
2422 : 0 : return -ENXIO;
2423 : :
2424 : 4 : r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2425 : : &target_info, changes, n_changes);
2426 [ - + ]: 4 : if (r < 0)
2427 : 0 : return r;
2428 : :
2429 [ - + ]: 4 : assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
2430 : :
2431 [ + - + + ]: 8 : STRV_FOREACH(f, files) {
2432 : : char ***l;
2433 : :
2434 : 4 : r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2435 : : &i, changes, n_changes);
2436 [ - + ]: 4 : if (r < 0)
2437 : 0 : return r;
2438 : :
2439 [ - + ]: 4 : assert(i->type == UNIT_FILE_TYPE_REGULAR);
2440 : :
2441 : : /* We didn't actually load anything from the unit
2442 : : * file, but instead just add in our new symlink to
2443 : : * create. */
2444 : :
2445 [ + - ]: 4 : if (dep == UNIT_WANTS)
2446 : 4 : l = &i->wanted_by;
2447 : : else
2448 : 0 : l = &i->required_by;
2449 : :
2450 : 4 : strv_free(*l);
2451 : 4 : *l = strv_new(target_info->name);
2452 [ - + ]: 4 : if (!*l)
2453 : 0 : return -ENOMEM;
2454 : : }
2455 : :
2456 : 4 : return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
2457 : : }
2458 : :
2459 : 80 : int unit_file_enable(
2460 : : UnitFileScope scope,
2461 : : UnitFileFlags flags,
2462 : : const char *root_dir,
2463 : : char **files,
2464 : : UnitFileChange **changes,
2465 : : size_t *n_changes) {
2466 : :
2467 : 80 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2468 : 80 : _cleanup_(install_context_done) InstallContext c = {};
2469 : : const char *config_path;
2470 : : UnitFileInstallInfo *i;
2471 : : char **f;
2472 : : int r;
2473 : :
2474 [ - + ]: 80 : assert(scope >= 0);
2475 [ - + ]: 80 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2476 : :
2477 : 80 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2478 [ - + ]: 80 : if (r < 0)
2479 : 0 : return r;
2480 : :
2481 [ - + ]: 80 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2482 [ - + ]: 80 : if (!config_path)
2483 : 0 : return -ENXIO;
2484 : :
2485 [ + - + + ]: 156 : STRV_FOREACH(f, files) {
2486 : 80 : r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2487 : : &i, changes, n_changes);
2488 [ + + ]: 80 : if (r < 0)
2489 : 4 : return r;
2490 : :
2491 [ - + ]: 76 : assert(i->type == UNIT_FILE_TYPE_REGULAR);
2492 : : }
2493 : :
2494 : : /* This will return the number of symlink rules that were
2495 : : supposed to be created, not the ones actually created. This
2496 : : is useful to determine whether the passed files had any
2497 : : installation data at all. */
2498 : :
2499 : 76 : return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_LOAD, changes, n_changes);
2500 : : }
2501 : :
2502 : 40 : int unit_file_disable(
2503 : : UnitFileScope scope,
2504 : : UnitFileFlags flags,
2505 : : const char *root_dir,
2506 : : char **files,
2507 : : UnitFileChange **changes,
2508 : : size_t *n_changes) {
2509 : :
2510 : 40 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2511 : 40 : _cleanup_(install_context_done) InstallContext c = {};
2512 : 40 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2513 : : const char *config_path;
2514 : : char **i;
2515 : : int r;
2516 : :
2517 [ - + ]: 40 : assert(scope >= 0);
2518 [ - + ]: 40 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2519 : :
2520 : 40 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2521 [ - + ]: 40 : if (r < 0)
2522 : 0 : return r;
2523 : :
2524 [ - + ]: 40 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2525 [ - + ]: 40 : if (!config_path)
2526 : 0 : return -ENXIO;
2527 : :
2528 [ + - + + ]: 80 : STRV_FOREACH(i, files) {
2529 [ - + ]: 40 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2530 : 0 : return -EINVAL;
2531 : :
2532 : 40 : r = install_info_add(&c, *i, NULL, false, NULL);
2533 [ - + ]: 40 : if (r < 0)
2534 : 0 : return r;
2535 : : }
2536 : :
2537 : 40 : r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
2538 [ - + ]: 40 : if (r < 0)
2539 : 0 : return r;
2540 : :
2541 : 40 : return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
2542 : : }
2543 : :
2544 : 4 : int unit_file_reenable(
2545 : : UnitFileScope scope,
2546 : : UnitFileFlags flags,
2547 : : const char *root_dir,
2548 : : char **files,
2549 : : UnitFileChange **changes,
2550 : : size_t *n_changes) {
2551 : :
2552 : : char **n;
2553 : : int r;
2554 : : size_t l, i;
2555 : :
2556 : : /* First, we invoke the disable command with only the basename... */
2557 : 4 : l = strv_length(files);
2558 [ - + - + ]: 4 : n = newa(char*, l+1);
2559 [ + + ]: 8 : for (i = 0; i < l; i++)
2560 : 4 : n[i] = basename(files[i]);
2561 : 4 : n[i] = NULL;
2562 : :
2563 : 4 : r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes);
2564 [ - + ]: 4 : if (r < 0)
2565 : 0 : return r;
2566 : :
2567 : : /* But the enable command with the full name */
2568 : 4 : return unit_file_enable(scope, flags, root_dir, files, changes, n_changes);
2569 : : }
2570 : :
2571 : 8 : int unit_file_set_default(
2572 : : UnitFileScope scope,
2573 : : UnitFileFlags flags,
2574 : : const char *root_dir,
2575 : : const char *name,
2576 : : UnitFileChange **changes,
2577 : : size_t *n_changes) {
2578 : :
2579 : 8 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2580 : 8 : _cleanup_(install_context_done) InstallContext c = {};
2581 : : UnitFileInstallInfo *i;
2582 : : const char *new_path;
2583 : : int r;
2584 : :
2585 [ - + ]: 8 : assert(scope >= 0);
2586 [ - + ]: 8 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2587 [ - + ]: 8 : assert(name);
2588 : :
2589 [ - + ]: 8 : if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
2590 : 0 : return -EINVAL;
2591 [ - + ]: 8 : if (streq(name, SPECIAL_DEFAULT_TARGET))
2592 : 0 : return -EINVAL;
2593 : :
2594 : 8 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2595 [ - + ]: 8 : if (r < 0)
2596 : 0 : return r;
2597 : :
2598 : 8 : r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes);
2599 [ + + ]: 8 : if (r < 0)
2600 : 4 : return r;
2601 : :
2602 [ + + + - : 20 : new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
- + - + +
+ + - ]
2603 : 4 : return create_symlink(&paths, i->path, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
2604 : : }
2605 : :
2606 : 12 : int unit_file_get_default(
2607 : : UnitFileScope scope,
2608 : : const char *root_dir,
2609 : : char **name) {
2610 : :
2611 : 12 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2612 : 12 : _cleanup_(install_context_done) InstallContext c = {};
2613 : : UnitFileInstallInfo *i;
2614 : : char *n;
2615 : : int r;
2616 : :
2617 [ - + ]: 12 : assert(scope >= 0);
2618 [ - + ]: 12 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2619 [ - + ]: 12 : assert(name);
2620 : :
2621 : 12 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2622 [ - + ]: 12 : if (r < 0)
2623 : 0 : return r;
2624 : :
2625 : 12 : r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2626 : : &i, NULL, NULL);
2627 [ + + ]: 12 : if (r < 0)
2628 : 8 : return r;
2629 : 4 : r = install_info_may_process(i, &paths, NULL, 0);
2630 [ - + ]: 4 : if (r < 0)
2631 : 0 : return r;
2632 : :
2633 : 4 : n = strdup(i->name);
2634 [ - + ]: 4 : if (!n)
2635 : 0 : return -ENOMEM;
2636 : :
2637 : 4 : *name = n;
2638 : 4 : return 0;
2639 : : }
2640 : :
2641 : 2828 : int unit_file_lookup_state(
2642 : : UnitFileScope scope,
2643 : : const LookupPaths *paths,
2644 : : const char *name,
2645 : : UnitFileState *ret) {
2646 : :
2647 : 2828 : _cleanup_(install_context_done) InstallContext c = {};
2648 : : UnitFileInstallInfo *i;
2649 : : UnitFileState state;
2650 : : int r;
2651 : :
2652 [ - + ]: 2828 : assert(paths);
2653 [ - + ]: 2828 : assert(name);
2654 : :
2655 [ - + ]: 2828 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2656 : 0 : return -EINVAL;
2657 : :
2658 : 2828 : r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2659 : : &i, NULL, NULL);
2660 [ + + ]: 2828 : if (r < 0)
2661 [ + - ]: 132 : return log_debug_errno(r, "Failed to discover unit %s: %m", name);
2662 : :
2663 [ + - - + ]: 2696 : assert(IN_SET(i->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED));
2664 [ + + + + ]: 2696 : log_debug("Found unit %s at %s (%s)", name, strna(i->path),
2665 : : i->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask");
2666 : :
2667 : : /* Shortcut things, if the caller just wants to know if this unit exists. */
2668 [ + + ]: 2696 : if (!ret)
2669 : 20 : return 0;
2670 : :
2671 [ + + - ]: 2676 : switch (i->type) {
2672 : :
2673 : 20 : case UNIT_FILE_TYPE_MASKED:
2674 : 20 : r = path_is_runtime(paths, i->path, true);
2675 [ - + ]: 20 : if (r < 0)
2676 : 0 : return r;
2677 : :
2678 [ - + ]: 20 : state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
2679 : 20 : break;
2680 : :
2681 : 2656 : case UNIT_FILE_TYPE_REGULAR:
2682 : 2656 : r = path_is_generator(paths, i->path);
2683 [ - + ]: 2656 : if (r < 0)
2684 : 0 : return r;
2685 [ + + ]: 2656 : if (r > 0) {
2686 : 40 : state = UNIT_FILE_GENERATED;
2687 : 40 : break;
2688 : : }
2689 : :
2690 : 2616 : r = path_is_transient(paths, i->path);
2691 [ - + ]: 2616 : if (r < 0)
2692 : 0 : return r;
2693 [ + + ]: 2616 : if (r > 0) {
2694 : 4 : state = UNIT_FILE_TRANSIENT;
2695 : 4 : break;
2696 : : }
2697 : :
2698 : : /* Check if any of the Alias= symlinks have been created.
2699 : : * We ignore other aliases, and only check those that would
2700 : : * be created by systemctl enable for this unit. */
2701 : 2612 : r = find_symlinks_in_scope(scope, paths, i, true, &state);
2702 [ - + ]: 2612 : if (r < 0)
2703 : 0 : return r;
2704 [ + + ]: 2612 : if (r > 0)
2705 : 512 : break;
2706 : :
2707 : : /* Check if the file is known under other names. If it is,
2708 : : * it might be in use. Report that as UNIT_FILE_INDIRECT. */
2709 : 2100 : r = find_symlinks_in_scope(scope, paths, i, false, &state);
2710 [ - + ]: 2100 : if (r < 0)
2711 : 0 : return r;
2712 [ + + ]: 2100 : if (r > 0)
2713 : 28 : state = UNIT_FILE_INDIRECT;
2714 : : else {
2715 [ + + ]: 2072 : if (unit_file_install_info_has_rules(i))
2716 : 968 : state = UNIT_FILE_DISABLED;
2717 [ + + ]: 1104 : else if (unit_file_install_info_has_also(i))
2718 : 68 : state = UNIT_FILE_INDIRECT;
2719 : : else
2720 : 1036 : state = UNIT_FILE_STATIC;
2721 : : }
2722 : :
2723 : 2100 : break;
2724 : :
2725 : 0 : default:
2726 : 0 : assert_not_reached("Unexpected unit file type.");
2727 : : }
2728 : :
2729 : 2676 : *ret = state;
2730 : 2676 : return 0;
2731 : : }
2732 : :
2733 : 728 : int unit_file_get_state(
2734 : : UnitFileScope scope,
2735 : : const char *root_dir,
2736 : : const char *name,
2737 : : UnitFileState *ret) {
2738 : :
2739 : 728 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2740 : : int r;
2741 : :
2742 [ - + ]: 728 : assert(scope >= 0);
2743 [ - + ]: 728 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2744 [ - + ]: 728 : assert(name);
2745 : :
2746 : 728 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2747 [ - + ]: 728 : if (r < 0)
2748 : 0 : return r;
2749 : :
2750 : 728 : return unit_file_lookup_state(scope, &paths, name, ret);
2751 : : }
2752 : :
2753 : 100 : int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) {
2754 : 100 : _cleanup_(install_context_done) InstallContext c = {};
2755 : : int r;
2756 : :
2757 [ - + ]: 100 : assert(paths);
2758 [ - + ]: 100 : assert(name);
2759 : :
2760 [ - + ]: 100 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2761 : 0 : return -EINVAL;
2762 : :
2763 : 100 : r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL);
2764 [ + + ]: 100 : if (r == -ENOENT)
2765 : 96 : return 0;
2766 [ - + ]: 4 : if (r < 0)
2767 : 0 : return r;
2768 : :
2769 : 4 : return 1;
2770 : : }
2771 : :
2772 : 44 : static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
2773 : 44 : _cleanup_strv_free_ char **instances = NULL;
2774 : 44 : _cleanup_free_ char *unit_name = NULL;
2775 : : int r;
2776 : :
2777 [ - + ]: 44 : assert(pattern);
2778 [ - + ]: 44 : assert(out_instances);
2779 [ - + ]: 44 : assert(out_unit_name);
2780 : :
2781 : 44 : r = extract_first_word(&pattern, &unit_name, NULL, EXTRACT_RETAIN_ESCAPE);
2782 [ - + ]: 44 : if (r < 0)
2783 : 0 : return r;
2784 : :
2785 : : /* We handle the instances logic when unit name is extracted */
2786 [ + + ]: 44 : if (pattern) {
2787 : : /* We only create instances when a rule of templated unit
2788 : : * is seen. A rule like enable foo@.service a b c will
2789 : : * result in an array of (a, b, c) as instance names */
2790 [ - + ]: 8 : if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
2791 : 0 : return -EINVAL;
2792 : :
2793 : 8 : instances = strv_split(pattern, WHITESPACE);
2794 [ - + ]: 8 : if (!instances)
2795 : 0 : return -ENOMEM;
2796 : :
2797 : 8 : *out_instances = TAKE_PTR(instances);
2798 : : }
2799 : :
2800 : 44 : *out_unit_name = TAKE_PTR(unit_name);
2801 : :
2802 : 44 : return 0;
2803 : : }
2804 : :
2805 : 28 : static int presets_find_config(UnitFileScope scope, const char *root_dir, char ***files) {
2806 : : static const char* const system_dirs[] = {CONF_PATHS("systemd/system-preset"), NULL};
2807 : : static const char* const user_dirs[] = {CONF_PATHS_USR("systemd/user-preset"), NULL};
2808 : : const char* const* dirs;
2809 : :
2810 [ - + ]: 28 : assert(scope >= 0);
2811 [ - + ]: 28 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2812 : :
2813 [ + - ]: 28 : if (scope == UNIT_FILE_SYSTEM)
2814 : 28 : dirs = system_dirs;
2815 [ # # # # ]: 0 : else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER))
2816 : 0 : dirs = user_dirs;
2817 : : else
2818 : 0 : assert_not_reached("Invalid unit file scope");
2819 : :
2820 : 28 : return conf_files_list_strv(files, ".preset", root_dir, 0, dirs);
2821 : : }
2822 : :
2823 : 28 : static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
2824 : 28 : _cleanup_(presets_freep) Presets ps = {};
2825 : 28 : size_t n_allocated = 0;
2826 : 28 : _cleanup_strv_free_ char **files = NULL;
2827 : : char **p;
2828 : : int r;
2829 : :
2830 [ - + ]: 28 : assert(scope >= 0);
2831 [ - + ]: 28 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2832 [ - + ]: 28 : assert(presets);
2833 : :
2834 : 28 : r = presets_find_config(scope, root_dir, &files);
2835 [ - + ]: 28 : if (r < 0)
2836 : 0 : return r;
2837 : :
2838 [ + - + + ]: 56 : STRV_FOREACH(p, files) {
2839 [ + - - ]: 28 : _cleanup_fclose_ FILE *f;
2840 : 28 : int n = 0;
2841 : :
2842 : 28 : f = fopen(*p, "re");
2843 [ - + ]: 28 : if (!f) {
2844 [ # # ]: 0 : if (errno == ENOENT)
2845 : 0 : continue;
2846 : :
2847 : 0 : return -errno;
2848 : : }
2849 : :
2850 : 72 : for (;;) {
2851 [ - - + + ]: 100 : _cleanup_free_ char *line = NULL;
2852 : 100 : PresetRule rule = {};
2853 : : const char *parameter;
2854 : : char *l;
2855 : :
2856 : 100 : r = read_line(f, LONG_LINE_MAX, &line);
2857 [ - + ]: 100 : if (r < 0)
2858 : 0 : return r;
2859 [ + + ]: 100 : if (r == 0)
2860 : 28 : break;
2861 : :
2862 : 72 : l = strstrip(line);
2863 : 72 : n++;
2864 : :
2865 [ - + ]: 72 : if (isempty(l))
2866 : 0 : continue;
2867 [ - + ]: 72 : if (strchr(COMMENTS, *l))
2868 : 0 : continue;
2869 : :
2870 : 72 : parameter = first_word(l, "enable");
2871 [ + + ]: 72 : if (parameter) {
2872 : : char *unit_name;
2873 : 44 : char **instances = NULL;
2874 : :
2875 : : /* Unit_name will remain the same as parameter when no instances are specified */
2876 : 44 : r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
2877 [ - + ]: 44 : if (r < 0) {
2878 [ # # ]: 0 : log_syntax(NULL, LOG_WARNING, *p, n, r, "Couldn't parse line '%s'. Ignoring.", line);
2879 : 0 : continue;
2880 : : }
2881 : :
2882 : 44 : rule = (PresetRule) {
2883 : : .pattern = unit_name,
2884 : : .action = PRESET_ENABLE,
2885 : : .instances = instances,
2886 : : };
2887 : : }
2888 : :
2889 : 72 : parameter = first_word(l, "disable");
2890 [ + + ]: 72 : if (parameter) {
2891 : : char *pattern;
2892 : :
2893 : 28 : pattern = strdup(parameter);
2894 [ - + ]: 28 : if (!pattern)
2895 : 0 : return -ENOMEM;
2896 : :
2897 : 28 : rule = (PresetRule) {
2898 : : .pattern = pattern,
2899 : : .action = PRESET_DISABLE,
2900 : : };
2901 : : }
2902 : :
2903 [ + - ]: 72 : if (rule.action) {
2904 [ - + ]: 72 : if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1))
2905 : 0 : return -ENOMEM;
2906 : :
2907 : 72 : ps.rules[ps.n_rules++] = rule;
2908 : 72 : continue;
2909 : : }
2910 : :
2911 [ # # ]: 0 : log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
2912 : : }
2913 : : }
2914 : :
2915 : 28 : *presets = ps;
2916 : 28 : ps = (Presets){};
2917 : :
2918 : 28 : return 0;
2919 : : }
2920 : :
2921 : 260 : static int pattern_match_multiple_instances(
2922 : : const PresetRule rule,
2923 : : const char *unit_name,
2924 : : char ***ret) {
2925 : :
2926 : 260 : _cleanup_free_ char *templated_name = NULL;
2927 : : int r;
2928 : :
2929 : : /* If no ret is needed or the rule itself does not have instances
2930 : : * initialized, we return not matching */
2931 [ + - + + ]: 260 : if (!ret || !rule.instances)
2932 : 204 : return 0;
2933 : :
2934 : 56 : r = unit_name_template(unit_name, &templated_name);
2935 [ + + ]: 56 : if (r < 0)
2936 : 44 : return r;
2937 [ + + ]: 12 : if (!streq(rule.pattern, templated_name))
2938 : 4 : return 0;
2939 : :
2940 : : /* Compose a list of specified instances when unit name is a template */
2941 [ + + ]: 8 : if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
2942 : 4 : _cleanup_free_ char *prefix = NULL;
2943 : 4 : _cleanup_strv_free_ char **out_strv = NULL;
2944 : : char **iter;
2945 : :
2946 : 4 : r = unit_name_to_prefix(unit_name, &prefix);
2947 [ - + ]: 4 : if (r < 0)
2948 : 0 : return r;
2949 : :
2950 [ + - + + ]: 16 : STRV_FOREACH(iter, rule.instances) {
2951 [ + - ]: 12 : _cleanup_free_ char *name = NULL;
2952 : 12 : r = unit_name_build(prefix, *iter, ".service", &name);
2953 [ - + ]: 12 : if (r < 0)
2954 : 0 : return r;
2955 : 12 : r = strv_extend(&out_strv, name);
2956 [ - + ]: 12 : if (r < 0)
2957 : 0 : return r;
2958 : : }
2959 : :
2960 : 4 : *ret = TAKE_PTR(out_strv);
2961 : 4 : return 1;
2962 : : } else {
2963 : : /* We now know the input unit name is an instance name */
2964 [ - + ]: 4 : _cleanup_free_ char *instance_name = NULL;
2965 : :
2966 : 4 : r = unit_name_to_instance(unit_name, &instance_name);
2967 [ - + ]: 4 : if (r < 0)
2968 : 0 : return r;
2969 : :
2970 [ + - ]: 4 : if (strv_find(rule.instances, instance_name))
2971 : 4 : return 1;
2972 : : }
2973 : 0 : return 0;
2974 : : }
2975 : :
2976 : 116 : static int query_presets(const char *name, const Presets presets, char ***instance_name_list) {
2977 : 116 : PresetAction action = PRESET_UNKNOWN;
2978 : : size_t i;
2979 : : char **s;
2980 [ - + ]: 116 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2981 : 0 : return -EINVAL;
2982 : :
2983 [ + - ]: 260 : for (i = 0; i < presets.n_rules; i++)
2984 [ + + + + ]: 512 : if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 ||
2985 : 252 : fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
2986 : 116 : action = presets.rules[i].action;
2987 : 116 : break;
2988 : : }
2989 : :
2990 [ - + + - ]: 116 : switch (action) {
2991 : 0 : case PRESET_UNKNOWN:
2992 [ # # ]: 0 : log_debug("Preset files don't specify rule for %s. Enabling.", name);
2993 : 0 : return 1;
2994 : 20 : case PRESET_ENABLE:
2995 [ + - + + ]: 24 : if (instance_name_list && *instance_name_list)
2996 [ + - + + ]: 16 : STRV_FOREACH(s, *instance_name_list)
2997 [ + - ]: 12 : log_debug("Preset files say enable %s.", *s);
2998 : : else
2999 [ + - ]: 16 : log_debug("Preset files say enable %s.", name);
3000 : 20 : return 1;
3001 : 96 : case PRESET_DISABLE:
3002 [ + - ]: 96 : log_debug("Preset files say disable %s.", name);
3003 : 96 : return 0;
3004 : 0 : default:
3005 : 0 : assert_not_reached("invalid preset action");
3006 : : }
3007 : : }
3008 : :
3009 : 0 : int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
3010 : 0 : _cleanup_(presets_freep) Presets presets = {};
3011 : : int r;
3012 : :
3013 : 0 : r = read_presets(scope, root_dir, &presets);
3014 [ # # ]: 0 : if (r < 0)
3015 : 0 : return r;
3016 : :
3017 : 0 : return query_presets(name, presets, NULL);
3018 : : }
3019 : :
3020 : 28 : static int execute_preset(
3021 : : UnitFileScope scope,
3022 : : InstallContext *plus,
3023 : : InstallContext *minus,
3024 : : const LookupPaths *paths,
3025 : : const char *config_path,
3026 : : char **files,
3027 : : UnitFilePresetMode mode,
3028 : : bool force,
3029 : : UnitFileChange **changes,
3030 : : size_t *n_changes) {
3031 : :
3032 : : int r;
3033 : :
3034 [ - + ]: 28 : assert(plus);
3035 [ - + ]: 28 : assert(minus);
3036 [ - + ]: 28 : assert(paths);
3037 [ - + ]: 28 : assert(config_path);
3038 : :
3039 [ + - ]: 28 : if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
3040 [ + - ]: 28 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
3041 : :
3042 : 28 : r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
3043 [ - + ]: 28 : if (r < 0)
3044 : 0 : return r;
3045 : :
3046 : 28 : r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
3047 : : } else
3048 : 0 : r = 0;
3049 : :
3050 [ + - ]: 28 : if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
3051 : : int q;
3052 : :
3053 : : /* Returns number of symlinks that where supposed to be installed. */
3054 : 28 : q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
3055 [ + - ]: 28 : if (r >= 0) {
3056 [ - + ]: 28 : if (q < 0)
3057 : 0 : r = q;
3058 : : else
3059 : 28 : r += q;
3060 : : }
3061 : : }
3062 : :
3063 : 28 : return r;
3064 : : }
3065 : :
3066 : 188 : static int preset_prepare_one(
3067 : : UnitFileScope scope,
3068 : : InstallContext *plus,
3069 : : InstallContext *minus,
3070 : : LookupPaths *paths,
3071 : : const char *name,
3072 : : Presets presets,
3073 : : UnitFileChange **changes,
3074 : : size_t *n_changes) {
3075 : :
3076 : 188 : _cleanup_(install_context_done) InstallContext tmp = {};
3077 : 188 : _cleanup_strv_free_ char **instance_name_list = NULL;
3078 : : UnitFileInstallInfo *i;
3079 : : int r;
3080 : :
3081 [ + - + + ]: 188 : if (install_info_find(plus, name) || install_info_find(minus, name))
3082 : 4 : return 0;
3083 : :
3084 : 184 : r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
3085 : : &i, changes, n_changes);
3086 [ - + ]: 184 : if (r < 0)
3087 : 0 : return r;
3088 [ + + ]: 184 : if (!streq(name, i->name)) {
3089 [ + - ]: 68 : log_debug("Skipping %s because it is an alias for %s.", name, i->name);
3090 : 68 : return 0;
3091 : : }
3092 : :
3093 : 116 : r = query_presets(name, presets, &instance_name_list);
3094 [ - + ]: 116 : if (r < 0)
3095 : 0 : return r;
3096 : :
3097 [ + + ]: 116 : if (r > 0) {
3098 [ + + ]: 20 : if (instance_name_list) {
3099 : : char **s;
3100 [ + - + + ]: 16 : STRV_FOREACH(s, instance_name_list) {
3101 : 12 : r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3102 : : &i, changes, n_changes);
3103 [ - + ]: 12 : if (r < 0)
3104 : 0 : return r;
3105 : : }
3106 : : } else {
3107 : 16 : r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3108 : : &i, changes, n_changes);
3109 [ - + ]: 16 : if (r < 0)
3110 : 0 : return r;
3111 : : }
3112 : :
3113 : : } else
3114 : 96 : r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
3115 : : &i, changes, n_changes);
3116 : :
3117 : 116 : return r;
3118 : : }
3119 : :
3120 : 20 : int unit_file_preset(
3121 : : UnitFileScope scope,
3122 : : UnitFileFlags flags,
3123 : : const char *root_dir,
3124 : : char **files,
3125 : : UnitFilePresetMode mode,
3126 : : UnitFileChange **changes,
3127 : : size_t *n_changes) {
3128 : :
3129 : 20 : _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
3130 : 20 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
3131 : 20 : _cleanup_(presets_freep) Presets presets = {};
3132 : : const char *config_path;
3133 : : char **i;
3134 : : int r;
3135 : :
3136 [ - + ]: 20 : assert(scope >= 0);
3137 [ - + ]: 20 : assert(scope < _UNIT_FILE_SCOPE_MAX);
3138 [ - + ]: 20 : assert(mode < _UNIT_FILE_PRESET_MAX);
3139 : :
3140 : 20 : r = lookup_paths_init(&paths, scope, 0, root_dir);
3141 [ - + ]: 20 : if (r < 0)
3142 : 0 : return r;
3143 : :
3144 [ - + ]: 20 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
3145 [ - + ]: 20 : if (!config_path)
3146 : 0 : return -ENXIO;
3147 : :
3148 : 20 : r = read_presets(scope, root_dir, &presets);
3149 [ - + ]: 20 : if (r < 0)
3150 : 0 : return r;
3151 : :
3152 [ + - + + ]: 40 : STRV_FOREACH(i, files) {
3153 : 20 : r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
3154 [ - + ]: 20 : if (r < 0)
3155 : 0 : return r;
3156 : : }
3157 : :
3158 : 20 : return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
3159 : : }
3160 : :
3161 : 8 : int unit_file_preset_all(
3162 : : UnitFileScope scope,
3163 : : UnitFileFlags flags,
3164 : : const char *root_dir,
3165 : : UnitFilePresetMode mode,
3166 : : UnitFileChange **changes,
3167 : : size_t *n_changes) {
3168 : :
3169 : 8 : _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
3170 : 8 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
3171 : 8 : _cleanup_(presets_freep) Presets presets = {};
3172 : 8 : const char *config_path = NULL;
3173 : : char **i;
3174 : : int r;
3175 : :
3176 [ - + ]: 8 : assert(scope >= 0);
3177 [ - + ]: 8 : assert(scope < _UNIT_FILE_SCOPE_MAX);
3178 [ - + ]: 8 : assert(mode < _UNIT_FILE_PRESET_MAX);
3179 : :
3180 : 8 : r = lookup_paths_init(&paths, scope, 0, root_dir);
3181 [ - + ]: 8 : if (r < 0)
3182 : 0 : return r;
3183 : :
3184 [ - + ]: 8 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
3185 [ - + ]: 8 : if (!config_path)
3186 : 0 : return -ENXIO;
3187 : :
3188 : 8 : r = read_presets(scope, root_dir, &presets);
3189 [ - + ]: 8 : if (r < 0)
3190 : 0 : return r;
3191 : :
3192 [ + - + + ]: 104 : STRV_FOREACH(i, paths.search_path) {
3193 [ + + - ]: 96 : _cleanup_closedir_ DIR *d = NULL;
3194 : : struct dirent *de;
3195 : :
3196 : 96 : d = opendir(*i);
3197 [ + + ]: 96 : if (!d) {
3198 [ + - ]: 72 : if (errno == ENOENT)
3199 : 72 : continue;
3200 : :
3201 : 0 : return -errno;
3202 : : }
3203 : :
3204 [ + + - + : 252 : FOREACH_DIRENT(de, d, return -errno) {
+ + ]
3205 : :
3206 [ + + ]: 180 : if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
3207 : 12 : continue;
3208 : :
3209 : 168 : dirent_ensure_type(d, de);
3210 : :
3211 [ + - - + ]: 168 : if (!IN_SET(de->d_type, DT_LNK, DT_REG))
3212 : 0 : continue;
3213 : :
3214 : : /* we don't pass changes[] in, because we want to handle errors on our own */
3215 : 168 : r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
3216 [ - + ]: 168 : if (r == -ERFKILL)
3217 : 0 : r = unit_file_changes_add(changes, n_changes,
3218 : 0 : UNIT_FILE_IS_MASKED, de->d_name, NULL);
3219 [ - + ]: 168 : else if (r == -ENOLINK)
3220 : 0 : r = unit_file_changes_add(changes, n_changes,
3221 : 0 : UNIT_FILE_IS_DANGLING, de->d_name, NULL);
3222 [ - + ]: 168 : else if (r == -EADDRNOTAVAIL) /* Ignore generated/transient units when applying preset */
3223 : 0 : continue;
3224 [ - + ]: 168 : if (r < 0)
3225 : 0 : return r;
3226 : : }
3227 : : }
3228 : :
3229 : 8 : return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
3230 : : }
3231 : :
3232 : 2100 : static void unit_file_list_free_one(UnitFileList *f) {
3233 [ - + ]: 2100 : if (!f)
3234 : 0 : return;
3235 : :
3236 : 2100 : free(f->path);
3237 : 2100 : free(f);
3238 : : }
3239 : :
3240 : 8 : Hashmap* unit_file_list_free(Hashmap *h) {
3241 [ + + ]: 2108 : return hashmap_free_with_destructor(h, unit_file_list_free_one);
3242 : : }
3243 : :
3244 [ - + ]: 2376 : DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
3245 : :
3246 : 8 : int unit_file_get_list(
3247 : : UnitFileScope scope,
3248 : : const char *root_dir,
3249 : : Hashmap *h,
3250 : : char **states,
3251 : : char **patterns) {
3252 : :
3253 : 8 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
3254 : : char **i;
3255 : : int r;
3256 : :
3257 [ - + ]: 8 : assert(scope >= 0);
3258 [ - + ]: 8 : assert(scope < _UNIT_FILE_SCOPE_MAX);
3259 [ - + ]: 8 : assert(h);
3260 : :
3261 : 8 : r = lookup_paths_init(&paths, scope, 0, root_dir);
3262 [ - + ]: 8 : if (r < 0)
3263 : 0 : return r;
3264 : :
3265 [ + - + + ]: 104 : STRV_FOREACH(i, paths.search_path) {
3266 [ + + - ]: 96 : _cleanup_closedir_ DIR *d = NULL;
3267 : : struct dirent *de;
3268 : :
3269 : 96 : d = opendir(*i);
3270 [ + + ]: 96 : if (!d) {
3271 [ + - ]: 60 : if (errno == ENOENT)
3272 : 60 : continue;
3273 [ # # # # ]: 0 : if (IN_SET(errno, ENOTDIR, EACCES)) {
3274 [ # # ]: 0 : log_debug_errno(errno, "Failed to open \"%s\": %m", *i);
3275 : 0 : continue;
3276 : : }
3277 : :
3278 : 0 : return -errno;
3279 : : }
3280 : :
3281 [ + + - + : 2484 : FOREACH_DIRENT(de, d, return -errno) {
+ + ]
3282 [ + + - ]: 2376 : _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
3283 : :
3284 [ + + ]: 2376 : if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
3285 : 256 : continue;
3286 : :
3287 [ - + ]: 2120 : if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
3288 : 0 : continue;
3289 : :
3290 [ + + ]: 2120 : if (hashmap_get(h, de->d_name))
3291 : 20 : continue;
3292 : :
3293 : 2100 : dirent_ensure_type(d, de);
3294 : :
3295 [ + - - + ]: 2100 : if (!IN_SET(de->d_type, DT_LNK, DT_REG))
3296 : 0 : continue;
3297 : :
3298 : 2100 : f = new0(UnitFileList, 1);
3299 [ - + ]: 2100 : if (!f)
3300 : 0 : return -ENOMEM;
3301 : :
3302 : 2100 : f->path = path_make_absolute(de->d_name, *i);
3303 [ - + ]: 2100 : if (!f->path)
3304 : 0 : return -ENOMEM;
3305 : :
3306 : 2100 : r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state);
3307 [ - + ]: 2100 : if (r < 0)
3308 : 0 : f->state = UNIT_FILE_BAD;
3309 : :
3310 [ - + ]: 2100 : if (!strv_isempty(states) &&
3311 [ # # ]: 0 : !strv_contains(states, unit_file_state_to_string(f->state)))
3312 : 0 : continue;
3313 : :
3314 : 2100 : r = hashmap_put(h, basename(f->path), f);
3315 [ - + ]: 2100 : if (r < 0)
3316 : 0 : return r;
3317 : :
3318 : 2100 : f = NULL; /* prevent cleanup */
3319 : : }
3320 : : }
3321 : :
3322 : 8 : return 0;
3323 : : }
3324 : :
3325 : : static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
3326 : : [UNIT_FILE_ENABLED] = "enabled",
3327 : : [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
3328 : : [UNIT_FILE_LINKED] = "linked",
3329 : : [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
3330 : : [UNIT_FILE_MASKED] = "masked",
3331 : : [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
3332 : : [UNIT_FILE_STATIC] = "static",
3333 : : [UNIT_FILE_DISABLED] = "disabled",
3334 : : [UNIT_FILE_INDIRECT] = "indirect",
3335 : : [UNIT_FILE_GENERATED] = "generated",
3336 : : [UNIT_FILE_TRANSIENT] = "transient",
3337 : : [UNIT_FILE_BAD] = "bad",
3338 : : };
3339 : :
3340 [ + + + + ]: 2140 : DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
3341 : :
3342 : : static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
3343 : : [UNIT_FILE_SYMLINK] = "symlink",
3344 : : [UNIT_FILE_UNLINK] = "unlink",
3345 : : [UNIT_FILE_IS_MASKED] = "masked",
3346 : : [UNIT_FILE_IS_DANGLING] = "dangling",
3347 : : };
3348 : :
3349 [ + + + + ]: 48 : DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
3350 : :
3351 : : static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
3352 : : [UNIT_FILE_PRESET_FULL] = "full",
3353 : : [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
3354 : : [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
3355 : : };
3356 : :
3357 [ + + + + ]: 40 : DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);
|