• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19979552133

05 Dec 2025 05:29PM UTC coverage: 72.765% (-0.2%) from 72.917%
19979552133

push

github

yuwata
udev-rules: use the right variable

We carefully prepare a copy of a local buffer to save in device cache
and then save the buffer there instead... This leads to abort in free()
on exit (also, copied is leaked).

Reproducer:
 # udevadm test /sys/block/sr0

Follow-up-for: a9559ebcb

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2406118

0 of 1 new or added line in 1 file covered. (0.0%)

1564 existing lines in 65 files now uncovered.

309707 of 425625 relevant lines covered (72.77%)

1149816.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.79
/src/shared/install.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <fnmatch.h>
5
#include <stdio.h>
6
#include <unistd.h>
7

8
#include "alloc-util.h"
9
#include "bus-common-errors.h"
10
#include "chase.h"
11
#include "conf-files.h"
12
#include "conf-parser.h"
13
#include "constants.h"
14
#include "dirent-util.h"
15
#include "errno-util.h"
16
#include "extract-word.h"
17
#include "fd-util.h"
18
#include "fileio.h"
19
#include "fs-util.h"
20
#include "glyph-util.h"
21
#include "hashmap.h"
22
#include "install.h"
23
#include "install-printf.h"
24
#include "log.h"
25
#include "mkdir-label.h"
26
#include "path-lookup.h"
27
#include "path-util.h"
28
#include "rm-rf.h"
29
#include "set.h"
30
#include "special.h"
31
#include "stat-util.h"
32
#include "string-table.h"
33
#include "string-util.h"
34
#include "strv.h"
35
#include "unit-file.h"
36
#include "unit-name.h"
37

38
#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
39

40
typedef enum SearchFlags {
41
        SEARCH_LOAD                   = 1 << 0,
42
        SEARCH_FOLLOW_CONFIG_SYMLINKS = 1 << 1,
43
        SEARCH_DROPIN                 = 1 << 2,
44
} SearchFlags;
45

46
typedef struct {
47
        RuntimeScope scope;
48
        OrderedHashmap *will_process;
49
        OrderedHashmap *have_processed;
50
} InstallContext;
51

52
struct UnitFilePresetRule {
53
        char *pattern;
54
        PresetAction action;
55
        char **instances;
56
};
57

58
/* NB! strings use past tense. */
59
static const char *const preset_action_past_tense_table[_PRESET_ACTION_MAX] = {
60
        [PRESET_UNKNOWN] = "unknown",
61
        [PRESET_ENABLE]  = "enabled",
62
        [PRESET_DISABLE] = "disabled",
63
        [PRESET_IGNORE]  = "ignored",
64
};
65

66
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(preset_action_past_tense, PresetAction);
3,309✔
67

68
static bool install_info_has_rules(const InstallInfo *i) {
2,229✔
69
        assert(i);
2,229✔
70

71
        return !strv_isempty(i->aliases) ||
2,229✔
72
               !strv_isempty(i->wanted_by) ||
2,214✔
73
               !strv_isempty(i->required_by) ||
1,899✔
74
               !strv_isempty(i->upheld_by);
1,897✔
75
}
76

77
static bool install_info_has_also(const InstallInfo *i) {
1,897✔
78
        assert(i);
1,897✔
79

80
        return !strv_isempty(i->also);
1,897✔
81
}
82

83
static void unit_file_preset_rule_done(UnitFilePresetRule *rule) {
50,810✔
84
        assert(rule);
50,810✔
85

86
        free(rule->pattern);
50,810✔
87
        strv_free(rule->instances);
50,810✔
88
}
50,810✔
89

90
void unit_file_presets_done(UnitFilePresets *p) {
925✔
91
        if (!p)
925✔
92
                return;
93

94
        FOREACH_ARRAY(rule, p->rules, p->n_rules)
17,966✔
95
                unit_file_preset_rule_done(rule);
17,041✔
96

97
        free(p->rules);
925✔
98
        p->n_rules = 0;
925✔
99
}
100

101
static const char *const install_mode_table[_INSTALL_MODE_MAX] = {
102
        [INSTALL_MODE_REGULAR] = "regular",
103
        [INSTALL_MODE_LINKED]  = "linked",
104
        [INSTALL_MODE_ALIAS]   = "alias",
105
        [INSTALL_MODE_MASKED]  = "masked",
106
};
107

108
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(install_mode, InstallMode);
2✔
109

110
static int in_search_path(const LookupPaths *lp, const char *path) {
720✔
111
        _cleanup_free_ char *parent = NULL;
720✔
112
        int r;
720✔
113

114
        /* Check if 'path' is in lp->search_path. */
115

116
        assert(lp);
720✔
117
        assert(path);
720✔
118

119
        r = path_extract_directory(path, &parent);
720✔
120
        if (r < 0)
720✔
121
                return r;
122

123
        return path_strv_contains(lp->search_path, parent);
720✔
124
}
125

126
static bool underneath_search_path(const LookupPaths *lp, const char *path) {
1✔
127
        /* Check if 'path' is underneath lp->search_path. */
128

129
        assert(lp);
1✔
130
        assert(path);
1✔
131

132
        return path_startswith_strv(path, lp->search_path);
1✔
133
}
134

135
static const char* skip_root(const char *root_dir, const char *path) {
1,383✔
136
        assert(path);
1,383✔
137

138
        if (!root_dir)
1,383✔
139
                return path;
140

141
        const char *e = path_startswith(path, root_dir);
100✔
142
        if (!e)
100✔
143
                return NULL;
144

145
        /* Make sure the returned path starts with a slash */
146
        if (e[0] != '/') {
98✔
147
                if (e == path || e[-1] != '/')
98✔
148
                        return NULL;
149

150
                e--;
98✔
151
        }
152

153
        return e;
154
}
155

156
static int path_is_generator(const LookupPaths *lp, const char *path) {
2,896✔
157
        _cleanup_free_ char *parent = NULL;
2,896✔
158
        int r;
2,896✔
159

160
        assert(lp);
2,896✔
161
        assert(path);
2,896✔
162

163
        r = path_extract_directory(path, &parent);
2,896✔
164
        if (r < 0)
2,896✔
165
                return r;
166

167
        return PATH_IN_SET(parent,
2,896✔
168
                           lp->generator,
169
                           lp->generator_early,
170
                           lp->generator_late);
171
}
172

173
static int path_is_transient(const LookupPaths *lp, const char *path) {
2,886✔
174
        _cleanup_free_ char *parent = NULL;
2,886✔
175
        int r;
2,886✔
176

177
        assert(lp);
2,886✔
178
        assert(path);
2,886✔
179

180
        r = path_extract_directory(path, &parent);
2,886✔
181
        if (r < 0)
2,886✔
182
                return r;
183

184
        return path_equal(parent, lp->transient);
2,886✔
185
}
186

187
static int path_is_control(const LookupPaths *lp, const char *path) {
×
188
        _cleanup_free_ char *parent = NULL;
×
189
        int r;
×
190

191
        assert(lp);
×
192
        assert(path);
×
193

194
        r = path_extract_directory(path, &parent);
×
195
        if (r < 0)
×
196
                return r;
197

198
        return PATH_IN_SET(parent,
×
199
                           lp->persistent_control,
200
                           lp->runtime_control);
201
}
202

203
static int path_is_config(const LookupPaths *lp, const char *path, bool check_parent) {
13✔
204
        _cleanup_free_ char *parent = NULL;
13✔
205
        int r;
13✔
206

207
        assert(lp);
13✔
208
        assert(path);
13✔
209

210
        /* Note that we do *not* have generic checks for /etc or /run in place, since with
211
         * them we couldn't discern configuration from transient or generated units */
212

213
        if (check_parent) {
13✔
214
                r = path_extract_directory(path, &parent);
13✔
215
                if (r < 0)
13✔
216
                        return r;
217

218
                path = parent;
13✔
219
        }
220

221
        return PATH_IN_SET(path,
13✔
222
                           lp->persistent_config,
223
                           lp->runtime_config);
224
}
225

226
static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_parent) {
606✔
227
        _cleanup_free_ char *parent = NULL;
606✔
228
        const char *rpath;
606✔
229
        int r;
606✔
230

231
        assert(lp);
606✔
232
        assert(path);
606✔
233

234
        /* Everything in /run is considered runtime. On top of that we also add
235
         * explicit checks for the various runtime directories, as safety net. */
236

237
        rpath = skip_root(lp->root_dir, path);
606✔
238
        if (rpath && path_startswith(rpath, "/run"))
1,212✔
239
                return true;
240

241
        if (check_parent) {
587✔
242
                r = path_extract_directory(path, &parent);
12✔
243
                if (r < 0)
12✔
244
                        return r;
245

246
                path = parent;
12✔
247
        }
248

249
        return PATH_IN_SET(path,
587✔
250
                           lp->runtime_config,
251
                           lp->generator,
252
                           lp->generator_early,
253
                           lp->generator_late,
254
                           lp->transient,
255
                           lp->runtime_control);
256
}
257

258
static int path_is_vendor_or_generator(const LookupPaths *lp, const char *path) {
5✔
259
        const char *rpath;
5✔
260

261
        assert(lp);
5✔
262
        assert(path);
5✔
263

264
        rpath = skip_root(lp->root_dir, path);
5✔
265
        if (!rpath)
5✔
266
                return 0;
267

268
        if (path_startswith(rpath, "/usr"))
5✔
269
                return true;
270

271
        if (path_is_generator(lp, rpath))
1✔
272
                return true;
273

274
        return path_equal(rpath, SYSTEM_DATA_UNIT_DIR);
1✔
275
}
276

277
static const char* config_path_from_flags(const LookupPaths *lp, UnitFileFlags flags) {
40✔
278
        assert(lp);
40✔
279

280
        if (FLAGS_SET(flags, UNIT_FILE_PORTABLE))
40✔
281
                return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_attached : lp->persistent_attached;
×
282
        else
283
                return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_config : lp->persistent_config;
40✔
284
}
285

286
InstallChangeType install_changes_add(
1,278✔
287
                InstallChange **changes,
288
                size_t *n_changes,
289
                InstallChangeType type, /* INSTALL_CHANGE_SYMLINK, _UNLINK, _IS_MASKED, _IS_DANGLING, … if positive or errno if negative */
290
                const char *path,
291
                const char *source) {
292

293
        _cleanup_free_ char *p = NULL, *s = NULL;
1,278✔
294
        int r;
1,278✔
295

296
        assert(!changes == !n_changes);
1,278✔
297
        assert(INSTALL_CHANGE_TYPE_VALID(type));
1,278✔
298

299
        /* Message formatting requires <path> to be set. */
300
        assert(path);
1,278✔
301

302
        /* Register a change or error. Note that the return value may be the error
303
         * that was passed in, or -ENOMEM generated internally. */
304

305
        if (!changes)
1,278✔
306
                return type;
307

308
        if (!GREEDY_REALLOC(*changes, *n_changes + 1))
196✔
309
                return -ENOMEM;
310

311
        r = path_simplify_alloc(path, &p);
196✔
312
        if (r < 0)
196✔
313
                return r;
314

315
        r = path_simplify_alloc(source, &s);
196✔
316
        if (r < 0)
196✔
317
                return r;
318

319
        (*changes)[(*n_changes)++] = (InstallChange) {
196✔
320
                .type = type,
321
                .path = TAKE_PTR(p),
196✔
322
                .source = TAKE_PTR(s),
196✔
323
        };
324

325
        return type;
196✔
326
}
327

328
void install_changes_free(InstallChange *changes, size_t n_changes) {
131✔
329
        assert(changes || n_changes == 0);
131✔
330

331
        FOREACH_ARRAY(i, changes, n_changes) {
327✔
332
                free(i->path);
196✔
333
                free(i->source);
196✔
334
        }
335

336
        free(changes);
131✔
337
}
131✔
338

339
static void install_change_dump_success(const InstallChange *change) {
96✔
340
        assert(change);
96✔
341
        assert(change->path);
96✔
342

343
        switch (change->type) {
96✔
344

345
        case INSTALL_CHANGE_SYMLINK:
346
                return log_info("Created symlink '%s' %s '%s'.",
38✔
347
                                change->path, glyph(GLYPH_ARROW_RIGHT), change->source);
348

349
        case INSTALL_CHANGE_UNLINK:
350
                return log_info("Removed '%s'.", change->path);
37✔
351

352
        case INSTALL_CHANGE_IS_MASKED:
353
                return log_info("Unit %s is masked, ignoring.", change->path);
21✔
354

355
        case INSTALL_CHANGE_IS_MASKED_GENERATOR:
356
                return log_info("Unit %s is masked via a generator and cannot be unmasked, skipping.", change->path);
×
357

358
        case INSTALL_CHANGE_IS_DANGLING:
359
                return log_info("Unit %s is an alias to a non-existent unit, ignoring.", change->path);
×
360

361
        case INSTALL_CHANGE_DESTINATION_NOT_PRESENT:
362
                return log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
×
363
                                   change->source, change->path);
364

365
        case INSTALL_CHANGE_AUXILIARY_FAILED:
366
                return log_warning("Failed to enable auxiliary unit %s, ignoring.", change->path);
×
367

368
        default:
×
369
                assert_not_reached();
×
370
        }
371
}
372

373
int install_change_dump_error(const InstallChange *change, char **ret_errmsg, const char **ret_bus_error) {
21✔
374
        char *m;
21✔
375
        const char *bus_error;
21✔
376

377
        /* Returns 0:   known error and ret_errmsg formatted
378
         *         < 0: non-recognizable error */
379

380
        assert(change);
21✔
381
        assert(change->path);
21✔
382
        assert(change->type < 0);
21✔
383
        assert(ret_errmsg);
21✔
384

385
        switch (change->type) {
21✔
386

387
        case -EEXIST:
×
388
                m = strjoin("File '", change->path, "' already exists",
×
389
                            change->source ? " and is a symlink to " : NULL, change->source);
390
                bus_error = BUS_ERROR_UNIT_EXISTS;
×
391
                break;
×
392

393
        case -ERFKILL:
20✔
394
                m = strjoin("Unit ", change->path, " is masked");
20✔
395
                bus_error = BUS_ERROR_UNIT_MASKED;
20✔
396
                break;
20✔
397

398
        case -EADDRNOTAVAIL:
×
399
                m = strjoin("Unit ", change->path, " is transient or generated");
×
400
                bus_error = BUS_ERROR_UNIT_GENERATED;
×
401
                break;
×
402

403
        case -ETXTBSY:
×
404
                m = strjoin("File '", change->path, "' is under the systemd unit hierarchy already");
×
405
                bus_error = BUS_ERROR_UNIT_BAD_PATH;
×
406
                break;
×
407

408
        case -EBADSLT:
×
409
                m = strjoin("Invalid specifier in unit ", change->path);
×
410
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
411
                break;
×
412

413
        case -EIDRM:
×
414
                m = strjoin("Refusing to operate on template unit ", change->source,
×
415
                            " when destination unit ", change->path, " is a non-template unit");
416
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
417
                break;
×
418

419
        case -EUCLEAN:
×
420
                m = strjoin("Invalid unit name ", change->path);
×
421
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
422
                break;
×
423

424
        case -ELOOP:
×
425
                m = strjoin("Refusing to operate on linked unit file ", change->path);
×
426
                bus_error = BUS_ERROR_UNIT_LINKED;
×
427
                break;
×
428

429
        case -EXDEV:
×
430
                if (change->source)
×
431
                        m = strjoin("Cannot alias ", change->source, " as ", change->path);
×
432
                else
433
                        m = strjoin("Invalid unit reference ", change->path);
×
434
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
435
                break;
436

437
        case -ENOENT:
×
438
                m = strjoin("Unit ", change->path, " does not exist");
×
439
                bus_error = BUS_ERROR_NO_SUCH_UNIT;
×
440
                break;
×
441

442
        case -ENOLINK:
×
443
                m = strjoin("Unit ", change->path, " is an unresolvable alias");
×
444
                bus_error = BUS_ERROR_NO_SUCH_UNIT;
×
445
                break;
×
446

447
        case -EUNATCH:
×
448
                m = strjoin("Cannot resolve specifiers in unit ", change->path);
×
449
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
450
                break;
×
451

452
        default:
453
                return change->type;
454
        }
455
        if (!m)
20✔
456
                return -ENOMEM;
457

458
        *ret_errmsg = m;
20✔
459
        if (ret_bus_error)
20✔
460
                *ret_bus_error = bus_error;
×
461

462
        return 0;
463
}
464

465
void install_changes_dump(
107✔
466
                int error,
467
                const char *verb,
468
                const InstallChange *changes,
469
                size_t n_changes,
470
                bool quiet) {
471

472
        bool err_logged = false;
107✔
473
        int r;
107✔
474

475
        /* If verb is not specified, errors are not allowed! */
476
        assert(verb || error >= 0);
107✔
477
        assert(changes || n_changes == 0);
107✔
478

479
        FOREACH_ARRAY(i, changes, n_changes)
224✔
480
                if (i->type >= 0) {
117✔
481
                        if (!quiet)
96✔
482
                                install_change_dump_success(i);
96✔
483
                } else {
484
                        _cleanup_free_ char *err_message = NULL;
21✔
485

486
                        assert(verb);
21✔
487

488
                        r = install_change_dump_error(i, &err_message, /* ret_bus_error = */ NULL);
21✔
489
                        if (r == -ENOMEM)
21✔
490
                                return (void) log_oom();
×
491
                        if (r < 0)
21✔
492
                                log_error_errno(r, "Failed to %s unit %s: %m", verb, i->path);
1✔
493
                        else
494
                                log_error_errno(i->type, "Failed to %s unit: %s", verb, err_message);
20✔
495

496
                        err_logged = true;
21✔
497
                }
498

499
        if (error < 0 && !err_logged)
107✔
500
                log_error_errno(error, "Failed to %s unit: %m.", verb);
107✔
501
}
502

503
/**
504
 * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is
505
 * concerned. If the target is in the unit search path, then anything with the same name is equivalent.
506
 * If outside the unit search path, paths must be identical.
507
 */
508
static int chroot_unit_symlinks_equivalent(
684✔
509
                const LookupPaths *lp,
510
                const char *src,
511
                const char *target_a,
512
                const char *target_b) {
513

514
        assert(lp);
684✔
515
        assert(src);
684✔
516
        assert(target_a);
684✔
517
        assert(target_b);
684✔
518

519
        /* This will give incorrect results if the paths are relative and go outside
520
         * of the chroot. False negatives are possible. */
521

522
        const char *root = lp->root_dir ?: "/";
684✔
523
        _cleanup_free_ char *dirname = NULL;
684✔
524
        int r;
684✔
525

526
        if (!path_is_absolute(target_a) || !path_is_absolute(target_b)) {
1,368✔
527
                r = path_extract_directory(src, &dirname);
×
528
                if (r < 0)
×
529
                        return r;
530
        }
531

532
        _cleanup_free_ char *a = path_join(path_is_absolute(target_a) ? root : dirname, target_a);
684✔
533
        _cleanup_free_ char *b = path_join(path_is_absolute(target_b) ? root : dirname, target_b);
1,368✔
534
        if (!a || !b)
684✔
535
                return log_oom();
×
536

537
        r = path_equal_or_inode_same(a, b, 0);
684✔
538
        if (r != 0)
684✔
539
                return r;
540

541
        _cleanup_free_ char *a_name = NULL, *b_name = NULL;
2✔
542
        r = path_extract_filename(a, &a_name);
2✔
543
        if (r < 0)
2✔
544
                return r;
545
        r = path_extract_filename(b, &b_name);
2✔
546
        if (r < 0)
2✔
547
                return r;
548

549
        return streq(a_name, b_name) &&
×
550
               path_startswith_strv(a, lp->search_path) &&
2✔
551
               path_startswith_strv(b, lp->search_path);
×
552
}
553

554
static int create_symlink(
733✔
555
                const LookupPaths *lp,
556
                const char *old_path,
557
                const char *new_path,
558
                bool force,
559
                InstallChange **changes,
560
                size_t *n_changes) {
561

562
        _cleanup_free_ char *dest = NULL;
733✔
563
        const char *rp;
733✔
564
        int r;
733✔
565

566
        assert(old_path);
733✔
567
        assert(new_path);
733✔
568

569
        rp = skip_root(lp->root_dir, old_path);
733✔
570
        if (rp)
733✔
571
                old_path = rp;
731✔
572

573
        /* Actually create a symlink, and remember that we did. This function is
574
         * smart enough to check if there's already a valid symlink in place.
575
         *
576
         * Returns 1 if a symlink was created or already exists and points to the
577
         * right place, or negative on error.
578
         */
579

580
        (void) mkdir_parents_label(new_path, 0755);
733✔
581

582
        if (symlink(old_path, new_path) >= 0) {
733✔
583
                r = install_changes_add(changes, n_changes, INSTALL_CHANGE_SYMLINK, new_path, old_path);
49✔
584
                if (r < 0)
49✔
585
                        return r;
586
                return 1;
49✔
587
        }
588

589
        if (errno != EEXIST)
684✔
590
                return install_changes_add(changes, n_changes, -errno, new_path, NULL);
×
591

592
        r = readlink_malloc(new_path, &dest);
684✔
593
        if (r < 0) {
684✔
594
                /* translate EINVAL (non-symlink exists) to EEXIST */
595
                if (r == -EINVAL)
×
596
                        r = -EEXIST;
×
597

598
                return install_changes_add(changes, n_changes, r, new_path, NULL);
×
599
        }
600

601
        if (chroot_unit_symlinks_equivalent(lp, new_path, dest, old_path)) {
684✔
602
                log_debug("Symlink %s %s %s already exists",
682✔
603
                          new_path, glyph(GLYPH_ARROW_RIGHT), dest);
604
                return 1;
682✔
605
        }
606

607
        if (!force)
2✔
608
                return install_changes_add(changes, n_changes, -EEXIST, new_path, dest);
×
609

610
        r = symlink_atomic(old_path, new_path);
2✔
611
        if (r < 0)
2✔
612
                return install_changes_add(changes, n_changes, r, new_path, NULL);
×
613

614
        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, new_path, NULL);
2✔
615
        if (r < 0)
2✔
616
                return r;
617
        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_SYMLINK, new_path, old_path);
2✔
618
        if (r < 0)
2✔
619
                return r;
×
620

621
        return 1;
622
}
623

624
static int mark_symlink_for_removal(
78✔
625
                Set **remove_symlinks_to,
626
                const char *p) {
627

628
        char *n;
78✔
629
        int r;
78✔
630

631
        assert(p);
78✔
632

633
        r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops_free);
78✔
634
        if (r < 0)
78✔
635
                return r;
78✔
636

637
        r = path_simplify_alloc(p, &n);
78✔
638
        if (r < 0)
78✔
639
                return r;
640

641
        r = set_consume(*remove_symlinks_to, n);
78✔
642
        if (r == -EEXIST)
78✔
643
                return 0;
644
        if (r < 0)
78✔
645
                return r;
×
646

647
        return 1;
648
}
649

650
static int remove_marked_symlinks_fd(
260✔
651
                Set *remove_symlinks_to,
652
                int fd,
653
                const char *path,
654
                const char *config_path,
655
                const LookupPaths *lp,
656
                bool dry_run,
657
                bool *restart,
658
                InstallChange **changes,
659
                size_t *n_changes) {
660

661
        _cleanup_closedir_ DIR *d = NULL;
260✔
662
        int r, ret = 0;
260✔
663

664
        assert(remove_symlinks_to);
260✔
665
        assert(fd >= 0);
260✔
666
        assert(path);
260✔
667
        assert(config_path);
260✔
668
        assert(lp);
260✔
669
        assert(restart);
260✔
670

671
        d = fdopendir(fd);
260✔
672
        if (!d) {
260✔
673
                safe_close(fd);
×
674
                return -errno;
×
675
        }
676

677
        rewinddir(d);
260✔
678

679
        FOREACH_DIRENT(de, d, return -errno)
1,708✔
680
                if (de->d_type == DT_DIR) {
928✔
681
                        _cleanup_close_ int nfd = -EBADF;
1,708✔
682
                        _cleanup_free_ char *p = NULL;
217✔
683

684
                        nfd = RET_NERRNO(openat(fd, de->d_name, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW));
217✔
685
                        if (nfd < 0) {
×
686
                                if (nfd != -ENOENT)
×
687
                                        RET_GATHER(ret, nfd);
×
688
                                continue;
×
689
                        }
690

691
                        p = path_make_absolute(de->d_name, path);
217✔
692
                        if (!p)
217✔
693
                                return -ENOMEM;
×
694

695
                        /* This will close nfd, regardless whether it succeeds or not */
696
                        RET_GATHER(ret, remove_marked_symlinks_fd(remove_symlinks_to,
217✔
697
                                                                  TAKE_FD(nfd), p,
698
                                                                  config_path, lp,
699
                                                                  dry_run,
700
                                                                  restart,
701
                                                                  changes, n_changes));
702

703
                } else if (de->d_type == DT_LNK) {
711✔
704
                        _cleanup_free_ char *p = NULL;
601✔
705
                        bool found;
601✔
706

707
                        if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
601✔
708
                                continue;
×
709

710
                        p = path_make_absolute(de->d_name, path);
601✔
711
                        if (!p)
601✔
712
                                return -ENOMEM;
713
                        path_simplify(p);
601✔
714

715
                        /* We remove all links pointing to a file or path that is marked, as well as all
716
                         * files sharing the same name as a file that is marked, and files sharing the same
717
                         * name after the instance has been removed. Do path chasing only if we don't already
718
                         * know that we want to remove the symlink. */
719
                        found = set_contains(remove_symlinks_to, de->d_name);
601✔
720

721
                        if (!found) {
601✔
722
                                _cleanup_free_ char *template = NULL;
×
723

724
                                r = unit_name_template(de->d_name, &template);
579✔
725
                                if (r < 0 && r != -EINVAL)
579✔
726
                                        return r;
×
727
                                if (r >= 0)
579✔
728
                                        found = set_contains(remove_symlinks_to, template);
579✔
729
                        }
730

731
                        if (!found) {
579✔
732
                                _cleanup_free_ char *dest = NULL, *dest_name = NULL;
×
733

734
                                r = chase(p, lp->root_dir, CHASE_NONEXISTENT, &dest, NULL);
569✔
735
                                if (r == -ENOENT)
569✔
736
                                        continue;
×
737
                                if (r < 0) {
569✔
738
                                        log_debug_errno(r, "Failed to resolve symlink \"%s\": %m", p);
×
739
                                        RET_GATHER(ret, install_changes_add(changes, n_changes, r, p, NULL));
×
740
                                        continue;
×
741
                                }
742

743
                                r = path_extract_filename(dest, &dest_name);
569✔
744
                                if (r < 0)
569✔
745
                                        return r;
×
746

747
                                found = set_contains(remove_symlinks_to, dest) ||
1,138✔
748
                                        set_contains(remove_symlinks_to, dest_name);
569✔
749
                        }
750

751
                        if (!found)
569✔
752
                                continue;
568✔
753

754
                        if (!dry_run) {
33✔
755
                                if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
32✔
756
                                        RET_GATHER(ret, install_changes_add(changes, n_changes, -errno, p, NULL));
×
757
                                        continue;
×
758
                                }
759

760
                                (void) rmdir_parents(p, config_path);
32✔
761
                        }
762

763
                        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, p, NULL);
33✔
764
                        if (r < 0)
33✔
765
                                return r;
766

767
                        /* Now, remember the full path (but with the root prefix removed) of
768
                         * the symlink we just removed, and remove any symlinks to it, too. */
769

770
                        const char *rp = skip_root(lp->root_dir, p);
33✔
771
                        r = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
33✔
772
                        if (r < 0)
33✔
773
                                return r;
774
                        if (r > 0 && !dry_run)
33✔
775
                                *restart = true;
32✔
776
                }
777

778
        return ret;
779
}
780

781
static int remove_marked_symlinks(
38✔
782
                Set *remove_symlinks_to,
783
                const char *config_path,
784
                const LookupPaths *lp,
785
                bool dry_run,
786
                InstallChange **changes,
787
                size_t *n_changes) {
788

789
        _cleanup_close_ int fd = -EBADF;
38✔
790
        bool restart;
38✔
791
        int r = 0;
38✔
792

793
        assert(config_path);
38✔
794
        assert(lp);
38✔
795

796
        if (set_isempty(remove_symlinks_to))
38✔
797
                return 0;
798

799
        fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
30✔
800
        if (fd < 0)
30✔
801
                return errno == ENOENT ? 0 : -errno;
×
802

803
        do {
43✔
804
                int cfd;
43✔
805
                restart = false;
43✔
806

807
                cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
43✔
808
                if (cfd < 0)
43✔
809
                        return -errno;
×
810

811
                /* This takes possession of cfd and closes it */
812
                RET_GATHER(r, remove_marked_symlinks_fd(remove_symlinks_to,
43✔
813
                                                        cfd, config_path,
814
                                                        config_path, lp,
815
                                                        dry_run,
816
                                                        &restart,
817
                                                        changes, n_changes));
818
        } while (restart);
43✔
819

820
        return r;
821
}
822

823
static int is_symlink_with_known_name(const InstallInfo *i, const char *name) {
467✔
824
        int r;
467✔
825

826
        assert(i);
467✔
827
        assert(name);
467✔
828

829
        if (streq(name, i->name))
467✔
830
                return true;
831

832
        if (strv_contains(i->aliases, name))
61✔
833
                return true;
834

835
        /* Look for template symlink matching DefaultInstance */
836
        if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
54✔
837
                _cleanup_free_ char *s = NULL;
10✔
838

839
                r = unit_name_replace_instance(i->name, i->default_instance, &s);
10✔
840
                if (r < 0) {
10✔
841
                        if (r != -EINVAL)
×
842
                                return r;
843

844
                } else if (streq(name, s))
10✔
845
                        return true;
846
        }
847

848
        return false;
849
}
850

851
static int find_symlinks_in_directory(
143,972✔
852
                DIR *dir,
853
                const char *dir_path,
854
                const char *root_dir,
855
                const InstallInfo *info,
856
                bool ignore_destination,
857
                bool match_name,
858
                bool ignore_same_name,
859
                const char *config_path,
860
                bool *same_name_link) {
861

862
        int r, ret = 0;
143,972✔
863

864
        assert(dir);
143,972✔
865
        assert(dir_path);
143,972✔
866
        assert(info);
143,972✔
867
        assert(unit_name_is_valid(info->name, UNIT_NAME_ANY));
143,972✔
868
        assert(config_path);
143,972✔
869
        assert(same_name_link);
143,972✔
870

871
        FOREACH_DIRENT(de, dir, return -errno) {
2,942,833✔
872
                bool found_path = false, found_dest = false, b = false;
2,511,790✔
873

874
                if (de->d_type != DT_LNK)
2,511,790✔
875
                        continue;
1,895,640✔
876

877
                if (!ignore_destination) {
616,150✔
878
                        _cleanup_free_ char *dest = NULL;
90,645✔
879

880
                        /* Acquire symlink destination */
881
                        r = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
90,645✔
882
                        if (r < 0) {
90,645✔
883
                                if (r != -ENOENT)
×
884
                                        RET_GATHER(ret, r);
×
885
                                continue;
×
886
                        }
887

888
                        /* Check if what the symlink points to matches what we are looking for */
889
                        found_dest = path_equal_filename(dest, info->name);
90,645✔
890
                }
891

892
                /* Check if the symlink itself matches what we are looking for.
893
                 *
894
                 * If ignore_destination is specified, we only look at the source name.
895
                 *
896
                 * If ignore_same_name is specified, we are in one of the directories which
897
                 * have lower priority than the unit file, and even if a file or symlink with
898
                 * this name was found, we should ignore it. */
899

900
                if (ignore_destination || !ignore_same_name)
616,150✔
901
                        found_path = streq(de->d_name, info->name);
584,156✔
902

903
                if (!found_path && ignore_destination) {
616,150✔
904
                        _cleanup_free_ char *template = NULL;
524,841✔
905

906
                        r = unit_name_template(de->d_name, &template);
524,841✔
907
                        if (r < 0 && r != -EINVAL)
524,841✔
908
                                return r;
×
909
                        if (r >= 0)
524,841✔
910
                                found_dest = streq(template, info->name);
31,130✔
911
                }
912

913
                if (found_path && found_dest) {
616,150✔
914
                        _cleanup_free_ char *p = NULL, *t = NULL;
×
915

916
                        /* Filter out same name links in the main config path */
917
                        p = path_make_absolute(de->d_name, dir_path);
4✔
918
                        t = path_make_absolute(info->name, config_path);
4✔
919
                        if (!p || !t)
4✔
920
                                return -ENOMEM;
×
921

922
                        b = path_equal(p, t);
4✔
923
                }
924

925
                if (b)
4✔
926
                        *same_name_link = true;
4✔
927
                else if (found_path || found_dest) {
616,146✔
928
                        if (!match_name)
774✔
929
                                return 1;
930

931
                        /* Check if symlink name is in the set of names used by [Install] */
932
                        r = is_symlink_with_known_name(info, de->d_name);
467✔
933
                        if (r != 0)
467✔
934
                                return r;
935
                }
936
        }
937

938
        return ret;
939
}
940

941
static int find_symlinks(
55,280✔
942
                const char *root_dir,
943
                const InstallInfo *i,
944
                bool match_name,
945
                bool ignore_same_name,
946
                const char *config_path,
947
                bool *same_name_link) {
948

949
        _cleanup_closedir_ DIR *config_dir = NULL;
55,280✔
950
        int r;
55,280✔
951

952
        assert(i);
55,280✔
953
        assert(config_path);
55,280✔
954
        assert(same_name_link);
55,280✔
955

956
        config_dir = opendir(config_path);
55,280✔
957
        if (!config_dir) {
55,280✔
958
                if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
24,051✔
959
                        return 0;
960
                return -errno;
×
961
        }
962

963
        FOREACH_DIRENT(de, config_dir, return -errno) {
2,200,783✔
964
                const char *suffix;
2,107,895✔
965
                _cleanup_free_ const char *path = NULL;
2,107,895✔
966
                _cleanup_closedir_ DIR *d = NULL;
2,107,895✔
967

968
                if (de->d_type != DT_DIR)
2,107,895✔
969
                        continue;
1,913,827✔
970

971
                suffix = strrchr(de->d_name, '.');
194,068✔
972
                if (!STRPTR_IN_SET(suffix, ".wants", ".requires", ".upholds"))
194,068✔
973
                        continue;
80,644✔
974

975
                path = path_join(config_path, de->d_name);
113,424✔
976
                if (!path)
113,424✔
977
                        return -ENOMEM;
978

979
                d = opendir(path);
113,424✔
980
                if (!d) {
113,424✔
981
                        log_error_errno(errno, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path);
×
982
                        continue;
×
983
                }
984

985
                r = find_symlinks_in_directory(d, path, root_dir, i,
113,424✔
986
                                               /* ignore_destination= */ true,
987
                                               /* match_name= */ match_name,
988
                                               /* ignore_same_name= */ ignore_same_name,
989
                                               config_path,
990
                                               same_name_link);
991
                if (r > 0)
113,424✔
992
                        return 1;
681✔
993
                if (r < 0)
112,743✔
994
                        log_debug_errno(r, "Failed to look up symlinks in \"%s\": %m", path);
112,743✔
995
        }
996

997
        /* We didn't find any suitable symlinks in .wants, .requires or .upholds directories,
998
         * let's look for linked unit files in this directory. */
999
        rewinddir(config_dir);
30,548✔
1000
        return find_symlinks_in_directory(config_dir, config_path, root_dir, i,
30,548✔
1001
                                          /* ignore_destination= */ false,
1002
                                          /* match_name= */ match_name,
1003
                                          /* ignore_same_name= */ ignore_same_name,
1004
                                          config_path,
1005
                                          same_name_link);
1006
}
1007

1008
static int find_symlinks_in_scope(
4,593✔
1009
                RuntimeScope scope,
1010
                const LookupPaths *lp,
1011
                const InstallInfo *info,
1012
                bool match_name,
1013
                UnitFileState *state) {
1014

1015
        bool same_name_link_runtime = false, same_name_link_config = false;
4,593✔
1016
        bool enabled_in_runtime = false, enabled_at_all = false;
4,593✔
1017
        bool ignore_same_name = false;
4,593✔
1018
        int r;
4,593✔
1019

1020
        assert(lp);
4,593✔
1021
        assert(info);
4,593✔
1022

1023
        /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name"
1024
         * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
1025
         * effectively masked, so we should ignore them. */
1026

1027
        STRV_FOREACH(p, lp->search_path)  {
59,736✔
1028
                bool same_name_link = false;
55,280✔
1029

1030
                r = find_symlinks(lp->root_dir, info, match_name, ignore_same_name, *p, &same_name_link);
55,280✔
1031
                if (r < 0)
55,280✔
1032
                        return r;
137✔
1033
                if (r > 0) {
55,280✔
1034
                        /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
1035

1036
                        if (path_equal(*p, lp->persistent_config)) {
723✔
1037
                                /* This is the best outcome, let's return it immediately. */
1038
                                *state = UNIT_FILE_ENABLED;
130✔
1039
                                return 1;
130✔
1040
                        }
1041

1042
                        /* look for global enablement of user units */
1043
                        if (scope == RUNTIME_SCOPE_USER && path_is_user_config_dir(*p)) {
593✔
1044
                                *state = UNIT_FILE_ENABLED;
7✔
1045
                                return 1;
7✔
1046
                        }
1047

1048
                        r = path_is_runtime(lp, *p, false);
586✔
1049
                        if (r < 0)
586✔
1050
                                return r;
1051
                        if (r > 0)
586✔
1052
                                enabled_in_runtime = true;
1053
                        else
1054
                                enabled_at_all = true;
573✔
1055

1056
                } else if (same_name_link) {
54,557✔
1057
                        if (path_equal(*p, lp->persistent_config))
4✔
1058
                                same_name_link_config = true;
1059
                        else {
1060
                                r = path_is_runtime(lp, *p, false);
2✔
1061
                                if (r < 0)
2✔
1062
                                        return r;
1063
                                if (r > 0)
2✔
1064
                                        same_name_link_runtime = true;
×
1065
                        }
1066
                }
1067

1068
                /* Check if next iteration will be "below" the unit file (either a regular file
1069
                 * or a symlink), and hence should be ignored */
1070
                if (!ignore_same_name && path_startswith(info->path, *p))
55,143✔
1071
                        ignore_same_name = true;
4,452✔
1072
        }
1073

1074
        if (enabled_in_runtime) {
4,456✔
1075
                *state = UNIT_FILE_ENABLED_RUNTIME;
13✔
1076
                return 1;
13✔
1077
        }
1078

1079
        /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
1080
         * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
1081
         * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
1082
         * something, and hence are a much stronger concept. */
1083
        if (enabled_at_all && unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
4,443✔
1084
                *state = UNIT_FILE_STATIC;
9✔
1085
                return 1;
9✔
1086
        }
1087

1088
        /* Hmm, we didn't find it, but maybe we found the same name
1089
         * link? */
1090
        if (same_name_link_config) {
4,434✔
1091
                *state = UNIT_FILE_LINKED;
2✔
1092
                return 1;
2✔
1093
        }
1094
        if (same_name_link_runtime) {
4,432✔
1095
                *state = UNIT_FILE_LINKED_RUNTIME;
×
1096
                return 1;
×
1097
        }
1098

1099
        return 0;
1100
}
1101

1102
static void install_info_clear(InstallInfo *i) {
27,712✔
1103
        if (!i)
27,712✔
1104
                return;
1105

1106
        i->name = mfree(i->name);
27,712✔
1107
        i->path = mfree(i->path);
27,712✔
1108
        i->root = mfree(i->root);
27,712✔
1109
        i->aliases = strv_free(i->aliases);
27,712✔
1110
        i->wanted_by = strv_free(i->wanted_by);
27,712✔
1111
        i->required_by = strv_free(i->required_by);
27,712✔
1112
        i->upheld_by = strv_free(i->upheld_by);
27,712✔
1113
        i->also = strv_free(i->also);
27,712✔
1114
        i->default_instance = mfree(i->default_instance);
27,712✔
1115
        i->symlink_target = mfree(i->symlink_target);
27,712✔
1116
}
1117

1118
static InstallInfo* install_info_free(InstallInfo *i) {
25,860✔
1119
        install_info_clear(i);
25,860✔
1120
        return mfree(i);
25,860✔
1121
}
1122

1123
DEFINE_TRIVIAL_CLEANUP_FUNC(InstallInfo*, install_info_free);
25,860✔
1124

1125
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
25,860✔
1126
                install_info_hash_ops,
1127
                char, string_hash_func, string_compare_func,
1128
                InstallInfo, install_info_free);
1129

1130
static void install_context_done(InstallContext *ctx) {
15,001✔
1131
        assert(ctx);
15,001✔
1132

1133
        ctx->will_process = ordered_hashmap_free(ctx->will_process);
15,001✔
1134
        ctx->have_processed = ordered_hashmap_free(ctx->have_processed);
15,001✔
1135
}
15,001✔
1136

1137
static InstallInfo *install_info_find(InstallContext *ctx, const char *name) {
47,467✔
1138
        InstallInfo *i;
47,467✔
1139

1140
        i = ordered_hashmap_get(ctx->have_processed, name);
47,467✔
1141
        if (i)
47,467✔
1142
                return i;
1143

1144
        return ordered_hashmap_get(ctx->will_process, name);
47,447✔
1145
}
1146

1147
static int install_info_may_process(
519✔
1148
                const InstallInfo *i,
1149
                const LookupPaths *lp,
1150
                InstallChange **changes,
1151
                size_t *n_changes) {
1152
        assert(i);
519✔
1153
        assert(lp);
519✔
1154

1155
        /* Checks whether the loaded unit file is one we should process, or is masked,
1156
         * transient or generated and thus not subject to enable/disable operations. */
1157

1158
        if (i->install_mode == INSTALL_MODE_MASKED)
519✔
1159
                return install_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
21✔
1160
        if (path_is_generator(lp, i->path) ||
996✔
1161
            path_is_transient(lp, i->path))
498✔
1162
                return install_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
×
1163

1164
        return 0;
1165
}
1166

1167
/**
1168
 * Adds a new InstallInfo entry under name in the InstallContext.will_process
1169
 * hashmap, or retrieves the existing one if already present.
1170
 *
1171
 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1172
 */
1173
static int install_info_add(
25,929✔
1174
                InstallContext *ctx,
1175
                const char *name,
1176
                const char *path,
1177
                const char *root,
1178
                bool auxiliary,
1179
                InstallInfo **ret) {
1180

1181
        _cleanup_free_ char *name_alloc = NULL;
25,929✔
1182
        int r;
25,929✔
1183

1184
        assert(ctx);
25,929✔
1185

1186
        if (!name) {
25,929✔
1187
                assert(path);
2✔
1188

1189
                r = path_extract_filename(path, &name_alloc);
2✔
1190
                if (r < 0)
2✔
1191
                        return r;
1192

1193
                name = name_alloc;
2✔
1194
        }
1195

1196
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
25,929✔
1197
                return -EINVAL;
1198

1199
        InstallInfo *i = install_info_find(ctx, name);
25,929✔
1200
        if (i) {
25,929✔
1201
                i->auxiliary = i->auxiliary && auxiliary;
69✔
1202

1203
                if (ret)
69✔
1204
                        *ret = i;
9✔
1205
                return 0;
69✔
1206
        }
1207

1208
        _cleanup_(install_info_freep) InstallInfo *new_info = new(InstallInfo, 1);
51,720✔
1209
        if (!new_info)
25,860✔
1210
                return -ENOMEM;
1211

1212
        *new_info = (InstallInfo) {
25,860✔
1213
                .install_mode = _INSTALL_MODE_INVALID,
1214
                .auxiliary = auxiliary,
1215
        };
1216

1217
        if (name_alloc)
25,860✔
1218
                new_info->name = TAKE_PTR(name_alloc);
2✔
1219
        else {
1220
                new_info->name = strdup(name);
25,858✔
1221
                if (!new_info->name)
25,858✔
1222
                        return -ENOMEM;
1223
        }
1224

1225
        r = strdup_to(&new_info->root, root);
25,860✔
1226
        if (r < 0)
25,860✔
1227
                return r;
1228

1229
        r = strdup_to(&new_info->path, path);
25,860✔
1230
        if (r < 0)
25,860✔
1231
                return r;
1232

1233
        r = ordered_hashmap_ensure_put(&ctx->will_process, &install_info_hash_ops, new_info->name, new_info);
25,860✔
1234
        if (r < 0)
25,860✔
1235
                return r;
1236
        i = TAKE_PTR(new_info);
25,860✔
1237

1238
        if (ret)
25,860✔
1239
                *ret = i;
25,641✔
1240
        return 1;
1241
}
1242

1243
static int config_parse_alias(
148✔
1244
                const char *unit,
1245
                const char *filename,
1246
                unsigned line,
1247
                const char *section,
1248
                unsigned section_line,
1249
                const char *lvalue,
1250
                int ltype,
1251
                const char *rvalue,
1252
                void *data,
1253
                void *userdata) {
1254

1255
        UnitType type;
148✔
1256

1257
        assert(unit);
148✔
1258
        assert(filename);
148✔
1259
        assert(lvalue);
148✔
1260
        assert(rvalue);
148✔
1261

1262
        type = unit_name_to_type(unit);
148✔
1263
        if (!unit_type_may_alias(type))
148✔
1264
                return log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1265
                                  "Alias= is not allowed for %s units, ignoring.",
1266
                                  unit_type_to_string(type));
1267

1268
        return config_parse_strv(unit, filename, line, section, section_line,
148✔
1269
                                 lvalue, ltype, rvalue, data, userdata);
1270
}
1271

1272
static int config_parse_also(
202✔
1273
                const char *unit,
1274
                const char *filename,
1275
                unsigned line,
1276
                const char *section,
1277
                unsigned section_line,
1278
                const char *lvalue,
1279
                int ltype,
1280
                const char *rvalue,
1281
                void *data,
1282
                void *userdata) {
1283

1284
        InstallInfo *info = ASSERT_PTR(userdata);
202✔
1285
        InstallContext *ctx = ASSERT_PTR(data);
202✔
1286
        int r;
202✔
1287

1288
        assert(unit);
202✔
1289
        assert(filename);
202✔
1290
        assert(lvalue);
202✔
1291
        assert(rvalue);
202✔
1292

1293
        for (;;) {
279✔
1294
                _cleanup_free_ char *word = NULL, *printed = NULL;
×
1295

1296
                r = extract_first_word(&rvalue, &word, NULL, 0);
481✔
1297
                if (r < 0)
481✔
1298
                        return r;
1299
                if (r == 0)
481✔
1300
                        break;
1301

1302
                r = install_name_printf(ctx->scope, info, word, &printed);
279✔
1303
                if (r < 0)
279✔
1304
                        return log_syntax(unit, LOG_WARNING, filename, line, r,
×
1305
                                          "Failed to resolve unit name in Also=\"%s\": %m", word);
1306

1307
                r = install_info_add(ctx, printed, NULL, info->root, /* auxiliary= */ true, NULL);
279✔
1308
                if (r < 0)
279✔
1309
                        return r;
1310

1311
                r = strv_push(&info->also, printed);
279✔
1312
                if (r < 0)
279✔
1313
                        return r;
1314

1315
                printed = NULL;
279✔
1316
        }
1317

1318
        return 0;
202✔
1319
}
1320

1321
static int config_parse_default_instance(
99✔
1322
                const char *unit,
1323
                const char *filename,
1324
                unsigned line,
1325
                const char *section,
1326
                unsigned section_line,
1327
                const char *lvalue,
1328
                int ltype,
1329
                const char *rvalue,
1330
                void *data,
1331
                void *userdata) {
1332

1333
        InstallContext *ctx = ASSERT_PTR(data);
99✔
1334
        InstallInfo *info = ASSERT_PTR(userdata);
99✔
1335
        _cleanup_free_ char *printed = NULL;
99✔
1336
        int r;
99✔
1337

1338
        assert(unit);
99✔
1339
        assert(filename);
99✔
1340
        assert(lvalue);
99✔
1341
        assert(rvalue);
99✔
1342

1343
        if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE))
99✔
1344
                /* When enabling an instance, we might be using a template unit file,
1345
                 * but we should ignore DefaultInstance silently. */
1346
                return 0;
1347
        if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
48✔
1348
                return log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1349
                                  "DefaultInstance= only makes sense for template units, ignoring.");
1350

1351
        r = install_name_printf(ctx->scope, info, rvalue, &printed);
48✔
1352
        if (r < 0)
48✔
1353
                return log_syntax(unit, LOG_WARNING, filename, line, r,
×
1354
                                  "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue);
1355

1356
        if (isempty(printed))
48✔
1357
                printed = mfree(printed);
×
1358

1359
        if (printed && !unit_instance_is_valid(printed))
48✔
1360
                return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
×
1361
                                  "Invalid DefaultInstance= value \"%s\".", printed);
1362

1363
        return free_and_replace(info->default_instance, printed);
48✔
1364
}
1365

1366
static int unit_file_load(
282,700✔
1367
                InstallContext *ctx,
1368
                InstallInfo *info,
1369
                const char *path,
1370
                const char *root_dir,
1371
                SearchFlags flags) {
1372

1373
        const ConfigTableItem items[] = {
282,700✔
1374
                { "Install", "Alias",           config_parse_alias,            0, &info->aliases           },
282,700✔
1375
                { "Install", "WantedBy",        config_parse_strv,             0, &info->wanted_by         },
282,700✔
1376
                { "Install", "RequiredBy",      config_parse_strv,             0, &info->required_by       },
282,700✔
1377
                { "Install", "UpheldBy",        config_parse_strv,             0, &info->upheld_by         },
282,700✔
1378
                { "Install", "DefaultInstance", config_parse_default_instance, 0, info                     },
1379
                { "Install", "Also",            config_parse_also,             0, ctx                      },
1380
                {}
1381
        };
1382

1383
        UnitType type;
282,700✔
1384
        _cleanup_fclose_ FILE *f = NULL;
282,700✔
1385
        _cleanup_close_ int fd = -EBADF;
282,700✔
1386
        struct stat st;
282,700✔
1387
        int r;
282,700✔
1388

1389
        assert(info);
282,700✔
1390
        assert(path);
282,700✔
1391

1392
        if (!(flags & SEARCH_DROPIN)) {
282,700✔
1393
                /* Loading or checking for the main unit file… */
1394

1395
                type = unit_name_to_type(info->name);
279,725✔
1396
                if (type < 0)
279,725✔
1397
                        return -EINVAL;
1398
                if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type))
279,725✔
1399
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1400
                                               "%s: unit type %s cannot be templated, ignoring.", path, unit_type_to_string(type));
1401

1402
                if (!(flags & SEARCH_LOAD)) {
279,725✔
1403
                        if (lstat(path, &st) < 0)
244,801✔
1404
                                return -errno;
223,226✔
1405

1406
                        if (null_or_empty(&st))
21,575✔
1407
                                info->install_mode = INSTALL_MODE_MASKED;
×
1408
                        else if (S_ISREG(st.st_mode))
21,575✔
1409
                                info->install_mode = INSTALL_MODE_REGULAR;
21,042✔
1410
                        else if (S_ISLNK(st.st_mode))
533✔
1411
                                return -ELOOP;
1412
                        else if (S_ISDIR(st.st_mode))
×
1413
                                return -EISDIR;
1414
                        else
1415
                                return -ENOTTY;
×
1416

1417
                        return 0;
21,042✔
1418
                }
1419

1420
                fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
34,924✔
1421
                if (fd < 0)
34,924✔
1422
                        return -errno;
31,830✔
1423
        } else {
1424
                /* 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. */
1425

1426
                if (!(flags & SEARCH_LOAD))
2,975✔
1427
                        return 0;
1428

1429
                fd = chase_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
2,367✔
1430
                if (fd < 0)
2,367✔
1431
                        return fd;
1432
        }
1433

1434
        if (fstat(fd, &st) < 0)
5,461✔
1435
                return -errno;
×
1436

1437
        if (null_or_empty(&st)) {
5,461✔
1438
                if ((flags & SEARCH_DROPIN) == 0)
×
1439
                        info->install_mode = INSTALL_MODE_MASKED;
×
1440

1441
                return 0;
×
1442
        }
1443

1444
        r = stat_verify_regular(&st);
5,461✔
1445
        if (r < 0)
5,461✔
1446
                return r;
1447

1448
        f = take_fdopen(&fd, "r");
5,461✔
1449
        if (!f)
5,461✔
1450
                return -errno;
×
1451

1452
        /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1453
        assert(ctx);
5,461✔
1454

1455
        r = config_parse(info->name, path, f,
5,461✔
1456
                         "Install\0"
1457
                         "-Unit\0"
1458
                         "-Automount\0"
1459
                         "-Device\0"
1460
                         "-Mount\0"
1461
                         "-Path\0"
1462
                         "-Scope\0"
1463
                         "-Service\0"
1464
                         "-Slice\0"
1465
                         "-Socket\0"
1466
                         "-Swap\0"
1467
                         "-Target\0"
1468
                         "-Timer\0",
1469
                         config_item_table_lookup, items,
1470
                         0, info,
1471
                         NULL);
1472
        if (r < 0)
5,461✔
1473
                return log_debug_errno(r, "Failed to parse \"%s\": %m", info->name);
×
1474

1475
        if ((flags & SEARCH_DROPIN) == 0)
5,461✔
1476
                info->install_mode = INSTALL_MODE_REGULAR;
3,094✔
1477

1478
        return
5,461✔
1479
                (int) strv_length(info->aliases) +
5,461✔
1480
                (int) strv_length(info->wanted_by) +
5,461✔
1481
                (int) strv_length(info->required_by) +
5,461✔
1482
                (int) strv_length(info->upheld_by);
5,461✔
1483
}
1484

1485
static int unit_file_load_or_readlink(
282,700✔
1486
                InstallContext *ctx,
1487
                InstallInfo *info,
1488
                const char *path,
1489
                const LookupPaths *lp,
1490
                SearchFlags flags) {
1491
        int r;
282,700✔
1492

1493
        r = unit_file_load(ctx, info, path, lp->root_dir, flags);
282,700✔
1494
        if (r != -ELOOP || (flags & SEARCH_DROPIN))
282,700✔
1495
                return r;
1496

1497
        /* This is a symlink, let's read and verify it. */
1498
        r = unit_file_resolve_symlink(lp->root_dir, lp->search_path,
679✔
1499
                                      NULL, AT_FDCWD, path,
1500
                                      true, &info->symlink_target);
1501
        if (r < 0)
679✔
1502
                return r;
1503
        bool outside_search_path = r > 0;
678✔
1504

1505
        r = null_or_empty_path_with_root(info->symlink_target, lp->root_dir);
678✔
1506
        if (r < 0 && r != -ENOENT)
678✔
1507
                return log_debug_errno(r, "Failed to stat %s: %m", info->symlink_target);
×
1508
        if (r > 0)
678✔
1509
                info->install_mode = INSTALL_MODE_MASKED;
181✔
1510
        else if (outside_search_path)
497✔
1511
                info->install_mode = INSTALL_MODE_LINKED;
9✔
1512
        else
1513
                info->install_mode = INSTALL_MODE_ALIAS;
488✔
1514

1515
        return 0;
1516
}
1517

1518
static int unit_file_search(
26,357✔
1519
                InstallContext *ctx,
1520
                InstallInfo *info,
1521
                const LookupPaths *lp,
1522
                SearchFlags flags) {
1523

1524
        const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
26,357✔
1525
        _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
26,357✔
1526
        _cleanup_free_ char *template = NULL;
26,357✔
1527
        bool found_unit = false;
26,357✔
1528
        int r, result;
26,357✔
1529

1530
        assert(info);
26,357✔
1531
        assert(lp);
26,357✔
1532

1533
        /* Was this unit already loaded? */
1534
        if (info->install_mode != _INSTALL_MODE_INVALID)
26,357✔
1535
                return 0;
1536

1537
        if (info->path)
25,787✔
1538
                return unit_file_load_or_readlink(ctx, info, info->path, lp, flags);
2✔
1539

1540
        assert(info->name);
25,785✔
1541

1542
        if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
25,785✔
1543
                r = unit_name_template(info->name, &template);
124✔
1544
                if (r < 0)
124✔
1545
                        return r;
1546
        }
1547

1548
        STRV_FOREACH(p, lp->search_path) {
279,415✔
1549
                _cleanup_free_ char *path = NULL;
253,631✔
1550

1551
                path = path_join(*p, info->name);
278,314✔
1552
                if (!path)
278,314✔
1553
                        return -ENOMEM;
1554

1555
                r = unit_file_load_or_readlink(ctx, info, path, lp, flags);
278,314✔
1556
                if (r >= 0) {
278,314✔
1557
                        info->path = TAKE_PTR(path);
24,683✔
1558
                        result = r;
24,683✔
1559
                        found_unit = true;
24,683✔
1560
                        break;
24,683✔
1561
                } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
253,631✔
1562
                        return r;
1563
        }
1564

1565
        if (!found_unit && template) {
1,101✔
1566

1567
                /* Unit file doesn't exist, however instance
1568
                 * enablement was requested.  We will check if it is
1569
                 * possible to load template unit file. */
1570

1571
                STRV_FOREACH(p, lp->search_path) {
1,399✔
1572
                        _cleanup_free_ char *path = NULL;
1,280✔
1573

1574
                        path = path_join(*p, template);
1,395✔
1575
                        if (!path)
1,395✔
1576
                                return -ENOMEM;
1577

1578
                        r = unit_file_load_or_readlink(ctx, info, path, lp, flags);
1,395✔
1579
                        if (r >= 0) {
1,395✔
1580
                                info->path = TAKE_PTR(path);
115✔
1581
                                result = r;
115✔
1582
                                found_unit = true;
115✔
1583
                                break;
115✔
1584
                        } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1,280✔
1585
                                return r;
1586
                }
1587
        }
1588

1589
        if (!found_unit)
24,798✔
1590
                return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
2,950✔
1591
                                       "Cannot find unit %s%s%s.",
1592
                                       info->name, template ? " or " : "", strempty(template));
1593

1594
        if (info->install_mode == INSTALL_MODE_MASKED)
24,798✔
1595
                return result;
1596

1597
        /* Search for drop-in directories */
1598

1599
        dropin_dir_name = strjoina(info->name, ".d");
123,085✔
1600
        STRV_FOREACH(p, lp->search_path) {
364,444✔
1601
                char *path;
339,827✔
1602

1603
                path = path_join(*p, dropin_dir_name);
339,827✔
1604
                if (!path)
339,827✔
1605
                        return -ENOMEM;
1606

1607
                r = strv_consume(&dirs, path);
339,827✔
1608
                if (r < 0)
339,827✔
1609
                        return r;
1610
        }
1611

1612
        if (template) {
24,617✔
1613
                dropin_template_dir_name = strjoina(template, ".d");
600✔
1614
                STRV_FOREACH(p, lp->search_path) {
1,642✔
1615
                        char *path;
1,522✔
1616

1617
                        path = path_join(*p, dropin_template_dir_name);
1,522✔
1618
                        if (!path)
1,522✔
1619
                                return -ENOMEM;
1620

1621
                        r = strv_consume(&dirs, path);
1,522✔
1622
                        if (r < 0)
1,522✔
1623
                                return r;
1624
                }
1625
        }
1626

1627
        /* Load drop-in conf files */
1628

1629
        r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs);
24,617✔
1630
        if (r < 0)
24,617✔
1631
                return log_debug_errno(r, "Failed to get list of conf files: %m");
×
1632

1633
        STRV_FOREACH(p, files) {
27,592✔
1634
                r = unit_file_load_or_readlink(ctx, info, *p, lp, flags | SEARCH_DROPIN);
2,975✔
1635
                if (r < 0)
2,975✔
1636
                        return log_debug_errno(r, "Failed to load conf file \"%s\": %m", *p);
×
1637
        }
1638

1639
        return result;
1640
}
1641

1642
static int install_info_follow(
511✔
1643
                InstallContext *ctx,
1644
                InstallInfo *info,
1645
                const LookupPaths *lp,
1646
                SearchFlags flags,
1647
                bool ignore_different_name) {
1648

1649
        assert(ctx);
511✔
1650
        assert(info);
511✔
1651

1652
        if (!IN_SET(info->install_mode, INSTALL_MODE_ALIAS, INSTALL_MODE_LINKED))
511✔
1653
                return -EINVAL;
1654
        if (!info->symlink_target)
511✔
1655
                return -EINVAL;
1656

1657
        /* If the basename doesn't match, the caller should add a complete new entry for this. */
1658

1659
        if (!ignore_different_name && !path_equal_filename(info->symlink_target, info->name))
511✔
1660
                return -EXDEV;
1661

1662
        free_and_replace(info->path, info->symlink_target);
14✔
1663
        info->install_mode = _INSTALL_MODE_INVALID;
14✔
1664

1665
        return unit_file_load_or_readlink(ctx, info, info->path, lp, flags);
14✔
1666
}
1667

1668
/**
1669
 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1670
 * target, maybe more than once. Propagate the instance name if present.
1671
 */
1672
static int install_info_traverse(
25,842✔
1673
                InstallContext *ctx,
1674
                const LookupPaths *lp,
1675
                InstallInfo *start,
1676
                SearchFlags flags,
1677
                InstallInfo **ret) {
1678

1679
        InstallInfo *i;
25,842✔
1680
        unsigned k = 0;
25,842✔
1681
        int r;
25,842✔
1682

1683
        assert(ctx);
25,842✔
1684
        assert(lp);
25,842✔
1685
        assert(start);
25,842✔
1686

1687
        r = unit_file_search(ctx, start, lp, flags);
25,842✔
1688
        if (r < 0)
25,842✔
1689
                return r;
25,842✔
1690

1691
        i = start;
24,855✔
1692
        while (IN_SET(i->install_mode, INSTALL_MODE_ALIAS, INSTALL_MODE_LINKED)) {
25,361✔
1693
                /* Follow the symlink */
1694

1695
                if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
506✔
1696
                        return -ELOOP;
1697

1698
                if (!FLAGS_SET(flags, SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
506✔
1699
                        r = path_is_config(lp, i->path, true);
6✔
1700
                        if (r < 0)
6✔
1701
                                return r;
1702
                        if (r > 0)
6✔
1703
                                return -ELOOP;
1704
                }
1705

1706
                r = install_info_follow(ctx, i, lp, flags,
1,012✔
1707
                                        /* If linked, don't look at the target name */
1708
                                        /* ignore_different_name= */ i->install_mode == INSTALL_MODE_LINKED);
506✔
1709
                if (r == -EXDEV && i->symlink_target) {
506✔
1710
                        _cleanup_free_ char *target_name = NULL, *unit_instance = NULL;
492✔
1711
                        const char *bn;
497✔
1712

1713
                        /* Target is an alias, create a new install info object and continue with that. */
1714

1715
                        r = path_extract_filename(i->symlink_target, &target_name);
497✔
1716
                        if (r < 0)
497✔
1717
                                return r;
1718

1719
                        if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
519✔
1720
                            unit_name_is_valid(target_name, UNIT_NAME_TEMPLATE)) {
22✔
1721

1722
                                _cleanup_free_ char *instance = NULL;
22✔
1723

1724
                                r = unit_name_to_instance(i->name, &instance);
22✔
1725
                                if (r < 0)
22✔
1726
                                        return r;
1727

1728
                                r = unit_name_replace_instance(target_name, instance, &unit_instance);
22✔
1729
                                if (r < 0)
22✔
1730
                                        return r;
1731

1732
                                if (streq(unit_instance, i->name)) {
22✔
1733
                                        /* We filled in the instance, and the target stayed the same? If so,
1734
                                         * then let's honour the link as it is. */
1735

1736
                                        r = install_info_follow(ctx, i, lp, flags, true);
5✔
1737
                                        if (r < 0)
5✔
1738
                                                return r;
1739

1740
                                        continue;
5✔
1741
                                }
1742

1743
                                bn = unit_instance;
17✔
1744
                        } else
1745
                                bn = target_name;
475✔
1746

1747
                        r = install_info_add(ctx, bn, NULL, lp->root_dir, /* auxiliary= */ false, &i);
492✔
1748
                        if (r < 0)
492✔
1749
                                return r;
1750

1751
                        /* Try again, with the new target we found. */
1752
                        r = unit_file_search(ctx, i, lp, flags);
492✔
1753
                        if (r == -ENOENT)
492✔
1754
                                /* Translate error code to highlight this specific case */
1755
                                return -ENOLINK;
1756
                }
1757
                if (r < 0)
501✔
1758
                        return r;
1759
        }
1760

1761
        if (ret)
24,855✔
1762
                *ret = i;
23,538✔
1763

1764
        return 0;
1765
}
1766

1767
/**
1768
 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1769
 * or the name (otherwise). root_dir is prepended to the path.
1770
 */
1771
static int install_info_add_auto(
25,143✔
1772
                InstallContext *ctx,
1773
                const LookupPaths *lp,
1774
                const char *name_or_path,
1775
                InstallInfo **ret) {
1776

1777
        assert(ctx);
25,143✔
1778
        assert(name_or_path);
25,143✔
1779

1780
        if (path_is_absolute(name_or_path)) {
25,143✔
1781
                _cleanup_free_ char *pp = path_join(lp->root_dir, name_or_path);
4✔
1782
                if (!pp)
2✔
1783
                        return -ENOMEM;
1784

1785
                return install_info_add(ctx, NULL, pp, lp->root_dir, /* auxiliary= */ false, ret);
2✔
1786
        } else
1787
                return install_info_add(ctx, name_or_path, NULL, lp->root_dir, /* auxiliary= */ false, ret);
25,141✔
1788
}
1789

1790
static int install_info_discover(
25,143✔
1791
                InstallContext *ctx,
1792
                const LookupPaths *lp,
1793
                const char *name_or_path,
1794
                SearchFlags flags,
1795
                InstallInfo **ret,
1796
                InstallChange **changes,
1797
                size_t *n_changes) {
1798

1799
        InstallInfo *info;
25,143✔
1800
        int r;
25,143✔
1801

1802
        assert(ctx);
25,143✔
1803
        assert(lp);
25,143✔
1804
        assert(name_or_path);
25,143✔
1805

1806
        r = install_info_add_auto(ctx, lp, name_or_path, &info);
25,143✔
1807
        if (r >= 0)
25,143✔
1808
                r = install_info_traverse(ctx, lp, info, flags, ret);
25,143✔
1809

1810
        if (r < 0)
25,143✔
1811
                return install_changes_add(changes, n_changes, r, name_or_path, NULL);
987✔
1812

1813
        return r;
1814
}
1815

1816
static int install_info_discover_and_check(
520✔
1817
                InstallContext *ctx,
1818
                const LookupPaths *lp,
1819
                const char *name_or_path,
1820
                SearchFlags flags,
1821
                InstallInfo **ret,
1822
                InstallChange **changes,
1823
                size_t *n_changes) {
1824

1825
        int r;
520✔
1826

1827
        r = install_info_discover(ctx, lp, name_or_path, flags, ret, changes, n_changes);
520✔
1828
        if (r < 0)
520✔
1829
                return r;
1830

1831
        return install_info_may_process(ret ? *ret : NULL, lp, changes, n_changes);
519✔
1832
}
1833

1834
int unit_file_verify_alias(
222✔
1835
                const InstallInfo *info,
1836
                const char *dst,
1837
                char **ret_dst,
1838
                InstallChange **changes,
1839
                size_t *n_changes) {
1840

1841
        _cleanup_free_ char *dst_updated = NULL;
222✔
1842
        int r;
222✔
1843

1844
        /* Verify that dst is a valid either a valid alias or a valid .wants/.requires symlink for the target
1845
         * unit *i. Return negative on error or if not compatible, zero on success.
1846
         *
1847
         * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
1848
         * inserted into dst. It is not normally set, even on success, so that the caller can easily
1849
         * distinguish the case where instance propagation occurred.
1850
         *
1851
         * Returns:
1852
         * -EXDEV when the alias doesn't match the unit,
1853
         * -EUCLEAN when the name is invalid,
1854
         * -ELOOP when the alias it to the unit itself.
1855
         */
1856

1857
        const char *path_alias = strrchr(dst, '/');
222✔
1858
        if (path_alias) {
222✔
1859
                /* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
1860
                _cleanup_free_ char *dir = NULL;
89✔
1861
                char *p;
89✔
1862

1863
                path_alias++; /* skip over slash */
89✔
1864

1865
                r = path_extract_directory(dst, &dir);
89✔
1866
                if (r < 0)
89✔
1867
                        return log_error_errno(r, "Failed to extract parent directory from '%s': %m", dst);
×
1868

1869
                p = endswith(dir, ".wants");
89✔
1870
                if (!p)
89✔
1871
                        p = endswith(dir, ".requires");
57✔
1872
                if (!p) {
57✔
1873
                        r = install_changes_add(changes, n_changes, -EXDEV, dst, NULL);
10✔
1874
                        if (r != -EXDEV)
10✔
1875
                                return r;
1876

1877
                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), "Invalid path \"%s\" in alias.", dir);
10✔
1878
                }
1879

1880
                *p = '\0'; /* dir should now be a unit name */
79✔
1881

1882
                UnitNameFlags type = unit_name_classify(dir);
79✔
1883
                if (type < 0) {
79✔
1884
                        r = install_changes_add(changes, n_changes, -EXDEV, dst, NULL);
1✔
1885
                        if (r != -EXDEV)
1✔
1886
                                return r;
1887
                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
1✔
1888
                                               "Invalid unit name component \"%s\" in alias.", dir);
1889
                }
1890

1891
                const bool instance_propagation = type == UNIT_NAME_TEMPLATE;
78✔
1892

1893
                /* That's the name we want to use for verification. */
1894
                r = unit_symlink_name_compatible(path_alias, info->name, instance_propagation);
78✔
1895
                if (r < 0)
78✔
1896
                        return log_error_errno(r, "Failed to verify alias validity: %m");
×
1897
                if (r == 0) {
78✔
1898
                        r = install_changes_add(changes, n_changes, -EXDEV, dst, info->name);
65✔
1899
                        if (r != -EXDEV)
65✔
1900
                                return r;
1901

1902
                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
65✔
1903
                                               "Invalid unit \"%s\" symlink \"%s\".",
1904
                                               info->name, dst);
1905
                }
1906

1907
        } else {
1908
                /* If the symlink target has an instance set and the symlink source doesn't, we "propagate
1909
                 * the instance", i.e. instantiate the symlink source with the target instance. */
1910
                if (unit_name_is_valid(dst, UNIT_NAME_TEMPLATE)) {
133✔
1911
                        _cleanup_free_ char *inst = NULL;
9✔
1912

1913
                        UnitNameFlags type = unit_name_to_instance(info->name, &inst);
9✔
1914
                        if (type < 0) {
9✔
1915
                                r = install_changes_add(changes, n_changes, -EUCLEAN, info->name, NULL);
×
1916
                                if (r != -EUCLEAN)
×
1917
                                        return r;
1918
                                return log_debug_errno(type, "Failed to extract instance name from \"%s\": %m", info->name);
×
1919
                        }
1920

1921
                        if (type == UNIT_NAME_INSTANCE) {
9✔
1922
                                r = unit_name_replace_instance(dst, inst, &dst_updated);
4✔
1923
                                if (r < 0)
4✔
1924
                                        return log_error_errno(r, "Failed to build unit name from %s+%s: %m",
×
1925
                                                               dst, inst);
1926
                        }
1927
                }
1928

1929
                r = unit_validate_alias_symlink_or_warn(LOG_DEBUG, dst_updated ?: dst, info->name);
262✔
1930
                if (r == -ELOOP)  /* -ELOOP means self-alias, which we (quietly) ignore */
133✔
1931
                        return r;
1932
                if (r < 0)
133✔
1933
                        return install_changes_add(changes, n_changes,
21✔
1934
                                                   r == -EINVAL ? -EXDEV : r,
1935
                                                   dst_updated ?: dst,
21✔
1936
                                                   info->name);
21✔
1937
        }
1938

1939
        *ret_dst = TAKE_PTR(dst_updated);
125✔
1940
        return 0;
125✔
1941
}
1942

1943
static int install_info_symlink_alias(
617✔
1944
                RuntimeScope scope,
1945
                InstallInfo *info,
1946
                const LookupPaths *lp,
1947
                const char *config_path,
1948
                bool force,
1949
                InstallChange **changes,
1950
                size_t *n_changes) {
1951

1952
        int r, ret = 0;
617✔
1953

1954
        assert(info);
617✔
1955
        assert(lp);
617✔
1956
        assert(config_path);
617✔
1957

1958
        STRV_FOREACH(s, info->aliases) {
719✔
1959
                _cleanup_free_ char *alias_path = NULL, *alias_target = NULL, *dst = NULL, *dst_updated = NULL;
102✔
1960

1961
                r = install_name_printf(scope, info, *s, &dst);
102✔
1962
                if (r < 0) {
102✔
1963
                        RET_GATHER(ret, install_changes_add(changes, n_changes, r, *s, NULL));
×
1964
                        continue;
×
1965
                }
1966

1967
                r = unit_file_verify_alias(info, dst, &dst_updated, changes, n_changes);
102✔
1968
                if (r < 0) {
102✔
1969
                        if (r != -ELOOP)
×
1970
                                RET_GATHER(ret, r);
×
1971
                        continue;
×
1972
                }
1973

1974
                alias_path = path_make_absolute(dst_updated ?: dst, config_path);
102✔
1975
                if (!alias_path)
102✔
1976
                        return -ENOMEM;
1977

1978
                r = in_search_path(lp, info->path);
102✔
1979
                if (r < 0)
102✔
1980
                        return r;
1981
                if (r == 0) {
102✔
1982
                        /* The unit path itself is outside of the search path. To
1983
                         * correctly apply the alias, we need the alias symlink to
1984
                         * point to the symlink that was created in the search path. */
1985
                        alias_target = path_join(config_path, info->name);
1✔
1986
                        if (!alias_target)
1✔
1987
                                return -ENOMEM;
1988
                }
1989

1990
                bool broken;
102✔
1991
                r = chase(alias_path, lp->root_dir, CHASE_NONEXISTENT, /* ret_path = */ NULL, /* ret_fd = */ NULL);
102✔
1992
                if (r < 0 && r != -ENOENT) {
102✔
1993
                        RET_GATHER(ret, r);
×
1994
                        continue;
×
1995
                }
1996
                broken = r == 0; /* symlink target does not exist? */
102✔
1997

1998
                r = create_symlink(lp, alias_target ?: info->path, alias_path, force || broken, changes, n_changes);
102✔
1999
                if (r != 0 && ret >= 0)
102✔
2000
                        ret = r;
102✔
2001
        }
2002

2003
        return ret;
2004
}
2005

2006
static int install_info_symlink_wants(
1,851✔
2007
                RuntimeScope scope,
2008
                UnitFileFlags file_flags,
2009
                InstallInfo *info,
2010
                const LookupPaths *lp,
2011
                const char *config_path,
2012
                char **list,
2013
                const char *suffix,
2014
                InstallChange **changes,
2015
                size_t *n_changes) {
2016

2017
        _cleanup_(install_info_clear) InstallInfo instance = {
×
2018
                .install_mode = _INSTALL_MODE_INVALID,
2019
        };
2020

2021
        UnitNameFlags valid_dst_type = UNIT_NAME_ANY;
1,851✔
2022
        const char *n;
1,851✔
2023
        int r, q;
1,851✔
2024

2025
        assert(info);
1,851✔
2026
        assert(lp);
1,851✔
2027
        assert(config_path);
1,851✔
2028

2029
        if (strv_isempty(list))
2,425✔
2030
                return 0;
2031

2032
        if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE))
574✔
2033
                /* Not a template unit. Use the name directly. */
2034
                n = info->name;
2035

2036
        else if (info->default_instance) {
22✔
2037
                /* If this is a template, and we have a default instance, use it. */
2038

2039
                r = unit_name_replace_instance(info->name, info->default_instance, &instance.name);
22✔
2040
                if (r < 0)
22✔
2041
                        return r;
2042

2043
                r = unit_file_search(NULL, &instance, lp, SEARCH_FOLLOW_CONFIG_SYMLINKS);
22✔
2044
                if (r < 0)
22✔
2045
                        return r;
2046

2047
                if (instance.install_mode == INSTALL_MODE_MASKED)
22✔
2048
                        return install_changes_add(changes, n_changes, -ERFKILL, instance.path, NULL);
×
2049

2050
                n = instance.name;
22✔
2051

2052
        } else {
2053
                /* We have a template, but no instance yet. When used with an instantiated unit, we will get
2054
                 * the instance from that unit. Cannot be used with non-instance units. */
2055

2056
                valid_dst_type = UNIT_NAME_INSTANCE | UNIT_NAME_TEMPLATE;
2057
                n = info->name;
2058
        }
2059

2060
        r = 0;
574✔
2061
        STRV_FOREACH(s, list) {
1,193✔
2062
                _cleanup_free_ char *path = NULL, *dst = NULL;
619✔
2063

2064
                q = install_name_printf(scope, info, *s, &dst);
619✔
2065
                if (q < 0) {
619✔
2066
                        RET_GATHER(r, install_changes_add(changes, n_changes, q, *s, NULL));
×
2067
                        continue;
×
2068
                }
2069

2070
                if (!unit_name_is_valid(dst, valid_dst_type)) {
619✔
2071
                        /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the
2072
                         * template status doesn't match. If we are doing presets don't bother reporting the
2073
                         * error. This also covers cases like 'systemctl preset serial-getty@.service', which
2074
                         * has no DefaultInstance, so there is nothing we can do. At the same time,
2075
                         * 'systemctl enable serial-getty@.service' should fail, the user should specify an
2076
                         * instance like in 'systemctl enable serial-getty@ttyS0.service'.
2077
                         */
2078
                        if (FLAGS_SET(file_flags, UNIT_FILE_IGNORE_AUXILIARY_FAILURE))
×
2079
                                continue;
×
2080

2081
                        if (unit_name_is_valid(dst, UNIT_NAME_ANY))
×
2082
                                RET_GATHER(r, install_changes_add(changes, n_changes, -EIDRM, dst, n));
×
2083
                        else
2084
                                RET_GATHER(r, install_changes_add(changes, n_changes, -EUCLEAN, dst, NULL));
×
2085

2086
                        continue;
×
2087
                }
2088

2089
                path = strjoin(config_path, "/", dst, suffix, n);
619✔
2090
                if (!path)
619✔
2091
                        return -ENOMEM;
2092

2093
                q = create_symlink(lp, info->path, path, /* force = */ true, changes, n_changes);
619✔
2094
                if (q != 0 && r >= 0)
619✔
2095
                        r = q;
619✔
2096

2097
                if (unit_file_exists(scope, lp, dst) == 0) {
619✔
2098
                        q = install_changes_add(changes, n_changes, INSTALL_CHANGE_DESTINATION_NOT_PRESENT, dst, info->path);
1✔
2099
                        if (q < 0)
1✔
2100
                                return q;
2101
                }
2102
        }
2103

2104
        return r;
2105
}
2106

2107
static int install_info_symlink_link(
617✔
2108
                InstallInfo *info,
2109
                const LookupPaths *lp,
2110
                const char *config_path,
2111
                bool force,
2112
                InstallChange **changes,
2113
                size_t *n_changes) {
2114

2115
        _cleanup_free_ char *path = NULL;
617✔
2116
        int r;
617✔
2117

2118
        assert(info);
617✔
2119
        assert(lp);
617✔
2120
        assert(config_path);
617✔
2121
        assert(info->path);
617✔
2122

2123
        r = in_search_path(lp, info->path);
617✔
2124
        if (r < 0)
617✔
2125
                return r;
2126
        if (r > 0)
617✔
2127
                return 0;
2128

2129
        path = path_join(config_path, info->name);
3✔
2130
        if (!path)
3✔
2131
                return -ENOMEM;
2132

2133
        return create_symlink(lp, info->path, path, force, changes, n_changes);
3✔
2134
}
2135

2136
static int install_info_apply(
617✔
2137
                RuntimeScope scope,
2138
                UnitFileFlags file_flags,
2139
                InstallInfo *info,
2140
                const LookupPaths *lp,
2141
                const char *config_path,
2142
                InstallChange **changes,
2143
                size_t *n_changes) {
2144

2145
        int r, q;
617✔
2146

2147
        assert(info);
617✔
2148
        assert(lp);
617✔
2149
        assert(config_path);
617✔
2150

2151
        if (info->install_mode != INSTALL_MODE_REGULAR)
617✔
2152
                return 0;
2153

2154
        bool force = file_flags & UNIT_FILE_FORCE;
617✔
2155

2156
        r = install_info_symlink_link(info, lp, config_path, force, changes, n_changes);
617✔
2157
        /* Do not count links to the unit file towards the "carries_install_info" count */
2158
        if (r < 0)
617✔
2159
                /* If linking of the file failed, do not try to create other symlinks,
2160
                 * because they might would pointing to a non-existent or wrong unit. */
2161
                return r;
2162

2163
        r = install_info_symlink_alias(scope, info, lp, config_path, force, changes, n_changes);
617✔
2164

2165
        q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes);
617✔
2166
        if (q != 0 && r >= 0)
617✔
2167
                r = q;
574✔
2168

2169
        q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->required_by, ".requires/", changes, n_changes);
617✔
2170
        if (q != 0 && r >= 0)
617✔
2171
                r = q;
×
2172

2173
        q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->upheld_by, ".upholds/", changes, n_changes);
617✔
2174
        if (q != 0 && r >= 0)
617✔
2175
                r = q;
×
2176

2177
        return r;
2178
}
2179

2180
static int install_context_apply(
57✔
2181
                InstallContext *ctx,
2182
                const LookupPaths *lp,
2183
                UnitFileFlags file_flags,
2184
                const char *config_path,
2185
                SearchFlags flags,
2186
                InstallChange **changes,
2187
                size_t *n_changes) {
2188

2189
        InstallInfo *i;
57✔
2190
        int r;
57✔
2191

2192
        assert(ctx);
57✔
2193
        assert(lp);
57✔
2194
        assert(config_path);
57✔
2195

2196
        if (ordered_hashmap_isempty(ctx->will_process))
57✔
2197
                return 0;
2198

2199
        r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &install_info_hash_ops);
52✔
2200
        if (r < 0)
52✔
2201
                return r;
2202

2203
        r = 0;
2204
        while ((i = ordered_hashmap_first(ctx->will_process))) {
695✔
2205
                int q;
643✔
2206

2207
                q = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name);
643✔
2208
                if (q < 0)
643✔
2209
                        return q;
2210

2211
                q = install_info_traverse(ctx, lp, i, flags, NULL);
643✔
2212
                if (q < 0) {
643✔
2213
                        if (i->auxiliary) {
×
2214
                                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_AUXILIARY_FAILED, i->name, NULL);
×
2215
                                if (q < 0)
×
2216
                                        return q;
2217
                                continue;
×
2218
                        }
2219

2220
                        return install_changes_add(changes, n_changes, q, i->name, NULL);
×
2221
                }
2222

2223
                /* We can attempt to process a masked unit when a different unit
2224
                 * that we were processing specifies it in Also=. */
2225
                if (i->install_mode == INSTALL_MODE_MASKED) {
643✔
2226
                        q = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_MASKED, i->path, NULL);
20✔
2227
                        if (q < 0)
20✔
2228
                                return q;
2229
                        if (r >= 0)
20✔
2230
                                /* Assume that something *could* have been enabled here,
2231
                                 * avoid "empty [Install] section" warning. */
2232
                                r += 1;
20✔
2233
                        continue;
20✔
2234
                }
2235

2236
                if (i->install_mode != INSTALL_MODE_REGULAR)
623✔
2237
                        continue;
6✔
2238

2239
                q = install_info_apply(ctx->scope, file_flags, i, lp, config_path, changes, n_changes);
617✔
2240
                if (r >= 0) {
617✔
2241
                        if (q < 0)
617✔
2242
                                r = q;
2243
                        else
2244
                                r += q;
617✔
2245
                }
2246
        }
2247

2248
        return r;
2249
}
2250

2251
static int install_context_mark_for_removal(
25✔
2252
                InstallContext *ctx,
2253
                const LookupPaths *lp,
2254
                Set **remove_symlinks_to,
2255
                const char *config_path,
2256
                InstallChange **changes,
2257
                size_t *n_changes) {
2258

2259
        InstallInfo *i;
25✔
2260
        int r;
25✔
2261

2262
        assert(ctx);
25✔
2263
        assert(lp);
25✔
2264
        assert(config_path);
25✔
2265

2266
        /* Marks all items for removal */
2267

2268
        if (ordered_hashmap_isempty(ctx->will_process))
25✔
2269
                return 0;
2270

2271
        r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &install_info_hash_ops);
21✔
2272
        if (r < 0)
21✔
2273
                return r;
2274

2275
        while ((i = ordered_hashmap_first(ctx->will_process))) {
62✔
2276

2277
                r = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name);
41✔
2278
                if (r < 0)
41✔
2279
                        return r;
2280

2281
                r = install_info_traverse(ctx, lp, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
41✔
2282
                if (r == -ENOLINK) {
41✔
2283
                        log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
×
2284
                        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_DANGLING, i->path ?: i->name, NULL);
×
2285
                        if (r < 0)
×
2286
                                return r;
2287
                } else if (r == -ENOENT) {
41✔
2288
                        if (i->auxiliary)  /* some unit specified in Also= or similar is missing */
×
2289
                                log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
×
2290
                        else {
2291
                                log_debug_errno(r, "Unit %s not found, removing name.", i->name);
×
2292
                                r = install_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
×
2293
                                /* In case there's no unit, we still want to remove any leftover symlink, even if
2294
                                 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2295
                                if (r != -ENOENT)
×
2296
                                        return r;
2297
                        }
2298
                } else if (r < 0) {
41✔
2299
                        log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
×
2300
                        int k = install_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
×
2301
                        if (k != r)
×
2302
                                return k;
2303
                } else if (i->install_mode == INSTALL_MODE_MASKED) {
41✔
2304
                        log_debug("Unit file %s is masked, ignoring.", i->name);
×
2305
                        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_MASKED, i->path ?: i->name, NULL);
×
2306
                        if (r < 0)
×
2307
                                return r;
2308
                        continue;
×
2309
                } else if (i->install_mode != INSTALL_MODE_REGULAR) {
41✔
2310
                        log_debug("Unit %s has install mode %s, ignoring.",
2✔
2311
                                  i->name, install_mode_to_string(i->install_mode) ?: "invalid");
2312
                        continue;
2✔
2313
                }
2314

2315
                r = mark_symlink_for_removal(remove_symlinks_to, i->name);
39✔
2316
                if (r < 0)
39✔
2317
                        return r;
2318
        }
2319

2320
        return 0;
2321
}
2322

2323
int unit_file_mask(
5✔
2324
                RuntimeScope scope,
2325
                UnitFileFlags flags,
2326
                const char *root_dir,
2327
                char * const *names,
2328
                InstallChange **changes,
2329
                size_t *n_changes) {
2330

2331
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
5✔
2332
        const char *config_path;
5✔
2333
        int r;
5✔
2334

2335
        assert(scope >= 0);
5✔
2336
        assert(scope < _RUNTIME_SCOPE_MAX);
5✔
2337

2338
        r = lookup_paths_init(&lp, scope, 0, root_dir);
5✔
2339
        if (r < 0)
5✔
2340
                return r;
2341

2342
        config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
5✔
2343
        if (!config_path)
5✔
2344
                return -ENXIO;
2345

2346
        r = 0;
2347

2348
        STRV_FOREACH(name, names) {
10✔
2349
                _cleanup_free_ char *path = NULL;
5✔
2350

2351
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) {
5✔
2352
                        RET_GATHER(r, -EINVAL);
×
2353
                        continue;
×
2354
                }
2355

2356
                path = path_make_absolute(*name, config_path);
5✔
2357
                if (!path)
5✔
2358
                        return -ENOMEM;
×
2359

2360
                RET_GATHER(r, create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes));
5✔
2361
        }
2362

2363
        return r;
2364
}
2365

2366
int unit_file_unmask(
5✔
2367
                RuntimeScope scope,
2368
                UnitFileFlags flags,
2369
                const char *root_dir,
2370
                char * const *names,
2371
                InstallChange **changes,
2372
                size_t *n_changes) {
2373

2374
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
5✔
2375
        _cleanup_set_free_ Set *remove_symlinks_to = NULL;
5✔
2376
        _cleanup_strv_free_ char **todo = NULL;
×
2377
        const char *config_path;
5✔
2378
        size_t n_todo = 0;
5✔
2379
        int r, q;
5✔
2380

2381
        assert(scope >= 0);
5✔
2382
        assert(scope < _RUNTIME_SCOPE_MAX);
5✔
2383

2384
        r = lookup_paths_init(&lp, scope, 0, root_dir);
5✔
2385
        if (r < 0)
5✔
2386
                return r;
2387

2388
        config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
5✔
2389
        if (!config_path)
5✔
2390
                return -ENXIO;
2391

2392
        bool dry_run = flags & UNIT_FILE_DRY_RUN;
5✔
2393

2394
        STRV_FOREACH(name, names) {
10✔
2395
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
5✔
2396
                        return -EINVAL;
5✔
2397

2398
                /* If root_dir is set, we don't care about kernel command line or generators.
2399
                 * But if it is not set, we need to check for interference. */
2400
                if (!root_dir) {
5✔
2401
                        _cleanup_(install_info_clear) InstallInfo info = {
×
2402
                                .name = *name,  /* We borrow *name temporarily… */
1✔
2403
                                .install_mode = _INSTALL_MODE_INVALID,
2404
                        };
2405

2406
                        r = unit_file_search(NULL, &info, &lp, 0);
1✔
2407
                        if (r < 0) {
1✔
2408
                                if (r != -ENOENT)
×
2409
                                        log_debug_errno(r, "Failed to look up unit %s, ignoring: %m", info.name);
×
2410
                        } else if (info.install_mode == INSTALL_MODE_MASKED &&
1✔
2411
                                   path_is_generator(&lp, info.path)) {
×
2412
                                r = install_changes_add(changes, n_changes,
×
2413
                                                        INSTALL_CHANGE_IS_MASKED_GENERATOR, info.name, info.path);
×
2414
                                if (r < 0)
×
2415
                                        return r;
×
2416
                        }
2417

2418
                        TAKE_PTR(info.name);  /* … and give it back here */
1✔
2419
                }
2420

2421
                _cleanup_free_ char *path = path_make_absolute(*name, config_path);
10✔
2422
                if (!path)
5✔
2423
                        return -ENOMEM;
2424

2425
                r = null_or_empty_path(path);
5✔
2426
                if (r == -ENOENT)
5✔
2427
                        continue;
2✔
2428
                if (r < 0)
3✔
2429
                        return r;
2430
                if (r == 0)
3✔
2431
                        continue;
×
2432

2433
                if (!GREEDY_REALLOC0(todo, n_todo + 2))
3✔
2434
                        return -ENOMEM;
2435

2436
                todo[n_todo] = strdup(*name);
3✔
2437
                if (!todo[n_todo])
3✔
2438
                        return -ENOMEM;
2439

2440
                n_todo++;
3✔
2441
        }
2442

2443
        strv_uniq(todo);
5✔
2444

2445
        r = 0;
5✔
2446
        STRV_FOREACH(i, todo) {
8✔
2447
                _cleanup_free_ char *path = NULL;
3✔
2448
                const char *rp;
3✔
2449

2450
                path = path_make_absolute(*i, config_path);
3✔
2451
                if (!path)
3✔
2452
                        return -ENOMEM;
2453

2454
                if (!dry_run && unlink(path) < 0) {
3✔
2455
                        if (errno != ENOENT)
×
2456
                                RET_GATHER(r, install_changes_add(changes, n_changes, -errno, path, NULL));
×
2457

2458
                        continue;
×
2459
                }
2460

2461
                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, path, NULL);
3✔
2462
                if (q < 0)
3✔
2463
                        return q;
2464

2465
                rp = skip_root(lp.root_dir, path);
3✔
2466
                q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
3✔
2467
                if (q < 0)
3✔
2468
                        return q;
2469
        }
2470

2471
        RET_GATHER(r, remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes));
5✔
2472

2473
        return r;
2474
}
2475

2476
int unit_file_link(
1✔
2477
                RuntimeScope scope,
2478
                UnitFileFlags flags,
2479
                const char *root_dir,
2480
                char * const *files,
2481
                InstallChange **changes,
2482
                size_t *n_changes) {
2483

2484
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
1✔
2485
        _cleanup_ordered_hashmap_free_ OrderedHashmap *todo = NULL;
1✔
2486
        const char *config_path;
1✔
2487
        int r;
1✔
2488

2489
        assert(scope >= 0);
1✔
2490
        assert(scope < _RUNTIME_SCOPE_MAX);
1✔
2491
        assert(changes);
1✔
2492
        assert(n_changes);
1✔
2493

2494
        r = lookup_paths_init(&lp, scope, 0, root_dir);
1✔
2495
        if (r < 0)
1✔
2496
                return r;
2497

2498
        config_path = FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
1✔
2499
        if (!config_path)
1✔
2500
                return -ENXIO;
2501

2502
        STRV_FOREACH(file, files) {
2✔
2503
                _cleanup_free_ char *fn = NULL, *path = NULL, *full = NULL;
1✔
2504

2505
                if (ordered_hashmap_contains(todo, *file))
1✔
2506
                        continue;
×
2507

2508
                if (!path_is_absolute(*file))
1✔
2509
                        return install_changes_add(changes, n_changes, -EINVAL, *file, NULL);
×
2510

2511
                r = path_extract_filename(*file, &fn);
1✔
2512
                if (r < 0)
1✔
2513
                        return install_changes_add(changes, n_changes, r, *file, NULL);
×
2514

2515
                if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
1✔
2516
                        return install_changes_add(changes, n_changes, -EUCLEAN, *file, NULL);
×
2517

2518
                full = path_join(lp.root_dir, *file);
1✔
2519
                if (!full)
1✔
2520
                        return -ENOMEM;
2521

2522
                r = verify_regular_at(AT_FDCWD, full, /* follow = */ false);
1✔
2523
                if (r < 0)
1✔
2524
                        return install_changes_add(changes, n_changes, r, *file, NULL);
×
2525

2526
                r = in_search_path(&lp, *file);
1✔
2527
                if (r < 0)
1✔
2528
                        return install_changes_add(changes, n_changes, r, *file, NULL);
×
2529
                if (r > 0)
1✔
2530
                        /* A silent noop if the file is already in the search path. */
2531
                        continue;
×
2532

2533
                if (underneath_search_path(&lp, *file))
1✔
2534
                        return install_changes_add(changes, n_changes, -ETXTBSY, *file, NULL);
×
2535

2536
                path = strdup(*file);
1✔
2537
                if (!path)
1✔
2538
                        return -ENOMEM;
2539

2540
                r = ordered_hashmap_ensure_put(&todo, &path_hash_ops_free_free, path, fn);
1✔
2541
                if (r < 0)
1✔
2542
                        return r;
2543
                assert(r > 0);
1✔
2544

2545
                TAKE_PTR(path);
1✔
2546
                TAKE_PTR(fn);
1✔
2547
        }
2548

2549
        r = 0;
1✔
2550

2551
        const char *fn, *path;
1✔
2552
        ORDERED_HASHMAP_FOREACH_KEY(fn, path, todo) {
2✔
2553
                _cleanup_free_ char *new_path = NULL;
1✔
2554

2555
                new_path = path_make_absolute(fn, config_path);
1✔
2556
                if (!new_path)
1✔
2557
                        return -ENOMEM;
×
2558

2559
                RET_GATHER(r, create_symlink(&lp, path, new_path, FLAGS_SET(flags, UNIT_FILE_FORCE), changes, n_changes));
1✔
2560
        }
2561

2562
        return r;
1✔
2563
}
2564

2565
static int path_shall_revert(const LookupPaths *lp, const char *path) {
1✔
2566
        int r;
1✔
2567

2568
        assert(lp);
1✔
2569
        assert(path);
1✔
2570

2571
        /* Checks whether the path is one where the drop-in directories shall be removed. */
2572

2573
        r = path_is_config(lp, path, true);
1✔
2574
        if (r != 0)
1✔
2575
                return r;
2576

2577
        r = path_is_control(lp, path);
×
2578
        if (r != 0)
×
2579
                return r;
2580

2581
        return path_is_transient(lp, path);
×
2582
}
2583

2584
int unit_file_revert(
4✔
2585
                RuntimeScope scope,
2586
                const char *root_dir,
2587
                char * const *names,
2588
                InstallChange **changes,
2589
                size_t *n_changes) {
2590

2591
        _cleanup_set_free_ Set *remove_symlinks_to = NULL;
4✔
2592
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
4✔
2593
        _cleanup_strv_free_ char **todo = NULL;
4✔
2594
        size_t n_todo = 0;
4✔
2595
        int r, q;
4✔
2596

2597
        /* Puts a unit file back into vendor state. This means:
2598
         *
2599
         * a) we remove all drop-in snippets added by the user ("config"), add to transient units
2600
         *    ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
2601
         *    generated ("generated").
2602
         *
2603
         * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
2604
         *    (i.e. in "config", but not in "transient" or "control" or even "generated").
2605
         *
2606
         * We remove all that in both the runtime and the persistent directories, if that applies.
2607
         */
2608

2609
        r = lookup_paths_init(&lp, scope, 0, root_dir);
4✔
2610
        if (r < 0)
4✔
2611
                return r;
2612

2613
        STRV_FOREACH(name, names) {
8✔
2614
                bool has_vendor = false;
4✔
2615

2616
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
4✔
2617
                        return -EINVAL;
2618

2619
                STRV_FOREACH(p, lp.search_path) {
52✔
2620
                        _cleanup_free_ char *path = NULL, *dropin = NULL;
48✔
2621
                        struct stat st;
48✔
2622

2623
                        path = path_make_absolute(*name, *p);
48✔
2624
                        if (!path)
48✔
2625
                                return -ENOMEM;
2626

2627
                        r = RET_NERRNO(lstat(path, &st));
48✔
2628
                        if (r < 0) {
42✔
2629
                                if (r != -ENOENT)
42✔
2630
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2631
                        } else if (S_ISREG(st.st_mode)) {
6✔
2632
                                /* Check if there's a vendor version */
2633
                                r = path_is_vendor_or_generator(&lp, path);
5✔
2634
                                if (r < 0)
5✔
2635
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2636
                                if (r > 0)
5✔
2637
                                        has_vendor = true;
4✔
2638
                        }
2639

2640
                        dropin = strjoin(path, ".d");
48✔
2641
                        if (!dropin)
48✔
2642
                                return -ENOMEM;
2643

2644
                        r = RET_NERRNO(lstat(dropin, &st));
48✔
2645
                        if (r < 0) {
47✔
2646
                                if (r != -ENOENT)
47✔
2647
                                        return install_changes_add(changes, n_changes, r, dropin, NULL);
×
2648
                        } else if (S_ISDIR(st.st_mode)) {
1✔
2649
                                /* Remove the drop-ins */
2650
                                r = path_shall_revert(&lp, dropin);
1✔
2651
                                if (r < 0)
1✔
2652
                                        return install_changes_add(changes, n_changes, r, dropin, NULL);
×
2653
                                if (r > 0) {
1✔
2654
                                        if (!GREEDY_REALLOC0(todo, n_todo + 2))
1✔
2655
                                                return -ENOMEM;
2656

2657
                                        todo[n_todo++] = TAKE_PTR(dropin);
1✔
2658
                                }
2659
                        }
2660
                }
2661

2662
                if (!has_vendor)
4✔
2663
                        continue;
×
2664

2665
                /* OK, there's a vendor version, hence drop all configuration versions */
2666
                STRV_FOREACH(p, lp.search_path) {
52✔
2667
                        _cleanup_free_ char *path = NULL;
48✔
2668
                        struct stat st;
48✔
2669

2670
                        path = path_make_absolute(*name, *p);
48✔
2671
                        if (!path)
48✔
2672
                                return -ENOMEM;
2673

2674
                        r = RET_NERRNO(lstat(path, &st));
48✔
2675
                        if (r < 0) {
42✔
2676
                                if (r != -ENOENT)
42✔
2677
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2678
                        } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
6✔
2679
                                r = path_is_config(&lp, path, true);
6✔
2680
                                if (r < 0)
6✔
2681
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2682
                                if (r > 0) {
6✔
2683
                                        if (!GREEDY_REALLOC0(todo, n_todo + 2))
2✔
2684
                                                return -ENOMEM;
2685

2686
                                        todo[n_todo++] = TAKE_PTR(path);
2✔
2687
                                }
2688
                        }
2689
                }
2690
        }
2691

2692
        strv_uniq(todo);
4✔
2693

2694
        r = 0;
4✔
2695
        STRV_FOREACH(i, todo) {
7✔
2696
                _cleanup_strv_free_ char **fs = NULL;
3✔
2697
                const char *rp;
3✔
2698

2699
                (void) get_files_in_directory(*i, &fs);
3✔
2700

2701
                q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
3✔
2702
                if (q < 0 && q != -ENOENT && r >= 0) {
3✔
2703
                        r = q;
×
2704
                        continue;
×
2705
                }
2706

2707
                STRV_FOREACH(j, fs) {
4✔
2708
                        _cleanup_free_ char *t = NULL;
1✔
2709

2710
                        t = path_join(*i, *j);
1✔
2711
                        if (!t)
1✔
2712
                                return -ENOMEM;
2713

2714
                        q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, t, NULL);
1✔
2715
                        if (q < 0)
1✔
2716
                                return q;
2717
                }
2718

2719
                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, *i, NULL);
3✔
2720
                if (q < 0)
3✔
2721
                        return q;
2722

2723
                rp = skip_root(lp.root_dir, *i);
3✔
2724
                q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
3✔
2725
                if (q < 0)
3✔
2726
                        return q;
2727
        }
2728

2729
        q = remove_marked_symlinks(remove_symlinks_to, lp.runtime_config, &lp, false, changes, n_changes);
4✔
2730
        if (r >= 0)
4✔
2731
                r = q;
4✔
2732

2733
        q = remove_marked_symlinks(remove_symlinks_to, lp.persistent_config, &lp, false, changes, n_changes);
4✔
2734
        if (r >= 0)
4✔
2735
                r = q;
4✔
2736

2737
        return r;
2738
}
2739

2740
int unit_file_add_dependency(
1✔
2741
                RuntimeScope scope,
2742
                UnitFileFlags file_flags,
2743
                const char *root_dir,
2744
                char * const *names,
2745
                const char *target,
2746
                UnitDependency dep,
2747
                InstallChange **changes,
2748
                size_t *n_changes) {
2749

2750
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
1✔
2751
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
1✔
2752
        InstallInfo *info, *target_info;
1✔
2753
        const char *config_path;
1✔
2754
        int r;
1✔
2755

2756
        assert(scope >= 0);
1✔
2757
        assert(scope < _RUNTIME_SCOPE_MAX);
1✔
2758
        assert(target);
1✔
2759
        assert(IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES));
1✔
2760

2761
        if (!unit_name_is_valid(target, UNIT_NAME_ANY))
1✔
2762
                return install_changes_add(changes, n_changes, -EUCLEAN, target, NULL);
×
2763

2764
        r = lookup_paths_init(&lp, scope, 0, root_dir);
1✔
2765
        if (r < 0)
1✔
2766
                return r;
2767

2768
        config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
1✔
2769
        if (!config_path)
1✔
2770
                return -ENXIO;
2771

2772
        r = install_info_discover_and_check(&ctx, &lp, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
1✔
2773
                                            &target_info, changes, n_changes);
2774
        if (r < 0)
1✔
2775
                return r;
2776

2777
        assert(target_info->install_mode == INSTALL_MODE_REGULAR);
1✔
2778

2779
        STRV_FOREACH(name, names) {
2✔
2780
                char ***l;
1✔
2781

2782
                r = install_info_discover_and_check(&ctx, &lp, *name,
1✔
2783
                                                    SEARCH_FOLLOW_CONFIG_SYMLINKS,
2784
                                                    &info, changes, n_changes);
2785
                if (r < 0)
1✔
2786
                        return r;
2787

2788
                assert(info->install_mode == INSTALL_MODE_REGULAR);
1✔
2789

2790
                /* We didn't actually load anything from the unit
2791
                 * file, but instead just add in our new symlink to
2792
                 * create. */
2793

2794
                if (dep == UNIT_WANTS)
1✔
2795
                        l = &info->wanted_by;
1✔
2796
                else if (dep == UNIT_REQUIRES)
×
2797
                        l = &info->required_by;
×
2798
                else
2799
                        l = &info->upheld_by;
×
2800

2801
                strv_free(*l);
1✔
2802
                *l = strv_new(target_info->name);
1✔
2803
                if (!*l)
1✔
2804
                        return -ENOMEM;
2805
        }
2806

2807
        return install_context_apply(&ctx, &lp, file_flags, config_path,
1✔
2808
                                     SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
2809
}
2810

2811
static int do_unit_file_enable(
27✔
2812
                const LookupPaths *lp,
2813
                RuntimeScope scope,
2814
                UnitFileFlags flags,
2815
                const char *config_path,
2816
                char * const *names_or_paths,
2817
                InstallChange **changes,
2818
                size_t *n_changes) {
2819

2820
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
27✔
2821
        InstallInfo *info;
27✔
2822
        int r;
27✔
2823

2824
        STRV_FOREACH(name, names_or_paths) {
53✔
2825
                r = install_info_discover_and_check(&ctx, lp, *name,
27✔
2826
                                                    SEARCH_LOAD | SEARCH_FOLLOW_CONFIG_SYMLINKS,
2827
                                                    &info, changes, n_changes);
2828
                if (r < 0)
27✔
2829
                        return r;
2830

2831
                assert(info->install_mode == INSTALL_MODE_REGULAR);
26✔
2832
        }
2833

2834
        /* This will return the number of symlink rules that were
2835
           supposed to be created, not the ones actually created. This
2836
           is useful to determine whether the passed units had any
2837
           installation data at all. */
2838

2839
        return install_context_apply(&ctx, lp, flags, config_path,
26✔
2840
                                     SEARCH_LOAD, changes, n_changes);
2841
}
2842

2843
int unit_file_enable(
25✔
2844
                RuntimeScope scope,
2845
                UnitFileFlags flags,
2846
                const char *root_dir,
2847
                char * const *names_or_paths,
2848
                InstallChange **changes,
2849
                size_t *n_changes) {
2850

2851
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
25✔
2852
        int r;
25✔
2853

2854
        assert(scope >= 0);
25✔
2855
        assert(scope < _RUNTIME_SCOPE_MAX);
25✔
2856

2857
        r = lookup_paths_init(&lp, scope, 0, root_dir);
25✔
2858
        if (r < 0)
25✔
2859
                return r;
2860

2861
        const char *config_path = config_path_from_flags(&lp, flags);
25✔
2862
        if (!config_path)
25✔
2863
                return -ENXIO;
2864

2865
        return do_unit_file_enable(&lp, scope, flags, config_path, names_or_paths, changes, n_changes);
25✔
2866
}
2867

2868
static int do_unit_file_disable(
15✔
2869
                const LookupPaths *lp,
2870
                RuntimeScope scope,
2871
                UnitFileFlags flags,
2872
                const char *config_path,
2873
                char * const *names,
2874
                InstallChange **changes,
2875
                size_t *n_changes) {
2876

2877
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
15✔
2878
        bool has_install_info = false;
15✔
2879
        int r;
15✔
2880

2881
        STRV_FOREACH(name, names) {
30✔
2882
                InstallInfo *info;
15✔
2883

2884
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
15✔
2885
                        return install_changes_add(changes, n_changes, -EUCLEAN, *name, NULL);
×
2886

2887
                r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, &info);
15✔
2888
                if (r >= 0)
15✔
2889
                        r = install_info_traverse(&ctx, lp, info, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
15✔
2890
                if (r < 0) {
15✔
2891
                        r = install_changes_add(changes, n_changes, r, *name, NULL);
×
2892
                        /* In case there's no unit, we still want to remove any leftover symlink, even if
2893
                         * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2894
                        if (r != -ENOENT)
×
2895
                                return r;
2896
                }
2897

2898
                /* If we enable multiple units, some with install info and others without,
2899
                 * the "empty [Install] section" warning is not shown. Let's make the behavior
2900
                 * of disable align with that. */
2901
                has_install_info = has_install_info || install_info_has_rules(info) || install_info_has_also(info);
15✔
2902
        }
2903

2904
        _cleanup_set_free_ Set *remove_symlinks_to = NULL;
15✔
2905
        r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes);
15✔
2906
        if (r >= 0)
15✔
2907
                r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
15✔
2908
        if (r < 0)
15✔
2909
                return r;
×
2910

2911
        /* The warning is shown only if it's a no-op */
2912
        return install_changes_have_modification(*changes, *n_changes) || has_install_info;
15✔
2913
}
2914

2915
int unit_file_disable(
13✔
2916
                RuntimeScope scope,
2917
                UnitFileFlags flags,
2918
                const char *root_dir,
2919
                char * const *files,
2920
                InstallChange **changes,
2921
                size_t *n_changes) {
2922

2923
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
13✔
2924
        int r;
13✔
2925

2926
        assert(scope >= 0);
13✔
2927
        assert(scope < _RUNTIME_SCOPE_MAX);
13✔
2928

2929
        r = lookup_paths_init(&lp, scope, 0, root_dir);
13✔
2930
        if (r < 0)
13✔
2931
                return r;
2932

2933
        const char *config_path = config_path_from_flags(&lp, flags);
13✔
2934
        if (!config_path)
13✔
2935
                return -ENXIO;
2936

2937
        return do_unit_file_disable(&lp, scope, flags, config_path, files, changes, n_changes);
13✔
2938
}
2939

2940
static int normalize_linked_files(
2✔
2941
                RuntimeScope scope,
2942
                const LookupPaths *lp,
2943
                char * const *names_or_paths,
2944
                char ***ret_names,
2945
                char ***ret_files) {
2946

2947
        /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/,
2948
         * but operates on real unit names. For each argument we look up the actual path
2949
         * where the unit is found. This way linked units can be re-enabled successfully. */
2950

2951
        _cleanup_strv_free_ char **files = NULL, **names = NULL;
2✔
2952
        int r;
2✔
2953

2954
        STRV_FOREACH(a, names_or_paths) {
4✔
2955
                _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
×
2956
                InstallInfo *i = NULL;
2✔
2957
                _cleanup_free_ char *n = NULL;
2✔
2958

2959
                r = path_extract_filename(*a, &n);
2✔
2960
                if (r < 0)
2✔
2961
                        return r;
2962
                if (r == O_DIRECTORY)
2✔
2963
                        return log_debug_errno(SYNTHETIC_ERRNO(EISDIR),
×
2964
                                               "Unexpected path to a directory \"%s\", refusing.", *a);
2965

2966
                if (!is_path(*a) && !unit_name_is_valid(*a, UNIT_NAME_INSTANCE)) {
2✔
2967
                        r = install_info_discover(&ctx, lp, n, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i, NULL, NULL);
2✔
2968
                        if (r < 0)
2✔
2969
                                log_debug_errno(r, "Failed to discover unit \"%s\", operating on name: %m", n);
×
2970
                }
2971

2972
                r = strv_consume(&names, TAKE_PTR(n));
2✔
2973
                if (r < 0)
2✔
2974
                        return r;
2975

2976
                const char *p = NULL;
2✔
2977
                if (i && i->path && i->root)
2✔
2978
                        /* Use startswith here, because we know that paths are normalized, and
2979
                         * path_startswith() would give us a relative path, but we need an absolute path
2980
                         * relative to i->root.
2981
                         *
2982
                         * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
2983
                         * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
2984
                         * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
2985
                         */
2986
                        p = startswith(i->path, i->root);
1✔
2987

2988
                r = strv_extend(&files, p ?: *a);
2✔
2989
                if (r < 0)
2✔
2990
                        return r;
2991
        }
2992

2993
        *ret_names = TAKE_PTR(names);
2✔
2994
        *ret_files = TAKE_PTR(files);
2✔
2995
        return 0;
2✔
2996
}
2997

2998
int unit_file_reenable(
2✔
2999
                RuntimeScope scope,
3000
                UnitFileFlags flags,
3001
                const char *root_dir,
3002
                char * const *names_or_paths,
3003
                InstallChange **changes,
3004
                size_t *n_changes) {
3005

3006
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
2✔
3007
        _cleanup_strv_free_ char **names = NULL, **files = NULL;
2✔
3008
        int r;
2✔
3009

3010
        assert(scope >= 0);
2✔
3011
        assert(scope < _RUNTIME_SCOPE_MAX);
2✔
3012

3013
        r = lookup_paths_init(&lp, scope, 0, root_dir);
2✔
3014
        if (r < 0)
2✔
3015
                return r;
3016

3017
        const char *config_path = config_path_from_flags(&lp, flags);
2✔
3018
        if (!config_path)
2✔
3019
                return -ENXIO;
3020

3021
        r = normalize_linked_files(scope, &lp, names_or_paths, &names, &files);
2✔
3022
        if (r < 0)
2✔
3023
                return r;
3024

3025
        /* First, we invoke the disable command with only the basename... */
3026
        r = do_unit_file_disable(&lp, scope, flags, config_path, names, changes, n_changes);
2✔
3027
        if (r < 0)
2✔
3028
                return r;
3029

3030
        /* But the enable command with the full name */
3031
        return do_unit_file_enable(&lp, scope, flags, config_path, files, changes, n_changes);
2✔
3032
}
3033

3034
int unit_file_set_default(
4✔
3035
                RuntimeScope scope,
3036
                UnitFileFlags flags,
3037
                const char *root_dir,
3038
                const char *name,
3039
                InstallChange **changes,
3040
                size_t *n_changes) {
3041

3042
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
4✔
3043
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
4✔
3044
        InstallInfo *info;
4✔
3045
        const char *new_path;
4✔
3046
        int r;
4✔
3047

3048
        assert(scope >= 0);
4✔
3049
        assert(scope < _RUNTIME_SCOPE_MAX);
4✔
3050
        assert(name);
4✔
3051

3052
        if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
4✔
3053
                return -EINVAL;
3054
        if (streq(name, SPECIAL_DEFAULT_TARGET))
4✔
3055
                return -EINVAL;
3056

3057
        r = lookup_paths_init(&lp, scope, 0, root_dir);
4✔
3058
        if (r < 0)
4✔
3059
                return r;
3060

3061
        r = install_info_discover_and_check(&ctx, &lp, name, 0, &info, changes, n_changes);
4✔
3062
        if (r < 0)
4✔
3063
                return r;
3064

3065
        new_path = strjoina(lp.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
15✔
3066
        return create_symlink(&lp, info->path, new_path, flags & UNIT_FILE_FORCE, changes, n_changes);
3✔
3067
}
3068

3069
int unit_file_get_default(
9✔
3070
                RuntimeScope scope,
3071
                const char *root_dir,
3072
                char **ret) {
3073

3074
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
9✔
3075
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
9✔
3076
        InstallInfo *info;
9✔
3077
        int r;
9✔
3078

3079
        assert(scope >= 0);
9✔
3080
        assert(scope < _RUNTIME_SCOPE_MAX);
9✔
3081
        assert(ret);
9✔
3082

3083
        r = lookup_paths_init(&lp, scope, 0, root_dir);
9✔
3084
        if (r < 0)
9✔
3085
                return r;
3086

3087
        r = install_info_discover(&ctx, &lp, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
9✔
3088
                                  &info, NULL, NULL);
3089
        if (r < 0)
9✔
3090
                return r;
3091

3092
        return strdup_to(ret, info->name);
7✔
3093
}
3094

3095
int unit_file_lookup_state(
2,519✔
3096
                RuntimeScope scope,
3097
                const LookupPaths *lp,
3098
                const char *name,
3099
                UnitFileState *ret) {
3100

3101
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
2,519✔
3102
        InstallInfo *info;
2,519✔
3103
        UnitFileState state;
2,519✔
3104
        int r;
2,519✔
3105

3106
        assert(lp);
2,519✔
3107
        assert(name);
2,519✔
3108

3109
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2,519✔
3110
                return -EINVAL;
3111

3112
        r = install_info_discover(&ctx, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2,519✔
3113
                                  &info, NULL, NULL);
3114
        if (r < 0)
2,519✔
3115
                return log_debug_errno(r, "Failed to discover unit %s: %m", name);
38✔
3116

3117
        assert(IN_SET(info->install_mode, INSTALL_MODE_REGULAR, INSTALL_MODE_MASKED));
2,481✔
3118
        log_debug("Found unit %s at %s (%s)", name, strna(info->path),
2,493✔
3119
                  info->install_mode == INSTALL_MODE_REGULAR ? "regular file" : "mask");
3120

3121
        /* Shortcut things, if the caller just wants to know if this unit exists. */
3122
        if (!ret)
2,481✔
3123
                return 0;
3124

3125
        switch (info->install_mode) {
2,473✔
3126

3127
        case INSTALL_MODE_MASKED:
18✔
3128
                r = path_is_runtime(lp, info->path, true);
18✔
3129
                if (r < 0)
18✔
3130
                        return r;
3131

3132
                state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
18✔
3133
                break;
18✔
3134

3135
        case INSTALL_MODE_REGULAR:
2,455✔
3136
                /* Check if the name we were querying is actually an alias */
3137
                if (!path_equal_filename(name, info->path) && !unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
2,455✔
3138
                        state = UNIT_FILE_ALIAS;
58✔
3139
                        break;
58✔
3140
                }
3141

3142
                r = path_is_generator(lp, info->path);
2,397✔
3143
                if (r < 0)
2,397✔
3144
                        return r;
3145
                if (r > 0) {
2,397✔
3146
                        state = UNIT_FILE_GENERATED;
9✔
3147
                        break;
9✔
3148
                }
3149

3150
                r = path_is_transient(lp, info->path);
2,388✔
3151
                if (r < 0)
2,388✔
3152
                        return r;
3153
                if (r > 0) {
2,388✔
3154
                        state = UNIT_FILE_TRANSIENT;
13✔
3155
                        break;
13✔
3156
                }
3157

3158
                /* Check if any of the Alias= symlinks have been created.
3159
                 * We ignore other aliases, and only check those that would
3160
                 * be created by systemctl enable for this unit. */
3161
                r = find_symlinks_in_scope(scope, lp, info, true, &state);
2,375✔
3162
                if (r < 0)
2,375✔
3163
                        return r;
3164
                if (r > 0)
2,375✔
3165
                        break;
3166

3167
                /* Check if the file is known under other names. If it is,
3168
                 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
3169
                r = find_symlinks_in_scope(scope, lp, info, false, &state);
2,218✔
3170
                if (r < 0)
2,218✔
3171
                        return r;
3172
                if (r > 0)
2,218✔
3173
                        state = UNIT_FILE_INDIRECT;
4✔
3174
                else {
3175
                        if (install_info_has_rules(info))
2,214✔
3176
                                state = UNIT_FILE_DISABLED;
319✔
3177
                        else if (install_info_has_also(info))
1,895✔
3178
                                state = UNIT_FILE_INDIRECT;
28✔
3179
                        else
3180
                                state = UNIT_FILE_STATIC;
1,867✔
3181
                }
3182

3183
                break;
3184

3185
        default:
×
3186
                assert_not_reached();
×
3187
        }
3188

3189
        *ret = state;
2,473✔
3190
        return 0;
2,473✔
3191
}
3192

3193
int unit_file_get_state(
480✔
3194
                RuntimeScope scope,
3195
                const char *root_dir,
3196
                const char *name,
3197
                UnitFileState *ret) {
3198

3199
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
480✔
3200
        int r;
480✔
3201

3202
        assert(scope >= 0);
480✔
3203
        assert(scope < _RUNTIME_SCOPE_MAX);
480✔
3204
        assert(name);
480✔
3205

3206
        r = lookup_paths_init(&lp, scope, 0, root_dir);
480✔
3207
        if (r < 0)
480✔
3208
                return r;
3209

3210
        return unit_file_lookup_state(scope, &lp, name, ret);
480✔
3211
}
3212

3213
int unit_file_exists_full(RuntimeScope scope, const LookupPaths *lp, const char *name, char **ret_path) {
1,563✔
3214
        _cleanup_(install_context_done) InstallContext c = {
1,563✔
3215
                .scope = scope,
3216
        };
3217
        int r;
1,563✔
3218

3219
        assert(lp);
1,563✔
3220
        assert(name);
1,563✔
3221

3222
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
1,563✔
3223
                return -EINVAL;
3224

3225
        InstallInfo *info = NULL;
1,563✔
3226
        r = install_info_discover(
3,126✔
3227
                        &c,
3228
                        lp,
3229
                        name,
3230
                        /* flags= */ 0,
3231
                        ret_path ? &info : NULL,
3232
                        /* changes= */ NULL,
3233
                        /* n_changes= */ NULL);
3234
        if (r == -ENOENT) {
1,563✔
3235
                if (ret_path)
945✔
3236
                        *ret_path = NULL;
×
3237
                return 0;
945✔
3238
        }
3239
        if (r < 0)
618✔
3240
                return r;
3241

3242
        if (ret_path) {
618✔
UNCOV
3243
                assert(info);
×
3244

UNCOV
3245
                r = strdup_to(ret_path, info->path);
×
UNCOV
3246
                if (r < 0)
×
3247
                        return r;
×
3248
        }
3249

3250
        return 1;
3251
}
3252

3253
static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
7,560✔
3254
        _cleanup_strv_free_ char **instances = NULL;
×
3255
        _cleanup_free_ char *unit_name = NULL;
7,560✔
3256
        int r;
7,560✔
3257

3258
        assert(pattern);
7,560✔
3259
        assert(out_instances);
7,560✔
3260
        assert(out_unit_name);
7,560✔
3261

3262
        r = extract_first_word(&pattern, &unit_name, NULL, EXTRACT_RETAIN_ESCAPE);
7,560✔
3263
        if (r < 0)
7,560✔
3264
                return r;
3265

3266
        /* We handle the instances logic when unit name is extracted */
3267
        if (pattern) {
7,560✔
3268
                /* We only create instances when a rule of templated unit
3269
                 * is seen. A rule like enable foo@.service a b c will
3270
                 * result in an array of (a, b, c) as instance names */
3271
                if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
2✔
3272
                        return -EINVAL;
3273

3274
                instances = strv_split(pattern, WHITESPACE);
2✔
3275
                if (!instances)
2✔
3276
                        return -ENOMEM;
3277

3278
                *out_instances = TAKE_PTR(instances);
2✔
3279
        }
3280

3281
        *out_unit_name = TAKE_PTR(unit_name);
7,560✔
3282

3283
        return 0;
7,560✔
3284
}
3285

3286
static int presets_find_config(RuntimeScope scope, const char *root_dir, char ***ret) {
294✔
3287
        static const char* const initrd_dirs[] = { CONF_PATHS("systemd/initrd-preset"), NULL };
294✔
3288
        static const char* const system_dirs[] = { CONF_PATHS("systemd/system-preset"), NULL };
294✔
3289
        static const char* const user_dirs[] = { CONF_PATHS("systemd/user-preset"), NULL };
294✔
3290
        const char* const* dirs;
294✔
3291
        int r;
294✔
3292

3293
        assert(scope >= 0);
294✔
3294
        assert(scope < _RUNTIME_SCOPE_MAX);
294✔
3295

3296
        if (scope == RUNTIME_SCOPE_SYSTEM) {
294✔
3297
                r = chase_and_access("/etc/initrd-release", root_dir, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
257✔
3298
                if (r < 0 && r != -ENOENT)
257✔
3299
                        return r;
3300

3301
                /* Make sure that we fall back to the system preset directories if we're operating on a root
3302
                 * directory without initrd preset directories. This makes sure that we don't regress when
3303
                 * using a newer systemctl to operate on a root directory with an older version of systemd
3304
                 * installed that doesn't yet known about initrd preset directories. */
3305
                if (r >= 0)
257✔
3306
                        STRV_FOREACH(d, initrd_dirs) {
×
3307
                                r = chase_and_access(*d, root_dir, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
×
3308
                                if (r >= 0)
×
3309
                                        return conf_files_list_strv(ret, ".preset", root_dir, 0, initrd_dirs);
×
3310
                                if (r != -ENOENT)
×
3311
                                        return r;
3312
                        }
3313

3314
                dirs = system_dirs;
3315
        } else if (IN_SET(scope, RUNTIME_SCOPE_GLOBAL, RUNTIME_SCOPE_USER))
37✔
3316
                dirs = user_dirs;
3317
        else
3318
                assert_not_reached();
×
3319

3320
        return conf_files_list_strv(ret, ".preset", root_dir, 0, dirs);
294✔
3321
}
3322

3323
static int read_presets(RuntimeScope scope, const char *root_dir, UnitFilePresets *presets) {
294✔
3324
        _cleanup_(unit_file_presets_done) UnitFilePresets ps = {};
294✔
3325
        _cleanup_strv_free_ char **files = NULL;
×
3326
        int r;
294✔
3327

3328
        assert(scope >= 0);
294✔
3329
        assert(scope < _RUNTIME_SCOPE_MAX);
294✔
3330
        assert(presets);
294✔
3331

3332
        r = presets_find_config(scope, root_dir, &files);
294✔
3333
        if (r < 0)
294✔
3334
                return r;
3335

3336
        STRV_FOREACH(p, files) {
1,342✔
3337
                _cleanup_fclose_ FILE *f = NULL;
1,342✔
3338
                int n = 0;
1,048✔
3339

3340
                f = fopen(*p, "re");
1,048✔
3341
                if (!f) {
1,048✔
3342
                        if (errno == ENOENT)
×
3343
                                continue;
×
3344

3345
                        return -errno;
×
3346
                }
3347

3348
                for (;;) {
33,769✔
3349
                        _cleanup_free_ char *line = NULL;
33,769✔
3350
                        _cleanup_(unit_file_preset_rule_done) UnitFilePresetRule rule = {};
33,769✔
3351
                        const char *parameter;
33,769✔
3352

3353
                        r = read_stripped_line(f, LONG_LINE_MAX, &line);
33,769✔
3354
                        if (r < 0)
33,769✔
3355
                                return r;
3356
                        if (r == 0)
33,769✔
3357
                                break;
3358

3359
                        n++;
32,721✔
3360

3361
                        if (isempty(line))
32,721✔
3362
                                continue;
6,554✔
3363
                        if (strchr(COMMENTS, line[0]))
26,167✔
3364
                                continue;
9,122✔
3365

3366
                        if ((parameter = first_word(line, "enable"))) {
17,045✔
3367
                                char *unit_name;
7,560✔
3368
                                char **instances = NULL;
7,560✔
3369

3370
                                /* Unit_name will remain the same as parameter when no instances are specified */
3371
                                r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
7,560✔
3372
                                if (r < 0) {
7,560✔
3373
                                        log_syntax(NULL, LOG_WARNING, *p, n, r, "Couldn't parse line '%s'. Ignoring.", line);
×
3374
                                        continue;
×
3375
                                }
3376

3377
                                rule = (UnitFilePresetRule) {
7,560✔
3378
                                        .pattern = unit_name,
3379
                                        .action = PRESET_ENABLE,
3380
                                        .instances = instances,
3381
                                };
3382

3383
                        } else if ((parameter = first_word(line, "disable"))) {
9,485✔
3384
                                char *pattern;
9,477✔
3385

3386
                                pattern = strdup(parameter);
9,477✔
3387
                                if (!pattern)
9,477✔
3388
                                        return -ENOMEM;
3389

3390
                                rule = (UnitFilePresetRule) {
9,477✔
3391
                                        .pattern = pattern,
3392
                                        .action = PRESET_DISABLE,
3393
                                };
3394

3395
                        } else if ((parameter = first_word(line, "ignore"))) {
8✔
3396
                                char *pattern;
4✔
3397

3398
                                pattern = strdup(parameter);
4✔
3399
                                if (!pattern)
4✔
3400
                                        return -ENOMEM;
3401

3402
                                rule = (UnitFilePresetRule) {
4✔
3403
                                        .pattern = pattern,
3404
                                        .action = PRESET_IGNORE,
3405
                                };
3406
                        }
3407

3408
                        if (rule.action != 0) {
17,045✔
3409
                                if (!GREEDY_REALLOC(ps.rules, ps.n_rules + 1))
17,041✔
3410
                                        return -ENOMEM;
3411

3412
                                ps.rules[ps.n_rules++] = TAKE_STRUCT(rule);
17,041✔
3413
                                continue;
17,041✔
3414
                        }
3415

3416
                        log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
4✔
3417
                }
3418
        }
3419

3420
        ps.initialized = true;
294✔
3421
        *presets = TAKE_STRUCT(ps);
294✔
3422

3423
        return 0;
294✔
3424
}
3425

3426
static int pattern_match_multiple_instances(
686,866✔
3427
                        const UnitFilePresetRule rule,
3428
                        const char *unit_name,
3429
                        char ***ret) {
3430

3431
        _cleanup_free_ char *templated_name = NULL;
686,866✔
3432
        int r;
686,866✔
3433

3434
        assert(unit_name);
686,866✔
3435

3436
        /* If no ret is needed or the rule itself does not have instances
3437
         * initialized, we return not matching */
3438
        if (!ret || !rule.instances)
686,866✔
3439
                return 0;
3440

3441
        r = unit_name_template(unit_name, &templated_name);
4✔
3442
        if (r < 0)
4✔
3443
                return r;
3444
        if (!streq(rule.pattern, templated_name))
2✔
3445
                return 0;
3446

3447
        /* Compose a list of specified instances when unit name is a template  */
3448
        if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
2✔
3449
                _cleanup_strv_free_ char **out_strv = NULL;
1✔
3450

3451
                STRV_FOREACH(iter, rule.instances) {
4✔
3452
                        _cleanup_free_ char *name = NULL;
3✔
3453

3454
                        r = unit_name_replace_instance(unit_name, *iter, &name);
3✔
3455
                        if (r < 0)
3✔
3456
                                return r;
3457

3458
                        r = strv_consume(&out_strv, TAKE_PTR(name));
3✔
3459
                        if (r < 0)
3✔
3460
                                return r;
3461
                }
3462

3463
                *ret = TAKE_PTR(out_strv);
1✔
3464
                return 1;
1✔
3465
        } else {
3466
                /* We now know the input unit name is an instance name */
3467
                _cleanup_free_ char *instance_name = NULL;
1✔
3468

3469
                r = unit_name_to_instance(unit_name, &instance_name);
1✔
3470
                if (r < 0)
1✔
3471
                        return r;
3472

3473
                if (strv_find(rule.instances, instance_name))
1✔
3474
                        return 1;
3475
        }
3476
        return 0;
×
3477
}
3478

3479
static int query_presets(const char *name, const UnitFilePresets *presets, char ***instance_name_list) {
10,888✔
3480
        PresetAction action = PRESET_UNKNOWN;
10,888✔
3481

3482
        assert(name);
10,888✔
3483
        assert(presets);
10,888✔
3484

3485
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
10,888✔
3486
                return -EINVAL;
3487

3488
        FOREACH_ARRAY(i, presets->rules, presets->n_rules)
686,901✔
3489
                if (pattern_match_multiple_instances(*i, name, instance_name_list) > 0 ||
1,373,730✔
3490
                    fnmatch(i->pattern, name, FNM_NOESCAPE) == 0) {
686,864✔
3491
                        action = i->action;
10,853✔
3492
                        break;
10,853✔
3493
                }
3494

3495
        switch (action) {
10,888✔
3496

3497
        case PRESET_UNKNOWN:
3498
                log_debug("Preset files don't specify rule for %s. Enabling.", name);
35✔
3499
                return PRESET_ENABLE;
3500

3501
        case PRESET_ENABLE:
562✔
3502
                if (instance_name_list && *instance_name_list)
562✔
3503
                        STRV_FOREACH(s, *instance_name_list)
4✔
3504
                                log_debug("Preset files say enable %s.", *s);
3✔
3505
                else
3506
                        log_debug("Preset files say enable %s.", name);
561✔
3507
                return PRESET_ENABLE;
3508

3509
        case PRESET_DISABLE:
3510
                log_debug("Preset files say disable %s.", name);
10,289✔
3511
                return PRESET_DISABLE;
3512

3513
        case PRESET_IGNORE:
3514
                log_debug("Preset files say ignore %s.", name);
2✔
3515
                return PRESET_IGNORE;
3516

3517
        default:
×
3518
                assert_not_reached();
×
3519
        }
3520
}
3521

3522
PresetAction unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
570✔
3523
        _cleanup_(unit_file_presets_done) UnitFilePresets tmp = {};
570✔
3524
        int r;
570✔
3525

3526
        if (!cached)
570✔
3527
                cached = &tmp;
258✔
3528
        if (!cached->initialized) {
570✔
3529
                r = read_presets(scope, root_dir, cached);
263✔
3530
                if (r < 0)
263✔
3531
                        return r;
3532
        }
3533

3534
        return query_presets(name, cached, NULL);
570✔
3535
}
3536

3537
static int execute_preset(
31✔
3538
                UnitFileFlags file_flags,
3539
                InstallContext *plus,
3540
                InstallContext *minus,
3541
                const LookupPaths *lp,
3542
                const char *config_path,
3543
                char * const *files,
3544
                UnitFilePresetMode mode,
3545
                InstallChange **changes,
3546
                size_t *n_changes) {
3547

3548
        int r;
31✔
3549

3550
        assert(plus);
31✔
3551
        assert(minus);
31✔
3552
        assert(lp);
31✔
3553
        assert(config_path);
31✔
3554

3555
        if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
31✔
3556
                _cleanup_set_free_ Set *remove_symlinks_to = NULL;
×
3557

3558
                r = install_context_mark_for_removal(minus, lp, &remove_symlinks_to, config_path, changes, n_changes);
10✔
3559
                if (r < 0)
10✔
3560
                        return r;
×
3561

3562
                r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, false, changes, n_changes);
10✔
3563
        } else
3564
                r = 0;
3565

3566
        if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
10✔
3567
                int q;
30✔
3568

3569
                /* Returns number of symlinks that where supposed to be installed. */
3570
                q = install_context_apply(plus, lp,
60✔
3571
                                          file_flags | UNIT_FILE_IGNORE_AUXILIARY_FAILURE,
30✔
3572
                                          config_path,
3573
                                          SEARCH_LOAD, changes, n_changes);
3574
                if (r >= 0) {
30✔
3575
                        if (q < 0)
30✔
3576
                                r = q;
3577
                        else
3578
                                r += q;
30✔
3579
                }
3580
        }
3581

3582
        return r;
3583
}
3584

3585
static int preset_prepare_one(
10,799✔
3586
                RuntimeScope scope,
3587
                InstallContext *plus,
3588
                InstallContext *minus,
3589
                LookupPaths *lp,
3590
                const char *name,
3591
                const UnitFilePresets *presets,
3592
                InstallChange **changes,
3593
                size_t *n_changes) {
3594

3595
        _cleanup_(install_context_done) InstallContext tmp = { .scope = scope };
10,799✔
3596
        _cleanup_strv_free_ char **instance_name_list = NULL;
×
3597
        InstallInfo *info;
10,799✔
3598
        int r;
10,799✔
3599

3600
        if (install_info_find(plus, name) || install_info_find(minus, name))
10,799✔
3601
                return 0;
100✔
3602

3603
        r = install_info_discover(&tmp, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
10,699✔
3604
                                  &info, changes, n_changes);
3605
        if (r < 0)
10,699✔
3606
                return r;
3607
        if (!streq(name, info->name)) {
10,698✔
3608
                log_debug("Skipping %s because it is an alias for %s.", name, info->name);
380✔
3609
                return 0;
380✔
3610
        }
3611

3612
        r = query_presets(name, presets, &instance_name_list);
10,318✔
3613
        if (r < 0)
10,318✔
3614
                return r;
3615

3616
        if (r == PRESET_ENABLE) {
10,318✔
3617
                if (instance_name_list)
485✔
3618
                        STRV_FOREACH(s, instance_name_list) {
4✔
3619
                                r = install_info_discover_and_check(plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3✔
3620
                                                                    &info, changes, n_changes);
3621
                                if (r < 0)
3✔
3622
                                        return r;
3623
                        }
3624
                else {
3625
                        r = install_info_discover_and_check(plus, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
10,799✔
3626
                                                            &info, changes, n_changes);
3627
                        if (r < 0)
3628
                                return r;
3629
                }
3630

3631
        } else if (r == PRESET_DISABLE)
9,833✔
3632
                r = install_info_discover(minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
9,831✔
3633
                                          &info, changes, n_changes);
3634

3635
        return r;
3636
}
3637

3638
int unit_file_preset(
9✔
3639
                RuntimeScope scope,
3640
                UnitFileFlags file_flags,
3641
                const char *root_dir,
3642
                char * const *names,
3643
                UnitFilePresetMode mode,
3644
                InstallChange **changes,
3645
                size_t *n_changes) {
3646

3647
        _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
9✔
3648
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
9✔
3649
        _cleanup_(unit_file_presets_done) UnitFilePresets presets = {};
9✔
3650
        const char *config_path;
9✔
3651
        int r;
9✔
3652

3653
        assert(scope >= 0);
9✔
3654
        assert(scope < _RUNTIME_SCOPE_MAX);
9✔
3655
        assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
9✔
3656

3657
        r = lookup_paths_init(&lp, scope, 0, root_dir);
9✔
3658
        if (r < 0)
9✔
3659
                return r;
3660

3661
        config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
9✔
3662
        if (!config_path)
9✔
3663
                return -ENXIO;
3664

3665
        r = read_presets(scope, root_dir, &presets);
9✔
3666
        if (r < 0)
9✔
3667
                return r;
3668

3669
        STRV_FOREACH(name, names) {
18✔
3670
                r = preset_prepare_one(scope, &plus, &minus, &lp, *name, &presets, changes, n_changes);
9✔
3671
                if (r < 0)
9✔
3672
                        return r;
3673
        }
3674

3675
        return execute_preset(file_flags, &plus, &minus, &lp, config_path, names, mode, changes, n_changes);
9✔
3676
}
3677

3678
int unit_file_preset_all(
22✔
3679
                RuntimeScope scope,
3680
                UnitFileFlags file_flags,
3681
                const char *root_dir,
3682
                UnitFilePresetMode mode,
3683
                InstallChange **changes,
3684
                size_t *n_changes) {
3685

3686
        _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
22✔
3687
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
22✔
3688
        _cleanup_(unit_file_presets_done) UnitFilePresets presets = {};
22✔
3689
        const char *config_path = NULL;
22✔
3690
        int r;
22✔
3691

3692
        assert(scope >= 0);
22✔
3693
        assert(scope < _RUNTIME_SCOPE_MAX);
22✔
3694
        assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
22✔
3695

3696
        r = lookup_paths_init(&lp, scope, 0, root_dir);
22✔
3697
        if (r < 0)
22✔
3698
                return r;
3699

3700
        config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
22✔
3701
        if (!config_path)
22✔
3702
                return -ENXIO;
3703

3704
        r = read_presets(scope, root_dir, &presets);
22✔
3705
        if (r < 0)
22✔
3706
                return r;
3707

3708
        r = 0;
22✔
3709
        STRV_FOREACH(i, lp.search_path) {
326✔
3710
                _cleanup_closedir_ DIR *d = NULL;
304✔
3711

3712
                d = opendir(*i);
304✔
3713
                if (!d) {
304✔
3714
                        if (errno != ENOENT)
166✔
3715
                                RET_GATHER(r, -errno);
×
3716
                        continue;
166✔
3717
                }
3718

3719
                FOREACH_DIRENT(de, d, RET_GATHER(r, -errno)) {
17,485✔
3720
                        int k;
17,071✔
3721

3722
                        if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
17,071✔
3723
                                continue;
6,281✔
3724

3725
                        if (!IN_SET(de->d_type, DT_LNK, DT_REG))
10,790✔
3726
                                continue;
×
3727

3728
                        k = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes);
10,790✔
3729
                        if (k < 0 &&
10,790✔
3730
                            !IN_SET(k, -EEXIST,
21✔
3731
                                       -ERFKILL,
3732
                                       -EADDRNOTAVAIL,
3733
                                       -ETXTBSY,
3734
                                       -EBADSLT,
3735
                                       -EIDRM,
3736
                                       -EUCLEAN,
3737
                                       -ELOOP,
3738
                                       -EXDEV,
3739
                                       -ENOENT,
3740
                                       -ENOLINK,
3741
                                       -EUNATCH))
3742
                                /* Ignore generated/transient/missing/invalid units when applying preset, propagate other errors.
3743
                                 * Coordinate with install_change_dump_error() above. */
3744
                                RET_GATHER(r, k);
1✔
3745
                }
3746
        }
3747

3748
        return execute_preset(file_flags, &plus, &minus, &lp, config_path, NULL, mode, changes, n_changes);
22✔
3749
}
3750

3751
static UnitFileList* unit_file_list_free(UnitFileList *f) {
911✔
3752
        if (!f)
911✔
3753
                return NULL;
3754

3755
        free(f->path);
911✔
3756
        return mfree(f);
911✔
3757
}
3758

3759
DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free);
911✔
3760

3761
DEFINE_PRIVATE_HASH_OPS_FULL(unit_file_list_hash_ops_free_free,
1,822✔
3762
                             char, string_hash_func, string_compare_func, free,
3763
                             UnitFileList, unit_file_list_free);
3764

3765
int unit_file_get_list(
4✔
3766
                RuntimeScope scope,
3767
                const char *root_dir,
3768
                char * const *states,
3769
                char * const *patterns,
3770
                Hashmap **ret) {
3771

3772
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
4✔
3773
        _cleanup_hashmap_free_ Hashmap *h = NULL;
×
3774
        int r;
4✔
3775

3776
        assert(scope >= 0);
4✔
3777
        assert(scope < _RUNTIME_SCOPE_MAX);
4✔
3778
        assert(ret);
4✔
3779

3780
        r = lookup_paths_init(&lp, scope, 0, root_dir);
4✔
3781
        if (r < 0)
4✔
3782
                return r;
3783

3784
        STRV_FOREACH(dirname, lp.search_path) {
52✔
3785
                _cleanup_closedir_ DIR *d = NULL;
52✔
3786

3787
                d = opendir(*dirname);
48✔
3788
                if (!d) {
48✔
3789
                        if (errno == ENOENT)
27✔
3790
                                continue;
27✔
3791
                        if (IN_SET(errno, ENOTDIR, EACCES)) {
×
3792
                                log_debug_errno(errno, "Failed to open \"%s\": %m", *dirname);
×
3793
                                continue;
×
3794
                        }
3795

3796
                        return -errno;
×
3797
                }
3798

3799
                FOREACH_DIRENT(de, d, return -errno) {
1,552✔
3800
                        if (!IN_SET(de->d_type, DT_LNK, DT_REG))
1,489✔
3801
                                continue;
578✔
3802

3803
                        if (hashmap_contains(h, de->d_name))
1,345✔
3804
                                continue;
10✔
3805

3806
                        if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
1,335✔
3807
                                continue;
×
3808

3809
                        if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
1,335✔
3810
                                continue;
424✔
3811

3812
                        UnitFileState state;
911✔
3813

3814
                        r = unit_file_lookup_state(scope, &lp, de->d_name, &state);
911✔
3815
                        if (r < 0)
911✔
3816
                                state = UNIT_FILE_BAD;
×
3817

3818
                        if (!strv_isempty(states) &&
911✔
3819
                            !strv_contains(states, unit_file_state_to_string(state)))
×
3820
                                continue;
×
3821

3822
                        _cleanup_(unit_file_list_freep) UnitFileList *f = new(UnitFileList, 1);
911✔
3823
                        if (!f)
911✔
3824
                                return -ENOMEM;
3825

3826
                        *f = (UnitFileList) {
1,822✔
3827
                                .path = path_make_absolute(de->d_name, *dirname),
911✔
3828
                                .state = state,
3829
                        };
3830
                        if (!f->path)
911✔
3831
                                return -ENOMEM;
3832

3833
                        _cleanup_free_ char *unit_name = strdup(de->d_name);
911✔
3834
                        if (!unit_name)
911✔
3835
                                return -ENOMEM;
3836

3837
                        r = hashmap_ensure_put(&h, &unit_file_list_hash_ops_free_free, unit_name, f);
911✔
3838
                        if (r < 0)
911✔
3839
                                return r;
3840
                        assert(r > 0);
911✔
3841

3842
                        TAKE_PTR(unit_name);
911✔
3843
                        TAKE_PTR(f);
911✔
3844
                }
3845
        }
3846

3847
        *ret = TAKE_PTR(h);
4✔
3848
        return 0;
4✔
3849
}
3850

3851
static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
3852
        [UNIT_FILE_ENABLED]         = "enabled",
3853
        [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
3854
        [UNIT_FILE_LINKED]          = "linked",
3855
        [UNIT_FILE_LINKED_RUNTIME]  = "linked-runtime",
3856
        [UNIT_FILE_ALIAS]           = "alias",
3857
        [UNIT_FILE_MASKED]          = "masked",
3858
        [UNIT_FILE_MASKED_RUNTIME]  = "masked-runtime",
3859
        [UNIT_FILE_STATIC]          = "static",
3860
        [UNIT_FILE_DISABLED]        = "disabled",
3861
        [UNIT_FILE_INDIRECT]        = "indirect",
3862
        [UNIT_FILE_GENERATED]       = "generated",
3863
        [UNIT_FILE_TRANSIENT]       = "transient",
3864
        [UNIT_FILE_BAD]             = "bad",
3865
};
3866

3867
DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
5,605✔
3868

3869
static const char* const install_change_type_table[_INSTALL_CHANGE_TYPE_MAX] = {
3870
        [INSTALL_CHANGE_SYMLINK]                 = "symlink",
3871
        [INSTALL_CHANGE_UNLINK]                  = "unlink",
3872
        [INSTALL_CHANGE_IS_MASKED]               = "masked",
3873
        [INSTALL_CHANGE_IS_MASKED_GENERATOR]     = "masked by generator",
3874
        [INSTALL_CHANGE_IS_DANGLING]             = "dangling",
3875
        [INSTALL_CHANGE_DESTINATION_NOT_PRESENT] = "destination not present",
3876
        [INSTALL_CHANGE_AUXILIARY_FAILED]        = "auxiliary unit failed",
3877
};
3878

3879
DEFINE_STRING_TABLE_LOOKUP(install_change_type, InstallChangeType);
120✔
3880

3881
static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MODE_MAX] = {
3882
        [UNIT_FILE_PRESET_FULL]         = "full",
3883
        [UNIT_FILE_PRESET_ENABLE_ONLY]  = "enable-only",
3884
        [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
3885
};
3886

3887
DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);
58✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc