1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "sd-id128.h"
4 
5 #include "chase-symlinks.h"
6 #include "dirent-util.h"
7 #include "fd-util.h"
8 #include "fs-util.h"
9 #include "macro.h"
10 #include "path-lookup.h"
11 #include "set.h"
12 #include "special.h"
13 #include "stat-util.h"
14 #include "string-util.h"
15 #include "strv.h"
16 #include "unit-file.h"
17 
unit_type_may_alias(UnitType type)18 bool unit_type_may_alias(UnitType type) {
19         return IN_SET(type,
20                       UNIT_SERVICE,
21                       UNIT_SOCKET,
22                       UNIT_TARGET,
23                       UNIT_DEVICE,
24                       UNIT_TIMER,
25                       UNIT_PATH);
26 }
27 
unit_type_may_template(UnitType type)28 bool unit_type_may_template(UnitType type) {
29         return IN_SET(type,
30                       UNIT_SERVICE,
31                       UNIT_SOCKET,
32                       UNIT_TARGET,
33                       UNIT_TIMER,
34                       UNIT_PATH);
35 }
36 
unit_symlink_name_compatible(const char * symlink,const char * target,bool instance_propagation)37 int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation) {
38         _cleanup_free_ char *template = NULL;
39         int r, un_type1, un_type2;
40 
41         un_type1 = unit_name_classify(symlink);
42 
43         /* The straightforward case: the symlink name matches the target and we have a valid unit */
44         if (streq(symlink, target) &&
45             (un_type1 & (UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)))
46                 return 1;
47 
48         r = unit_name_template(symlink, &template);
49         if (r == -EINVAL)
50                 return 0; /* Not a template */
51         if (r < 0)
52                 return r;
53 
54         un_type2 = unit_name_classify(target);
55 
56         /* An instance name points to a target that is just the template name */
57         if (un_type1 == UNIT_NAME_INSTANCE &&
58             un_type2 == UNIT_NAME_TEMPLATE &&
59             streq(template, target))
60                 return 1;
61 
62         /* foo@.target.requires/bar@.service: instance will be propagated */
63         if (instance_propagation &&
64             un_type1 == UNIT_NAME_TEMPLATE &&
65             un_type2 == UNIT_NAME_TEMPLATE &&
66             streq(template, target))
67                 return 1;
68 
69         return 0;
70 }
71 
unit_validate_alias_symlink_or_warn(int log_level,const char * filename,const char * target)72 int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target) {
73         const char *src, *dst;
74         _cleanup_free_ char *src_instance = NULL, *dst_instance = NULL;
75         UnitType src_unit_type, dst_unit_type;
76         UnitNameFlags src_name_type, dst_name_type;
77 
78         /* Check if the *alias* symlink is valid. This applies to symlinks like
79          * /etc/systemd/system/dbus.servicedbus-broker.service, but not to .wants or .requires symlinks
80          * and such. Neither does this apply to symlinks which *link* units, i.e. symlinks to outside of the
81          * unit lookup path.
82          *
83          * -EINVAL is returned if the something is wrong with the source filename or the source unit type is
84          *         not allowed to symlink,
85          * -EXDEV if the target filename is not a valid unit name or doesn't match the source,
86          * -ELOOP for an alias to self.
87          */
88 
89         src = basename(filename);
90         dst = basename(target);
91 
92         /* src checks */
93 
94         src_name_type = unit_name_to_instance(src, &src_instance);
95         if (src_name_type < 0)
96                 return log_full_errno(log_level, src_name_type,
97                                       "%s: not a valid unit name \"%s\": %m", filename, src);
98 
99         src_unit_type = unit_name_to_type(src);
100         assert(src_unit_type >= 0); /* unit_name_to_instance() checked the suffix already */
101 
102         if (!unit_type_may_alias(src_unit_type))
103                 return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL),
104                                       "%s: symlinks are not allowed for units of this type, rejecting.",
105                                       filename);
106 
107         if (src_name_type != UNIT_NAME_PLAIN &&
108             !unit_type_may_template(src_unit_type))
109                 return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL),
110                                       "%s: templates not allowed for %s units, rejecting.",
111                                       filename, unit_type_to_string(src_unit_type));
112 
113         /* dst checks */
114 
115         if (streq(src, dst))
116                 return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
117                                        "%s: unit self-alias: %s → %s, ignoring.",
118                                        filename, src, dst);
119 
120         dst_name_type = unit_name_to_instance(dst, &dst_instance);
121         if (dst_name_type < 0)
122                 return log_full_errno(log_level, dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type,
123                                       "%s points to \"%s\" which is not a valid unit name: %m",
124                                       filename, dst);
125 
126         if (!(dst_name_type == src_name_type ||
127               (src_name_type == UNIT_NAME_INSTANCE && dst_name_type == UNIT_NAME_TEMPLATE)))
128                 return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
129                                       "%s: symlink target name type \"%s\" does not match source, rejecting.",
130                                       filename, dst);
131 
132         if (dst_name_type == UNIT_NAME_INSTANCE) {
133                 assert(src_instance);
134                 assert(dst_instance);
135                 if (!streq(src_instance, dst_instance))
136                         return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
137                                               "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
138                                               filename, dst);
139         }
140 
141         dst_unit_type = unit_name_to_type(dst);
142         if (dst_unit_type != src_unit_type)
143                 return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
144                                       "%s: symlink target \"%s\" has incompatible suffix, rejecting.",
145                                       filename, dst);
146 
147         return 0;
148 }
149 
150 #define FOLLOW_MAX 8
151 
unit_ids_map_get(Hashmap * unit_ids_map,const char * unit_name,const char ** ret_fragment_path)152 static int unit_ids_map_get(
153                 Hashmap *unit_ids_map,
154                 const char *unit_name,
155                 const char **ret_fragment_path) {
156 
157         /* Resolve recursively until we hit an absolute path, i.e. a non-aliased unit.
158          *
159          * We distinguish the case where unit_name was not found in the hashmap at all, and the case where
160          * some symlink was broken.
161          *
162          * If a symlink target points to an instance name, then we also check for the template. */
163 
164         const char *id = NULL;
165         int r;
166 
167         for (unsigned n = 0; n < FOLLOW_MAX; n++) {
168                 const char *t = hashmap_get(unit_ids_map, id ?: unit_name);
169                 if (!t) {
170                         _cleanup_free_ char *template = NULL;
171 
172                         if (!id)
173                                 return -ENOENT;
174 
175                         r = unit_name_template(id, &template);
176                         if (r == -EINVAL)
177                                 return -ENXIO; /* we failed to find the symlink target */
178                         if (r < 0)
179                                 return log_error_errno(r, "Failed to determine template name for %s: %m", id);
180 
181                         t = hashmap_get(unit_ids_map, template);
182                         if (!t)
183                                 return -ENXIO;
184 
185                         /* We successfully switched from instanced name to a template, let's continue */
186                 }
187 
188                 if (path_is_absolute(t)) {
189                         if (ret_fragment_path)
190                                 *ret_fragment_path = t;
191                         return 0;
192                 }
193 
194                 id = t;
195         }
196 
197         return -ELOOP;
198 }
199 
lookup_paths_mtime_exclude(const LookupPaths * lp,const char * path)200 static bool lookup_paths_mtime_exclude(const LookupPaths *lp, const char *path) {
201         /* Paths that are under our exclusive control. Users shall not alter those directly. */
202 
203         return streq_ptr(path, lp->generator) ||
204                streq_ptr(path, lp->generator_early) ||
205                streq_ptr(path, lp->generator_late) ||
206                streq_ptr(path, lp->transient) ||
207                streq_ptr(path, lp->persistent_control) ||
208                streq_ptr(path, lp->runtime_control);
209 }
210 
211 #define HASH_KEY SD_ID128_MAKE(4e,86,1b,e3,39,b3,40,46,98,5d,b8,11,34,8f,c3,c1)
212 
lookup_paths_timestamp_hash_same(const LookupPaths * lp,uint64_t timestamp_hash,uint64_t * ret_new)213 bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new) {
214         struct siphash state;
215 
216         siphash24_init(&state, HASH_KEY.bytes);
217 
218         STRV_FOREACH(dir, lp->search_path) {
219                 struct stat st;
220 
221                 if (lookup_paths_mtime_exclude(lp, *dir))
222                         continue;
223 
224                 /* Determine the latest lookup path modification time */
225                 if (stat(*dir, &st) < 0) {
226                         if (errno == ENOENT)
227                                 continue;
228 
229                         log_debug_errno(errno, "Failed to stat %s, ignoring: %m", *dir);
230                         continue;
231                 }
232 
233                 siphash24_compress_usec_t(timespec_load(&st.st_mtim), &state);
234         }
235 
236         uint64_t updated = siphash24_finalize(&state);
237         if (ret_new)
238                 *ret_new = updated;
239         if (updated != timestamp_hash)
240                 log_debug("Modification times have changed, need to update cache.");
241         return updated == timestamp_hash;
242 }
243 
directory_name_is_valid(const char * name)244 static int directory_name_is_valid(const char *name) {
245 
246         /* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/ or .d/ */
247 
248         FOREACH_STRING(suffix, ".wants", ".requires", ".d") {
249                 _cleanup_free_ char *chopped = NULL;
250                 const char *e;
251 
252                 e = endswith(name, suffix);
253                 if (!e)
254                         continue;
255 
256                 chopped = strndup(name, e - name);
257                 if (!chopped)
258                         return log_oom();
259 
260                 if (unit_name_is_valid(chopped, UNIT_NAME_ANY) ||
261                     unit_type_from_string(chopped) >= 0)
262                         return true;
263         }
264 
265         return false;
266 }
267 
unit_file_resolve_symlink(const char * root_dir,char ** search_path,const char * dir,int dirfd,const char * filename,bool resolve_destination_target,char ** ret_destination)268 int unit_file_resolve_symlink(
269                 const char *root_dir,
270                 char **search_path,
271                 const char *dir,
272                 int dirfd,
273                 const char *filename,
274                 bool resolve_destination_target,
275                 char **ret_destination) {
276 
277         _cleanup_free_ char *target = NULL, *simplified = NULL, *dst = NULL, *_dir = NULL, *_filename = NULL;
278         int r;
279 
280         /* This can be called with either dir+dirfd valid and filename just a name,
281          * or !dir && dirfd==AT_FDCWD, and filename being a full path.
282          *
283          * If resolve_destination_target is true, an absolute path will be returned.
284          * If not, an absolute path is returned for linked unit files, and a relative
285          * path otherwise.
286          *
287          * Returns an error, false if this is an alias, true if it's a linked unit file. */
288 
289         assert(filename);
290         assert(ret_destination);
291         assert(dir || path_is_absolute(filename));
292         assert(dirfd >= 0 || dirfd == AT_FDCWD);
293 
294         r = readlinkat_malloc(dirfd, filename, &target);
295         if (r < 0)
296                 return log_warning_errno(r, "Failed to read symlink %s%s%s: %m",
297                                          dir, dir ? "/" : "", filename);
298 
299         if (!dir) {
300                 r = path_extract_directory(filename, &_dir);
301                 if (r < 0)
302                         return r;
303                 dir = _dir;
304 
305                 r = path_extract_filename(filename, &_filename);
306                 if (r < 0)
307                         return r;
308                 if (r == O_DIRECTORY)
309                         return log_warning_errno(SYNTHETIC_ERRNO(EISDIR),
310                                                  "Unexpected path to a directory \"%s\", refusing.", filename);
311                 filename = _filename;
312         }
313 
314         bool is_abs = path_is_absolute(target);
315         if (root_dir || !is_abs) {
316                 char *target_abs = path_join(is_abs ? root_dir : dir, target);
317                 if (!target_abs)
318                         return log_oom();
319 
320                 free_and_replace(target, target_abs);
321         }
322 
323         /* Get rid of "." and ".." components in target path */
324         r = chase_symlinks(target, root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL);
325         if (r < 0)
326                 return log_warning_errno(r, "Failed to resolve symlink %s/%s pointing to %s: %m",
327                                          dir, filename, target);
328 
329         assert(path_is_absolute(simplified));
330 
331         /* Check if the symlink remain inside of of our search path.
332          * If yes, it is an alias. Verify that it is valid.
333          *
334          * If no, then this is a linked unit file or mask, and we don't care about the target name
335          * when loading units, and we return the link *source* (resolve_destination_target == false);
336          * When this is called for installation purposes, we want the final destination,
337          * so we return the *target*.
338          */
339         const char *tail = path_startswith_strv(simplified, search_path);
340         if (tail) {  /* An alias */
341                 _cleanup_free_ char *target_name = NULL;
342 
343                 r = path_extract_filename(simplified, &target_name);
344                 if (r < 0)
345                         return r;
346 
347                 r = unit_validate_alias_symlink_or_warn(LOG_NOTICE, filename, simplified);
348                 if (r < 0)
349                         return r;
350                 if (is_path(tail))
351                         log_warning("Suspicious symlink %s/%s→%s, treating as alias.",
352                                     dir, filename, simplified);
353 
354                 dst = resolve_destination_target ? TAKE_PTR(simplified) : TAKE_PTR(target_name);
355 
356         } else {
357                 log_debug("Linked unit file: %s/%s → %s", dir, filename, simplified);
358 
359                 if (resolve_destination_target)
360                         dst = TAKE_PTR(simplified);
361                 else {
362                         dst = path_join(dir, filename);
363                         if (!dst)
364                                 return log_oom();
365                 }
366         }
367 
368         *ret_destination = TAKE_PTR(dst);
369         return !tail;  /* true if linked unit file */
370 }
371 
unit_file_build_name_map(const LookupPaths * lp,uint64_t * cache_timestamp_hash,Hashmap ** unit_ids_map,Hashmap ** unit_names_map,Set ** path_cache)372 int unit_file_build_name_map(
373                 const LookupPaths *lp,
374                 uint64_t *cache_timestamp_hash,
375                 Hashmap **unit_ids_map,
376                 Hashmap **unit_names_map,
377                 Set **path_cache) {
378 
379         /* Build two mappings: any name → main unit (i.e. the end result of symlink resolution), unit name →
380          * all aliases (i.e. the entry for a given key is a list of all names which point to this key). The
381          * key is included in the value iff we saw a file or symlink with that name. In other words, if we
382          * have a key, but it is not present in the value for itself, there was an alias pointing to it, but
383          * the unit itself is not loadable.
384          *
385          * At the same, build a cache of paths where to find units. The non-const parameters are for input
386          * and output. Existing contents will be freed before the new contents are stored.
387          */
388 
389         _cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL;
390         _cleanup_set_free_free_ Set *paths = NULL;
391         _cleanup_strv_free_ char **expanded_search_path = NULL;
392         uint64_t timestamp_hash;
393         int r;
394 
395         /* Before doing anything, check if the timestamp hash that was passed is still valid.
396          * If yes, do nothing. */
397         if (cache_timestamp_hash &&
398             lookup_paths_timestamp_hash_same(lp, *cache_timestamp_hash, &timestamp_hash))
399                 return 0;
400 
401         /* The timestamp hash is now set based on the mtimes from before when we start reading files.
402          * If anything is modified concurrently, we'll consider the cache outdated. */
403 
404         if (path_cache) {
405                 paths = set_new(&path_hash_ops_free);
406                 if (!paths)
407                         return log_oom();
408         }
409 
410         /* Go over all our search paths, chase their symlinks and store the result in the
411          * expanded_search_path list.
412          *
413          * This is important for cases where any of the unit directories itself are symlinks into other
414          * directories and would therefore cause all of the unit files to be recognized as linked units.
415          *
416          * This is important for distributions such as NixOS where most paths in /etc/ are symlinks to some
417          * other location on the filesystem (e.g.  into /nix/store/).
418          *
419          * Search paths are ordered by priority (highest first), and we need to maintain this order.
420          * If a resolved path is already in the list, we don't need to include.
421          *
422          * Note that we build a list that contains both the original paths and the resolved symlinks:
423          * we need the latter for the case where the directory is symlinked, as described above, and
424          * the former for the case where some unit file alias is a dangling symlink that points to one
425          * of the "original" directories (and can't be followed).
426          */
427         STRV_FOREACH(dir, lp->search_path) {
428                 _cleanup_free_ char *resolved_dir = NULL;
429 
430                 r = strv_extend(&expanded_search_path, *dir);
431                 if (r < 0)
432                         return log_oom();
433 
434                 r = chase_symlinks(*dir, NULL, 0, &resolved_dir, NULL);
435                 if (r < 0) {
436                         if (r != -ENOENT)
437                                 log_warning_errno(r, "Failed to resolve symlink %s, ignoring: %m", *dir);
438                         continue;
439                 }
440 
441                 if (strv_contains(expanded_search_path, resolved_dir))
442                         continue;
443 
444                 if (strv_consume(&expanded_search_path, TAKE_PTR(resolved_dir)) < 0)
445                         return log_oom();
446         }
447 
448         STRV_FOREACH(dir, lp->search_path) {
449                 _cleanup_closedir_ DIR *d = NULL;
450 
451                 d = opendir(*dir);
452                 if (!d) {
453                         if (errno != ENOENT)
454                                 log_warning_errno(errno, "Failed to open \"%s\", ignoring: %m", *dir);
455                         continue;
456                 }
457 
458                 FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) {
459                         _unused_ _cleanup_free_ char *_filename_free = NULL;
460                         char *filename;
461                         _cleanup_free_ char *dst = NULL;
462                         bool symlink_to_dir = false;
463 
464                         /* We only care about valid units and dirs with certain suffixes, let's ignore the
465                          * rest. */
466 
467                         if (de->d_type == DT_REG) {
468 
469                                 /* Accept a regular file whose name is a valid unit file name. */
470                                 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
471                                         continue;
472 
473                         } else if (de->d_type == DT_DIR) {
474 
475                                 if (!paths) /* Skip directories early unless path_cache is requested */
476                                         continue;
477 
478                                 r = directory_name_is_valid(de->d_name);
479                                 if (r < 0)
480                                         return r;
481                                 if (r == 0)
482                                         continue;
483 
484                         } else if (de->d_type == DT_LNK) {
485 
486                                 /* Accept a symlink file whose name is a valid unit file name or
487                                  * ending in .wants/, .requires/ or .d/. */
488 
489                                 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) {
490                                         _cleanup_free_ char *target = NULL;
491 
492                                         if (!paths) /* Skip symlink to a directory early unless path_cache is requested */
493                                                 continue;
494 
495                                         r = directory_name_is_valid(de->d_name);
496                                         if (r < 0)
497                                                 return r;
498                                         if (r == 0)
499                                                 continue;
500 
501                                         r = readlinkat_malloc(dirfd(d), de->d_name, &target);
502                                         if (r < 0) {
503                                                 log_warning_errno(r, "Failed to read symlink %s/%s, ignoring: %m",
504                                                                   *dir, de->d_name);
505                                                 continue;
506                                         }
507 
508                                         r = is_dir(target, /* follow = */ true);
509                                         if (r <= 0)
510                                                 continue;
511 
512                                         symlink_to_dir = true;
513                                 }
514 
515                         } else
516                                 continue;
517 
518                         filename = path_join(*dir, de->d_name);
519                         if (!filename)
520                                 return log_oom();
521 
522                         if (paths) {
523                                 r = set_put(paths, filename);
524                                 if (r < 0)
525                                         return log_oom();
526                                 if (r == 0)
527                                         _filename_free = filename; /* Make sure we free the filename. */
528                         } else
529                                 _filename_free = filename; /* Make sure we free the filename. */
530 
531                         if (de->d_type == DT_DIR || (de->d_type == DT_LNK && symlink_to_dir))
532                                 continue;
533 
534                         assert(IN_SET(de->d_type, DT_REG, DT_LNK));
535 
536                         /* search_path is ordered by priority (highest first). If the name is already mapped
537                          * to something (incl. itself), it means that we have already seen it, and we should
538                          * ignore it here. */
539                         if (hashmap_contains(ids, de->d_name))
540                                 continue;
541 
542                         if (de->d_type == DT_LNK) {
543                                 /* We don't explicitly check for alias loops here. unit_ids_map_get() which
544                                  * limits the number of hops should be used to access the map. */
545 
546                                 r = unit_file_resolve_symlink(lp->root_dir, expanded_search_path,
547                                                               *dir, dirfd(d), de->d_name,
548                                                               /* resolve_destination_target= */ false,
549                                                               &dst);
550                                 if (r == -ENOMEM)
551                                         return r;
552                                 if (r < 0)  /* we ignore other errors here */
553                                         continue;
554 
555                         } else {
556                                 dst = TAKE_PTR(_filename_free); /* Grab the copy we made previously, if available. */
557                                 if (!dst) {
558                                         dst = strdup(filename);
559                                         if (!dst)
560                                                 return log_oom();
561                                 }
562 
563                                 log_debug("%s: normal unit file: %s", __func__, dst);
564                         }
565 
566                         _cleanup_free_ char *key = strdup(de->d_name);
567                         if (!key)
568                                 return log_oom();
569 
570                         r = hashmap_ensure_put(&ids, &string_hash_ops_free_free, key, dst);
571                         if (r < 0)
572                                 return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
573                                                          de->d_name, dst);
574                         key = dst = NULL;
575                 }
576         }
577 
578         /* Let's also put the names in the reverse db. */
579         const char *dummy, *src;
580         HASHMAP_FOREACH_KEY(dummy, src, ids) {
581                 _cleanup_free_ char *inst = NULL, *dst_inst = NULL;
582                 const char *dst;
583 
584                 r = unit_ids_map_get(ids, src, &dst);
585                 if (r < 0)
586                         continue;
587 
588                 if (null_or_empty_path(dst) != 0)
589                         continue;
590 
591                 dst = basename(dst);
592 
593                 /* If we have an symlink from an instance name to a template name, it is an alias just for
594                  * this specific instance, foo@id.service ↔ template@id.service. */
595                 if (unit_name_is_valid(dst, UNIT_NAME_TEMPLATE)) {
596                         UnitNameFlags t = unit_name_to_instance(src, &inst);
597                         if (t < 0)
598                                 return log_error_errno(t, "Failed to extract instance part from %s: %m", src);
599                         if (t == UNIT_NAME_INSTANCE) {
600                                 r = unit_name_replace_instance(dst, inst, &dst_inst);
601                                 if (r < 0) {
602                                         /* This might happen e.g. if the combined length is too large.
603                                          * Let's not make too much of a fuss. */
604                                         log_debug_errno(r, "Failed to build alias name (%s + %s), ignoring: %m",
605                                                         dst, inst);
606                                         continue;
607                                 }
608 
609                                 dst = dst_inst;
610                         }
611                 }
612 
613                 r = string_strv_hashmap_put(&names, dst, src);
614                 if (r < 0)
615                         return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m", dst, src);
616         }
617 
618         if (cache_timestamp_hash)
619                 *cache_timestamp_hash = timestamp_hash;
620 
621         hashmap_free_and_replace(*unit_ids_map, ids);
622         hashmap_free_and_replace(*unit_names_map, names);
623         if (path_cache)
624                 set_free_and_replace(*path_cache, paths);
625 
626         return 1;
627 }
628 
add_name(const char * unit_name,Set ** names,const char * name)629 static int add_name(
630                 const char *unit_name,
631                 Set **names,
632                 const char *name) {
633         int r;
634 
635         assert(names);
636         assert(name);
637 
638         r = set_put_strdup(names, name);
639         if (r < 0)
640                 return r;
641         if (r > 0 && !streq(unit_name, name))
642                 log_debug("Unit %s has alias %s.", unit_name, name);
643         return r;
644 }
645 
add_names(Hashmap * unit_ids_map,Hashmap * unit_name_map,const char * unit_name,const char * fragment_basename,UnitNameFlags name_type,const char * instance,Set ** names,const char * name)646 static int add_names(
647                 Hashmap *unit_ids_map,
648                 Hashmap *unit_name_map,
649                 const char *unit_name,
650                 const char *fragment_basename,  /* Only set when adding additional names based on fragment path */
651                 UnitNameFlags name_type,
652                 const char *instance,
653                 Set **names,
654                 const char *name) {
655 
656         char **aliases;
657         int r;
658 
659         assert(name_type == UNIT_NAME_PLAIN || instance);
660 
661         /* The unit has its own name if it's not a template. If we're looking at a fragment, the fragment
662          * name (possibly with instance inserted), is also always one of the unit names. */
663         if (name_type != UNIT_NAME_TEMPLATE) {
664                 r = add_name(unit_name, names, name);
665                 if (r < 0)
666                         return r;
667         }
668 
669         /* Add any aliases of the name to the set of names.
670          *
671          * We don't even need to know which fragment we will use. The unit_name_map should return the same
672          * set of names for any of the aliases. */
673         aliases = hashmap_get(unit_name_map, name);
674         STRV_FOREACH(alias, aliases) {
675                 if (name_type == UNIT_NAME_INSTANCE && unit_name_is_valid(*alias, UNIT_NAME_TEMPLATE)) {
676                         _cleanup_free_ char *inst = NULL;
677                         const char *inst_fragment = NULL;
678 
679                         r = unit_name_replace_instance(*alias, instance, &inst);
680                         if (r < 0)
681                                 return log_debug_errno(r, "Cannot build instance name %s + %s: %m",
682                                                        *alias, instance);
683 
684                         /* Exclude any aliases that point in some other direction.
685                          *
686                          * See https://github.com/systemd/systemd/pull/13119#discussion_r308145418. */
687                         r = unit_ids_map_get(unit_ids_map, inst, &inst_fragment);
688                         if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
689                                 return log_debug_errno(r, "Cannot find instance fragment %s: %m", inst);
690 
691                         if (inst_fragment &&
692                             !streq(basename(inst_fragment), fragment_basename)) {
693                                 log_debug("Instance %s has fragment %s and is not an alias of %s.",
694                                           inst, inst_fragment, unit_name);
695                                 continue;
696                         }
697 
698                         r = set_consume(*names, TAKE_PTR(inst));
699                         if (r > 0)
700                                 log_debug("Unit %s has alias %s.", unit_name, inst);
701                 } else
702                         r = add_name(unit_name, names, *alias);
703 
704                 if (r < 0)
705                         return r;
706         }
707 
708         return 0;
709 }
710 
unit_file_find_fragment(Hashmap * unit_ids_map,Hashmap * unit_name_map,const char * unit_name,const char ** ret_fragment_path,Set ** ret_names)711 int unit_file_find_fragment(
712                 Hashmap *unit_ids_map,
713                 Hashmap *unit_name_map,
714                 const char *unit_name,
715                 const char **ret_fragment_path,
716                 Set **ret_names) {
717 
718         const char *fragment = NULL;
719         _cleanup_free_ char *template = NULL, *instance = NULL;
720         _cleanup_set_free_ Set *names = NULL;
721         int r;
722 
723         /* Finds a fragment path, and returns the set of names:
724          * if we have …/foo.service and …/foo-alias.service→foo.service,
725          * and …/foo@.service and …/foo-alias@.service→foo@.service,
726          * and …/foo@inst.service,
727          * this should return:
728          * foo.service → …/foo.service, {foo.service, foo-alias.service},
729          * foo-alias.service → …/foo.service, {foo.service, foo-alias.service},
730          * foo@.service → …/foo@.service, {foo@.service, foo-alias@.service},
731          * foo-alias@.service → …/foo@.service, {foo@.service, foo-alias@.service},
732          * foo@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
733          * foo-alias@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
734          * foo-alias@inst.service → …/foo@inst.service, {foo@inst.service, foo-alias@inst.service}.
735          */
736 
737         UnitNameFlags name_type = unit_name_to_instance(unit_name, &instance);
738         if (name_type < 0)
739                 return name_type;
740 
741         if (ret_names) {
742                 r = add_names(unit_ids_map, unit_name_map, unit_name, NULL, name_type, instance, &names, unit_name);
743                 if (r < 0)
744                         return r;
745         }
746 
747         /* First try to load fragment under the original name */
748         r = unit_ids_map_get(unit_ids_map, unit_name, &fragment);
749         if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
750                 return log_debug_errno(r, "Cannot load unit %s: %m", unit_name);
751 
752         if (!fragment && name_type == UNIT_NAME_INSTANCE) {
753                 /* Look for a fragment under the template name */
754 
755                 r = unit_name_template(unit_name, &template);
756                 if (r < 0)
757                         return log_debug_errno(r, "Failed to determine template name: %m");
758 
759                 r = unit_ids_map_get(unit_ids_map, template, &fragment);
760                 if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
761                         return log_debug_errno(r, "Cannot load template %s: %m", template);
762         }
763 
764         if (fragment && ret_names) {
765                 const char *fragment_basename = basename(fragment);
766 
767                 if (!streq(fragment_basename, unit_name)) {
768                         /* Add names based on the fragment name to the set of names */
769                         r = add_names(unit_ids_map, unit_name_map, unit_name, fragment_basename, name_type, instance, &names, fragment_basename);
770                         if (r < 0)
771                                 return r;
772                 }
773         }
774 
775         *ret_fragment_path = fragment;
776         if (ret_names)
777                 *ret_names = TAKE_PTR(names);
778 
779         return 0;
780 }
781 
782 static const char * const rlmap[] = {
783         "emergency", SPECIAL_EMERGENCY_TARGET,
784         "-b",        SPECIAL_EMERGENCY_TARGET,
785         "rescue",    SPECIAL_RESCUE_TARGET,
786         "single",    SPECIAL_RESCUE_TARGET,
787         "-s",        SPECIAL_RESCUE_TARGET,
788         "s",         SPECIAL_RESCUE_TARGET,
789         "S",         SPECIAL_RESCUE_TARGET,
790         "1",         SPECIAL_RESCUE_TARGET,
791         "2",         SPECIAL_MULTI_USER_TARGET,
792         "3",         SPECIAL_MULTI_USER_TARGET,
793         "4",         SPECIAL_MULTI_USER_TARGET,
794         "5",         SPECIAL_GRAPHICAL_TARGET,
795         NULL
796 };
797 
798 static const char * const rlmap_initrd[] = {
799         "emergency", SPECIAL_EMERGENCY_TARGET,
800         "rescue",    SPECIAL_RESCUE_TARGET,
801         NULL
802 };
803 
runlevel_to_target(const char * word)804 const char* runlevel_to_target(const char *word) {
805         const char * const *rlmap_ptr;
806 
807         if (!word)
808                 return NULL;
809 
810         if (in_initrd()) {
811                 word = startswith(word, "rd.");
812                 if (!word)
813                         return NULL;
814         }
815 
816         rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
817 
818         for (size_t i = 0; rlmap_ptr[i]; i += 2)
819                 if (streq(word, rlmap_ptr[i]))
820                         return rlmap_ptr[i+1];
821 
822         return NULL;
823 }
824