]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/install.c
Standarize on one spelling of symlink error message
[thirdparty/systemd.git] / src / shared / install.c
CommitLineData
83096483
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
83096483
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty <of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
83096483 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
83096483
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <fcntl.h>
24#include <unistd.h>
25#include <string.h>
26#include <fnmatch.h>
27
28#include "util.h"
49e942b2 29#include "mkdir.h"
83096483
LP
30#include "hashmap.h"
31#include "set.h"
9eb977db 32#include "path-util.h"
83096483
LP
33#include "path-lookup.h"
34#include "strv.h"
35#include "unit-name.h"
36#include "install.h"
37#include "conf-parser.h"
2c21044f 38#include "conf-files.h"
7584d236
ZJS
39#include "specifier.h"
40#include "install-printf.h"
83096483
LP
41
42typedef struct {
43 Hashmap *will_install;
44 Hashmap *have_installed;
45} InstallContext;
46
d9e5e694
ZJS
47#define _cleanup_lookup_paths_free_ \
48 __attribute__((cleanup(lookup_paths_free)))
49#define _cleanup_install_context_done_ \
50 __attribute__((cleanup(install_context_done)))
51
83096483
LP
52static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
53 assert(paths);
54 assert(scope >= 0);
55 assert(scope < _UNIT_FILE_SCOPE_MAX);
56
57 zero(*paths);
58
59 return lookup_paths_init(paths,
67445f4e 60 scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
07719a21
LP
61 scope == UNIT_FILE_USER,
62 NULL, NULL, NULL);
83096483
LP
63}
64
65static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
66 char *p = NULL;
67 int r;
68
69 assert(scope >= 0);
70 assert(scope < _UNIT_FILE_SCOPE_MAX);
71 assert(ret);
72
73 switch (scope) {
74
75 case UNIT_FILE_SYSTEM:
76
77 if (root_dir && runtime)
d380a3bc
BN
78 asprintf(&p, "%s/run/systemd/system", root_dir);
79 else if (runtime)
83096483
LP
80 p = strdup("/run/systemd/system");
81 else if (root_dir)
82 asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH);
83 else
84 p = strdup(SYSTEM_CONFIG_UNIT_PATH);
85
86 break;
87
88 case UNIT_FILE_GLOBAL:
89
90 if (root_dir)
91 return -EINVAL;
92
93 if (runtime)
94 p = strdup("/run/systemd/user");
95 else
96 p = strdup(USER_CONFIG_UNIT_PATH);
97 break;
98
99 case UNIT_FILE_USER:
100
101 if (root_dir || runtime)
102 return -EINVAL;
103
104 r = user_config_home(&p);
105 if (r <= 0)
106 return r < 0 ? r : -ENOENT;
107
108 break;
109
110 default:
111 assert_not_reached("Bad scope");
112 }
113
114 if (!p)
115 return -ENOMEM;
116
117 *ret = p;
118 return 0;
119}
120
121static int add_file_change(
122 UnitFileChange **changes,
123 unsigned *n_changes,
124 UnitFileChangeType type,
125 const char *path,
126 const char *source) {
127
128 UnitFileChange *c;
129 unsigned i;
130
83096483
LP
131 assert(path);
132 assert(!changes == !n_changes);
133
134 if (!changes)
135 return 0;
136
137 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
138 if (!c)
139 return -ENOMEM;
140
141 *changes = c;
142 i = *n_changes;
143
144 c[i].type = type;
145 c[i].path = strdup(path);
146 if (!c[i].path)
147 return -ENOMEM;
148
149 if (source) {
150 c[i].source = strdup(source);
151 if (!c[i].source) {
152 free(c[i].path);
153 return -ENOMEM;
154 }
155 } else
156 c[i].source = NULL;
157
158 *n_changes = i+1;
159 return 0;
160}
161
162static int mark_symlink_for_removal(
163 Set **remove_symlinks_to,
164 const char *p) {
165
166 char *n;
167 int r;
168
169 assert(p);
170
171 r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func);
172 if (r < 0)
173 return r;
174
175 n = strdup(p);
176 if (!n)
177 return -ENOMEM;
178
179 path_kill_slashes(n);
180
181 r = set_put(*remove_symlinks_to, n);
182 if (r < 0) {
183 free(n);
184 return r == -EEXIST ? 0 : r;
185 }
186
187 return 0;
188}
189
190static int remove_marked_symlinks_fd(
191 Set *remove_symlinks_to,
192 int fd,
193 const char *path,
194 const char *config_path,
195 bool *deleted,
196 UnitFileChange **changes,
29283ea4
MS
197 unsigned *n_changes,
198 char** files) {
83096483
LP
199
200 int r = 0;
7fd1b19b 201 _cleanup_closedir_ DIR *d = NULL;
83096483
LP
202
203 assert(remove_symlinks_to);
204 assert(fd >= 0);
205 assert(path);
206 assert(config_path);
207 assert(deleted);
208
209 d = fdopendir(fd);
210 if (!d) {
211 close_nointr_nofail(fd);
212 return -errno;
213 }
214
215 rewinddir(d);
216
217 for (;;) {
7d5e9c0f
LP
218 struct dirent *de;
219 union dirent_storage buf;
83096483
LP
220 int k;
221
7d5e9c0f 222 k = readdir_r(d, &buf.de, &de);
83096483
LP
223 if (k != 0) {
224 r = -errno;
225 break;
226 }
227
228 if (!de)
229 break;
230
231 if (ignore_file(de->d_name))
232 continue;
233
234 dirent_ensure_type(d, de);
235
236 if (de->d_type == DT_DIR) {
237 int nfd, q;
7fd1b19b 238 _cleanup_free_ char *p = NULL;
83096483
LP
239
240 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
241 if (nfd < 0) {
242 if (errno == ENOENT)
243 continue;
244
245 if (r == 0)
246 r = -errno;
247 continue;
248 }
249
250 p = path_make_absolute(de->d_name, path);
251 if (!p) {
252 close_nointr_nofail(nfd);
d9e5e694 253 return -ENOMEM;
83096483
LP
254 }
255
256 /* This will close nfd, regardless whether it succeeds or not */
29283ea4 257 q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, files);
83096483
LP
258
259 if (r == 0)
260 r = q;
261
262 } else if (de->d_type == DT_LNK) {
7fd1b19b 263 _cleanup_free_ char *p = NULL, *dest = NULL;
83096483
LP
264 int q;
265 bool found;
266
267 p = path_make_absolute(de->d_name, path);
d9e5e694
ZJS
268 if (!p)
269 return -ENOMEM;
83096483
LP
270
271 q = readlink_and_canonicalize(p, &dest);
272 if (q < 0) {
83096483
LP
273 if (q == -ENOENT)
274 continue;
275
276 if (r == 0)
277 r = q;
278 continue;
279 }
280
281 found =
282 set_get(remove_symlinks_to, dest) ||
9eb977db 283 set_get(remove_symlinks_to, path_get_file_name(dest));
83096483 284
29283ea4
MS
285 if (unit_name_is_instance(p))
286 found = found && strv_contains(files, path_get_file_name(p));
287
83096483
LP
288 if (found) {
289
290 if (unlink(p) < 0 && errno != ENOENT) {
291
292 if (r == 0)
293 r = -errno;
294 } else {
295 rmdir_parents(p, config_path);
296 path_kill_slashes(p);
297
298 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
299
300 if (!set_get(remove_symlinks_to, p)) {
301
302 q = mark_symlink_for_removal(&remove_symlinks_to, p);
303 if (q < 0) {
304 if (r == 0)
305 r = q;
306 } else
307 *deleted = true;
308 }
309 }
310 }
83096483
LP
311 }
312 }
313
83096483
LP
314 return r;
315}
316
317static int remove_marked_symlinks(
318 Set *remove_symlinks_to,
319 const char *config_path,
320 UnitFileChange **changes,
29283ea4
MS
321 unsigned *n_changes,
322 char** files) {
83096483
LP
323
324 int fd, r = 0;
325 bool deleted;
326
327 assert(config_path);
328
329 if (set_size(remove_symlinks_to) <= 0)
330 return 0;
331
332 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
333 if (fd < 0)
334 return -errno;
335
336 do {
337 int q, cfd;
338 deleted = false;
339
340 cfd = dup(fd);
341 if (cfd < 0) {
342 r = -errno;
343 break;
344 }
345
346 /* This takes possession of cfd and closes it */
29283ea4 347 q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, files);
83096483
LP
348 if (r == 0)
349 r = q;
350 } while (deleted);
351
352 close_nointr_nofail(fd);
353
354 return r;
355}
356
357static int find_symlinks_fd(
358 const char *name,
359 int fd,
360 const char *path,
361 const char *config_path,
362 bool *same_name_link) {
363
364 int r = 0;
7fd1b19b 365 _cleanup_closedir_ DIR *d = NULL;
83096483
LP
366
367 assert(name);
368 assert(fd >= 0);
369 assert(path);
370 assert(config_path);
371 assert(same_name_link);
372
373 d = fdopendir(fd);
374 if (!d) {
375 close_nointr_nofail(fd);
376 return -errno;
377 }
378
379 for (;;) {
380 int k;
7d5e9c0f
LP
381 struct dirent *de;
382 union dirent_storage buf;
83096483 383
7d5e9c0f 384 k = readdir_r(d, &buf.de, &de);
ea55addc
ZJS
385 if (k != 0)
386 return -errno;
83096483
LP
387
388 if (!de)
ea55addc 389 return r;
83096483
LP
390
391 if (ignore_file(de->d_name))
392 continue;
393
394 dirent_ensure_type(d, de);
395
396 if (de->d_type == DT_DIR) {
397 int nfd, q;
7fd1b19b 398 _cleanup_free_ char *p = NULL;
83096483
LP
399
400 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
401 if (nfd < 0) {
402 if (errno == ENOENT)
403 continue;
404
405 if (r == 0)
406 r = -errno;
407 continue;
408 }
409
410 p = path_make_absolute(de->d_name, path);
411 if (!p) {
412 close_nointr_nofail(nfd);
ea55addc 413 return -ENOMEM;
83096483
LP
414 }
415
416 /* This will close nfd, regardless whether it succeeds or not */
417 q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
83096483 418
ea55addc
ZJS
419 if (q > 0)
420 return 1;
83096483
LP
421
422 if (r == 0)
423 r = q;
424
425 } else if (de->d_type == DT_LNK) {
7fd1b19b 426 _cleanup_free_ char *p = NULL, *dest = NULL;
83096483
LP
427 bool found_path, found_dest, b = false;
428 int q;
429
430 /* Acquire symlink name */
431 p = path_make_absolute(de->d_name, path);
ea55addc
ZJS
432 if (!p)
433 return -ENOMEM;
83096483
LP
434
435 /* Acquire symlink destination */
436 q = readlink_and_canonicalize(p, &dest);
437 if (q < 0) {
83096483
LP
438 if (q == -ENOENT)
439 continue;
440
441 if (r == 0)
442 r = q;
443 continue;
444 }
445
446 /* Check if the symlink itself matches what we
447 * are looking for */
448 if (path_is_absolute(name))
449 found_path = path_equal(p, name);
450 else
451 found_path = streq(de->d_name, name);
452
453 /* Check if what the symlink points to
454 * matches what we are looking for */
455 if (path_is_absolute(name))
456 found_dest = path_equal(dest, name);
457 else
9eb977db 458 found_dest = streq(path_get_file_name(dest), name);
83096483 459
83096483 460 if (found_path && found_dest) {
7fd1b19b 461 _cleanup_free_ char *t = NULL;
83096483
LP
462
463 /* Filter out same name links in the main
464 * config path */
465 t = path_make_absolute(name, config_path);
ea55addc
ZJS
466 if (!t)
467 return -ENOMEM;
83096483
LP
468
469 b = path_equal(t, p);
83096483
LP
470 }
471
83096483
LP
472 if (b)
473 *same_name_link = true;
ea55addc
ZJS
474 else if (found_path || found_dest)
475 return 1;
83096483
LP
476 }
477 }
478
83096483
LP
479 return r;
480}
481
482static int find_symlinks(
483 const char *name,
484 const char *config_path,
485 bool *same_name_link) {
486
487 int fd;
488
489 assert(name);
490 assert(config_path);
491 assert(same_name_link);
492
493 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
d5891fda
ZJS
494 if (fd < 0) {
495 if (errno == ENOENT)
496 return 0;
83096483 497 return -errno;
d5891fda 498 }
83096483
LP
499
500 /* This takes possession of fd and closes it */
501 return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
502}
503
504static int find_symlinks_in_scope(
505 UnitFileScope scope,
506 const char *root_dir,
507 const char *name,
508 UnitFileState *state) {
509
510 int r;
7fd1b19b 511 _cleanup_free_ char *path = NULL;
83096483
LP
512 bool same_name_link_runtime = false, same_name_link = false;
513
514 assert(scope >= 0);
515 assert(scope < _UNIT_FILE_SCOPE_MAX);
516 assert(name);
517
518 if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) {
519
520 /* First look in runtime config path */
521 r = get_config_path(scope, true, root_dir, &path);
522 if (r < 0)
523 return r;
524
525 r = find_symlinks(name, path, &same_name_link_runtime);
83096483
LP
526 if (r < 0)
527 return r;
528 else if (r > 0) {
529 *state = UNIT_FILE_ENABLED_RUNTIME;
530 return r;
531 }
532 }
533
534 /* Then look in the normal config path */
535 r = get_config_path(scope, false, root_dir, &path);
536 if (r < 0)
537 return r;
538
539 r = find_symlinks(name, path, &same_name_link);
83096483
LP
540 if (r < 0)
541 return r;
542 else if (r > 0) {
543 *state = UNIT_FILE_ENABLED;
544 return r;
545 }
546
547 /* Hmm, we didn't find it, but maybe we found the same name
548 * link? */
549 if (same_name_link_runtime) {
550 *state = UNIT_FILE_LINKED_RUNTIME;
551 return 1;
552 } else if (same_name_link) {
553 *state = UNIT_FILE_LINKED;
554 return 1;
555 }
556
557 return 0;
558}
559
560int unit_file_mask(
561 UnitFileScope scope,
562 bool runtime,
563 const char *root_dir,
564 char *files[],
565 bool force,
566 UnitFileChange **changes,
567 unsigned *n_changes) {
568
d8831ed5 569 char **i;
7fd1b19b 570 _cleanup_free_ char *prefix;
83096483
LP
571 int r;
572
573 assert(scope >= 0);
574 assert(scope < _UNIT_FILE_SCOPE_MAX);
575
576 r = get_config_path(scope, runtime, root_dir, &prefix);
577 if (r < 0)
578 return r;
579
580 STRV_FOREACH(i, files) {
7fd1b19b 581 _cleanup_free_ char *path = NULL;
83096483 582
5f739699 583 if (!unit_name_is_valid(*i, true)) {
83096483
LP
584 if (r == 0)
585 r = -EINVAL;
586 continue;
587 }
588
589 path = path_make_absolute(*i, prefix);
590 if (!path) {
591 r = -ENOMEM;
592 break;
593 }
594
595 if (symlink("/dev/null", path) >= 0) {
596 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
597
83096483
LP
598 continue;
599 }
600
601 if (errno == EEXIST) {
602
d8831ed5 603 if (null_or_empty_path(path) > 0)
83096483 604 continue;
83096483
LP
605
606 if (force) {
607 unlink(path);
608
609 if (symlink("/dev/null", path) >= 0) {
610
611 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
612 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
613
83096483
LP
614 continue;
615 }
616 }
617
618 if (r == 0)
619 r = -EEXIST;
620 } else {
621 if (r == 0)
622 r = -errno;
623 }
83096483
LP
624 }
625
83096483
LP
626 return r;
627}
628
629int unit_file_unmask(
630 UnitFileScope scope,
631 bool runtime,
632 const char *root_dir,
633 char *files[],
634 UnitFileChange **changes,
635 unsigned *n_changes) {
636
637 char **i, *config_path = NULL;
638 int r, q;
639 Set *remove_symlinks_to = NULL;
640
641 assert(scope >= 0);
642 assert(scope < _UNIT_FILE_SCOPE_MAX);
643
644 r = get_config_path(scope, runtime, root_dir, &config_path);
645 if (r < 0)
646 goto finish;
647
648 STRV_FOREACH(i, files) {
649 char *path;
650
5f739699 651 if (!unit_name_is_valid(*i, true)) {
83096483
LP
652 if (r == 0)
653 r = -EINVAL;
654 continue;
655 }
656
657 path = path_make_absolute(*i, config_path);
658 if (!path) {
659 r = -ENOMEM;
660 break;
661 }
662
663 q = null_or_empty_path(path);
664 if (q > 0) {
665 if (unlink(path) >= 0) {
666 mark_symlink_for_removal(&remove_symlinks_to, path);
667 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
668
669 free(path);
670 continue;
671 }
672
673 q = -errno;
674 }
675
676 if (q != -ENOENT && r == 0)
677 r = q;
678
679 free(path);
680 }
681
682
683finish:
29283ea4 684 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
83096483
LP
685 if (r == 0)
686 r = q;
687
688 set_free_free(remove_symlinks_to);
689 free(config_path);
690
691 return r;
692}
693
694int unit_file_link(
695 UnitFileScope scope,
696 bool runtime,
697 const char *root_dir,
698 char *files[],
699 bool force,
700 UnitFileChange **changes,
701 unsigned *n_changes) {
702
7fd1b19b 703 _cleanup_lookup_paths_free_ LookupPaths paths = {};
d9e5e694 704 char **i;
7fd1b19b 705 _cleanup_free_ char *config_path = NULL;
83096483
LP
706 int r, q;
707
708 assert(scope >= 0);
709 assert(scope < _UNIT_FILE_SCOPE_MAX);
710
83096483
LP
711 r = lookup_paths_init_from_scope(&paths, scope);
712 if (r < 0)
713 return r;
714
715 r = get_config_path(scope, runtime, root_dir, &config_path);
716 if (r < 0)
d9e5e694 717 return r;
83096483
LP
718
719 STRV_FOREACH(i, files) {
7fd1b19b 720 _cleanup_free_ char *path = NULL;
d9e5e694 721 char *fn;
83096483
LP
722 struct stat st;
723
9eb977db 724 fn = path_get_file_name(*i);
83096483
LP
725
726 if (!path_is_absolute(*i) ||
5f739699 727 !unit_name_is_valid(fn, true)) {
83096483
LP
728 if (r == 0)
729 r = -EINVAL;
730 continue;
731 }
732
733 if (lstat(*i, &st) < 0) {
734 if (r == 0)
735 r = -errno;
736 continue;
737 }
738
739 if (!S_ISREG(st.st_mode)) {
740 r = -ENOENT;
741 continue;
742 }
743
744 q = in_search_path(*i, paths.unit_path);
d9e5e694
ZJS
745 if (q < 0)
746 return q;
83096483
LP
747
748 if (q > 0)
749 continue;
750
751 path = path_make_absolute(fn, config_path);
d9e5e694
ZJS
752 if (!path)
753 return -ENOMEM;
83096483
LP
754
755 if (symlink(*i, path) >= 0) {
756 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
83096483
LP
757 continue;
758 }
759
760 if (errno == EEXIST) {
7fd1b19b 761 _cleanup_free_ char *dest = NULL;
83096483
LP
762
763 q = readlink_and_make_absolute(path, &dest);
764
765 if (q < 0 && errno != ENOENT) {
83096483
LP
766 if (r == 0)
767 r = q;
83096483
LP
768 continue;
769 }
770
d9e5e694 771 if (q >= 0 && path_equal(dest, *i))
83096483 772 continue;
83096483
LP
773
774 if (force) {
775 unlink(path);
776
777 if (symlink(*i, path) >= 0) {
778
779 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
780 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
781
83096483
LP
782 continue;
783 }
784 }
785
786 if (r == 0)
787 r = -EEXIST;
788 } else {
789 if (r == 0)
790 r = -errno;
791 }
83096483
LP
792 }
793
83096483
LP
794 return r;
795}
796
797void unit_file_list_free(Hashmap *h) {
798 UnitFileList *i;
799
800 while ((i = hashmap_steal_first(h))) {
801 free(i->path);
802 free(i);
803 }
804
805 hashmap_free(h);
806}
807
808void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
809 unsigned i;
810
811 assert(changes || n_changes == 0);
812
813 if (!changes)
814 return;
815
816 for (i = 0; i < n_changes; i++) {
817 free(changes[i].path);
818 free(changes[i].source);
819 }
820
821 free(changes);
822}
823
824static void install_info_free(InstallInfo *i) {
825 assert(i);
826
827 free(i->name);
828 free(i->path);
829 strv_free(i->aliases);
830 strv_free(i->wanted_by);
78d54bd4 831 strv_free(i->required_by);
83096483
LP
832 free(i);
833}
834
835static void install_info_hashmap_free(Hashmap *m) {
836 InstallInfo *i;
837
838 if (!m)
839 return;
840
841 while ((i = hashmap_steal_first(m)))
842 install_info_free(i);
843
844 hashmap_free(m);
845}
846
847static void install_context_done(InstallContext *c) {
848 assert(c);
849
850 install_info_hashmap_free(c->will_install);
851 install_info_hashmap_free(c->have_installed);
852
853 c->will_install = c->have_installed = NULL;
854}
855
856static int install_info_add(
857 InstallContext *c,
858 const char *name,
859 const char *path) {
860 InstallInfo *i = NULL;
861 int r;
862
863 assert(c);
864 assert(name || path);
865
866 if (!name)
9eb977db 867 name = path_get_file_name(path);
83096483 868
5f739699 869 if (!unit_name_is_valid(name, true))
83096483
LP
870 return -EINVAL;
871
872 if (hashmap_get(c->have_installed, name) ||
873 hashmap_get(c->will_install, name))
874 return 0;
875
876 r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func);
877 if (r < 0)
878 return r;
879
880 i = new0(InstallInfo, 1);
881 if (!i)
882 return -ENOMEM;
883
884 i->name = strdup(name);
885 if (!i->name) {
886 r = -ENOMEM;
887 goto fail;
888 }
889
890 if (path) {
891 i->path = strdup(path);
892 if (!i->path) {
893 r = -ENOMEM;
894 goto fail;
895 }
896 }
897
898 r = hashmap_put(c->will_install, i->name, i);
899 if (r < 0)
900 goto fail;
901
902 return 0;
903
904fail:
905 if (i)
906 install_info_free(i);
907
908 return r;
909}
910
911static int install_info_add_auto(
912 InstallContext *c,
913 const char *name_or_path) {
914
915 assert(c);
916 assert(name_or_path);
917
918 if (path_is_absolute(name_or_path))
919 return install_info_add(c, NULL, name_or_path);
920 else
921 return install_info_add(c, name_or_path, NULL);
922}
923
e8e581bf
ZJS
924static int config_parse_also(const char *unit,
925 const char *filename,
926 unsigned line,
927 const char *section,
928 const char *lvalue,
929 int ltype,
930 const char *rvalue,
931 void *data,
932 void *userdata) {
83096483
LP
933
934 char *w;
935 size_t l;
936 char *state;
937 InstallContext *c = data;
938
939 assert(filename);
940 assert(lvalue);
941 assert(rvalue);
942
943 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7fd1b19b 944 _cleanup_free_ char *n;
83096483
LP
945 int r;
946
947 n = strndup(w, l);
948 if (!n)
949 return -ENOMEM;
950
951 r = install_info_add(c, n, NULL);
d9e5e694 952 if (r < 0)
83096483 953 return r;
83096483
LP
954 }
955
956 return 0;
957}
958
e8e581bf
ZJS
959static int config_parse_user(const char *unit,
960 const char *filename,
961 unsigned line,
962 const char *section,
963 const char *lvalue,
964 int ltype,
965 const char *rvalue,
966 void *data,
967 void *userdata) {
3f0b2f0f
ZJS
968
969 InstallInfo *i = data;
970 char* printed;
971
972 assert(filename);
973 assert(lvalue);
974 assert(rvalue);
975
976 printed = install_full_printf(i, rvalue);
977 if (!printed)
978 return -ENOMEM;
979
980 free(i->user);
981 i->user = printed;
982
983 return 0;
984}
985
83096483
LP
986static int unit_file_load(
987 InstallContext *c,
988 InstallInfo *info,
989 const char *path,
990 bool allow_symlink) {
991
f975e971 992 const ConfigTableItem items[] = {
78d54bd4
LP
993 { "Install", "Alias", config_parse_strv, 0, &info->aliases },
994 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
995 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
996 { "Install", "Also", config_parse_also, 0, c },
3f0b2f0f 997 { "Exec", "User", config_parse_user, 0, info },
f975e971 998 { NULL, NULL, NULL, 0, NULL }
83096483
LP
999 };
1000
1001 int fd;
7fd1b19b 1002 _cleanup_fclose_ FILE *f = NULL;
83096483
LP
1003 int r;
1004
1005 assert(c);
1006 assert(info);
1007 assert(path);
1008
1009 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
1010 if (fd < 0)
1011 return -errno;
1012
1013 f = fdopen(fd, "re");
1014 if (!f) {
1015 close_nointr_nofail(fd);
1016 return -ENOMEM;
1017 }
1018
e8e581bf
ZJS
1019 r = config_parse(NULL, path, f, NULL,
1020 config_item_table_lookup, (void*) items, true, info);
83096483
LP
1021 if (r < 0)
1022 return r;
1023
78d54bd4
LP
1024 return
1025 strv_length(info->aliases) +
1026 strv_length(info->wanted_by) +
1027 strv_length(info->required_by);
83096483
LP
1028}
1029
1030static int unit_file_search(
1031 InstallContext *c,
1032 InstallInfo *info,
1033 LookupPaths *paths,
1034 const char *root_dir,
1035 bool allow_symlink) {
1036
1037 char **p;
1038 int r;
1039
1040 assert(c);
1041 assert(info);
1042 assert(paths);
1043
1044 if (info->path)
1045 return unit_file_load(c, info, info->path, allow_symlink);
1046
1047 assert(info->name);
1048
1049 STRV_FOREACH(p, paths->unit_path) {
1050 char *path = NULL;
1051
1052 if (isempty(root_dir))
1053 asprintf(&path, "%s/%s", *p, info->name);
1054 else
1055 asprintf(&path, "%s/%s/%s", root_dir, *p, info->name);
1056
1057 if (!path)
1058 return -ENOMEM;
1059
1060 r = unit_file_load(c, info, path, allow_symlink);
1061
1062 if (r >= 0)
1063 info->path = path;
29283ea4
MS
1064 else {
1065 if (r == -ENOENT && unit_name_is_instance(info->name)) {
1066 /* unit file doesn't exist, however instance enablement was request */
1067 /* we will check if it is possible to load template unit file */
1068 char *template = NULL,
1069 *template_path = NULL,
1070 *template_dir = NULL;
1071
1072 template = unit_name_template(info->name);
1073 if (!template) {
1074 free(path);
1075 return -ENOMEM;
1076 }
1077
1078 /* we will reuse path variable since we don't need it anymore */
1079 template_dir = path;
1080 *(strrchr(path, '/') + 1) = '\0';
1081
1082 template_path = strjoin(template_dir, template, NULL);
1083 if (!template_path) {
1084 free(path);
1085 free(template);
1086 return -ENOMEM;
1087 }
1088
1089 /* let's try to load template unit */
1090 r = unit_file_load(c, info, template_path, allow_symlink);
1091 if (r >= 0) {
1092 info->path = strdup(template_path);
1093 if (!info->path) {
1094 free(path);
1095 free(template);
1096 free(template_path);
1097 return -ENOMEM;
1098 }
1099 }
1100
1101 free(template);
1102 free(template_path);
1103 }
83096483 1104 free(path);
29283ea4 1105 }
83096483
LP
1106
1107 if (r != -ENOENT && r != -ELOOP)
1108 return r;
1109 }
1110
1111 return -ENOENT;
1112}
1113
1114static int unit_file_can_install(
1115 LookupPaths *paths,
1116 const char *root_dir,
1117 const char *name,
1118 bool allow_symlink) {
1119
7fd1b19b 1120 _cleanup_install_context_done_ InstallContext c = {};
83096483
LP
1121 InstallInfo *i;
1122 int r;
1123
1124 assert(paths);
1125 assert(name);
1126
83096483
LP
1127 r = install_info_add_auto(&c, name);
1128 if (r < 0)
1129 return r;
1130
1131 assert_se(i = hashmap_first(c.will_install));
1132
1133 r = unit_file_search(&c, i, paths, root_dir, allow_symlink);
1134
1135 if (r >= 0)
78d54bd4
LP
1136 r =
1137 strv_length(i->aliases) +
1138 strv_length(i->wanted_by) +
1139 strv_length(i->required_by);
83096483 1140
83096483
LP
1141 return r;
1142}
1143
1144static int create_symlink(
1145 const char *old_path,
1146 const char *new_path,
1147 bool force,
1148 UnitFileChange **changes,
1149 unsigned *n_changes) {
1150
7fd1b19b 1151 _cleanup_free_ char *dest = NULL;
83096483
LP
1152 int r;
1153
1154 assert(old_path);
1155 assert(new_path);
1156
d2e54fae 1157 mkdir_parents_label(new_path, 0755);
83096483
LP
1158
1159 if (symlink(old_path, new_path) >= 0) {
1160 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1161 return 0;
1162 }
1163
1164 if (errno != EEXIST)
1165 return -errno;
1166
1167 r = readlink_and_make_absolute(new_path, &dest);
1168 if (r < 0)
1169 return r;
1170
d9e5e694 1171 if (path_equal(dest, old_path))
83096483 1172 return 0;
83096483 1173
ba49b4a1 1174 if (!force)
83096483
LP
1175 return -EEXIST;
1176
1177 unlink(new_path);
1178
1179 if (symlink(old_path, new_path) >= 0) {
1180 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
1181 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1182 return 0;
1183 }
1184
1185 return -errno;
1186}
1187
1188static int install_info_symlink_alias(
1189 InstallInfo *i,
1190 const char *config_path,
1191 bool force,
1192 UnitFileChange **changes,
1193 unsigned *n_changes) {
1194
1195 char **s;
1196 int r = 0, q;
1197
1198 assert(i);
1199 assert(config_path);
1200
1201 STRV_FOREACH(s, i->aliases) {
7fd1b19b 1202 _cleanup_free_ char *alias_path = NULL, *dst = NULL;
83096483 1203
7584d236
ZJS
1204 dst = install_full_printf(i, *s);
1205 if (!dst)
1206 return -ENOMEM;
83096483 1207
7584d236 1208 alias_path = path_make_absolute(dst, config_path);
83096483
LP
1209 if (!alias_path)
1210 return -ENOMEM;
1211
1212 q = create_symlink(i->path, alias_path, force, changes, n_changes);
83096483
LP
1213 if (r == 0)
1214 r = q;
1215 }
1216
1217 return r;
1218}
1219
1220static int install_info_symlink_wants(
1221 InstallInfo *i,
1222 const char *config_path,
1223 bool force,
1224 UnitFileChange **changes,
1225 unsigned *n_changes) {
1226
1227 char **s;
1228 int r = 0, q;
1229
1230 assert(i);
1231 assert(config_path);
1232
1233 STRV_FOREACH(s, i->wanted_by) {
7fd1b19b 1234 _cleanup_free_ char *path = NULL, *dst = NULL;
83096483 1235
7584d236
ZJS
1236 dst = install_full_printf(i, *s);
1237 if (!dst)
1238 return -ENOMEM;
1239
1240 if (!unit_name_is_valid(dst, true)) {
83096483
LP
1241 r = -EINVAL;
1242 continue;
1243 }
1244
7584d236 1245 if (asprintf(&path, "%s/%s.wants/%s", config_path, dst, i->name) < 0)
83096483
LP
1246 return -ENOMEM;
1247
1248 q = create_symlink(i->path, path, force, changes, n_changes);
83096483
LP
1249
1250 if (r == 0)
1251 r = q;
1252 }
1253
1254 return r;
1255}
1256
78d54bd4
LP
1257static int install_info_symlink_requires(
1258 InstallInfo *i,
1259 const char *config_path,
1260 bool force,
1261 UnitFileChange **changes,
1262 unsigned *n_changes) {
1263
1264 char **s;
1265 int r = 0, q;
1266
1267 assert(i);
1268 assert(config_path);
1269
1270 STRV_FOREACH(s, i->required_by) {
7fd1b19b 1271 _cleanup_free_ char *path = NULL, *dst = NULL;
78d54bd4 1272
7584d236
ZJS
1273 dst = install_full_printf(i, *s);
1274 if (!dst)
1275 return -ENOMEM;
1276
1277 if (!unit_name_is_valid(dst, true)) {
78d54bd4
LP
1278 r = -EINVAL;
1279 continue;
1280 }
1281
7584d236 1282 if (asprintf(&path, "%s/%s.requires/%s", config_path, dst, i->name) < 0)
78d54bd4
LP
1283 return -ENOMEM;
1284
1285 q = create_symlink(i->path, path, force, changes, n_changes);
78d54bd4
LP
1286
1287 if (r == 0)
1288 r = q;
1289 }
1290
1291 return r;
1292}
1293
83096483
LP
1294static int install_info_symlink_link(
1295 InstallInfo *i,
1296 LookupPaths *paths,
1297 const char *config_path,
1298 bool force,
1299 UnitFileChange **changes,
1300 unsigned *n_changes) {
1301
1302 int r;
7fd1b19b 1303 _cleanup_free_ char *path = NULL;
83096483
LP
1304
1305 assert(i);
1306 assert(paths);
1307 assert(config_path);
1308 assert(i->path);
1309
1310 r = in_search_path(i->path, paths->unit_path);
1311 if (r != 0)
1312 return r;
1313
1314 if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
1315 return -ENOMEM;
1316
1317 r = create_symlink(i->path, path, force, changes, n_changes);
83096483
LP
1318 return r;
1319}
1320
1321static int install_info_apply(
1322 InstallInfo *i,
1323 LookupPaths *paths,
1324 const char *config_path,
1325 bool force,
1326 UnitFileChange **changes,
1327 unsigned *n_changes) {
1328
1329 int r, q;
1330
1331 assert(i);
1332 assert(paths);
1333 assert(config_path);
1334
1335 r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
1336
1337 q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
1338 if (r == 0)
1339 r = q;
1340
78d54bd4
LP
1341 q = install_info_symlink_requires(i, config_path, force, changes, n_changes);
1342 if (r == 0)
1343 r = q;
1344
83096483
LP
1345 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1346 if (r == 0)
1347 r = q;
1348
1349 return r;
1350}
1351
1352static int install_context_apply(
1353 InstallContext *c,
1354 LookupPaths *paths,
1355 const char *config_path,
1356 const char *root_dir,
1357 bool force,
1358 UnitFileChange **changes,
1359 unsigned *n_changes) {
1360
1361 InstallInfo *i;
1362 int r = 0, q;
1363
1364 assert(c);
1365 assert(paths);
1366 assert(config_path);
1367
1368 while ((i = hashmap_first(c->will_install))) {
1369
1370 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1371 if (q < 0)
1372 return q;
1373
1374 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1375
1376 q = unit_file_search(c, i, paths, root_dir, false);
1377 if (q < 0) {
1378 if (r >= 0)
1379 r = q;
1380
1381 return r;
1382 } else if (r >= 0)
1383 r += q;
1384
1385 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1386 if (r >= 0 && q < 0)
1387 r = q;
1388 }
1389
1390 return r;
1391}
1392
1393static int install_context_mark_for_removal(
1394 InstallContext *c,
1395 LookupPaths *paths,
1396 Set **remove_symlinks_to,
1397 const char *config_path,
1398 const char *root_dir) {
1399
1400 InstallInfo *i;
1401 int r = 0, q;
1402
1403 assert(c);
1404 assert(paths);
1405 assert(config_path);
1406
1407 /* Marks all items for removal */
1408
1409 while ((i = hashmap_first(c->will_install))) {
1410
1411 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1412 if (q < 0)
1413 return q;
1414
1415 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1416
1417 q = unit_file_search(c, i, paths, root_dir, false);
1418 if (q < 0) {
1419 if (r >= 0)
1420 r = q;
1421
1422 return r;
1423 } else if (r >= 0)
1424 r += q;
1425
29283ea4
MS
1426 if (unit_name_is_instance(i->name)) {
1427 char *unit_file = NULL;
1428
1429 unit_file = path_get_file_name(i->path);
1430
1431 if (unit_name_is_instance(unit_file))
1432 /* unit file named as instance exists, thus all symlinks pointing to it, will be removed */
1433 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1434 else
1435 /* does not exist, thus we will mark for removal symlinks to template unit file */
1436 q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
1437 } else
1438 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1439
83096483
LP
1440 if (r >= 0 && q < 0)
1441 r = q;
1442 }
1443
1444 return r;
1445}
1446
1447int unit_file_enable(
1448 UnitFileScope scope,
1449 bool runtime,
1450 const char *root_dir,
1451 char *files[],
1452 bool force,
1453 UnitFileChange **changes,
1454 unsigned *n_changes) {
1455
7fd1b19b
HH
1456 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1457 _cleanup_install_context_done_ InstallContext c = {};
d9e5e694 1458 char **i;
7fd1b19b 1459 _cleanup_free_ char *config_path = NULL;
83096483
LP
1460 int r;
1461
1462 assert(scope >= 0);
1463 assert(scope < _UNIT_FILE_SCOPE_MAX);
1464
83096483
LP
1465 r = lookup_paths_init_from_scope(&paths, scope);
1466 if (r < 0)
1467 return r;
1468
1469 r = get_config_path(scope, runtime, root_dir, &config_path);
1470 if (r < 0)
d9e5e694 1471 return r;
83096483
LP
1472
1473 STRV_FOREACH(i, files) {
1474 r = install_info_add_auto(&c, *i);
1475 if (r < 0)
d9e5e694 1476 return r;
83096483
LP
1477 }
1478
729e3769
LP
1479 /* This will return the number of symlink rules that were
1480 supposed to be created, not the ones actually created. This is
20f59e42 1481 useful to determine whether the passed files had any
729e3769 1482 installation data at all. */
83096483 1483 r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
83096483
LP
1484 return r;
1485}
1486
1487int unit_file_disable(
1488 UnitFileScope scope,
1489 bool runtime,
1490 const char *root_dir,
1491 char *files[],
1492 UnitFileChange **changes,
1493 unsigned *n_changes) {
1494
7fd1b19b
HH
1495 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1496 _cleanup_install_context_done_ InstallContext c = {};
d9e5e694 1497 char **i;
7fd1b19b
HH
1498 _cleanup_free_ char *config_path = NULL;
1499 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
83096483
LP
1500 int r, q;
1501
1502 assert(scope >= 0);
1503 assert(scope < _UNIT_FILE_SCOPE_MAX);
1504
83096483
LP
1505 r = lookup_paths_init_from_scope(&paths, scope);
1506 if (r < 0)
1507 return r;
1508
1509 r = get_config_path(scope, runtime, root_dir, &config_path);
1510 if (r < 0)
d9e5e694 1511 return r;
83096483
LP
1512
1513 STRV_FOREACH(i, files) {
1514 r = install_info_add_auto(&c, *i);
1515 if (r < 0)
d9e5e694 1516 return r;
83096483
LP
1517 }
1518
1519 r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
1520
29283ea4 1521 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
83096483 1522 if (r == 0)
034a2a52 1523 r = q;
83096483 1524
83096483
LP
1525 return r;
1526}
1527
1528int unit_file_reenable(
1529 UnitFileScope scope,
1530 bool runtime,
1531 const char *root_dir,
1532 char *files[],
1533 bool force,
1534 UnitFileChange **changes,
1535 unsigned *n_changes) {
1536
7fd1b19b
HH
1537 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1538 _cleanup_install_context_done_ InstallContext c = {};
d9e5e694 1539 char **i;
7fd1b19b
HH
1540 _cleanup_free_ char *config_path = NULL;
1541 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
83096483
LP
1542 int r, q;
1543
1544 assert(scope >= 0);
1545 assert(scope < _UNIT_FILE_SCOPE_MAX);
1546
83096483
LP
1547 r = lookup_paths_init_from_scope(&paths, scope);
1548 if (r < 0)
1549 return r;
1550
1551 r = get_config_path(scope, runtime, root_dir, &config_path);
1552 if (r < 0)
d9e5e694 1553 return r;
83096483
LP
1554
1555 STRV_FOREACH(i, files) {
1556 r = mark_symlink_for_removal(&remove_symlinks_to, *i);
1557 if (r < 0)
d9e5e694 1558 return r;
83096483
LP
1559
1560 r = install_info_add_auto(&c, *i);
1561 if (r < 0)
d9e5e694 1562 return r;
83096483
LP
1563 }
1564
29283ea4 1565 r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
83096483 1566
729e3769 1567 /* Returns number of symlinks that where supposed to be installed. */
83096483
LP
1568 q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1569 if (r == 0)
1570 r = q;
1571
83096483
LP
1572 return r;
1573}
1574
1575UnitFileState unit_file_get_state(
1576 UnitFileScope scope,
1577 const char *root_dir,
1578 const char *name) {
1579
7fd1b19b 1580 _cleanup_lookup_paths_free_ LookupPaths paths = {};
83096483 1581 UnitFileState state = _UNIT_FILE_STATE_INVALID;
d9e5e694 1582 char **i;
7fd1b19b 1583 _cleanup_free_ char *path = NULL;
83096483
LP
1584 int r;
1585
1586 assert(scope >= 0);
1587 assert(scope < _UNIT_FILE_SCOPE_MAX);
1588 assert(name);
1589
83096483
LP
1590 if (root_dir && scope != UNIT_FILE_SYSTEM)
1591 return -EINVAL;
1592
5f739699 1593 if (!unit_name_is_valid(name, true))
83096483
LP
1594 return -EINVAL;
1595
1596 r = lookup_paths_init_from_scope(&paths, scope);
1597 if (r < 0)
1598 return r;
1599
1600 STRV_FOREACH(i, paths.unit_path) {
1601 struct stat st;
1602
1603 free(path);
1604 path = NULL;
1605
1606 if (root_dir)
1607 asprintf(&path, "%s/%s/%s", root_dir, *i, name);
1608 else
1609 asprintf(&path, "%s/%s", *i, name);
1610
d9e5e694
ZJS
1611 if (!path)
1612 return -ENOMEM;
83096483
LP
1613
1614 if (lstat(path, &st) < 0) {
81006b8a 1615 r = -errno;
83096483
LP
1616 if (errno == ENOENT)
1617 continue;
1618
d9e5e694 1619 return -errno;
83096483
LP
1620 }
1621
d9e5e694
ZJS
1622 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
1623 return -ENOENT;
83096483
LP
1624
1625 r = null_or_empty_path(path);
1626 if (r < 0 && r != -ENOENT)
d9e5e694 1627 return r;
83096483
LP
1628 else if (r > 0) {
1629 state = path_startswith(*i, "/run") ?
1630 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
d9e5e694 1631 return state;
83096483
LP
1632 }
1633
1634 r = find_symlinks_in_scope(scope, root_dir, name, &state);
d9e5e694
ZJS
1635 if (r < 0)
1636 return r;
1637 else if (r > 0)
1638 return state;
83096483
LP
1639
1640 r = unit_file_can_install(&paths, root_dir, path, true);
bcb161b0 1641 if (r < 0 && errno != ENOENT)
d9e5e694
ZJS
1642 return r;
1643 else if (r > 0)
1644 return UNIT_FILE_DISABLED;
1645 else if (r == 0)
1646 return UNIT_FILE_STATIC;
83096483
LP
1647 }
1648
83096483
LP
1649 return r < 0 ? r : state;
1650}
1651
1652int unit_file_query_preset(UnitFileScope scope, const char *name) {
7fd1b19b 1653 _cleanup_strv_free_ char **files = NULL;
d9e5e694 1654 char **i;
83096483
LP
1655 int r;
1656
1657 assert(scope >= 0);
1658 assert(scope < _UNIT_FILE_SCOPE_MAX);
1659 assert(name);
1660
1661 if (scope == UNIT_FILE_SYSTEM)
7850b3b8 1662 r = conf_files_list(&files, ".preset", NULL,
a7480dba
LP
1663 "/etc/systemd/system-preset",
1664 "/usr/local/lib/systemd/system-preset",
1665 "/usr/lib/systemd/system-preset",
b4bdfefa 1666#ifdef HAVE_SPLIT_USR
a7480dba 1667 "/lib/systemd/system-preset",
b4bdfefa 1668#endif
83096483
LP
1669 NULL);
1670 else if (scope == UNIT_FILE_GLOBAL)
7850b3b8 1671 r = conf_files_list(&files, ".preset", NULL,
a7480dba
LP
1672 "/etc/systemd/user-preset",
1673 "/usr/local/lib/systemd/user-preset",
1674 "/usr/lib/systemd/user-preset",
83096483
LP
1675 NULL);
1676 else
1677 return 1;
1678
1679 if (r < 0)
1680 return r;
1681
1682 STRV_FOREACH(i, files) {
7fd1b19b 1683 _cleanup_fclose_ FILE *f;
83096483
LP
1684
1685 f = fopen(*i, "re");
1686 if (!f) {
1687 if (errno == ENOENT)
1688 continue;
1689
d9e5e694 1690 return -errno;
83096483
LP
1691 }
1692
1693 for (;;) {
1694 char line[LINE_MAX], *l;
1695
1696 if (!fgets(line, sizeof(line), f))
1697 break;
1698
1699 l = strstrip(line);
1700 if (!*l)
1701 continue;
1702
d3b6d0c2 1703 if (strchr(COMMENTS "\n", *l))
83096483
LP
1704 continue;
1705
1706 if (first_word(l, "enable")) {
1707 l += 6;
1708 l += strspn(l, WHITESPACE);
1709
d9e5e694
ZJS
1710 if (fnmatch(l, name, FNM_NOESCAPE) == 0)
1711 return 1;
1712
83096483
LP
1713 } else if (first_word(l, "disable")) {
1714 l += 7;
1715 l += strspn(l, WHITESPACE);
1716
d9e5e694
ZJS
1717 if (fnmatch(l, name, FNM_NOESCAPE) == 0)
1718 return 0;
1719
83096483
LP
1720 } else
1721 log_debug("Couldn't parse line '%s'", l);
1722 }
83096483
LP
1723 }
1724
1725 /* Default is "enable" */
d9e5e694 1726 return 1;
83096483
LP
1727}
1728
1729int unit_file_preset(
1730 UnitFileScope scope,
1731 bool runtime,
1732 const char *root_dir,
1733 char *files[],
1734 bool force,
1735 UnitFileChange **changes,
1736 unsigned *n_changes) {
1737
7fd1b19b
HH
1738 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1739 _cleanup_install_context_done_ InstallContext plus = {}, minus = {};
d9e5e694 1740 char **i;
7fd1b19b
HH
1741 _cleanup_free_ char *config_path = NULL;
1742 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
83096483
LP
1743 int r, q;
1744
1745 assert(scope >= 0);
1746 assert(scope < _UNIT_FILE_SCOPE_MAX);
1747
83096483
LP
1748 r = lookup_paths_init_from_scope(&paths, scope);
1749 if (r < 0)
1750 return r;
1751
1752 r = get_config_path(scope, runtime, root_dir, &config_path);
1753 if (r < 0)
d9e5e694 1754 return r;
83096483
LP
1755
1756 STRV_FOREACH(i, files) {
1757
d9e5e694
ZJS
1758 if (!unit_name_is_valid(*i, true))
1759 return -EINVAL;
83096483
LP
1760
1761 r = unit_file_query_preset(scope, *i);
1762 if (r < 0)
d9e5e694 1763 return r;
83096483
LP
1764
1765 if (r)
1766 r = install_info_add_auto(&plus, *i);
1767 else
1768 r = install_info_add_auto(&minus, *i);
1769
1770 if (r < 0)
d9e5e694 1771 return r;
83096483
LP
1772 }
1773
d9e5e694
ZJS
1774 r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to,
1775 config_path, root_dir);
83096483 1776
d9e5e694
ZJS
1777 q = remove_marked_symlinks(remove_symlinks_to, config_path,
1778 changes, n_changes, files);
83096483
LP
1779 if (r == 0)
1780 r = q;
1781
729e3769 1782 /* Returns number of symlinks that where supposed to be installed. */
d9e5e694
ZJS
1783 q = install_context_apply(&plus, &paths, config_path, root_dir, force,
1784 changes, n_changes);
83096483
LP
1785 if (r == 0)
1786 r = q;
1787
83096483
LP
1788 return r;
1789}
1790
d9e5e694
ZJS
1791static void unitfilelist_free(UnitFileList **f) {
1792 if (!*f)
1793 return;
1794
1795 free((*f)->path);
1796 free(*f);
1797}
1798
83096483
LP
1799int unit_file_get_list(
1800 UnitFileScope scope,
1801 const char *root_dir,
1802 Hashmap *h) {
1803
7fd1b19b 1804 _cleanup_lookup_paths_free_ LookupPaths paths = {};
d9e5e694 1805 char **i;
7fd1b19b
HH
1806 _cleanup_free_ char *buf = NULL;
1807 _cleanup_closedir_ DIR *d = NULL;
83096483
LP
1808 int r;
1809
1810 assert(scope >= 0);
1811 assert(scope < _UNIT_FILE_SCOPE_MAX);
1812 assert(h);
1813
83096483
LP
1814 if (root_dir && scope != UNIT_FILE_SYSTEM)
1815 return -EINVAL;
1816
1817 r = lookup_paths_init_from_scope(&paths, scope);
1818 if (r < 0)
1819 return r;
1820
1821 STRV_FOREACH(i, paths.unit_path) {
83096483
LP
1822 const char *units_dir;
1823
1824 free(buf);
1825 buf = NULL;
1826
1827 if (root_dir) {
d9e5e694
ZJS
1828 if (asprintf(&buf, "%s/%s", root_dir, *i) < 0)
1829 return -ENOMEM;
1830
83096483
LP
1831 units_dir = buf;
1832 } else
1833 units_dir = *i;
1834
1835 if (d)
1836 closedir(d);
1837
1838 d = opendir(units_dir);
1839 if (!d) {
1840 if (errno == ENOENT)
1841 continue;
1842
d9e5e694 1843 return -errno;
83096483
LP
1844 }
1845
1846 for (;;) {
7d5e9c0f
LP
1847 struct dirent *de;
1848 union dirent_storage buffer;
d9e5e694
ZJS
1849 UnitFileList __attribute__((cleanup(unitfilelist_free)))
1850 *f = NULL;
83096483 1851
7d5e9c0f 1852 r = readdir_r(d, &buffer.de, &de);
d9e5e694
ZJS
1853 if (r != 0)
1854 return -r;
83096483
LP
1855
1856 if (!de)
1857 break;
1858
1859 if (ignore_file(de->d_name))
1860 continue;
1861
5f739699 1862 if (!unit_name_is_valid(de->d_name, true))
83096483
LP
1863 continue;
1864
1865 if (hashmap_get(h, de->d_name))
1866 continue;
1867
1868 r = dirent_ensure_type(d, de);
1869 if (r < 0) {
fb5ef067 1870 if (r == -ENOENT)
83096483
LP
1871 continue;
1872
d9e5e694 1873 return r;
83096483
LP
1874 }
1875
1876 if (de->d_type != DT_LNK && de->d_type != DT_REG)
1877 continue;
1878
1879 f = new0(UnitFileList, 1);
d9e5e694
ZJS
1880 if (!f)
1881 return -ENOMEM;
83096483
LP
1882
1883 f->path = path_make_absolute(de->d_name, units_dir);
d9e5e694
ZJS
1884 if (!f->path)
1885 return -ENOMEM;
83096483
LP
1886
1887 r = null_or_empty_path(f->path);
d9e5e694
ZJS
1888 if (r < 0 && r != -ENOENT)
1889 return r;
1890 else if (r > 0) {
83096483
LP
1891 f->state =
1892 path_startswith(*i, "/run") ?
1893 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1894 goto found;
1895 }
1896
1897 r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
d9e5e694
ZJS
1898 if (r < 0)
1899 return r;
1900 else if (r > 0) {
b5b46d59 1901 f->state = UNIT_FILE_ENABLED;
83096483 1902 goto found;
b5b46d59 1903 }
83096483
LP
1904
1905 r = unit_file_can_install(&paths, root_dir, f->path, true);
b5b46d59
LP
1906 if (r == -EINVAL || /* Invalid setting? */
1907 r == -EBADMSG || /* Invalid format? */
1908 r == -ENOENT /* Included file not found? */)
1909 f->state = UNIT_FILE_INVALID;
d9e5e694
ZJS
1910 else if (r < 0)
1911 return r;
1912 else if (r > 0)
83096483 1913 f->state = UNIT_FILE_DISABLED;
b5b46d59 1914 else
83096483 1915 f->state = UNIT_FILE_STATIC;
83096483
LP
1916
1917 found:
9eb977db 1918 r = hashmap_put(h, path_get_file_name(f->path), f);
d9e5e694
ZJS
1919 if (r < 0)
1920 return r;
1921 f = NULL; /* prevent cleanup */
83096483
LP
1922 }
1923 }
1924
83096483
LP
1925 return r;
1926}
1927
1928static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
1929 [UNIT_FILE_ENABLED] = "enabled",
771faa9a 1930 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
83096483
LP
1931 [UNIT_FILE_LINKED] = "linked",
1932 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
1933 [UNIT_FILE_MASKED] = "masked",
1934 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
1935 [UNIT_FILE_STATIC] = "static",
b5b46d59
LP
1936 [UNIT_FILE_DISABLED] = "disabled",
1937 [UNIT_FILE_INVALID] = "invalid",
83096483
LP
1938};
1939
1940DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
c0576cd6
LP
1941
1942static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
1943 [UNIT_FILE_SYMLINK] = "symlink",
1944 [UNIT_FILE_UNLINK] = "unlink",
1945};
1946
1947DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);