]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/shared/install.c
core: rework reboot parameter logic a bit
[thirdparty/systemd.git] / src / shared / install.c
... / ...
CommitLineData
1/***
2 This file is part of systemd.
3
4 Copyright 2011 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty <of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
20#include <dirent.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <fnmatch.h>
24#include <limits.h>
25#include <stddef.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/stat.h>
30#include <unistd.h>
31
32#include "alloc-util.h"
33#include "conf-files.h"
34#include "conf-parser.h"
35#include "dirent-util.h"
36#include "extract-word.h"
37#include "fd-util.h"
38#include "fileio.h"
39#include "fs-util.h"
40#include "hashmap.h"
41#include "install-printf.h"
42#include "install.h"
43#include "log.h"
44#include "macro.h"
45#include "mkdir.h"
46#include "path-lookup.h"
47#include "path-util.h"
48#include "set.h"
49#include "special.h"
50#include "stat-util.h"
51#include "string-table.h"
52#include "string-util.h"
53#include "strv.h"
54#include "unit-name.h"
55
56#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
57
58typedef enum SearchFlags {
59 SEARCH_LOAD = 1,
60 SEARCH_FOLLOW_CONFIG_SYMLINKS = 2,
61} SearchFlags;
62
63typedef struct {
64 OrderedHashmap *will_process;
65 OrderedHashmap *have_processed;
66} InstallContext;
67
68static int in_search_path(const LookupPaths *p, const char *path) {
69 _cleanup_free_ char *parent = NULL;
70 char **i;
71
72 assert(path);
73
74 parent = dirname_malloc(path);
75 if (!parent)
76 return -ENOMEM;
77
78 STRV_FOREACH(i, p->search_path)
79 if (path_equal(parent, *i))
80 return true;
81
82 return false;
83}
84
85static const char* skip_root(const LookupPaths *p, const char *path) {
86 if (p->root_dir) {
87 char *e;
88
89 e = path_startswith(path, p->root_dir);
90 if (!e)
91 return NULL;
92
93 /* Make sure the returned path starts with a slash */
94 if (e[0] != '/') {
95 if (e == path || e[-1] != '/')
96 return NULL;
97
98 e--;
99 }
100
101 return e;
102 }
103
104 return path;
105}
106
107static int path_is_generator(const LookupPaths *p, const char *path) {
108 _cleanup_free_ char *parent = NULL;
109
110 assert(p);
111 assert(path);
112
113 parent = dirname_malloc(path);
114 if (!parent)
115 return -ENOMEM;
116
117 return path_equal(p->generator, parent) ||
118 path_equal(p->generator_early, parent) ||
119 path_equal(p->generator_late, parent);
120}
121
122static int path_is_transient(const LookupPaths *p, const char *path) {
123 _cleanup_free_ char *parent = NULL;
124
125 assert(p);
126 assert(path);
127
128 parent = dirname_malloc(path);
129 if (!parent)
130 return -ENOMEM;
131
132 return path_equal(p->transient, parent);
133}
134
135static int path_is_config(const LookupPaths *p, const char *path) {
136 _cleanup_free_ char *parent = NULL;
137 const char *rpath;
138
139 assert(p);
140 assert(path);
141
142 /* Checks whether the specified path is intended for configuration or is outside of it. We check both the
143 * top-level directory and the one actually configured. The latter is particularly relevant for cases where we
144 * operate on a root directory. */
145
146 rpath = skip_root(p, path);
147 if (rpath && (path_startswith(rpath, "/etc") || path_startswith(rpath, "/run")))
148 return true;
149
150 parent = dirname_malloc(path);
151 if (!parent)
152 return -ENOMEM;
153
154 return path_equal(parent, p->persistent_config) ||
155 path_equal(parent, p->runtime_config);
156}
157
158static int path_is_runtime(const LookupPaths *p, const char *path) {
159 _cleanup_free_ char *parent = NULL;
160 const char *rpath;
161
162 assert(p);
163 assert(path);
164
165 rpath = skip_root(p, path);
166 if (rpath && path_startswith(rpath, "/run"))
167 return true;
168
169 parent = dirname_malloc(path);
170 if (!parent)
171 return -ENOMEM;
172
173 return path_equal(parent, p->runtime_config);
174}
175
176int unit_file_changes_add(
177 UnitFileChange **changes,
178 unsigned *n_changes,
179 UnitFileChangeType type,
180 const char *path,
181 const char *source) {
182
183 UnitFileChange *c;
184 unsigned i;
185
186 assert(path);
187 assert(!changes == !n_changes);
188
189 if (!changes)
190 return 0;
191
192 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
193 if (!c)
194 return -ENOMEM;
195
196 *changes = c;
197 i = *n_changes;
198
199 c[i].type = type;
200 c[i].path = strdup(path);
201 if (!c[i].path)
202 return -ENOMEM;
203
204 path_kill_slashes(c[i].path);
205
206 if (source) {
207 c[i].source = strdup(source);
208 if (!c[i].source) {
209 free(c[i].path);
210 return -ENOMEM;
211 }
212
213 path_kill_slashes(c[i].path);
214 } else
215 c[i].source = NULL;
216
217 *n_changes = i+1;
218 return 0;
219}
220
221void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
222 unsigned i;
223
224 assert(changes || n_changes == 0);
225
226 if (!changes)
227 return;
228
229 for (i = 0; i < n_changes; i++) {
230 free(changes[i].path);
231 free(changes[i].source);
232 }
233
234 free(changes);
235}
236
237static int create_symlink(
238 const char *old_path,
239 const char *new_path,
240 bool force,
241 UnitFileChange **changes,
242 unsigned *n_changes) {
243
244 _cleanup_free_ char *dest = NULL;
245 int r;
246
247 assert(old_path);
248 assert(new_path);
249
250 /* Actually create a symlink, and remember that we did. Is
251 * smart enough to check if there's already a valid symlink in
252 * place. */
253
254 mkdir_parents_label(new_path, 0755);
255
256 if (symlink(old_path, new_path) >= 0) {
257 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
258 return 1;
259 }
260
261 if (errno != EEXIST)
262 return -errno;
263
264 r = readlink_malloc(new_path, &dest);
265 if (r < 0)
266 return r;
267
268 if (path_equal(dest, old_path))
269 return 0;
270
271 if (!force)
272 return -EEXIST;
273
274 r = symlink_atomic(old_path, new_path);
275 if (r < 0)
276 return r;
277
278 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
279 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
280
281 return 1;
282}
283
284static int mark_symlink_for_removal(
285 Set **remove_symlinks_to,
286 const char *p) {
287
288 char *n;
289 int r;
290
291 assert(p);
292
293 r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops);
294 if (r < 0)
295 return r;
296
297 n = strdup(p);
298 if (!n)
299 return -ENOMEM;
300
301 path_kill_slashes(n);
302
303 r = set_consume(*remove_symlinks_to, n);
304 if (r == -EEXIST)
305 return 0;
306 if (r < 0)
307 return r;
308
309 return 1;
310}
311
312static int remove_marked_symlinks_fd(
313 Set *remove_symlinks_to,
314 int fd,
315 const char *path,
316 const char *config_path,
317 const LookupPaths *lp,
318 bool *restart,
319 UnitFileChange **changes,
320 unsigned *n_changes) {
321
322 _cleanup_closedir_ DIR *d = NULL;
323 struct dirent *de;
324 int r = 0;
325
326 assert(remove_symlinks_to);
327 assert(fd >= 0);
328 assert(path);
329 assert(config_path);
330 assert(lp);
331 assert(restart);
332
333 d = fdopendir(fd);
334 if (!d) {
335 safe_close(fd);
336 return -errno;
337 }
338
339 rewinddir(d);
340
341 FOREACH_DIRENT(de, d, return -errno) {
342
343 dirent_ensure_type(d, de);
344
345 if (de->d_type == DT_DIR) {
346 _cleanup_free_ char *p = NULL;
347 int nfd, q;
348
349 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
350 if (nfd < 0) {
351 if (errno == ENOENT)
352 continue;
353
354 if (r == 0)
355 r = -errno;
356 continue;
357 }
358
359 p = path_make_absolute(de->d_name, path);
360 if (!p) {
361 safe_close(nfd);
362 return -ENOMEM;
363 }
364
365 /* This will close nfd, regardless whether it succeeds or not */
366 q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, restart, changes, n_changes);
367 if (q < 0 && r == 0)
368 r = q;
369
370 } else if (de->d_type == DT_LNK) {
371 _cleanup_free_ char *p = NULL, *dest = NULL;
372 const char *rp;
373 bool found;
374 int q;
375
376 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
377 continue;
378
379 p = path_make_absolute(de->d_name, path);
380 if (!p)
381 return -ENOMEM;
382
383 q = readlink_malloc(p, &dest);
384 if (q == -ENOENT)
385 continue;
386 if (q < 0) {
387 if (r == 0)
388 r = q;
389 continue;
390 }
391
392 /* We remove all links pointing to a file or path that is marked, as well as all files sharing
393 * the same name as a file that is marked. */
394
395 found =
396 set_contains(remove_symlinks_to, dest) ||
397 set_contains(remove_symlinks_to, basename(dest)) ||
398 set_contains(remove_symlinks_to, de->d_name);
399
400 if (!found)
401 continue;
402
403 if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
404 if (r == 0)
405 r = -errno;
406 continue;
407 }
408
409 path_kill_slashes(p);
410 (void) rmdir_parents(p, config_path);
411
412 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
413
414 /* Now, remember the full path (but with the root prefix removed) of the symlink we just
415 * removed, and remove any symlinks to it, too */
416
417 rp = skip_root(lp, p);
418 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
419 if (q < 0)
420 return q;
421 if (q > 0)
422 *restart = true;
423 }
424 }
425
426 return r;
427}
428
429static int remove_marked_symlinks(
430 Set *remove_symlinks_to,
431 const char *config_path,
432 const LookupPaths *lp,
433 UnitFileChange **changes,
434 unsigned *n_changes) {
435
436 _cleanup_close_ int fd = -1;
437 bool restart;
438 int r = 0;
439
440 assert(config_path);
441 assert(lp);
442
443 if (set_size(remove_symlinks_to) <= 0)
444 return 0;
445
446 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
447 if (fd < 0)
448 return -errno;
449
450 do {
451 int q, cfd;
452 restart = false;
453
454 cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
455 if (cfd < 0)
456 return -errno;
457
458 /* This takes possession of cfd and closes it */
459 q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, &restart, changes, n_changes);
460 if (r == 0)
461 r = q;
462 } while (restart);
463
464 return r;
465}
466
467static int find_symlinks_fd(
468 const char *root_dir,
469 const char *name,
470 int fd,
471 const char *path,
472 const char *config_path,
473 const LookupPaths *lp,
474 bool *same_name_link) {
475
476 _cleanup_closedir_ DIR *d = NULL;
477 struct dirent *de;
478 int r = 0;
479
480 assert(name);
481 assert(fd >= 0);
482 assert(path);
483 assert(config_path);
484 assert(lp);
485 assert(same_name_link);
486
487 d = fdopendir(fd);
488 if (!d) {
489 safe_close(fd);
490 return -errno;
491 }
492
493 FOREACH_DIRENT(de, d, return -errno) {
494
495 dirent_ensure_type(d, de);
496
497 if (de->d_type == DT_DIR) {
498 _cleanup_free_ char *p = NULL;
499 int nfd, q;
500
501 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
502 if (nfd < 0) {
503 if (errno == ENOENT)
504 continue;
505
506 if (r == 0)
507 r = -errno;
508 continue;
509 }
510
511 p = path_make_absolute(de->d_name, path);
512 if (!p) {
513 safe_close(nfd);
514 return -ENOMEM;
515 }
516
517 /* This will close nfd, regardless whether it succeeds or not */
518 q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link);
519 if (q > 0)
520 return 1;
521 if (r == 0)
522 r = q;
523
524 } else if (de->d_type == DT_LNK) {
525 _cleanup_free_ char *p = NULL, *dest = NULL;
526 bool found_path, found_dest, b = false;
527 int q;
528
529 /* Acquire symlink name */
530 p = path_make_absolute(de->d_name, path);
531 if (!p)
532 return -ENOMEM;
533
534 /* Acquire symlink destination */
535 q = readlink_malloc(p, &dest);
536 if (q == -ENOENT)
537 continue;
538 if (q < 0) {
539 if (r == 0)
540 r = q;
541 continue;
542 }
543
544 /* Make absolute */
545 if (!path_is_absolute(dest)) {
546 char *x;
547
548 x = prefix_root(root_dir, dest);
549 if (!x)
550 return -ENOMEM;
551
552 free(dest);
553 dest = x;
554 }
555
556 /* Check if the symlink itself matches what we
557 * are looking for */
558 if (path_is_absolute(name))
559 found_path = path_equal(p, name);
560 else
561 found_path = streq(de->d_name, name);
562
563 /* Check if what the symlink points to
564 * matches what we are looking for */
565 if (path_is_absolute(name))
566 found_dest = path_equal(dest, name);
567 else
568 found_dest = streq(basename(dest), name);
569
570 if (found_path && found_dest) {
571 _cleanup_free_ char *t = NULL;
572
573 /* Filter out same name links in the main
574 * config path */
575 t = path_make_absolute(name, config_path);
576 if (!t)
577 return -ENOMEM;
578
579 b = path_equal(t, p);
580 }
581
582 if (b)
583 *same_name_link = true;
584 else if (found_path || found_dest)
585 return 1;
586 }
587 }
588
589 return r;
590}
591
592static int find_symlinks(
593 const char *root_dir,
594 const char *name,
595 const char *config_path,
596 const LookupPaths *lp,
597 bool *same_name_link) {
598
599 int fd;
600
601 assert(name);
602 assert(config_path);
603 assert(same_name_link);
604
605 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
606 if (fd < 0) {
607 if (errno == ENOENT)
608 return 0;
609 return -errno;
610 }
611
612 /* This takes possession of fd and closes it */
613 return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link);
614}
615
616static int find_symlinks_in_scope(
617 UnitFileScope scope,
618 const LookupPaths *paths,
619 const char *name,
620 UnitFileState *state) {
621
622 bool same_name_link_runtime = false, same_name_link = false;
623 int r;
624
625 assert(scope >= 0);
626 assert(scope < _UNIT_FILE_SCOPE_MAX);
627 assert(paths);
628 assert(name);
629
630 /* First look in the persistent config path */
631 r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link);
632 if (r < 0)
633 return r;
634 if (r > 0) {
635 *state = UNIT_FILE_ENABLED;
636 return r;
637 }
638
639 /* Then look in runtime config path */
640 r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime);
641 if (r < 0)
642 return r;
643 if (r > 0) {
644 *state = UNIT_FILE_ENABLED_RUNTIME;
645 return r;
646 }
647
648 /* Hmm, we didn't find it, but maybe we found the same name
649 * link? */
650 if (same_name_link) {
651 *state = UNIT_FILE_LINKED;
652 return 1;
653 }
654 if (same_name_link_runtime) {
655 *state = UNIT_FILE_LINKED_RUNTIME;
656 return 1;
657 }
658
659 return 0;
660}
661
662static void install_info_free(UnitFileInstallInfo *i) {
663
664 if (!i)
665 return;
666
667 free(i->name);
668 free(i->path);
669 strv_free(i->aliases);
670 strv_free(i->wanted_by);
671 strv_free(i->required_by);
672 strv_free(i->also);
673 free(i->default_instance);
674 free(i->symlink_target);
675 free(i);
676}
677
678static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) {
679 UnitFileInstallInfo *i;
680
681 if (!m)
682 return NULL;
683
684 while ((i = ordered_hashmap_steal_first(m)))
685 install_info_free(i);
686
687 return ordered_hashmap_free(m);
688}
689
690static void install_context_done(InstallContext *c) {
691 assert(c);
692
693 c->will_process = install_info_hashmap_free(c->will_process);
694 c->have_processed = install_info_hashmap_free(c->have_processed);
695}
696
697static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
698 UnitFileInstallInfo *i;
699
700 i = ordered_hashmap_get(c->have_processed, name);
701 if (i)
702 return i;
703
704 return ordered_hashmap_get(c->will_process, name);
705}
706
707static int install_info_may_process(UnitFileInstallInfo *i, const LookupPaths *paths) {
708 assert(i);
709 assert(paths);
710
711 /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus
712 * not subject to enable/disable operations. */
713
714 if (i->type == UNIT_FILE_TYPE_MASKED)
715 return -ESHUTDOWN;
716 if (path_is_generator(paths, i->path))
717 return -EADDRNOTAVAIL;
718 if (path_is_transient(paths, i->path))
719 return -EADDRNOTAVAIL;
720
721 return 0;
722}
723
724static int install_info_add(
725 InstallContext *c,
726 const char *name,
727 const char *path,
728 UnitFileInstallInfo **ret) {
729
730 UnitFileInstallInfo *i = NULL;
731 int r;
732
733 assert(c);
734 assert(name || path);
735
736 if (!name)
737 name = basename(path);
738
739 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
740 return -EINVAL;
741
742 i = install_info_find(c, name);
743 if (i) {
744 if (ret)
745 *ret = i;
746 return 0;
747 }
748
749 r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
750 if (r < 0)
751 return r;
752
753 i = new0(UnitFileInstallInfo, 1);
754 if (!i)
755 return -ENOMEM;
756 i->type = _UNIT_FILE_TYPE_INVALID;
757
758 i->name = strdup(name);
759 if (!i->name) {
760 r = -ENOMEM;
761 goto fail;
762 }
763
764 if (path) {
765 i->path = strdup(path);
766 if (!i->path) {
767 r = -ENOMEM;
768 goto fail;
769 }
770 }
771
772 r = ordered_hashmap_put(c->will_process, i->name, i);
773 if (r < 0)
774 goto fail;
775
776 if (ret)
777 *ret = i;
778
779 return 0;
780
781fail:
782 install_info_free(i);
783 return r;
784}
785
786static int config_parse_also(
787 const char *unit,
788 const char *filename,
789 unsigned line,
790 const char *section,
791 unsigned section_line,
792 const char *lvalue,
793 int ltype,
794 const char *rvalue,
795 void *data,
796 void *userdata) {
797
798 UnitFileInstallInfo *i = userdata;
799 InstallContext *c = data;
800 int r;
801
802 assert(filename);
803 assert(lvalue);
804 assert(rvalue);
805
806 for (;;) {
807 _cleanup_free_ char *word = NULL;
808
809 r = extract_first_word(&rvalue, &word, NULL, 0);
810 if (r < 0)
811 return r;
812 if (r == 0)
813 break;
814
815 r = install_info_add(c, word, NULL, NULL);
816 if (r < 0)
817 return r;
818
819 r = strv_push(&i->also, word);
820 if (r < 0)
821 return r;
822
823 word = NULL;
824 }
825
826 return 0;
827}
828
829static int config_parse_default_instance(
830 const char *unit,
831 const char *filename,
832 unsigned line,
833 const char *section,
834 unsigned section_line,
835 const char *lvalue,
836 int ltype,
837 const char *rvalue,
838 void *data,
839 void *userdata) {
840
841 UnitFileInstallInfo *i = data;
842 char *printed;
843 int r;
844
845 assert(filename);
846 assert(lvalue);
847 assert(rvalue);
848
849 r = install_full_printf(i, rvalue, &printed);
850 if (r < 0)
851 return r;
852
853 if (!unit_instance_is_valid(printed)) {
854 free(printed);
855 return -EINVAL;
856 }
857
858 free(i->default_instance);
859 i->default_instance = printed;
860
861 return 0;
862}
863
864static int unit_file_load(
865 InstallContext *c,
866 UnitFileInstallInfo *info,
867 const char *path,
868 SearchFlags flags) {
869
870 const ConfigTableItem items[] = {
871 { "Install", "Alias", config_parse_strv, 0, &info->aliases },
872 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
873 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
874 { "Install", "DefaultInstance", config_parse_default_instance, 0, info },
875 { "Install", "Also", config_parse_also, 0, c },
876 {}
877 };
878
879 _cleanup_fclose_ FILE *f = NULL;
880 _cleanup_close_ int fd = -1;
881 struct stat st;
882 int r;
883
884 assert(c);
885 assert(info);
886 assert(path);
887
888 if (!(flags & SEARCH_LOAD)) {
889 r = lstat(path, &st);
890 if (r < 0)
891 return -errno;
892
893 if (null_or_empty(&st))
894 info->type = UNIT_FILE_TYPE_MASKED;
895 else if (S_ISREG(st.st_mode))
896 info->type = UNIT_FILE_TYPE_REGULAR;
897 else if (S_ISLNK(st.st_mode))
898 return -ELOOP;
899 else if (S_ISDIR(st.st_mode))
900 return -EISDIR;
901 else
902 return -ENOTTY;
903
904 return 0;
905 }
906
907 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
908 if (fd < 0)
909 return -errno;
910 if (fstat(fd, &st) < 0)
911 return -errno;
912 if (null_or_empty(&st)) {
913 info->type = UNIT_FILE_TYPE_MASKED;
914 return 0;
915 }
916 if (S_ISDIR(st.st_mode))
917 return -EISDIR;
918 if (!S_ISREG(st.st_mode))
919 return -ENOTTY;
920
921 f = fdopen(fd, "re");
922 if (!f)
923 return -errno;
924 fd = -1;
925
926 r = config_parse(NULL, path, f,
927 NULL,
928 config_item_table_lookup, items,
929 true, true, false, info);
930 if (r < 0)
931 return r;
932
933 info->type = UNIT_FILE_TYPE_REGULAR;
934
935 return
936 (int) strv_length(info->aliases) +
937 (int) strv_length(info->wanted_by) +
938 (int) strv_length(info->required_by);
939}
940
941static int unit_file_load_or_readlink(
942 InstallContext *c,
943 UnitFileInstallInfo *info,
944 const char *path,
945 const char *root_dir,
946 SearchFlags flags) {
947
948 _cleanup_free_ char *target = NULL;
949 int r;
950
951 r = unit_file_load(c, info, path, flags);
952 if (r != -ELOOP)
953 return r;
954
955 /* This is a symlink, let's read it. */
956
957 r = readlink_malloc(path, &target);
958 if (r < 0)
959 return r;
960
961 if (path_equal(target, "/dev/null"))
962 info->type = UNIT_FILE_TYPE_MASKED;
963 else {
964 const char *bn;
965 UnitType a, b;
966
967 bn = basename(target);
968
969 if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
970
971 if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
972 return -EINVAL;
973
974 } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
975
976 if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
977 return -EINVAL;
978
979 } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
980
981 if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
982 return -EINVAL;
983 } else
984 return -EINVAL;
985
986 /* Enforce that the symlink destination does not
987 * change the unit file type. */
988
989 a = unit_name_to_type(info->name);
990 b = unit_name_to_type(bn);
991 if (a < 0 || b < 0 || a != b)
992 return -EINVAL;
993
994 if (path_is_absolute(target))
995 /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
996 info->symlink_target = prefix_root(root_dir, target);
997 else
998 /* This is a relative path, take it relative to the dir the symlink is located in. */
999 info->symlink_target = file_in_same_dir(path, target);
1000 if (!info->symlink_target)
1001 return -ENOMEM;
1002
1003 info->type = UNIT_FILE_TYPE_SYMLINK;
1004 }
1005
1006 return 0;
1007}
1008
1009static int unit_file_search(
1010 InstallContext *c,
1011 UnitFileInstallInfo *info,
1012 const LookupPaths *paths,
1013 SearchFlags flags) {
1014
1015 char **p;
1016 int r;
1017
1018 assert(c);
1019 assert(info);
1020 assert(paths);
1021
1022 /* Was this unit already loaded? */
1023 if (info->type != _UNIT_FILE_TYPE_INVALID)
1024 return 0;
1025
1026 if (info->path)
1027 return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags);
1028
1029 assert(info->name);
1030
1031 STRV_FOREACH(p, paths->search_path) {
1032 _cleanup_free_ char *path = NULL;
1033
1034 path = strjoin(*p, "/", info->name, NULL);
1035 if (!path)
1036 return -ENOMEM;
1037
1038 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
1039 if (r < 0) {
1040 if (r != -ENOENT)
1041 return r;
1042 } else {
1043 info->path = path;
1044 path = NULL;
1045 return r;
1046 }
1047 }
1048
1049 if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1050
1051 /* Unit file doesn't exist, however instance
1052 * enablement was requested. We will check if it is
1053 * possible to load template unit file. */
1054
1055 _cleanup_free_ char *template = NULL;
1056
1057 r = unit_name_template(info->name, &template);
1058 if (r < 0)
1059 return r;
1060
1061 STRV_FOREACH(p, paths->search_path) {
1062 _cleanup_free_ char *path = NULL;
1063
1064 path = strjoin(*p, "/", template, NULL);
1065 if (!path)
1066 return -ENOMEM;
1067
1068 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
1069 if (r < 0) {
1070 if (r != -ENOENT)
1071 return r;
1072 } else {
1073 info->path = path;
1074 path = NULL;
1075 return r;
1076 }
1077 }
1078 }
1079
1080 return -ENOENT;
1081}
1082
1083static int install_info_follow(
1084 InstallContext *c,
1085 UnitFileInstallInfo *i,
1086 const char *root_dir,
1087 SearchFlags flags) {
1088
1089 assert(c);
1090 assert(i);
1091
1092 if (i->type != UNIT_FILE_TYPE_SYMLINK)
1093 return -EINVAL;
1094 if (!i->symlink_target)
1095 return -EINVAL;
1096
1097 /* If the basename doesn't match, the caller should add a
1098 * complete new entry for this. */
1099
1100 if (!streq(basename(i->symlink_target), i->name))
1101 return -EXDEV;
1102
1103 free(i->path);
1104 i->path = i->symlink_target;
1105 i->symlink_target = NULL;
1106 i->type = _UNIT_FILE_TYPE_INVALID;
1107
1108 return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
1109}
1110
1111static int install_info_traverse(
1112 UnitFileScope scope,
1113 InstallContext *c,
1114 const LookupPaths *paths,
1115 UnitFileInstallInfo *start,
1116 SearchFlags flags,
1117 UnitFileInstallInfo **ret) {
1118
1119 UnitFileInstallInfo *i;
1120 unsigned k = 0;
1121 int r;
1122
1123 assert(paths);
1124 assert(start);
1125 assert(c);
1126
1127 r = unit_file_search(c, start, paths, flags);
1128 if (r < 0)
1129 return r;
1130
1131 i = start;
1132 while (i->type == UNIT_FILE_TYPE_SYMLINK) {
1133 /* Follow the symlink */
1134
1135 if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
1136 return -ELOOP;
1137
1138 if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
1139 r = path_is_config(paths, i->path);
1140 if (r < 0)
1141 return r;
1142 if (r > 0)
1143 return -ELOOP;
1144 }
1145
1146 r = install_info_follow(c, i, paths->root_dir, flags);
1147 if (r < 0) {
1148 _cleanup_free_ char *buffer = NULL;
1149 const char *bn;
1150
1151 if (r != -EXDEV)
1152 return r;
1153
1154 /* Target has a different name, create a new
1155 * install info object for that, and continue
1156 * with that. */
1157
1158 bn = basename(i->symlink_target);
1159
1160 if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
1161 unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
1162
1163 _cleanup_free_ char *instance = NULL;
1164
1165 r = unit_name_to_instance(i->name, &instance);
1166 if (r < 0)
1167 return r;
1168
1169 r = unit_name_replace_instance(bn, instance, &buffer);
1170 if (r < 0)
1171 return r;
1172
1173 bn = buffer;
1174 }
1175
1176 r = install_info_add(c, bn, NULL, &i);
1177 if (r < 0)
1178 return r;
1179
1180 r = unit_file_search(c, i, paths, flags);
1181 if (r < 0)
1182 return r;
1183 }
1184
1185 /* Try again, with the new target we found. */
1186 }
1187
1188 if (ret)
1189 *ret = i;
1190
1191 return 0;
1192}
1193
1194static int install_info_add_auto(
1195 InstallContext *c,
1196 const LookupPaths *paths,
1197 const char *name_or_path,
1198 UnitFileInstallInfo **ret) {
1199
1200 assert(c);
1201 assert(name_or_path);
1202
1203 if (path_is_absolute(name_or_path)) {
1204 const char *pp;
1205
1206 pp = prefix_roota(paths->root_dir, name_or_path);
1207
1208 return install_info_add(c, NULL, pp, ret);
1209 } else
1210 return install_info_add(c, name_or_path, NULL, ret);
1211}
1212
1213static int install_info_discover(
1214 UnitFileScope scope,
1215 InstallContext *c,
1216 const LookupPaths *paths,
1217 const char *name,
1218 SearchFlags flags,
1219 UnitFileInstallInfo **ret) {
1220
1221 UnitFileInstallInfo *i;
1222 int r;
1223
1224 assert(c);
1225 assert(paths);
1226 assert(name);
1227
1228 r = install_info_add_auto(c, paths, name, &i);
1229 if (r < 0)
1230 return r;
1231
1232 return install_info_traverse(scope, c, paths, i, flags, ret);
1233}
1234
1235static int install_info_symlink_alias(
1236 UnitFileInstallInfo *i,
1237 const LookupPaths *paths,
1238 const char *config_path,
1239 bool force,
1240 UnitFileChange **changes,
1241 unsigned *n_changes) {
1242
1243 char **s;
1244 int r = 0, q;
1245
1246 assert(i);
1247 assert(paths);
1248 assert(config_path);
1249
1250 STRV_FOREACH(s, i->aliases) {
1251 _cleanup_free_ char *alias_path = NULL, *dst = NULL;
1252 const char *rp;
1253
1254 q = install_full_printf(i, *s, &dst);
1255 if (q < 0)
1256 return q;
1257
1258 alias_path = path_make_absolute(dst, config_path);
1259 if (!alias_path)
1260 return -ENOMEM;
1261
1262 rp = skip_root(paths, i->path);
1263
1264 q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes);
1265 if (r == 0)
1266 r = q;
1267 }
1268
1269 return r;
1270}
1271
1272static int install_info_symlink_wants(
1273 UnitFileInstallInfo *i,
1274 const LookupPaths *paths,
1275 const char *config_path,
1276 char **list,
1277 const char *suffix,
1278 bool force,
1279 UnitFileChange **changes,
1280 unsigned *n_changes) {
1281
1282 _cleanup_free_ char *buf = NULL;
1283 const char *n;
1284 char **s;
1285 int r = 0, q;
1286
1287 assert(i);
1288 assert(paths);
1289 assert(config_path);
1290
1291 if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
1292
1293 /* Don't install any symlink if there's no default
1294 * instance configured */
1295
1296 if (!i->default_instance)
1297 return 0;
1298
1299 r = unit_name_replace_instance(i->name, i->default_instance, &buf);
1300 if (r < 0)
1301 return r;
1302
1303 n = buf;
1304 } else
1305 n = i->name;
1306
1307 STRV_FOREACH(s, list) {
1308 _cleanup_free_ char *path = NULL, *dst = NULL;
1309 const char *rp;
1310
1311 q = install_full_printf(i, *s, &dst);
1312 if (q < 0)
1313 return q;
1314
1315 if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
1316 r = -EINVAL;
1317 continue;
1318 }
1319
1320 path = strjoin(config_path, "/", dst, suffix, n, NULL);
1321 if (!path)
1322 return -ENOMEM;
1323
1324 rp = skip_root(paths, i->path);
1325
1326 q = create_symlink(rp ?: i->path, path, force, changes, n_changes);
1327 if (r == 0)
1328 r = q;
1329 }
1330
1331 return r;
1332}
1333
1334static int install_info_symlink_link(
1335 UnitFileInstallInfo *i,
1336 const LookupPaths *paths,
1337 const char *config_path,
1338 bool force,
1339 UnitFileChange **changes,
1340 unsigned *n_changes) {
1341
1342 _cleanup_free_ char *path = NULL;
1343 const char *rp;
1344 int r;
1345
1346 assert(i);
1347 assert(paths);
1348 assert(config_path);
1349 assert(i->path);
1350
1351 r = in_search_path(paths, i->path);
1352 if (r != 0)
1353 return r;
1354
1355 path = strjoin(config_path, "/", i->name, NULL);
1356 if (!path)
1357 return -ENOMEM;
1358
1359 rp = skip_root(paths, i->path);
1360
1361 return create_symlink(rp ?: i->path, path, force, changes, n_changes);
1362}
1363
1364static int install_info_apply(
1365 UnitFileInstallInfo *i,
1366 const LookupPaths *paths,
1367 const char *config_path,
1368 bool force,
1369 UnitFileChange **changes,
1370 unsigned *n_changes) {
1371
1372 int r, q;
1373
1374 assert(i);
1375 assert(paths);
1376 assert(config_path);
1377
1378 if (i->type != UNIT_FILE_TYPE_REGULAR)
1379 return 0;
1380
1381 r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
1382
1383 q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
1384 if (r == 0)
1385 r = q;
1386
1387 q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", force, changes, n_changes);
1388 if (r == 0)
1389 r = q;
1390
1391 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1392 if (r == 0)
1393 r = q;
1394
1395 return r;
1396}
1397
1398static int install_context_apply(
1399 UnitFileScope scope,
1400 InstallContext *c,
1401 const LookupPaths *paths,
1402 const char *config_path,
1403 bool force,
1404 SearchFlags flags,
1405 UnitFileChange **changes,
1406 unsigned *n_changes) {
1407
1408 UnitFileInstallInfo *i;
1409 int r;
1410
1411 assert(c);
1412 assert(paths);
1413 assert(config_path);
1414
1415 if (ordered_hashmap_isempty(c->will_process))
1416 return 0;
1417
1418 r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
1419 if (r < 0)
1420 return r;
1421
1422 r = 0;
1423 while ((i = ordered_hashmap_first(c->will_process))) {
1424 int q;
1425
1426 q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1427 if (q < 0)
1428 return q;
1429
1430 r = install_info_traverse(scope, c, paths, i, flags, NULL);
1431 if (r < 0)
1432 return r;
1433
1434 if (i->type != UNIT_FILE_TYPE_REGULAR)
1435 continue;
1436
1437 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1438 if (r >= 0) {
1439 if (q < 0)
1440 r = q;
1441 else
1442 r+= q;
1443 }
1444 }
1445
1446 return r;
1447}
1448
1449static int install_context_mark_for_removal(
1450 UnitFileScope scope,
1451 InstallContext *c,
1452 const LookupPaths *paths,
1453 Set **remove_symlinks_to,
1454 const char *config_path) {
1455
1456 UnitFileInstallInfo *i;
1457 int r;
1458
1459 assert(c);
1460 assert(paths);
1461 assert(config_path);
1462
1463 /* Marks all items for removal */
1464
1465 if (ordered_hashmap_isempty(c->will_process))
1466 return 0;
1467
1468 r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
1469 if (r < 0)
1470 return r;
1471
1472 while ((i = ordered_hashmap_first(c->will_process))) {
1473
1474 r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1475 if (r < 0)
1476 return r;
1477
1478 r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
1479 if (r < 0)
1480 return r;
1481
1482 if (i->type != UNIT_FILE_TYPE_REGULAR)
1483 continue;
1484
1485 r = mark_symlink_for_removal(remove_symlinks_to, i->name);
1486 if (r < 0)
1487 return r;
1488 }
1489
1490 return 0;
1491}
1492
1493int unit_file_mask(
1494 UnitFileScope scope,
1495 bool runtime,
1496 const char *root_dir,
1497 char **files,
1498 bool force,
1499 UnitFileChange **changes,
1500 unsigned *n_changes) {
1501
1502 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1503 const char *config_path;
1504 char **i;
1505 int r;
1506
1507 assert(scope >= 0);
1508 assert(scope < _UNIT_FILE_SCOPE_MAX);
1509
1510 r = lookup_paths_init(&paths, scope, root_dir);
1511 if (r < 0)
1512 return r;
1513
1514 config_path = runtime ? paths.runtime_config : paths.persistent_config;
1515
1516 STRV_FOREACH(i, files) {
1517 _cleanup_free_ char *path = NULL;
1518 int q;
1519
1520 if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
1521 if (r == 0)
1522 r = -EINVAL;
1523 continue;
1524 }
1525
1526 path = path_make_absolute(*i, config_path);
1527 if (!path)
1528 return -ENOMEM;
1529
1530 q = create_symlink("/dev/null", path, force, changes, n_changes);
1531 if (q < 0 && r >= 0)
1532 r = q;
1533 }
1534
1535 return r;
1536}
1537
1538int unit_file_unmask(
1539 UnitFileScope scope,
1540 bool runtime,
1541 const char *root_dir,
1542 char **files,
1543 UnitFileChange **changes,
1544 unsigned *n_changes) {
1545
1546 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1547 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
1548 _cleanup_free_ char **todo = NULL;
1549 size_t n_todo = 0, n_allocated = 0;
1550 const char *config_path;
1551 char **i;
1552 int r, q;
1553
1554 assert(scope >= 0);
1555 assert(scope < _UNIT_FILE_SCOPE_MAX);
1556
1557 r = lookup_paths_init(&paths, scope, root_dir);
1558 if (r < 0)
1559 return r;
1560
1561 config_path = runtime ? paths.runtime_config : paths.persistent_config;
1562
1563 STRV_FOREACH(i, files) {
1564 _cleanup_free_ char *path = NULL;
1565
1566 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
1567 return -EINVAL;
1568
1569 path = path_make_absolute(*i, config_path);
1570 if (!path)
1571 return -ENOMEM;
1572
1573 r = null_or_empty_path(path);
1574 if (r == -ENOENT)
1575 continue;
1576 if (r < 0)
1577 return r;
1578 if (r == 0)
1579 continue;
1580
1581 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
1582 return -ENOMEM;
1583
1584 todo[n_todo++] = *i;
1585 }
1586
1587 strv_uniq(todo);
1588
1589 r = 0;
1590 STRV_FOREACH(i, todo) {
1591 _cleanup_free_ char *path = NULL;
1592 const char *rp;
1593
1594 path = path_make_absolute(*i, config_path);
1595 if (!path)
1596 return -ENOMEM;
1597
1598 if (unlink(path) < 0) {
1599 if (errno != -ENOENT && r >= 0)
1600 r = -errno;
1601
1602 continue;
1603 }
1604
1605 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
1606
1607 rp = skip_root(&paths, path);
1608 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
1609 if (q < 0)
1610 return q;
1611 }
1612
1613 q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes);
1614 if (r >= 0)
1615 r = q;
1616
1617 return r;
1618}
1619
1620int unit_file_link(
1621 UnitFileScope scope,
1622 bool runtime,
1623 const char *root_dir,
1624 char **files,
1625 bool force,
1626 UnitFileChange **changes,
1627 unsigned *n_changes) {
1628
1629 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1630 _cleanup_free_ char **todo = NULL;
1631 size_t n_todo = 0, n_allocated = 0;
1632 const char *config_path;
1633 char **i;
1634 int r, q;
1635
1636 assert(scope >= 0);
1637 assert(scope < _UNIT_FILE_SCOPE_MAX);
1638
1639 r = lookup_paths_init(&paths, scope, root_dir);
1640 if (r < 0)
1641 return r;
1642
1643 config_path = runtime ? paths.runtime_config : paths.persistent_config;
1644
1645 STRV_FOREACH(i, files) {
1646 _cleanup_free_ char *full = NULL;
1647 struct stat st;
1648 char *fn;
1649
1650 if (!path_is_absolute(*i))
1651 return -EINVAL;
1652
1653 fn = basename(*i);
1654 if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
1655 return -EINVAL;
1656
1657 full = prefix_root(paths.root_dir, *i);
1658 if (!full)
1659 return -ENOMEM;
1660
1661 if (lstat(full, &st) < 0)
1662 return -errno;
1663 if (S_ISLNK(st.st_mode))
1664 return -ELOOP;
1665 if (S_ISDIR(st.st_mode))
1666 return -EISDIR;
1667 if (!S_ISREG(st.st_mode))
1668 return -ENOTTY;
1669
1670 q = in_search_path(&paths, *i);
1671 if (q < 0)
1672 return q;
1673 if (q > 0)
1674 continue;
1675
1676 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
1677 return -ENOMEM;
1678
1679 todo[n_todo++] = *i;
1680 }
1681
1682 strv_uniq(todo);
1683
1684 r = 0;
1685 STRV_FOREACH(i, todo) {
1686 _cleanup_free_ char *new_path = NULL;
1687 const char *old_path;
1688
1689 old_path = skip_root(&paths, *i);
1690 new_path = path_make_absolute(basename(*i), config_path);
1691 if (!new_path)
1692 return -ENOMEM;
1693
1694 q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes);
1695 if (q < 0 && r >= 0)
1696 r = q;
1697 }
1698
1699 return r;
1700}
1701
1702int unit_file_add_dependency(
1703 UnitFileScope scope,
1704 bool runtime,
1705 const char *root_dir,
1706 char **files,
1707 const char *target,
1708 UnitDependency dep,
1709 bool force,
1710 UnitFileChange **changes,
1711 unsigned *n_changes) {
1712
1713 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1714 _cleanup_(install_context_done) InstallContext c = {};
1715 UnitFileInstallInfo *i, *target_info;
1716 const char *config_path;
1717 char **f;
1718 int r;
1719
1720 assert(scope >= 0);
1721 assert(scope < _UNIT_FILE_SCOPE_MAX);
1722 assert(target);
1723
1724 if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
1725 return -EINVAL;
1726
1727 if (!unit_name_is_valid(target, UNIT_NAME_ANY))
1728 return -EINVAL;
1729
1730 r = lookup_paths_init(&paths, scope, root_dir);
1731 if (r < 0)
1732 return r;
1733
1734 config_path = runtime ? paths.runtime_config : paths.persistent_config;
1735
1736 r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
1737 if (r < 0)
1738 return r;
1739 r = install_info_may_process(target_info, &paths);
1740 if (r < 0)
1741 return r;
1742
1743 assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
1744
1745 STRV_FOREACH(f, files) {
1746 char ***l;
1747
1748 r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
1749 if (r < 0)
1750 return r;
1751 r = install_info_may_process(i, &paths);
1752 if (r < 0)
1753 return r;
1754
1755 assert(i->type == UNIT_FILE_TYPE_REGULAR);
1756
1757 /* We didn't actually load anything from the unit
1758 * file, but instead just add in our new symlink to
1759 * create. */
1760
1761 if (dep == UNIT_WANTS)
1762 l = &i->wanted_by;
1763 else
1764 l = &i->required_by;
1765
1766 strv_free(*l);
1767 *l = strv_new(target_info->name, NULL);
1768 if (!*l)
1769 return -ENOMEM;
1770 }
1771
1772 return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
1773}
1774
1775int unit_file_enable(
1776 UnitFileScope scope,
1777 bool runtime,
1778 const char *root_dir,
1779 char **files,
1780 bool force,
1781 UnitFileChange **changes,
1782 unsigned *n_changes) {
1783
1784 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1785 _cleanup_(install_context_done) InstallContext c = {};
1786 const char *config_path;
1787 UnitFileInstallInfo *i;
1788 char **f;
1789 int r;
1790
1791 assert(scope >= 0);
1792 assert(scope < _UNIT_FILE_SCOPE_MAX);
1793
1794 r = lookup_paths_init(&paths, scope, root_dir);
1795 if (r < 0)
1796 return r;
1797
1798 config_path = runtime ? paths.runtime_config : paths.persistent_config;
1799
1800 STRV_FOREACH(f, files) {
1801 r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD, &i);
1802 if (r < 0)
1803 return r;
1804 r = install_info_may_process(i, &paths);
1805 if (r < 0)
1806 return r;
1807
1808 assert(i->type == UNIT_FILE_TYPE_REGULAR);
1809 }
1810
1811 /* This will return the number of symlink rules that were
1812 supposed to be created, not the ones actually created. This
1813 is useful to determine whether the passed files had any
1814 installation data at all. */
1815
1816 return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_LOAD, changes, n_changes);
1817}
1818
1819int unit_file_disable(
1820 UnitFileScope scope,
1821 bool runtime,
1822 const char *root_dir,
1823 char **files,
1824 UnitFileChange **changes,
1825 unsigned *n_changes) {
1826
1827 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1828 _cleanup_(install_context_done) InstallContext c = {};
1829 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
1830 const char *config_path;
1831 char **i;
1832 int r;
1833
1834 assert(scope >= 0);
1835 assert(scope < _UNIT_FILE_SCOPE_MAX);
1836
1837 r = lookup_paths_init(&paths, scope, root_dir);
1838 if (r < 0)
1839 return r;
1840
1841 config_path = runtime ? paths.runtime_config : paths.persistent_config;
1842
1843 STRV_FOREACH(i, files) {
1844 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
1845 return -EINVAL;
1846
1847 r = install_info_add(&c, *i, NULL, NULL);
1848 if (r < 0)
1849 return r;
1850 }
1851
1852 r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path);
1853 if (r < 0)
1854 return r;
1855
1856 return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes);
1857}
1858
1859int unit_file_reenable(
1860 UnitFileScope scope,
1861 bool runtime,
1862 const char *root_dir,
1863 char **files,
1864 bool force,
1865 UnitFileChange **changes,
1866 unsigned *n_changes) {
1867
1868 char **n;
1869 int r;
1870 size_t l, i;
1871
1872 /* First, we invoke the disable command with only the basename... */
1873 l = strv_length(files);
1874 n = newa(char*, l+1);
1875 for (i = 0; i < l; i++)
1876 n[i] = basename(files[i]);
1877 n[i] = NULL;
1878
1879 r = unit_file_disable(scope, runtime, root_dir, n, changes, n_changes);
1880 if (r < 0)
1881 return r;
1882
1883 /* But the enable command with the full name */
1884 return unit_file_enable(scope, runtime, root_dir, files, force, changes, n_changes);
1885}
1886
1887int unit_file_set_default(
1888 UnitFileScope scope,
1889 const char *root_dir,
1890 const char *name,
1891 bool force,
1892 UnitFileChange **changes,
1893 unsigned *n_changes) {
1894
1895 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1896 _cleanup_(install_context_done) InstallContext c = {};
1897 UnitFileInstallInfo *i;
1898 const char *new_path, *old_path;
1899 int r;
1900
1901 assert(scope >= 0);
1902 assert(scope < _UNIT_FILE_SCOPE_MAX);
1903 assert(name);
1904
1905 if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
1906 return -EINVAL;
1907 if (streq(name, SPECIAL_DEFAULT_TARGET))
1908 return -EINVAL;
1909
1910 r = lookup_paths_init(&paths, scope, root_dir);
1911 if (r < 0)
1912 return r;
1913
1914 r = install_info_discover(scope, &c, &paths, name, 0, &i);
1915 if (r < 0)
1916 return r;
1917 r = install_info_may_process(i, &paths);
1918 if (r < 0)
1919 return r;
1920
1921 old_path = skip_root(&paths, i->path);
1922 new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
1923
1924 return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes);
1925}
1926
1927int unit_file_get_default(
1928 UnitFileScope scope,
1929 const char *root_dir,
1930 char **name) {
1931
1932 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1933 _cleanup_(install_context_done) InstallContext c = {};
1934 UnitFileInstallInfo *i;
1935 char *n;
1936 int r;
1937
1938 assert(scope >= 0);
1939 assert(scope < _UNIT_FILE_SCOPE_MAX);
1940 assert(name);
1941
1942 r = lookup_paths_init(&paths, scope, root_dir);
1943 if (r < 0)
1944 return r;
1945
1946 r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
1947 if (r < 0)
1948 return r;
1949 r = install_info_may_process(i, &paths);
1950 if (r < 0)
1951 return r;
1952
1953 n = strdup(i->name);
1954 if (!n)
1955 return -ENOMEM;
1956
1957 *name = n;
1958 return 0;
1959}
1960
1961int unit_file_lookup_state(
1962 UnitFileScope scope,
1963 const LookupPaths *paths,
1964 const char *name,
1965 UnitFileState *ret) {
1966
1967 _cleanup_(install_context_done) InstallContext c = {};
1968 UnitFileInstallInfo *i;
1969 UnitFileState state;
1970 int r;
1971
1972 assert(paths);
1973 assert(name);
1974
1975 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
1976 return -EINVAL;
1977
1978 r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
1979 if (r < 0)
1980 return r;
1981
1982 /* Shortcut things, if the caller just wants to know if this unit exists. */
1983 if (!ret)
1984 return 0;
1985
1986 switch (i->type) {
1987
1988 case UNIT_FILE_TYPE_MASKED:
1989 r = path_is_runtime(paths, i->path);
1990 if (r < 0)
1991 return r;
1992
1993 state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1994 break;
1995
1996 case UNIT_FILE_TYPE_REGULAR:
1997 r = path_is_generator(paths, i->path);
1998 if (r < 0)
1999 return r;
2000 if (r > 0) {
2001 state = UNIT_FILE_GENERATED;
2002 break;
2003 }
2004
2005 r = path_is_transient(paths, i->path);
2006 if (r < 0)
2007 return r;
2008 if (r > 0) {
2009 state = UNIT_FILE_TRANSIENT;
2010 break;
2011 }
2012
2013 r = find_symlinks_in_scope(scope, paths, i->name, &state);
2014 if (r < 0)
2015 return r;
2016 if (r == 0) {
2017 if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i))
2018 state = UNIT_FILE_DISABLED;
2019 else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i))
2020 state = UNIT_FILE_INDIRECT;
2021 else
2022 state = UNIT_FILE_STATIC;
2023 }
2024
2025 break;
2026
2027 default:
2028 assert_not_reached("Unexpect unit file type.");
2029 }
2030
2031 *ret = state;
2032 return 0;
2033}
2034
2035int unit_file_get_state(
2036 UnitFileScope scope,
2037 const char *root_dir,
2038 const char *name,
2039 UnitFileState *ret) {
2040
2041 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2042 int r;
2043
2044 assert(scope >= 0);
2045 assert(scope < _UNIT_FILE_SCOPE_MAX);
2046 assert(name);
2047
2048 r = lookup_paths_init(&paths, scope, root_dir);
2049 if (r < 0)
2050 return r;
2051
2052 return unit_file_lookup_state(scope, &paths, name, ret);
2053}
2054
2055int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
2056 _cleanup_strv_free_ char **files = NULL;
2057 char **p;
2058 int r;
2059
2060 assert(scope >= 0);
2061 assert(scope < _UNIT_FILE_SCOPE_MAX);
2062 assert(name);
2063
2064 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2065 return -EINVAL;
2066
2067 if (scope == UNIT_FILE_SYSTEM)
2068 r = conf_files_list(&files, ".preset", root_dir,
2069 "/etc/systemd/system-preset",
2070 "/usr/local/lib/systemd/system-preset",
2071 "/usr/lib/systemd/system-preset",
2072#ifdef HAVE_SPLIT_USR
2073 "/lib/systemd/system-preset",
2074#endif
2075 NULL);
2076 else if (scope == UNIT_FILE_GLOBAL)
2077 r = conf_files_list(&files, ".preset", root_dir,
2078 "/etc/systemd/user-preset",
2079 "/usr/local/lib/systemd/user-preset",
2080 "/usr/lib/systemd/user-preset",
2081 NULL);
2082 else
2083 return 1; /* Default is "enable" */
2084
2085 if (r < 0)
2086 return r;
2087
2088 STRV_FOREACH(p, files) {
2089 _cleanup_fclose_ FILE *f;
2090 char line[LINE_MAX];
2091
2092 f = fopen(*p, "re");
2093 if (!f) {
2094 if (errno == ENOENT)
2095 continue;
2096
2097 return -errno;
2098 }
2099
2100 FOREACH_LINE(line, f, return -errno) {
2101 const char *parameter;
2102 char *l;
2103
2104 l = strstrip(line);
2105
2106 if (isempty(l))
2107 continue;
2108 if (strchr(COMMENTS, *l))
2109 continue;
2110
2111 parameter = first_word(l, "enable");
2112 if (parameter) {
2113 if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
2114 log_debug("Preset file says enable %s.", name);
2115 return 1;
2116 }
2117
2118 continue;
2119 }
2120
2121 parameter = first_word(l, "disable");
2122 if (parameter) {
2123 if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) {
2124 log_debug("Preset file says disable %s.", name);
2125 return 0;
2126 }
2127
2128 continue;
2129 }
2130
2131 log_debug("Couldn't parse line '%s'", l);
2132 }
2133 }
2134
2135 /* Default is "enable" */
2136 log_debug("Preset file doesn't say anything about %s, enabling.", name);
2137 return 1;
2138}
2139
2140static int execute_preset(
2141 UnitFileScope scope,
2142 InstallContext *plus,
2143 InstallContext *minus,
2144 const LookupPaths *paths,
2145 const char *config_path,
2146 char **files,
2147 UnitFilePresetMode mode,
2148 bool force,
2149 UnitFileChange **changes,
2150 unsigned *n_changes) {
2151
2152 int r;
2153
2154 assert(plus);
2155 assert(minus);
2156 assert(paths);
2157 assert(config_path);
2158
2159 if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
2160 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2161
2162 r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path);
2163 if (r < 0)
2164 return r;
2165
2166 r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, changes, n_changes);
2167 } else
2168 r = 0;
2169
2170 if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
2171 int q;
2172
2173 /* Returns number of symlinks that where supposed to be installed. */
2174 q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
2175 if (r >= 0) {
2176 if (q < 0)
2177 r = q;
2178 else
2179 r+= q;
2180 }
2181 }
2182
2183 return r;
2184}
2185
2186static int preset_prepare_one(
2187 UnitFileScope scope,
2188 InstallContext *plus,
2189 InstallContext *minus,
2190 LookupPaths *paths,
2191 UnitFilePresetMode mode,
2192 const char *name) {
2193
2194 UnitFileInstallInfo *i;
2195 int r;
2196
2197 if (install_info_find(plus, name) ||
2198 install_info_find(minus, name))
2199 return 0;
2200
2201 r = unit_file_query_preset(scope, paths->root_dir, name);
2202 if (r < 0)
2203 return r;
2204
2205 if (r > 0) {
2206 r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
2207 if (r < 0)
2208 return r;
2209
2210 r = install_info_may_process(i, paths);
2211 if (r < 0)
2212 return r;
2213 } else
2214 r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
2215
2216 return r;
2217}
2218
2219int unit_file_preset(
2220 UnitFileScope scope,
2221 bool runtime,
2222 const char *root_dir,
2223 char **files,
2224 UnitFilePresetMode mode,
2225 bool force,
2226 UnitFileChange **changes,
2227 unsigned *n_changes) {
2228
2229 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
2230 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2231 const char *config_path;
2232 char **i;
2233 int r;
2234
2235 assert(scope >= 0);
2236 assert(scope < _UNIT_FILE_SCOPE_MAX);
2237 assert(mode < _UNIT_FILE_PRESET_MAX);
2238
2239 r = lookup_paths_init(&paths, scope, root_dir);
2240 if (r < 0)
2241 return r;
2242
2243 config_path = runtime ? paths.runtime_config : paths.persistent_config;
2244
2245 STRV_FOREACH(i, files) {
2246 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2247 return -EINVAL;
2248
2249 r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i);
2250 if (r < 0)
2251 return r;
2252 }
2253
2254 return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, force, changes, n_changes);
2255}
2256
2257int unit_file_preset_all(
2258 UnitFileScope scope,
2259 bool runtime,
2260 const char *root_dir,
2261 UnitFilePresetMode mode,
2262 bool force,
2263 UnitFileChange **changes,
2264 unsigned *n_changes) {
2265
2266 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
2267 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2268 const char *config_path = NULL;
2269 char **i;
2270 int r;
2271
2272 assert(scope >= 0);
2273 assert(scope < _UNIT_FILE_SCOPE_MAX);
2274 assert(mode < _UNIT_FILE_PRESET_MAX);
2275
2276 r = lookup_paths_init(&paths, scope, root_dir);
2277 if (r < 0)
2278 return r;
2279
2280 config_path = runtime ? paths.runtime_config : paths.persistent_config;
2281
2282 STRV_FOREACH(i, paths.search_path) {
2283 _cleanup_closedir_ DIR *d = NULL;
2284 struct dirent *de;
2285
2286 d = opendir(*i);
2287 if (!d) {
2288 if (errno == ENOENT)
2289 continue;
2290
2291 return -errno;
2292 }
2293
2294 FOREACH_DIRENT(de, d, return -errno) {
2295
2296 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
2297 continue;
2298
2299 dirent_ensure_type(d, de);
2300
2301 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
2302 continue;
2303
2304 r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name);
2305 if (r < 0)
2306 return r;
2307 }
2308 }
2309
2310 return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, force, changes, n_changes);
2311}
2312
2313static void unit_file_list_free_one(UnitFileList *f) {
2314 if (!f)
2315 return;
2316
2317 free(f->path);
2318 free(f);
2319}
2320
2321Hashmap* unit_file_list_free(Hashmap *h) {
2322 UnitFileList *i;
2323
2324 while ((i = hashmap_steal_first(h)))
2325 unit_file_list_free_one(i);
2326
2327 return hashmap_free(h);
2328}
2329
2330DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
2331
2332int unit_file_get_list(
2333 UnitFileScope scope,
2334 const char *root_dir,
2335 Hashmap *h) {
2336
2337 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2338 char **i;
2339 int r;
2340
2341 assert(scope >= 0);
2342 assert(scope < _UNIT_FILE_SCOPE_MAX);
2343 assert(h);
2344
2345 r = lookup_paths_init(&paths, scope, root_dir);
2346 if (r < 0)
2347 return r;
2348
2349 STRV_FOREACH(i, paths.search_path) {
2350 _cleanup_closedir_ DIR *d = NULL;
2351 struct dirent *de;
2352
2353 d = opendir(*i);
2354 if (!d) {
2355 if (errno == ENOENT)
2356 continue;
2357
2358 return -errno;
2359 }
2360
2361 FOREACH_DIRENT(de, d, return -errno) {
2362 _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
2363
2364 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
2365 continue;
2366
2367 if (hashmap_get(h, de->d_name))
2368 continue;
2369
2370 dirent_ensure_type(d, de);
2371
2372 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
2373 continue;
2374
2375 f = new0(UnitFileList, 1);
2376 if (!f)
2377 return -ENOMEM;
2378
2379 f->path = path_make_absolute(de->d_name, *i);
2380 if (!f->path)
2381 return -ENOMEM;
2382
2383 r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state);
2384 if (r < 0)
2385 f->state = UNIT_FILE_BAD;
2386
2387 r = hashmap_put(h, basename(f->path), f);
2388 if (r < 0)
2389 return r;
2390
2391 f = NULL; /* prevent cleanup */
2392 }
2393 }
2394
2395 return 0;
2396}
2397
2398static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
2399 [UNIT_FILE_ENABLED] = "enabled",
2400 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
2401 [UNIT_FILE_LINKED] = "linked",
2402 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
2403 [UNIT_FILE_MASKED] = "masked",
2404 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
2405 [UNIT_FILE_STATIC] = "static",
2406 [UNIT_FILE_DISABLED] = "disabled",
2407 [UNIT_FILE_INDIRECT] = "indirect",
2408 [UNIT_FILE_GENERATED] = "generated",
2409 [UNIT_FILE_TRANSIENT] = "transient",
2410 [UNIT_FILE_BAD] = "bad",
2411};
2412
2413DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
2414
2415static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
2416 [UNIT_FILE_SYMLINK] = "symlink",
2417 [UNIT_FILE_UNLINK] = "unlink",
2418};
2419
2420DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
2421
2422static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
2423 [UNIT_FILE_PRESET_FULL] = "full",
2424 [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
2425 [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
2426};
2427
2428DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);