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