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

systemd / systemd / 25615496818

09 May 2026 05:08PM UTC coverage: 72.511% (-0.1%) from 72.64%
25615496818

push

github

bluca
hwdb/keyboard: fix KP_Enter on Clevo PA70ES

The ITE keyboard controller firmware (version 0xAB83) is shared
between the Clevo PA70ES and the X+ piccolo series.

The piccolo's hwdb rule matches by input device ID
(evdev:input:b0011v0001p0001eAB83*) and remaps scan code 0x9c
(KP_Enter) to Enter, since the piccolo has no numpad and its
main Enter key sends the wrong scan code.

The Clevo PA70ES has a real numpad. The piccolo rule matches it
because both laptops use the same ITE controller firmware, which
breaks KP_Enter on the PA70ES.

Add a DMI-specific override that restores KEY_KPENTER for 0x9c
on the PA70ES.

The piccolo rule should ideally be narrowed to use DMI matching
instead of input device ID to avoid catching other laptops with
the same ITE controller firmware.

326196 of 449859 relevant lines covered (72.51%)

1210161.93 hits per line

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

86.05
/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 struct {
41
        RuntimeScope scope;
42
        OrderedHashmap *will_process;
43
        OrderedHashmap *have_processed;
44
} InstallContext;
45

46
struct UnitFilePresetRule {
47
        char *pattern;
48
        PresetAction action;
49
        char **instances;
50
};
51

52
/* NB! strings use past tense. */
53
static const char *const preset_action_past_tense_table[_PRESET_ACTION_MAX] = {
54
        [PRESET_UNKNOWN] = "unknown",
55
        [PRESET_ENABLE]  = "enabled",
56
        [PRESET_DISABLE] = "disabled",
57
        [PRESET_IGNORE]  = "ignored",
58
};
59

60
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(preset_action_past_tense, PresetAction);
4,307✔
61

62
static bool install_info_has_rules(const InstallInfo *i) {
2,823✔
63
        assert(i);
2,823✔
64

65
        return !strv_isempty(i->aliases) ||
2,823✔
66
               !strv_isempty(i->wanted_by) ||
2,808✔
67
               !strv_isempty(i->required_by) ||
2,488✔
68
               !strv_isempty(i->upheld_by);
2,486✔
69
}
70

71
static bool install_info_has_also(const InstallInfo *i) {
2,486✔
72
        assert(i);
2,486✔
73

74
        return !strv_isempty(i->also);
2,486✔
75
}
76

77
static void unit_file_preset_rule_done(UnitFilePresetRule *rule) {
88,835✔
78
        assert(rule);
88,835✔
79

80
        free(rule->pattern);
88,835✔
81
        strv_free(rule->instances);
88,835✔
82
}
88,835✔
83

84
void unit_file_presets_done(UnitFilePresets *p) {
1,315✔
85
        if (!p)
1,315✔
86
                return;
87

88
        FOREACH_ARRAY(rule, p->rules, p->n_rules)
31,650✔
89
                unit_file_preset_rule_done(rule);
30,335✔
90

91
        free(p->rules);
1,315✔
92
        p->n_rules = 0;
1,315✔
93
}
94

95
static const char *const install_mode_table[_INSTALL_MODE_MAX] = {
96
        [INSTALL_MODE_REGULAR] = "regular",
97
        [INSTALL_MODE_LINKED]  = "linked",
98
        [INSTALL_MODE_ALIAS]   = "alias",
99
        [INSTALL_MODE_MASKED]  = "masked",
100
};
101

102
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(install_mode, InstallMode);
2✔
103

104
static int in_search_path(const LookupPaths *lp, const char *path) {
688✔
105
        _cleanup_free_ char *parent = NULL;
688✔
106
        int r;
688✔
107

108
        /* Check if 'path' is in lp->search_path. */
109

110
        assert(lp);
688✔
111
        assert(path);
688✔
112

113
        r = path_extract_directory(path, &parent);
688✔
114
        if (r < 0)
688✔
115
                return r;
116

117
        return path_strv_contains(lp->search_path, parent);
688✔
118
}
119

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

123
        assert(lp);
1✔
124
        assert(path);
1✔
125

126
        return path_startswith_strv(path, lp->search_path);
1✔
127
}
128

129
static const char* skip_root(const char *root_dir, const char *path) {
1,479✔
130
        assert(path);
1,479✔
131

132
        if (!root_dir)
1,479✔
133
                return path;
134

135
        const char *e = path_startswith(path, root_dir);
100✔
136
        if (!e)
100✔
137
                return NULL;
138

139
        /* Make sure the returned path starts with a slash */
140
        if (e[0] != '/') {
98✔
141
                if (e == path || e[-1] != '/')
98✔
142
                        return NULL;
143

144
                e--;
98✔
145
        }
146

147
        return e;
148
}
149

150
static int path_is_generator(const LookupPaths *lp, const char *path) {
3,525✔
151
        _cleanup_free_ char *parent = NULL;
3,525✔
152
        int r;
3,525✔
153

154
        assert(lp);
3,525✔
155
        assert(path);
3,525✔
156

157
        r = path_extract_directory(path, &parent);
3,525✔
158
        if (r < 0)
3,525✔
159
                return r;
160

161
        return PATH_IN_SET(parent,
3,525✔
162
                           lp->generator,
163
                           lp->generator_early,
164
                           lp->generator_late);
165
}
166

167
static int path_is_transient(const LookupPaths *lp, const char *path) {
3,513✔
168
        _cleanup_free_ char *parent = NULL;
3,513✔
169
        int r;
3,513✔
170

171
        assert(lp);
3,513✔
172
        assert(path);
3,513✔
173

174
        r = path_extract_directory(path, &parent);
3,513✔
175
        if (r < 0)
3,513✔
176
                return r;
177

178
        return path_equal(parent, lp->transient);
3,513✔
179
}
180

181
static int path_is_control(const LookupPaths *lp, const char *path) {
×
182
        _cleanup_free_ char *parent = NULL;
×
183
        int r;
×
184

185
        assert(lp);
×
186
        assert(path);
×
187

188
        r = path_extract_directory(path, &parent);
×
189
        if (r < 0)
×
190
                return r;
191

192
        return PATH_IN_SET(parent,
×
193
                           lp->persistent_control,
194
                           lp->runtime_control);
195
}
196

197
static int path_is_config(const LookupPaths *lp, const char *path, bool check_parent) {
13✔
198
        _cleanup_free_ char *parent = NULL;
13✔
199
        int r;
13✔
200

201
        assert(lp);
13✔
202
        assert(path);
13✔
203

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

207
        if (check_parent) {
13✔
208
                r = path_extract_directory(path, &parent);
13✔
209
                if (r < 0)
13✔
210
                        return r;
211

212
                path = parent;
13✔
213
        }
214

215
        return PATH_IN_SET(path,
13✔
216
                           lp->persistent_config,
217
                           lp->runtime_config);
218
}
219

220
static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_parent) {
726✔
221
        _cleanup_free_ char *parent = NULL;
726✔
222
        const char *rpath;
726✔
223
        int r;
726✔
224

225
        assert(lp);
726✔
226
        assert(path);
726✔
227

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

231
        rpath = skip_root(lp->root_dir, path);
726✔
232
        if (rpath && path_startswith(rpath, "/run"))
1,452✔
233
                return true;
234

235
        if (check_parent) {
635✔
236
                r = path_extract_directory(path, &parent);
20✔
237
                if (r < 0)
20✔
238
                        return r;
239

240
                path = parent;
20✔
241
        }
242

243
        return PATH_IN_SET(path,
635✔
244
                           lp->runtime_config,
245
                           lp->generator,
246
                           lp->generator_early,
247
                           lp->generator_late,
248
                           lp->transient,
249
                           lp->runtime_control);
250
}
251

252
static int path_is_vendor_or_generator(const LookupPaths *lp, const char *path) {
5✔
253
        const char *rpath;
5✔
254

255
        assert(lp);
5✔
256
        assert(path);
5✔
257

258
        rpath = skip_root(lp->root_dir, path);
5✔
259
        if (!rpath)
5✔
260
                return 0;
261

262
        if (path_startswith(rpath, "/usr"))
5✔
263
                return true;
264

265
        if (path_is_generator(lp, rpath))
1✔
266
                return true;
267

268
        return path_equal(rpath, SYSTEM_DATA_UNIT_DIR);
1✔
269
}
270

271
static const char* config_path_from_flags(const LookupPaths *lp, UnitFileFlags flags) {
40✔
272
        assert(lp);
40✔
273

274
        if (FLAGS_SET(flags, UNIT_FILE_PORTABLE))
40✔
275
                return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_attached : lp->persistent_attached;
×
276
        else
277
                return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_config : lp->persistent_config;
40✔
278
}
279

280
InstallChangeType install_changes_add(
1,464✔
281
                InstallChange **changes,
282
                size_t *n_changes,
283
                InstallChangeType type, /* INSTALL_CHANGE_SYMLINK, _UNLINK, _IS_MASKED, _IS_DANGLING, … if positive or errno if negative */
284
                const char *path,
285
                const char *source) {
286

287
        _cleanup_free_ char *p = NULL, *s = NULL;
1,464✔
288
        int r;
1,464✔
289

290
        assert(!changes == !n_changes);
1,464✔
291
        assert(INSTALL_CHANGE_TYPE_VALID(type));
1,464✔
292

293
        /* Message formatting requires <path> to be set. */
294
        assert(path);
1,464✔
295

296
        /* Register a change or error. Note that the return value may be the error
297
         * that was passed in, or -ENOMEM generated internally. */
298

299
        if (!changes)
1,464✔
300
                return type;
301

302
        if (!GREEDY_REALLOC(*changes, *n_changes + 1))
201✔
303
                return -ENOMEM;
304

305
        r = path_simplify_alloc(path, &p);
201✔
306
        if (r < 0)
201✔
307
                return r;
308

309
        r = path_simplify_alloc(source, &s);
201✔
310
        if (r < 0)
201✔
311
                return r;
312

313
        (*changes)[(*n_changes)++] = (InstallChange) {
201✔
314
                .type = type,
315
                .path = TAKE_PTR(p),
201✔
316
                .source = TAKE_PTR(s),
201✔
317
        };
318

319
        return type;
201✔
320
}
321

322
static void install_change_done(InstallChange *change) {
201✔
323
        assert(change);
201✔
324

325
        change->path = mfree(change->path);
201✔
326
        change->source = mfree(change->source);
201✔
327
}
201✔
328

329
DEFINE_ARRAY_FREE_FUNC(install_changes_free, InstallChange, install_change_done);
339✔
330

331
static void install_change_dump_success(const InstallChange *change) {
96✔
332
        assert(change);
96✔
333
        assert(change->path);
96✔
334

335
        switch (change->type) {
96✔
336

337
        case INSTALL_CHANGE_SYMLINK:
338
                return log_info("Created symlink '%s' %s '%s'.",
38✔
339
                                change->path, glyph(GLYPH_ARROW_RIGHT), change->source);
340

341
        case INSTALL_CHANGE_UNLINK:
342
                return log_info("Removed '%s'.", change->path);
39✔
343

344
        case INSTALL_CHANGE_IS_MASKED:
345
                return log_info("Unit %s is masked, ignoring.", change->path);
19✔
346

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

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

353
        case INSTALL_CHANGE_DESTINATION_NOT_PRESENT:
354
                return log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
×
355
                                   change->source, change->path);
356

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

360
        default:
×
361
                assert_not_reached();
×
362
        }
363
}
364

365
/* Generated/transient/missing/invalid units when applying presets.
366
 * Coordinate with install_change_dump_error() below. */
367
static bool ERRNO_IS_NEG_UNIT_ISSUE(intmax_t r) {
19✔
368
        return IN_SET(r,
19✔
369
                      -EEXIST,
370
                      -ERFKILL,
371
                      -EADDRNOTAVAIL,
372
                      -ETXTBSY,
373
                      -EBADSLT,
374
                      -EIDRM,
375
                      -EUCLEAN,
376
                      -ELOOP,
377
                      -EXDEV,
378
                      -ENOENT,
379
                      -ENOLINK,
380
                      -EUNATCH);
381
}
382

383
int install_change_dump_error(const InstallChange *change, char **ret_errmsg, const char **ret_bus_error) {
19✔
384
        char *m;
19✔
385
        const char *bus_error;
19✔
386

387
        /* Returns 0:   known error and ret_errmsg formatted
388
         *         < 0: non-recognizable error */
389

390
        assert(change);
19✔
391
        assert(change->path);
19✔
392
        assert(change->type < 0);
19✔
393
        assert(ret_errmsg);
19✔
394

395
        switch (change->type) {
19✔
396

397
        case -EEXIST:
×
398
                m = strjoin("File '", change->path, "' already exists",
×
399
                            change->source ? " and is a symlink to " : NULL, change->source);
400
                bus_error = BUS_ERROR_UNIT_EXISTS;
×
401
                break;
×
402

403
        case -ERFKILL:
18✔
404
                m = strjoin("Unit ", change->path, " is masked");
18✔
405
                bus_error = BUS_ERROR_UNIT_MASKED;
18✔
406
                break;
18✔
407

408
        case -EADDRNOTAVAIL:
×
409
                m = strjoin("Unit ", change->path, " is transient or generated");
×
410
                bus_error = BUS_ERROR_UNIT_GENERATED;
×
411
                break;
×
412

413
        case -ETXTBSY:
×
414
                m = strjoin("File '", change->path, "' is under the systemd unit hierarchy already");
×
415
                bus_error = BUS_ERROR_UNIT_BAD_PATH;
×
416
                break;
×
417

418
        case -EBADSLT:
×
419
                m = strjoin("Invalid specifier in unit ", change->path);
×
420
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
421
                break;
×
422

423
        case -EIDRM:
×
424
                m = strjoin("Refusing to operate on template unit ", change->source,
×
425
                            " when destination unit ", change->path, " is a non-template unit");
426
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
427
                break;
×
428

429
        case -EUCLEAN:
×
430
                m = strjoin("Invalid unit name ", change->path);
×
431
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
432
                break;
×
433

434
        case -ELOOP:
×
435
                m = strjoin("Refusing to operate on linked unit file ", change->path);
×
436
                bus_error = BUS_ERROR_UNIT_LINKED;
×
437
                break;
×
438

439
        case -EXDEV:
×
440
                if (change->source)
×
441
                        m = strjoin("Cannot alias ", change->source, " as ", change->path);
×
442
                else
443
                        m = strjoin("Invalid unit reference ", change->path);
×
444
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
445
                break;
446

447
        case -ENOENT:
×
448
                m = strjoin("Unit ", change->path, " does not exist");
×
449
                bus_error = BUS_ERROR_NO_SUCH_UNIT;
×
450
                break;
×
451

452
        case -ENOLINK:
×
453
                m = strjoin("Unit ", change->path, " is an unresolvable alias");
×
454
                bus_error = BUS_ERROR_NO_SUCH_UNIT;
×
455
                break;
×
456

457
        case -EUNATCH:
×
458
                m = strjoin("Cannot resolve specifiers in unit ", change->path);
×
459
                bus_error = BUS_ERROR_BAD_UNIT_SETTING;
×
460
                break;
×
461

462
        default:
463
                return change->type;
464
        }
465
        if (!m)
18✔
466
                return -ENOMEM;
467

468
        *ret_errmsg = m;
18✔
469
        if (ret_bus_error)
18✔
470
                *ret_bus_error = bus_error;
×
471

472
        return 0;
473
}
474

475
int install_changes_dump(
107✔
476
                int error,
477
                const char *verb,
478
                const InstallChange *changes,
479
                size_t n_changes,
480
                bool quiet) {
481

482
        bool err_logged = false;
107✔
483
        int r;
107✔
484

485
        /* If verb is not specified, errors are not allowed! */
486
        assert(verb || error >= 0);
107✔
487
        assert(changes || n_changes == 0);
107✔
488

489
        /* An error is returned if 'error' contains an error or if any of the changes failed. */
490

491
        FOREACH_ARRAY(i, changes, n_changes)
222✔
492
                if (i->type >= 0) {
115✔
493
                        if (!quiet)
96✔
494
                                install_change_dump_success(i);
96✔
495
                } else {
496
                        _cleanup_free_ char *err_message = NULL;
19✔
497

498
                        assert(verb);
19✔
499

500
                        r = install_change_dump_error(i, &err_message, /* ret_bus_error= */ NULL);
19✔
501
                        if (r == -ENOMEM)
19✔
502
                                return log_oom();
×
503
                        if (r < 0)
19✔
504
                                RET_GATHER(error,
1✔
505
                                           log_error_errno(r, "Failed to %s unit %s: %m", verb, i->path));
506
                        else
507
                                RET_GATHER(error,
18✔
508
                                           log_error_errno(i->type, "Failed to %s unit: %s", verb, err_message));
509

510
                        err_logged = true;
19✔
511
                }
512

513
        if (error < 0 && !err_logged)
107✔
514
                log_error_errno(error, "Failed to %s units: %m.", verb);
107✔
515

516
        return error;
517
}
518

519
/**
520
 * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is
521
 * concerned. If the target is in the unit search path, then anything with the same name is equivalent.
522
 * If outside the unit search path, paths must be identical.
523
 */
524
static int chroot_unit_symlinks_equivalent(
653✔
525
                const LookupPaths *lp,
526
                const char *src,
527
                const char *target_a,
528
                const char *target_b) {
529

530
        assert(lp);
653✔
531
        assert(src);
653✔
532
        assert(target_a);
653✔
533
        assert(target_b);
653✔
534

535
        /* This will give incorrect results if the paths are relative and go outside
536
         * of the chroot. False negatives are possible. */
537

538
        const char *root = lp->root_dir ?: "/";
653✔
539
        _cleanup_free_ char *dirname = NULL;
653✔
540
        int r;
653✔
541

542
        if (!path_is_absolute(target_a) || !path_is_absolute(target_b)) {
1,306✔
543
                r = path_extract_directory(src, &dirname);
×
544
                if (r < 0)
×
545
                        return r;
546
        }
547

548
        _cleanup_free_ char *a = path_join(path_is_absolute(target_a) ? root : dirname, target_a);
653✔
549
        _cleanup_free_ char *b = path_join(path_is_absolute(target_b) ? root : dirname, target_b);
1,306✔
550
        if (!a || !b)
653✔
551
                return log_oom();
×
552

553
        r = path_equal_or_inode_same(a, b, 0);
653✔
554
        if (r != 0)
653✔
555
                return r;
556

557
        _cleanup_free_ char *a_name = NULL, *b_name = NULL;
2✔
558
        r = path_extract_filename(a, &a_name);
2✔
559
        if (r < 0)
2✔
560
                return r;
561
        r = path_extract_filename(b, &b_name);
2✔
562
        if (r < 0)
2✔
563
                return r;
564

565
        return streq(a_name, b_name) &&
×
566
               path_startswith_strv(a, lp->search_path) &&
2✔
567
               path_startswith_strv(b, lp->search_path);
×
568
}
569

570
static int create_symlink(
707✔
571
                const LookupPaths *lp,
572
                const char *old_path,
573
                const char *new_path,
574
                bool force,
575
                InstallChange **changes,
576
                size_t *n_changes) {
577

578
        _cleanup_free_ char *dest = NULL;
707✔
579
        const char *rp;
707✔
580
        int r;
707✔
581

582
        assert(old_path);
707✔
583
        assert(new_path);
707✔
584

585
        rp = skip_root(lp->root_dir, old_path);
707✔
586
        if (rp)
707✔
587
                old_path = rp;
705✔
588

589
        /* Actually create a symlink, and remember that we did. This function is
590
         * smart enough to check if there's already a valid symlink in place.
591
         *
592
         * Returns 1 if a symlink was created or already exists and points to the
593
         * right place, or negative on error.
594
         */
595

596
        (void) mkdir_parents_label(new_path, 0755);
707✔
597

598
        if (symlink(old_path, new_path) >= 0) {
707✔
599
                r = install_changes_add(changes, n_changes, INSTALL_CHANGE_SYMLINK, new_path, old_path);
54✔
600
                if (r < 0)
54✔
601
                        return r;
602
                return 1;
54✔
603
        }
604

605
        if (errno != EEXIST)
653✔
606
                return install_changes_add(changes, n_changes, -errno, new_path, NULL);
×
607

608
        r = readlink_malloc(new_path, &dest);
653✔
609
        if (r < 0) {
653✔
610
                /* translate EINVAL (non-symlink exists) to EEXIST */
611
                if (r == -EINVAL)
×
612
                        r = -EEXIST;
×
613

614
                return install_changes_add(changes, n_changes, r, new_path, NULL);
×
615
        }
616

617
        if (chroot_unit_symlinks_equivalent(lp, new_path, dest, old_path)) {
653✔
618
                log_debug("Symlink %s %s %s already exists",
651✔
619
                          new_path, glyph(GLYPH_ARROW_RIGHT), dest);
620
                return 1;
621
        }
622

623
        if (!force)
2✔
624
                return install_changes_add(changes, n_changes, -EEXIST, new_path, dest);
×
625

626
        r = symlink_atomic(old_path, new_path);
2✔
627
        if (r < 0)
2✔
628
                return install_changes_add(changes, n_changes, r, new_path, NULL);
×
629

630
        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, new_path, NULL);
2✔
631
        if (r < 0)
2✔
632
                return r;
633
        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_SYMLINK, new_path, old_path);
2✔
634
        if (r < 0)
2✔
635
                return r;
×
636

637
        return 1;
638
}
639

640
static int mark_symlink_for_removal(
80✔
641
                Set **remove_symlinks_to,
642
                const char *p) {
643

644
        char *n;
80✔
645
        int r;
80✔
646

647
        assert(remove_symlinks_to);
80✔
648
        assert(p);
80✔
649

650
        r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops_free);
80✔
651
        if (r < 0)
80✔
652
                return r;
80✔
653

654
        r = path_simplify_alloc(p, &n);
80✔
655
        if (r < 0)
80✔
656
                return r;
657

658
        r = set_consume(*remove_symlinks_to, n);
80✔
659
        if (r == -EEXIST)
80✔
660
                return 0;
661
        if (r < 0)
80✔
662
                return r;
×
663

664
        return 1;
665
}
666

667
static int remove_marked_symlinks_fd(
284✔
668
                Set *remove_symlinks_to,
669
                int fd,
670
                const char *path,
671
                const char *config_path,
672
                const LookupPaths *lp,
673
                bool dry_run,
674
                bool *restart,
675
                InstallChange **changes,
676
                size_t *n_changes) {
677

678
        _cleanup_closedir_ DIR *d = NULL;
284✔
679
        int r, ret = 0;
284✔
680

681
        assert(remove_symlinks_to);
284✔
682
        assert(fd >= 0);
284✔
683
        assert(path);
284✔
684
        assert(config_path);
284✔
685
        assert(lp);
284✔
686
        assert(restart);
284✔
687

688
        d = fdopendir(fd);
284✔
689
        if (!d) {
284✔
690
                safe_close(fd);
×
691
                return -errno;
×
692
        }
693

694
        rewinddir(d);
284✔
695

696
        FOREACH_DIRENT(de, d, return -errno)
1,918✔
697
                if (de->d_type == DT_DIR) {
1,066✔
698
                        _cleanup_close_ int nfd = -EBADF;
1,918✔
699
                        _cleanup_free_ char *p = NULL;
239✔
700

701
                        nfd = RET_NERRNO(openat(fd, de->d_name, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW));
239✔
702
                        if (nfd < 0) {
×
703
                                if (nfd != -ENOENT)
×
704
                                        RET_GATHER(ret, nfd);
×
705
                                continue;
×
706
                        }
707

708
                        p = path_make_absolute(de->d_name, path);
239✔
709
                        if (!p)
239✔
710
                                return -ENOMEM;
×
711

712
                        /* This will close nfd, regardless whether it succeeds or not */
713
                        RET_GATHER(ret, remove_marked_symlinks_fd(remove_symlinks_to,
239✔
714
                                                                  TAKE_FD(nfd), p,
715
                                                                  config_path, lp,
716
                                                                  dry_run,
717
                                                                  restart,
718
                                                                  changes, n_changes));
719

720
                } else if (de->d_type == DT_LNK) {
827✔
721
                        _cleanup_free_ char *p = NULL;
715✔
722
                        bool found;
715✔
723

724
                        if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
715✔
725
                                continue;
×
726

727
                        p = path_make_absolute(de->d_name, path);
715✔
728
                        if (!p)
715✔
729
                                return -ENOMEM;
730
                        path_simplify(p);
715✔
731

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

738
                        if (!found) {
715✔
739
                                _cleanup_free_ char *template = NULL;
×
740

741
                                r = unit_name_template(de->d_name, &template);
693✔
742
                                if (r < 0 && r != -EINVAL)
693✔
743
                                        return r;
×
744
                                if (r >= 0)
693✔
745
                                        found = set_contains(remove_symlinks_to, template);
693✔
746
                        }
747

748
                        if (!found) {
693✔
749
                                _cleanup_free_ char *dest = NULL, *dest_name = NULL;
×
750

751
                                r = chase(p, lp->root_dir, CHASE_NONEXISTENT, &dest, NULL);
683✔
752
                                if (r == -ENOENT)
683✔
753
                                        continue;
×
754
                                if (r < 0) {
683✔
755
                                        log_debug_errno(r, "Failed to resolve symlink \"%s\": %m", p);
×
756
                                        RET_GATHER(ret, install_changes_add(changes, n_changes, r, p, NULL));
×
757
                                        continue;
×
758
                                }
759

760
                                r = path_extract_filename(dest, &dest_name);
683✔
761
                                if (r < 0)
683✔
762
                                        return r;
×
763

764
                                found = set_contains(remove_symlinks_to, dest) ||
1,366✔
765
                                        set_contains(remove_symlinks_to, dest_name);
683✔
766
                        }
767

768
                        if (!found)
683✔
769
                                continue;
682✔
770

771
                        if (!dry_run) {
33✔
772
                                if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
32✔
773
                                        RET_GATHER(ret, install_changes_add(changes, n_changes, -errno, p, NULL));
×
774
                                        continue;
×
775
                                }
776

777
                                (void) rmdir_parents(p, config_path);
32✔
778
                        }
779

780
                        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, p, NULL);
33✔
781
                        if (r < 0)
33✔
782
                                return r;
783

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

787
                        const char *rp = skip_root(lp->root_dir, p);
33✔
788
                        r = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
33✔
789
                        if (r < 0)
33✔
790
                                return r;
791
                        if (r > 0 && !dry_run)
33✔
792
                                *restart = true;
32✔
793
                }
794

795
        return ret;
796
}
797

798
static int remove_marked_symlinks(
42✔
799
                Set *remove_symlinks_to,
800
                const char *config_path,
801
                const LookupPaths *lp,
802
                bool dry_run,
803
                InstallChange **changes,
804
                size_t *n_changes) {
805

806
        _cleanup_close_ int fd = -EBADF;
42✔
807
        bool restart;
42✔
808
        int r = 0;
42✔
809

810
        assert(config_path);
42✔
811
        assert(lp);
42✔
812

813
        if (set_isempty(remove_symlinks_to))
42✔
814
                return 0;
815

816
        fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
32✔
817
        if (fd < 0)
32✔
818
                return errno == ENOENT ? 0 : -errno;
×
819

820
        do {
45✔
821
                int cfd;
45✔
822
                restart = false;
45✔
823

824
                cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
45✔
825
                if (cfd < 0)
45✔
826
                        return -errno;
×
827

828
                /* This takes possession of cfd and closes it */
829
                RET_GATHER(r, remove_marked_symlinks_fd(remove_symlinks_to,
45✔
830
                                                        cfd, config_path,
831
                                                        config_path, lp,
832
                                                        dry_run,
833
                                                        &restart,
834
                                                        changes, n_changes));
835
        } while (restart);
45✔
836

837
        return r;
838
}
839

840
static int is_symlink_with_known_name(const InstallInfo *i, const char *name) {
627✔
841
        int r;
627✔
842

843
        assert(i);
627✔
844
        assert(name);
627✔
845

846
        if (streq(name, i->name))
627✔
847
                return true;
848

849
        if (strv_contains(i->aliases, name))
199✔
850
                return true;
851

852
        /* Look for template symlink matching DefaultInstance */
853
        if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
192✔
854
                _cleanup_free_ char *s = NULL;
10✔
855

856
                r = unit_name_replace_instance(i->name, i->default_instance, &s);
10✔
857
                if (r < 0) {
10✔
858
                        if (r != -EINVAL)
×
859
                                return r;
860

861
                } else if (streq(name, s))
10✔
862
                        return true;
863
        }
864

865
        return false;
866
}
867

868
static int find_symlinks_in_directory(
195,772✔
869
                DIR *dir,
870
                const char *dir_path,
871
                const char *root_dir,
872
                const InstallInfo *info,
873
                bool ignore_destination,
874
                bool match_name,
875
                bool ignore_same_name,
876
                const char *config_path,
877
                bool *same_name_link) {
878

879
        int r, ret = 0;
195,772✔
880

881
        assert(dir);
195,772✔
882
        assert(dir_path);
195,772✔
883
        assert(info);
195,772✔
884
        assert(unit_name_is_valid(info->name, UNIT_NAME_ANY));
195,772✔
885
        assert(config_path);
195,772✔
886
        assert(same_name_link);
195,772✔
887

888
        FOREACH_DIRENT(de, dir, return -errno) {
4,189,724✔
889
                bool found_path = false, found_dest = false, b = false;
3,602,988✔
890

891
                if (de->d_type != DT_LNK)
3,602,988✔
892
                        continue;
2,726,021✔
893

894
                if (!ignore_destination) {
876,967✔
895
                        _cleanup_free_ char *dest = NULL;
157,700✔
896

897
                        /* Acquire symlink destination */
898
                        r = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
157,700✔
899
                        if (r < 0) {
157,700✔
900
                                if (r != -ENOENT)
×
901
                                        RET_GATHER(ret, r);
×
902
                                continue;
×
903
                        }
904

905
                        /* Check if what the symlink points to matches what we are looking for */
906
                        found_dest = path_equal_filename(dest, info->name);
157,700✔
907
                }
908

909
                /* Check if the symlink itself matches what we are looking for.
910
                 *
911
                 * If ignore_destination is specified, we only look at the source name.
912
                 *
913
                 * If ignore_same_name is specified, we are in one of the directories which
914
                 * have lower priority than the unit file, and even if a file or symlink with
915
                 * this name was found, we should ignore it. */
916

917
                if (ignore_destination || !ignore_same_name)
876,967✔
918
                        found_path = streq(de->d_name, info->name);
810,065✔
919

920
                if (!found_path && ignore_destination) {
876,967✔
921
                        _cleanup_free_ char *template = NULL;
718,565✔
922

923
                        r = unit_name_template(de->d_name, &template);
718,565✔
924
                        if (r < 0 && r != -EINVAL)
718,565✔
925
                                return r;
×
926
                        if (r >= 0)
718,565✔
927
                                found_dest = streq(template, info->name);
39,900✔
928
                }
929

930
                if (found_path && found_dest) {
876,967✔
931
                        _cleanup_free_ char *p = NULL, *t = NULL;
×
932

933
                        /* Filter out same name links in the main config path */
934
                        p = path_make_absolute(de->d_name, dir_path);
68✔
935
                        t = path_make_absolute(info->name, config_path);
68✔
936
                        if (!p || !t)
68✔
937
                                return -ENOMEM;
×
938

939
                        b = path_equal(p, t);
68✔
940
                }
941

942
                if (b)
68✔
943
                        *same_name_link = true;
68✔
944
                else if (found_path || found_dest) {
876,899✔
945
                        if (!match_name)
966✔
946
                                return 1;
947

948
                        /* Check if symlink name is in the set of names used by [Install] */
949
                        r = is_symlink_with_known_name(info, de->d_name);
627✔
950
                        if (r != 0)
627✔
951
                                return r;
952
                }
953
        }
954

955
        return ret;
956
}
957

958
static int find_symlinks(
72,095✔
959
                const char *root_dir,
960
                const InstallInfo *i,
961
                bool match_name,
962
                bool ignore_same_name,
963
                const char *config_path,
964
                bool *same_name_link) {
965

966
        _cleanup_closedir_ DIR *config_dir = NULL;
72,095✔
967
        int r;
72,095✔
968

969
        assert(i);
72,095✔
970
        assert(config_path);
72,095✔
971
        assert(same_name_link);
72,095✔
972

973
        config_dir = opendir(config_path);
72,095✔
974
        if (!config_dir) {
72,095✔
975
                if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
31,172✔
976
                        return 0;
977
                return -errno;
×
978
        }
979

980
        FOREACH_DIRENT(de, config_dir, return -errno) {
3,136,762✔
981
                const char *suffix;
3,014,806✔
982
                _cleanup_free_ const char *path = NULL;
3,014,806✔
983
                _cleanup_closedir_ DIR *d = NULL;
3,014,806✔
984

985
                if (de->d_type != DT_DIR)
3,014,806✔
986
                        continue;
2,754,807✔
987

988
                suffix = strrchr(de->d_name, '.');
259,999✔
989
                if (!STRPTR_IN_SET(suffix, ".wants", ".requires", ".upholds"))
259,999✔
990
                        continue;
104,431✔
991

992
                path = path_join(config_path, de->d_name);
155,568✔
993
                if (!path)
155,568✔
994
                        return -ENOMEM;
995

996
                d = opendir(path);
155,568✔
997
                if (!d) {
155,568✔
998
                        log_error_errno(errno, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path);
×
999
                        continue;
×
1000
                }
1001

1002
                r = find_symlinks_in_directory(d, path, root_dir, i,
155,568✔
1003
                                               /* ignore_destination= */ true,
1004
                                               /* match_name= */ match_name,
1005
                                               /* ignore_same_name= */ ignore_same_name,
1006
                                               config_path,
1007
                                               same_name_link);
1008
                if (r > 0)
155,568✔
1009
                        return 1;
719✔
1010
                if (r < 0)
154,849✔
1011
                        log_debug_errno(r, "Failed to look up symlinks in \"%s\": %m", path);
154,849✔
1012
        }
1013

1014
        /* We didn't find any suitable symlinks in .wants, .requires or .upholds directories,
1015
         * let's look for linked unit files in this directory. */
1016
        rewinddir(config_dir);
40,204✔
1017
        return find_symlinks_in_directory(config_dir, config_path, root_dir, i,
40,204✔
1018
                                          /* ignore_destination= */ false,
1019
                                          /* match_name= */ match_name,
1020
                                          /* ignore_same_name= */ ignore_same_name,
1021
                                          config_path,
1022
                                          same_name_link);
1023
}
1024

1025
static int find_symlinks_in_scope(
5,867✔
1026
                RuntimeScope scope,
1027
                const LookupPaths *lp,
1028
                const InstallInfo *info,
1029
                bool match_name,
1030
                UnitFileState *state) {
1031

1032
        bool same_name_link_runtime = false, same_name_link_config = false;
5,867✔
1033
        bool enabled_in_runtime = false, enabled_at_all = false;
5,867✔
1034
        bool ignore_same_name = false;
5,867✔
1035
        int r;
5,867✔
1036

1037
        assert(lp);
5,867✔
1038
        assert(info);
5,867✔
1039
        assert(state);
5,867✔
1040

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

1045
        STRV_FOREACH(p, lp->search_path)  {
77,819✔
1046
                bool same_name_link = false;
72,095✔
1047

1048
                r = find_symlinks(lp->root_dir, info, match_name, ignore_same_name, *p, &same_name_link);
72,095✔
1049
                if (r < 0)
72,095✔
1050
                        return r;
143✔
1051
                if (r > 0) {
72,095✔
1052
                        /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
1053

1054
                        if (path_equal(*p, lp->persistent_config)) {
777✔
1055
                                /* This is the best outcome, let's return it immediately. */
1056
                                *state = UNIT_FILE_ENABLED;
135✔
1057
                                return 1;
135✔
1058
                        }
1059

1060
                        /* look for global enablement of user units */
1061
                        if (scope == RUNTIME_SCOPE_USER && path_is_user_config_dir(*p)) {
642✔
1062
                                *state = UNIT_FILE_ENABLED;
8✔
1063
                                return 1;
8✔
1064
                        }
1065

1066
                        r = path_is_runtime(lp, *p, false);
634✔
1067
                        if (r < 0)
634✔
1068
                                return r;
1069
                        if (r > 0)
634✔
1070
                                enabled_in_runtime = true;
1071
                        else
1072
                                enabled_at_all = true;
613✔
1073

1074
                } else if (same_name_link) {
71,318✔
1075
                        if (path_equal(*p, lp->persistent_config))
68✔
1076
                                same_name_link_config = true;
1077
                        else {
1078
                                r = path_is_runtime(lp, *p, false);
66✔
1079
                                if (r < 0)
66✔
1080
                                        return r;
1081
                                if (r > 0)
66✔
1082
                                        same_name_link_runtime = true;
64✔
1083
                        }
1084
                }
1085

1086
                /* Check if next iteration will be "below" the unit file (either a regular file
1087
                 * or a symlink), and hence should be ignored */
1088
                if (!ignore_same_name && path_startswith(info->path, *p))
71,952✔
1089
                        ignore_same_name = true;
5,656✔
1090
        }
1091

1092
        if (enabled_in_runtime) {
5,724✔
1093
                *state = UNIT_FILE_ENABLED_RUNTIME;
21✔
1094
                return 1;
21✔
1095
        }
1096

1097
        /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
1098
         * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
1099
         * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
1100
         * something, and hence are a much stronger concept. */
1101
        if (enabled_at_all && unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
5,703✔
1102
                *state = UNIT_FILE_STATIC;
9✔
1103
                return 1;
9✔
1104
        }
1105

1106
        /* Hmm, we didn't find it, but maybe we found the same name
1107
         * link? */
1108
        if (same_name_link_config) {
5,694✔
1109
                *state = UNIT_FILE_LINKED;
2✔
1110
                return 1;
2✔
1111
        }
1112
        if (same_name_link_runtime) {
5,692✔
1113
                *state = UNIT_FILE_LINKED_RUNTIME;
64✔
1114
                return 1;
64✔
1115
        }
1116

1117
        return 0;
1118
}
1119

1120
static void install_info_clear(InstallInfo *i) {
27,292✔
1121
        if (!i)
27,292✔
1122
                return;
1123

1124
        i->name = mfree(i->name);
27,292✔
1125
        i->path = mfree(i->path);
27,292✔
1126
        i->root = mfree(i->root);
27,292✔
1127
        i->aliases = strv_free(i->aliases);
27,292✔
1128
        i->wanted_by = strv_free(i->wanted_by);
27,292✔
1129
        i->required_by = strv_free(i->required_by);
27,292✔
1130
        i->upheld_by = strv_free(i->upheld_by);
27,292✔
1131
        i->also = strv_free(i->also);
27,292✔
1132
        i->default_instance = mfree(i->default_instance);
27,292✔
1133
        i->symlink_target = mfree(i->symlink_target);
27,292✔
1134
}
1135

1136
static InstallInfo* install_info_free(InstallInfo *i) {
25,556✔
1137
        install_info_clear(i);
25,556✔
1138
        return mfree(i);
25,556✔
1139
}
1140

1141
DEFINE_TRIVIAL_CLEANUP_FUNC(InstallInfo*, install_info_free);
25,556✔
1142

1143
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
25,556✔
1144
                install_info_hash_ops,
1145
                char, string_hash_func, string_compare_func,
1146
                InstallInfo, install_info_free);
1147

1148
static void install_context_done(InstallContext *ctx) {
15,241✔
1149
        assert(ctx);
15,241✔
1150

1151
        ctx->will_process = ordered_hashmap_free(ctx->will_process);
15,241✔
1152
        ctx->have_processed = ordered_hashmap_free(ctx->have_processed);
15,241✔
1153
}
15,241✔
1154

1155
static InstallInfo *install_info_find(InstallContext *ctx, const char *name) {
45,975✔
1156
        InstallInfo *i;
45,975✔
1157

1158
        i = ordered_hashmap_get(ctx->have_processed, name);
45,975✔
1159
        if (i)
45,975✔
1160
                return i;
1161

1162
        return ordered_hashmap_get(ctx->will_process, name);
45,957✔
1163
}
1164

1165
static int install_info_may_process(
471✔
1166
                const InstallInfo *i,
1167
                const LookupPaths *lp,
1168
                InstallChange **changes,
1169
                size_t *n_changes) {
1170
        assert(i);
471✔
1171
        assert(lp);
471✔
1172

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

1176
        if (i->install_mode == INSTALL_MODE_MASKED)
471✔
1177
                return install_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
19✔
1178
        if (path_is_generator(lp, i->path) ||
904✔
1179
            path_is_transient(lp, i->path))
452✔
1180
                return install_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
×
1181

1182
        return 0;
1183
}
1184

1185
/**
1186
 * Adds a new InstallInfo entry under name in the InstallContext.will_process
1187
 * hashmap, or retrieves the existing one if already present.
1188
 *
1189
 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1190
 */
1191
static int install_info_add(
25,619✔
1192
                InstallContext *ctx,
1193
                const char *name,
1194
                const char *path,
1195
                const char *root,
1196
                bool auxiliary,
1197
                InstallInfo **ret) {
1198

1199
        _cleanup_free_ char *name_alloc = NULL;
25,619✔
1200
        int r;
25,619✔
1201

1202
        assert(ctx);
25,619✔
1203

1204
        if (!name) {
25,619✔
1205
                assert(path);
2✔
1206

1207
                r = path_extract_filename(path, &name_alloc);
2✔
1208
                if (r < 0)
2✔
1209
                        return r;
1210

1211
                name = name_alloc;
2✔
1212
        }
1213

1214
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
25,619✔
1215
                return -EINVAL;
1216

1217
        InstallInfo *i = install_info_find(ctx, name);
25,619✔
1218
        if (i) {
25,619✔
1219
                i->auxiliary = i->auxiliary && auxiliary;
63✔
1220

1221
                if (ret)
63✔
1222
                        *ret = i;
9✔
1223
                return 0;
1224
        }
1225

1226
        _cleanup_(install_info_freep) InstallInfo *new_info = new(InstallInfo, 1);
51,112✔
1227
        if (!new_info)
25,556✔
1228
                return -ENOMEM;
1229

1230
        *new_info = (InstallInfo) {
25,556✔
1231
                .install_mode = _INSTALL_MODE_INVALID,
1232
                .auxiliary = auxiliary,
1233
        };
1234

1235
        if (name_alloc)
25,556✔
1236
                new_info->name = TAKE_PTR(name_alloc);
2✔
1237
        else {
1238
                new_info->name = strdup(name);
25,554✔
1239
                if (!new_info->name)
25,554✔
1240
                        return -ENOMEM;
1241
        }
1242

1243
        r = strdup_to(&new_info->root, root);
25,556✔
1244
        if (r < 0)
25,556✔
1245
                return r;
1246

1247
        r = strdup_to(&new_info->path, path);
25,556✔
1248
        if (r < 0)
25,556✔
1249
                return r;
1250

1251
        r = ordered_hashmap_ensure_put(&ctx->will_process, &install_info_hash_ops, new_info->name, new_info);
25,556✔
1252
        if (r < 0)
25,556✔
1253
                return r;
1254
        i = TAKE_PTR(new_info);
25,556✔
1255

1256
        if (ret)
25,556✔
1257
                *ret = i;
25,325✔
1258
        return 1;
1259
}
1260

1261
static int config_parse_alias(
167✔
1262
                const char *unit,
1263
                const char *filename,
1264
                unsigned line,
1265
                const char *section,
1266
                unsigned section_line,
1267
                const char *lvalue,
1268
                int ltype,
1269
                const char *rvalue,
1270
                void *data,
1271
                void *userdata) {
1272

1273
        UnitType type;
167✔
1274

1275
        assert(unit);
167✔
1276
        assert(filename);
167✔
1277
        assert(lvalue);
167✔
1278
        assert(rvalue);
167✔
1279

1280
        type = unit_name_to_type(unit);
167✔
1281
        if (!unit_type_may_alias(type))
167✔
1282
                return log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1283
                                  "Alias= is not allowed for %s units, ignoring.",
1284
                                  unit_type_to_string(type));
1285

1286
        return config_parse_strv(unit, filename, line, section, section_line,
167✔
1287
                                 lvalue, ltype, rvalue, data, userdata);
1288
}
1289

1290
static int config_parse_also(
190✔
1291
                const char *unit,
1292
                const char *filename,
1293
                unsigned line,
1294
                const char *section,
1295
                unsigned section_line,
1296
                const char *lvalue,
1297
                int ltype,
1298
                const char *rvalue,
1299
                void *data,
1300
                void *userdata) {
1301

1302
        InstallInfo *info = ASSERT_PTR(userdata);
190✔
1303
        InstallContext *ctx = ASSERT_PTR(data);
190✔
1304
        int r;
190✔
1305

1306
        assert(unit);
190✔
1307
        assert(filename);
190✔
1308
        assert(lvalue);
190✔
1309
        assert(rvalue);
190✔
1310

1311
        for (;;) {
285✔
1312
                _cleanup_free_ char *word = NULL, *printed = NULL;
×
1313

1314
                r = extract_first_word(&rvalue, &word, NULL, 0);
475✔
1315
                if (r < 0)
475✔
1316
                        return r;
1317
                if (r == 0)
475✔
1318
                        break;
1319

1320
                r = install_name_printf(ctx->scope, info, word, &printed);
285✔
1321
                if (r < 0)
285✔
1322
                        return log_syntax(unit, LOG_WARNING, filename, line, r,
×
1323
                                          "Failed to resolve unit name in Also=\"%s\": %m", word);
1324

1325
                r = install_info_add(ctx, printed, NULL, info->root, /* auxiliary= */ true, NULL);
285✔
1326
                if (r < 0)
285✔
1327
                        return r;
1328

1329
                r = strv_push(&info->also, printed);
285✔
1330
                if (r < 0)
285✔
1331
                        return r;
1332

1333
                printed = NULL;
285✔
1334
        }
1335

1336
        return 0;
190✔
1337
}
1338

1339
static int config_parse_default_instance(
99✔
1340
                const char *unit,
1341
                const char *filename,
1342
                unsigned line,
1343
                const char *section,
1344
                unsigned section_line,
1345
                const char *lvalue,
1346
                int ltype,
1347
                const char *rvalue,
1348
                void *data,
1349
                void *userdata) {
1350

1351
        InstallContext *ctx = ASSERT_PTR(data);
99✔
1352
        InstallInfo *info = ASSERT_PTR(userdata);
99✔
1353
        _cleanup_free_ char *printed = NULL;
99✔
1354
        int r;
99✔
1355

1356
        assert(unit);
99✔
1357
        assert(filename);
99✔
1358
        assert(lvalue);
99✔
1359
        assert(rvalue);
99✔
1360

1361
        if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE))
99✔
1362
                /* When enabling an instance, we might be using a template unit file,
1363
                 * but we should ignore DefaultInstance silently. */
1364
                return 0;
1365
        if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
46✔
1366
                return log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1367
                                  "DefaultInstance= only makes sense for template units, ignoring.");
1368

1369
        r = install_name_printf(ctx->scope, info, rvalue, &printed);
46✔
1370
        if (r < 0)
46✔
1371
                return log_syntax(unit, LOG_WARNING, filename, line, r,
×
1372
                                  "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue);
1373

1374
        if (isempty(printed))
46✔
1375
                printed = mfree(printed);
×
1376

1377
        if (printed && !unit_instance_is_valid(printed))
46✔
1378
                return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
×
1379
                                  "Invalid DefaultInstance= value \"%s\".", printed);
1380

1381
        return free_and_replace(info->default_instance, printed);
46✔
1382
}
1383

1384
static int unit_file_load(
280,520✔
1385
                InstallContext *ctx,
1386
                InstallInfo *info,
1387
                const char *path,
1388
                const char *root_dir,
1389
                SearchFlags flags) {
1390

1391
        const ConfigTableItem items[] = {
280,520✔
1392
                { "Install", "Alias",           config_parse_alias,            0, &info->aliases           },
280,520✔
1393
                { "Install", "WantedBy",        config_parse_strv,             0, &info->wanted_by         },
280,520✔
1394
                { "Install", "RequiredBy",      config_parse_strv,             0, &info->required_by       },
280,520✔
1395
                { "Install", "UpheldBy",        config_parse_strv,             0, &info->upheld_by         },
280,520✔
1396
                { "Install", "DefaultInstance", config_parse_default_instance, 0, info                     },
1397
                { "Install", "Also",            config_parse_also,             0, ctx                      },
1398
                {}
1399
        };
1400

1401
        UnitType type;
280,520✔
1402
        _cleanup_fclose_ FILE *f = NULL;
280,520✔
1403
        _cleanup_close_ int fd = -EBADF;
280,520✔
1404
        struct stat st;
280,520✔
1405
        int r;
280,520✔
1406

1407
        assert(info);
280,520✔
1408
        assert(path);
280,520✔
1409

1410
        if (!(flags & SEARCH_DROPIN)) {
280,520✔
1411
                /* Loading or checking for the main unit file… */
1412

1413
                type = unit_name_to_type(info->name);
276,694✔
1414
                if (type < 0)
276,694✔
1415
                        return -EINVAL;
1416
                if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type))
276,694✔
1417
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1418
                                               "%s: unit type %s cannot be templated, ignoring.", path, unit_type_to_string(type));
1419

1420
                if (!(flags & SEARCH_LOAD)) {
276,694✔
1421
                        if (lstat(path, &st) < 0)
235,911✔
1422
                                return -errno;
215,494✔
1423

1424
                        if (null_or_empty(&st))
20,417✔
1425
                                info->install_mode = INSTALL_MODE_MASKED;
×
1426
                        else if (S_ISREG(st.st_mode))
20,417✔
1427
                                info->install_mode = INSTALL_MODE_REGULAR;
19,790✔
1428
                        else if (S_ISLNK(st.st_mode))
627✔
1429
                                return -ELOOP;
1430
                        else if (S_ISDIR(st.st_mode))
×
1431
                                return -EISDIR;
1432
                        else
1433
                                return -ENOTTY;
×
1434

1435
                        return 0;
1436
                }
1437

1438
                fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
40,783✔
1439
                if (fd < 0)
40,783✔
1440
                        return -errno;
37,038✔
1441
        } else {
1442
                /* 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. */
1443

1444
                if (!(flags & SEARCH_LOAD))
3,826✔
1445
                        return 0;
1446

1447
                fd = chase_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
3,275✔
1448
                if (fd < 0)
3,275✔
1449
                        return fd;
1450
        }
1451

1452
        if (fstat(fd, &st) < 0)
7,020✔
1453
                return -errno;
×
1454

1455
        if (null_or_empty(&st)) {
7,020✔
1456
                if ((flags & SEARCH_DROPIN) == 0)
×
1457
                        info->install_mode = INSTALL_MODE_MASKED;
×
1458

1459
                return 0;
1460
        }
1461

1462
        r = stat_verify_regular(&st);
7,020✔
1463
        if (r < 0)
7,020✔
1464
                return r;
1465

1466
        f = take_fdopen(&fd, "r");
7,020✔
1467
        if (!f)
7,020✔
1468
                return -errno;
×
1469

1470
        /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1471
        assert(ctx);
7,020✔
1472

1473
        r = config_parse(info->name, path, f,
7,020✔
1474
                         "Install\0"
1475
                         "-Unit\0"
1476
                         "-Automount\0"
1477
                         "-Device\0"
1478
                         "-Mount\0"
1479
                         "-Path\0"
1480
                         "-Scope\0"
1481
                         "-Service\0"
1482
                         "-Slice\0"
1483
                         "-Socket\0"
1484
                         "-Swap\0"
1485
                         "-Target\0"
1486
                         "-Timer\0",
1487
                         config_item_table_lookup, items,
1488
                         0, info,
1489
                         NULL);
1490
        if (r < 0)
7,020✔
1491
                return log_debug_errno(r, "Failed to parse \"%s\": %m", info->name);
4✔
1492

1493
        if ((flags & SEARCH_DROPIN) == 0)
7,016✔
1494
                info->install_mode = INSTALL_MODE_REGULAR;
3,741✔
1495

1496
        return
7,016✔
1497
                (int) strv_length(info->aliases) +
7,016✔
1498
                (int) strv_length(info->wanted_by) +
7,016✔
1499
                (int) strv_length(info->required_by) +
7,016✔
1500
                (int) strv_length(info->upheld_by);
7,016✔
1501
}
1502

1503
static int unit_file_load_or_readlink(
280,520✔
1504
                InstallContext *ctx,
1505
                InstallInfo *info,
1506
                const char *path,
1507
                const LookupPaths *lp,
1508
                SearchFlags flags) {
1509
        int r;
280,520✔
1510

1511
        r = unit_file_load(ctx, info, path, lp->root_dir, flags);
280,520✔
1512
        if (r != -ELOOP || (flags & SEARCH_DROPIN))
280,520✔
1513
                return r;
1514

1515
        /* This is a symlink, let's read and verify it. */
1516
        r = unit_file_resolve_symlink(lp->root_dir, lp->search_path,
859✔
1517
                                      NULL, AT_FDCWD, path,
1518
                                      true, &info->symlink_target);
1519
        if (r < 0)
859✔
1520
                return r;
1521
        bool outside_search_path = r > 0;
858✔
1522

1523
        r = null_or_empty_path_with_root(info->symlink_target, lp->root_dir);
858✔
1524
        if (r < 0 && r != -ENOENT)
858✔
1525
                return log_debug_errno(r, "Failed to stat %s: %m", info->symlink_target);
×
1526
        if (r > 0)
858✔
1527
                info->install_mode = INSTALL_MODE_MASKED;
175✔
1528
        else if (outside_search_path)
683✔
1529
                info->install_mode = INSTALL_MODE_LINKED;
73✔
1530
        else
1531
                info->install_mode = INSTALL_MODE_ALIAS;
610✔
1532

1533
        return 0;
1534
}
1535

1536
static int unit_file_search(
26,001✔
1537
                InstallContext *ctx,
1538
                InstallInfo *info,
1539
                const LookupPaths *lp,
1540
                SearchFlags flags) {
1541

1542
        const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
26,001✔
1543
        _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
26,001✔
1544
        _cleanup_free_ char *template = NULL;
26,001✔
1545
        bool found_unit = false;
26,001✔
1546
        int r, result;
26,001✔
1547

1548
        assert(info);
26,001✔
1549
        assert(lp);
26,001✔
1550

1551
        /* Was this unit already loaded? */
1552
        if (info->install_mode != _INSTALL_MODE_INVALID)
26,001✔
1553
                return 0;
1554

1555
        if (info->path)
25,479✔
1556
                return unit_file_load_or_readlink(ctx, info, info->path, lp, flags);
2✔
1557

1558
        assert(info->name);
25,477✔
1559

1560
        if (!FLAGS_SET(flags, SEARCH_IGNORE_TEMPLATE) && unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
25,477✔
1561
                r = unit_name_template(info->name, &template);
165✔
1562
                if (r < 0)
165✔
1563
                        return r;
1564
        }
1565

1566
        STRV_FOREACH(p, lp->search_path) {
276,257✔
1567
                _cleanup_free_ char *path = NULL;
250,785✔
1568

1569
                path = path_join(*p, info->name);
274,958✔
1570
                if (!path)
274,958✔
1571
                        return -ENOMEM;
1572

1573
                r = unit_file_load_or_readlink(ctx, info, path, lp, flags);
274,958✔
1574
                if (r >= 0) {
274,958✔
1575
                        info->path = TAKE_PTR(path);
24,173✔
1576
                        result = r;
24,173✔
1577
                        found_unit = true;
24,173✔
1578
                        break;
24,173✔
1579
                } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
250,785✔
1580
                        return r;
1581
        }
1582

1583
        if (!found_unit && template) {
25,472✔
1584

1585
                /* Unit file doesn't exist, however instance
1586
                 * enablement was requested.  We will check if it is
1587
                 * possible to load template unit file. */
1588

1589
                STRV_FOREACH(p, lp->search_path) {
1,660✔
1590
                        _cleanup_free_ char *path = NULL;
1,520✔
1591

1592
                        path = path_join(*p, template);
1,656✔
1593
                        if (!path)
1,656✔
1594
                                return -ENOMEM;
1595

1596
                        r = unit_file_load_or_readlink(ctx, info, path, lp, flags);
1,656✔
1597
                        if (r >= 0) {
1,656✔
1598
                                info->path = TAKE_PTR(path);
136✔
1599
                                result = r;
136✔
1600
                                found_unit = true;
136✔
1601
                                break;
136✔
1602
                        } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1,520✔
1603
                                return r;
1604
                }
1605
        }
1606

1607
        if (!found_unit)
136✔
1608
                return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
3,481✔
1609
                                       "Cannot find unit %s%s%s.",
1610
                                       info->name, template ? " or " : "", strempty(template));
1611

1612
        if (info->install_mode == INSTALL_MODE_MASKED)
24,309✔
1613
                return result;
1614

1615
        /* Search for drop-in directories */
1616

1617
        dropin_dir_name = strjoina(info->name, ".d");
120,670✔
1618
        STRV_FOREACH(p, lp->search_path) {
356,594✔
1619
                char *path;
332,460✔
1620

1621
                path = path_join(*p, dropin_dir_name);
332,460✔
1622
                if (!path)
332,460✔
1623
                        return -ENOMEM;
1624

1625
                r = strv_consume(&dirs, path);
332,460✔
1626
                if (r < 0)
332,460✔
1627
                        return r;
1628
        }
1629

1630
        if (template) {
24,134✔
1631
                dropin_template_dir_name = strjoina(template, ".d");
805✔
1632
                STRV_FOREACH(p, lp->search_path) {
2,249✔
1633
                        char *path;
2,088✔
1634

1635
                        path = path_join(*p, dropin_template_dir_name);
2,088✔
1636
                        if (!path)
2,088✔
1637
                                return -ENOMEM;
1638

1639
                        r = strv_consume(&dirs, path);
2,088✔
1640
                        if (r < 0)
2,088✔
1641
                                return r;
1642
                }
1643
        }
1644

1645
        /* Load drop-in conf files */
1646

1647
        r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs);
24,134✔
1648
        if (r < 0)
24,134✔
1649
                return log_debug_errno(r, "Failed to get list of conf files: %m");
×
1650

1651
        STRV_FOREACH(p, files) {
27,960✔
1652
                r = unit_file_load_or_readlink(ctx, info, *p, lp, flags | SEARCH_DROPIN);
3,826✔
1653
                if (r < 0)
3,826✔
1654
                        return log_debug_errno(r, "Failed to load conf file \"%s\": %m", *p);
×
1655
        }
1656

1657
        return result;
1658
}
1659

1660
static int install_info_follow(
697✔
1661
                InstallContext *ctx,
1662
                InstallInfo *info,
1663
                const LookupPaths *lp,
1664
                SearchFlags flags,
1665
                bool ignore_different_name) {
1666

1667
        assert(ctx);
697✔
1668
        assert(info);
697✔
1669

1670
        if (!IN_SET(info->install_mode, INSTALL_MODE_ALIAS, INSTALL_MODE_LINKED))
697✔
1671
                return -EINVAL;
1672
        if (!info->symlink_target)
697✔
1673
                return -EINVAL;
1674

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

1677
        if (!ignore_different_name && !path_equal_filename(info->symlink_target, info->name))
697✔
1678
                return -EXDEV;
1679

1680
        free_and_replace(info->path, info->symlink_target);
78✔
1681
        info->install_mode = _INSTALL_MODE_INVALID;
78✔
1682

1683
        return unit_file_load_or_readlink(ctx, info, info->path, lp, flags);
78✔
1684
}
1685

1686
/**
1687
 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1688
 * target, maybe more than once. Propagate the instance name if present.
1689
 */
1690
static int install_info_traverse(
25,362✔
1691
                InstallContext *ctx,
1692
                const LookupPaths *lp,
1693
                InstallInfo *start,
1694
                SearchFlags flags,
1695
                InstallInfo **ret) {
1696

1697
        InstallInfo *i;
25,362✔
1698
        unsigned k = 0;
25,362✔
1699
        int r;
25,362✔
1700

1701
        assert(ctx);
25,362✔
1702
        assert(lp);
25,362✔
1703
        assert(start);
25,362✔
1704

1705
        r = unit_file_search(ctx, start, lp, flags);
25,362✔
1706
        if (r < 0)
25,362✔
1707
                return r;
25,362✔
1708

1709
        i = start;
24,194✔
1710
        while (IN_SET(i->install_mode, INSTALL_MODE_ALIAS, INSTALL_MODE_LINKED)) {
24,886✔
1711
                /* Follow the symlink */
1712

1713
                if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
692✔
1714
                        return -ELOOP;
1715

1716
                if (!FLAGS_SET(flags, SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
692✔
1717
                        r = path_is_config(lp, i->path, true);
6✔
1718
                        if (r < 0)
6✔
1719
                                return r;
1720
                        if (r > 0)
6✔
1721
                                return -ELOOP;
1722
                }
1723

1724
                r = install_info_follow(ctx, i, lp, flags,
1,384✔
1725
                                        /* If linked, don't look at the target name */
1726
                                        /* ignore_different_name= */ i->install_mode == INSTALL_MODE_LINKED);
692✔
1727
                if (r == -EXDEV && i->symlink_target) {
692✔
1728
                        _cleanup_free_ char *target_name = NULL, *unit_instance = NULL;
614✔
1729
                        const char *bn;
619✔
1730

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

1733
                        r = path_extract_filename(i->symlink_target, &target_name);
619✔
1734
                        if (r < 0)
619✔
1735
                                return r;
1736

1737
                        if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
661✔
1738
                            unit_name_is_valid(target_name, UNIT_NAME_TEMPLATE)) {
42✔
1739

1740
                                _cleanup_free_ char *instance = NULL;
42✔
1741

1742
                                r = unit_name_to_instance(i->name, &instance);
42✔
1743
                                if (r < 0)
42✔
1744
                                        return r;
1745

1746
                                r = unit_name_replace_instance(target_name, instance, &unit_instance);
42✔
1747
                                if (r < 0)
42✔
1748
                                        return r;
1749

1750
                                if (streq(unit_instance, i->name)) {
42✔
1751
                                        /* We filled in the instance, and the target stayed the same? If so,
1752
                                         * then let's honour the link as it is. */
1753

1754
                                        r = install_info_follow(ctx, i, lp, flags, true);
5✔
1755
                                        if (r < 0)
5✔
1756
                                                return r;
1757

1758
                                        continue;
5✔
1759
                                }
1760

1761
                                bn = unit_instance;
37✔
1762
                        } else
1763
                                bn = target_name;
577✔
1764

1765
                        r = install_info_add(ctx, bn, NULL, lp->root_dir, /* auxiliary= */ false, &i);
614✔
1766
                        if (r < 0)
614✔
1767
                                return r;
1768

1769
                        /* Try again, with the new target we found. */
1770
                        r = unit_file_search(ctx, i, lp, flags);
614✔
1771
                        if (r == -ENOENT)
614✔
1772
                                /* Translate error code to highlight this specific case */
1773
                                return -ENOLINK;
1774
                }
1775
                if (r < 0)
687✔
1776
                        return r;
1777
        }
1778

1779
        if (ret)
24,194✔
1780
                *ret = i;
22,959✔
1781

1782
        return 0;
1783
}
1784

1785
/**
1786
 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1787
 * or the name (otherwise). root_dir is prepended to the path.
1788
 */
1789
static int install_info_add_auto(
24,705✔
1790
                InstallContext *ctx,
1791
                const LookupPaths *lp,
1792
                const char *name_or_path,
1793
                InstallInfo **ret) {
1794

1795
        assert(ctx);
24,705✔
1796
        assert(name_or_path);
24,705✔
1797

1798
        if (path_is_absolute(name_or_path)) {
24,705✔
1799
                _cleanup_free_ char *pp = path_join(lp->root_dir, name_or_path);
4✔
1800
                if (!pp)
2✔
1801
                        return -ENOMEM;
1802

1803
                return install_info_add(ctx, NULL, pp, lp->root_dir, /* auxiliary= */ false, ret);
2✔
1804
        } else
1805
                return install_info_add(ctx, name_or_path, NULL, lp->root_dir, /* auxiliary= */ false, ret);
24,703✔
1806
}
1807

1808
static int install_info_discover(
24,705✔
1809
                InstallContext *ctx,
1810
                const LookupPaths *lp,
1811
                const char *name_or_path,
1812
                SearchFlags flags,
1813
                InstallInfo **ret,
1814
                InstallChange **changes,
1815
                size_t *n_changes) {
1816

1817
        InstallInfo *info;
24,705✔
1818
        int r;
24,705✔
1819

1820
        assert(ctx);
24,705✔
1821
        assert(lp);
24,705✔
1822
        assert(name_or_path);
24,705✔
1823

1824
        r = install_info_add_auto(ctx, lp, name_or_path, &info);
24,705✔
1825
        if (r >= 0)
24,705✔
1826
                r = install_info_traverse(ctx, lp, info, flags, ret);
24,705✔
1827

1828
        if (r < 0)
24,705✔
1829
                return install_changes_add(changes, n_changes, r, name_or_path, NULL);
1,168✔
1830

1831
        return r;
1832
}
1833

1834
static int install_info_discover_and_check(
472✔
1835
                InstallContext *ctx,
1836
                const LookupPaths *lp,
1837
                const char *name_or_path,
1838
                SearchFlags flags,
1839
                InstallInfo **ret,
1840
                InstallChange **changes,
1841
                size_t *n_changes) {
1842

1843
        int r;
472✔
1844

1845
        POINTER_MAY_BE_NULL(ret);
472✔
1846

1847
        r = install_info_discover(ctx, lp, name_or_path, flags, ret, changes, n_changes);
472✔
1848
        if (r < 0)
472✔
1849
                return r;
1850

1851
        return install_info_may_process(ret ? *ret : NULL, lp, changes, n_changes);
471✔
1852
}
1853

1854
int unit_file_verify_alias(
230✔
1855
                const InstallInfo *info,
1856
                const char *dst,
1857
                char **ret_dst,
1858
                InstallChange **changes,
1859
                size_t *n_changes) {
1860

1861
        _cleanup_free_ char *dst_updated = NULL;
230✔
1862
        int r;
230✔
1863

1864
        assert(ret_dst);
230✔
1865

1866
        /* Verify that dst is a valid either a valid alias or a valid .wants/.requires symlink for the target
1867
         * unit *i. Return negative on error or if not compatible, zero on success.
1868
         *
1869
         * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
1870
         * inserted into dst. It is not normally set, even on success, so that the caller can easily
1871
         * distinguish the case where instance propagation occurred.
1872
         *
1873
         * Returns:
1874
         * -EXDEV when the alias doesn't match the unit,
1875
         * -EUCLEAN when the name is invalid,
1876
         * -ELOOP when the alias it to the unit itself.
1877
         */
1878

1879
        const char *path_alias = strrchr(dst, '/');
230✔
1880
        if (path_alias) {
230✔
1881
                /* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
1882
                _cleanup_free_ char *dir = NULL;
89✔
1883
                char *p;
89✔
1884

1885
                path_alias++; /* skip over slash */
89✔
1886

1887
                r = path_extract_directory(dst, &dir);
89✔
1888
                if (r < 0)
89✔
1889
                        return log_error_errno(r, "Failed to extract parent directory from '%s': %m", dst);
×
1890

1891
                p = endswith(dir, ".wants");
89✔
1892
                if (!p)
89✔
1893
                        p = endswith(dir, ".requires");
57✔
1894
                if (!p) {
57✔
1895
                        r = install_changes_add(changes, n_changes, -EXDEV, dst, NULL);
10✔
1896
                        if (r != -EXDEV)
10✔
1897
                                return r;
1898

1899
                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), "Invalid path \"%s\" in alias.", dir);
10✔
1900
                }
1901

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

1904
                UnitNameFlags type = unit_name_classify(dir);
79✔
1905
                if (type < 0) {
79✔
1906
                        r = install_changes_add(changes, n_changes, -EXDEV, dst, NULL);
1✔
1907
                        if (r != -EXDEV)
1✔
1908
                                return r;
1909
                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
1✔
1910
                                               "Invalid unit name component \"%s\" in alias.", dir);
1911
                }
1912

1913
                const bool instance_propagation = type == UNIT_NAME_TEMPLATE;
78✔
1914

1915
                /* That's the name we want to use for verification. */
1916
                r = unit_symlink_name_compatible(path_alias, info->name, instance_propagation);
78✔
1917
                if (r < 0)
78✔
1918
                        return log_error_errno(r, "Failed to verify alias validity: %m");
×
1919
                if (r == 0) {
78✔
1920
                        r = install_changes_add(changes, n_changes, -EXDEV, dst, info->name);
65✔
1921
                        if (r != -EXDEV)
65✔
1922
                                return r;
1923

1924
                        return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
65✔
1925
                                               "Invalid unit \"%s\" symlink \"%s\".",
1926
                                               info->name, dst);
1927
                }
1928

1929
        } else {
1930
                /* If the symlink target has an instance set and the symlink source doesn't, we "propagate
1931
                 * the instance", i.e. instantiate the symlink source with the target instance. */
1932
                if (unit_name_is_valid(dst, UNIT_NAME_TEMPLATE)) {
141✔
1933
                        _cleanup_free_ char *inst = NULL;
27✔
1934

1935
                        UnitNameFlags type = unit_name_to_instance(info->name, &inst);
27✔
1936
                        if (type < 0) {
27✔
1937
                                r = install_changes_add(changes, n_changes, -EUCLEAN, info->name, NULL);
×
1938
                                if (r != -EUCLEAN)
×
1939
                                        return r;
1940
                                return log_debug_errno(type, "Failed to extract instance name from \"%s\": %m", info->name);
×
1941
                        }
1942

1943
                        if (type == UNIT_NAME_INSTANCE) {
27✔
1944
                                r = unit_name_replace_instance(dst, inst, &dst_updated);
4✔
1945
                                if (r < 0)
4✔
1946
                                        return log_error_errno(r, "Failed to build unit name from %s+%s: %m",
×
1947
                                                               dst, inst);
1948
                        }
1949
                }
1950

1951
                r = unit_validate_alias_symlink_or_warn(LOG_DEBUG, dst_updated ?: dst, info->name);
278✔
1952
                if (r == -ELOOP)  /* -ELOOP means self-alias, which we (quietly) ignore */
141✔
1953
                        return r;
1954
                if (r < 0)
141✔
1955
                        return install_changes_add(changes, n_changes,
21✔
1956
                                                   r == -EINVAL ? -EXDEV : r,
1957
                                                   dst_updated ?: dst,
21✔
1958
                                                   info->name);
21✔
1959
        }
1960

1961
        *ret_dst = TAKE_PTR(dst_updated);
133✔
1962
        return 0;
133✔
1963
}
1964

1965
static int install_info_symlink_alias(
577✔
1966
                RuntimeScope scope,
1967
                UnitFileFlags file_flags,
1968
                InstallInfo *info,
1969
                const LookupPaths *lp,
1970
                const char *config_path,
1971
                bool force,
1972
                InstallChange **changes,
1973
                size_t *n_changes) {
1974

1975
        int r, ret = 0;
577✔
1976

1977
        assert(info);
577✔
1978
        assert(lp);
577✔
1979
        assert(config_path);
577✔
1980

1981
        STRV_FOREACH(s, info->aliases) {
687✔
1982
                _cleanup_free_ char *alias_path = NULL, *alias_target = NULL, *dst = NULL, *dst_updated = NULL;
110✔
1983

1984
                r = install_name_printf(scope, info, *s, &dst);
110✔
1985
                if (r < 0) {
110✔
1986
                        RET_GATHER(ret, install_changes_add(changes, n_changes, r, *s, NULL));
×
1987
                        continue;
×
1988
                }
1989

1990
                r = unit_file_verify_alias(info, dst, &dst_updated, changes, n_changes);
110✔
1991
                if (r < 0) {
110✔
1992
                        if (r != -ELOOP)
×
1993
                                RET_GATHER(ret, r);
×
1994
                        continue;
×
1995
                }
1996

1997
                alias_path = path_make_absolute(dst_updated ?: dst, config_path);
110✔
1998
                if (!alias_path)
110✔
1999
                        return -ENOMEM;
2000

2001
                r = in_search_path(lp, info->path);
110✔
2002
                if (r < 0)
110✔
2003
                        return r;
2004
                if (r == 0) {
110✔
2005
                        /* The unit path itself is outside of the search path. To
2006
                         * correctly apply the alias, we need the alias symlink to
2007
                         * point to the symlink that was created in the search path. */
2008
                        alias_target = path_join(config_path, info->name);
1✔
2009
                        if (!alias_target)
1✔
2010
                                return -ENOMEM;
2011
                }
2012

2013
                bool broken;
110✔
2014
                r = chase(alias_path, lp->root_dir, CHASE_NONEXISTENT, /* ret_path= */ NULL, /* ret_fd= */ NULL);
110✔
2015
                if (r < 0 && r != -ENOENT) {
110✔
2016
                        RET_GATHER(ret, r);
×
2017
                        continue;
×
2018
                }
2019
                broken = r == 0; /* symlink target does not exist? */
110✔
2020

2021
                r = create_symlink(lp, alias_target ?: info->path, alias_path, force || broken, changes, n_changes);
110✔
2022
                if (r == -EEXIST && FLAGS_SET(file_flags, UNIT_FILE_IGNORE_AUXILIARY_FAILURE))
110✔
2023
                        /* We cannot realize the alias because a conflicting alias exists.
2024
                         * Do not propagate this as error. */
2025
                        continue;
×
2026
                if (r != 0 && ret >= 0)
110✔
2027
                        ret = r;
110✔
2028
        }
2029

2030
        return ret;
2031
}
2032

2033
static int install_info_symlink_wants(
1,731✔
2034
                RuntimeScope scope,
2035
                UnitFileFlags file_flags,
2036
                InstallInfo *info,
2037
                const LookupPaths *lp,
2038
                const char *config_path,
2039
                char **list,
2040
                const char *suffix,
2041
                InstallChange **changes,
2042
                size_t *n_changes) {
2043

2044
        _cleanup_(install_info_clear) InstallInfo instance = {
×
2045
                .install_mode = _INSTALL_MODE_INVALID,
2046
        };
2047

2048
        UnitNameFlags valid_dst_type = UNIT_NAME_ANY;
1,731✔
2049
        const char *n;
1,731✔
2050
        int r, q;
1,731✔
2051

2052
        assert(info);
1,731✔
2053
        assert(lp);
1,731✔
2054
        assert(config_path);
1,731✔
2055

2056
        if (strv_isempty(list))
2,269✔
2057
                return 0;
2058

2059
        if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE))
538✔
2060
                /* Not a template unit. Use the name directly. */
2061
                n = info->name;
2062

2063
        else if (info->default_instance) {
20✔
2064
                /* If this is a template, and we have a default instance, use it. */
2065

2066
                r = unit_name_replace_instance(info->name, info->default_instance, &instance.name);
20✔
2067
                if (r < 0)
20✔
2068
                        return r;
2069

2070
                r = unit_file_search(NULL, &instance, lp, SEARCH_FOLLOW_CONFIG_SYMLINKS);
20✔
2071
                if (r < 0)
20✔
2072
                        return r;
2073

2074
                if (instance.install_mode == INSTALL_MODE_MASKED)
20✔
2075
                        return install_changes_add(changes, n_changes, -ERFKILL, instance.path, NULL);
×
2076

2077
                n = instance.name;
20✔
2078

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

2083
                valid_dst_type = UNIT_NAME_INSTANCE | UNIT_NAME_TEMPLATE;
2084
                n = info->name;
2085
        }
2086

2087
        r = 0;
538✔
2088
        STRV_FOREACH(s, list) {
1,117✔
2089
                _cleanup_free_ char *path = NULL, *dst = NULL;
579✔
2090

2091
                q = install_name_printf(scope, info, *s, &dst);
579✔
2092
                if (q < 0) {
579✔
2093
                        RET_GATHER(r, install_changes_add(changes, n_changes, q, *s, NULL));
×
2094
                        continue;
×
2095
                }
2096

2097
                if (!unit_name_is_valid(dst, valid_dst_type)) {
579✔
2098
                        /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the
2099
                         * template status doesn't match. If we are doing presets don't bother reporting the
2100
                         * error. This also covers cases like 'systemctl preset serial-getty@.service', which
2101
                         * has no DefaultInstance, so there is nothing we can do. At the same time,
2102
                         * 'systemctl enable serial-getty@.service' should fail, the user should specify an
2103
                         * instance like in 'systemctl enable serial-getty@ttyS0.service'.
2104
                         */
2105
                        if (FLAGS_SET(file_flags, UNIT_FILE_IGNORE_AUXILIARY_FAILURE))
×
2106
                                continue;
×
2107

2108
                        if (unit_name_is_valid(dst, UNIT_NAME_ANY))
×
2109
                                RET_GATHER(r, install_changes_add(changes, n_changes, -EIDRM, dst, n));
×
2110
                        else
2111
                                RET_GATHER(r, install_changes_add(changes, n_changes, -EUCLEAN, dst, NULL));
×
2112

2113
                        continue;
×
2114
                }
2115

2116
                path = strjoin(config_path, "/", dst, suffix, n);
579✔
2117
                if (!path)
579✔
2118
                        return -ENOMEM;
2119

2120
                q = create_symlink(lp, info->path, path, /* force= */ true, changes, n_changes);
579✔
2121
                if (q != 0 && r >= 0)
579✔
2122
                        r = q;
579✔
2123

2124
                if (unit_file_exists(scope, lp, dst) == 0) {
579✔
2125
                        q = install_changes_add(changes, n_changes, INSTALL_CHANGE_DESTINATION_NOT_PRESENT, dst, info->path);
1✔
2126
                        if (q < 0)
1✔
2127
                                return q;
2128
                }
2129
        }
2130

2131
        return r;
2132
}
2133

2134
static int install_info_symlink_link(
577✔
2135
                InstallInfo *info,
2136
                const LookupPaths *lp,
2137
                const char *config_path,
2138
                bool force,
2139
                InstallChange **changes,
2140
                size_t *n_changes) {
2141

2142
        _cleanup_free_ char *path = NULL;
577✔
2143
        int r;
577✔
2144

2145
        assert(info);
577✔
2146
        assert(lp);
577✔
2147
        assert(config_path);
577✔
2148
        assert(info->path);
577✔
2149

2150
        r = in_search_path(lp, info->path);
577✔
2151
        if (r < 0)
577✔
2152
                return r;
2153
        if (r > 0)
577✔
2154
                return 0;
2155

2156
        path = path_join(config_path, info->name);
3✔
2157
        if (!path)
3✔
2158
                return -ENOMEM;
2159

2160
        return create_symlink(lp, info->path, path, force, changes, n_changes);
3✔
2161
}
2162

2163
static int install_info_apply(
577✔
2164
                RuntimeScope scope,
2165
                UnitFileFlags file_flags,
2166
                InstallInfo *info,
2167
                const LookupPaths *lp,
2168
                const char *config_path,
2169
                InstallChange **changes,
2170
                size_t *n_changes) {
2171

2172
        int r, q;
577✔
2173

2174
        assert(info);
577✔
2175
        assert(lp);
577✔
2176
        assert(config_path);
577✔
2177

2178
        if (info->install_mode != INSTALL_MODE_REGULAR)
577✔
2179
                return 0;
2180

2181
        bool force = file_flags & UNIT_FILE_FORCE;
577✔
2182

2183
        r = install_info_symlink_link(info, lp, config_path, force, changes, n_changes);
577✔
2184
        /* Do not count links to the unit file towards the "carries_install_info" count */
2185
        if (r < 0)
577✔
2186
                /* If linking of the file failed, do not try to create other symlinks,
2187
                 * because they might would pointing to a non-existent or wrong unit. */
2188
                return r;
2189

2190
        r = install_info_symlink_alias(scope, file_flags, info, lp, config_path, force, changes, n_changes);
577✔
2191

2192
        q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes);
577✔
2193
        if (q != 0 && r >= 0)
577✔
2194
                r = q;
538✔
2195

2196
        q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->required_by, ".requires/", changes, n_changes);
577✔
2197
        if (q != 0 && r >= 0)
577✔
2198
                r = q;
×
2199

2200
        q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->upheld_by, ".upholds/", changes, n_changes);
577✔
2201
        if (q != 0 && r >= 0)
577✔
2202
                r = q;
×
2203

2204
        return r;
2205
}
2206

2207
static int install_context_apply(
55✔
2208
                InstallContext *ctx,
2209
                const LookupPaths *lp,
2210
                UnitFileFlags file_flags,
2211
                const char *config_path,
2212
                SearchFlags flags,
2213
                InstallChange **changes,
2214
                size_t *n_changes) {
2215

2216
        InstallInfo *i;
55✔
2217
        int r;
55✔
2218

2219
        assert(ctx);
55✔
2220
        assert(lp);
55✔
2221
        assert(config_path);
55✔
2222

2223
        if (ordered_hashmap_isempty(ctx->will_process))
55✔
2224
                return 0;
2225

2226
        r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &install_info_hash_ops);
50✔
2227
        if (r < 0)
50✔
2228
                return r;
2229

2230
        r = 0;
2231
        while ((i = ordered_hashmap_first(ctx->will_process))) {
651✔
2232
                int q;
601✔
2233

2234
                q = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name);
601✔
2235
                if (q < 0)
601✔
2236
                        return q;
2237

2238
                q = install_info_traverse(ctx, lp, i, flags, NULL);
601✔
2239
                if (q < 0) {
601✔
2240
                        if (i->auxiliary) {
×
2241
                                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_AUXILIARY_FAILED, i->name, NULL);
×
2242
                                if (q < 0)
×
2243
                                        return q;
2244
                                continue;
×
2245
                        }
2246

2247
                        return install_changes_add(changes, n_changes, q, i->name, NULL);
×
2248
                }
2249

2250
                /* We can attempt to process a masked unit when a different unit
2251
                 * that we were processing specifies it in Also=. */
2252
                if (i->install_mode == INSTALL_MODE_MASKED) {
601✔
2253
                        q = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_MASKED, i->path, NULL);
18✔
2254
                        if (q < 0)
18✔
2255
                                return q;
2256
                        if (r >= 0)
18✔
2257
                                /* Assume that something *could* have been enabled here,
2258
                                 * avoid "empty [Install] section" warning. */
2259
                                r += 1;
18✔
2260
                        continue;
18✔
2261
                }
2262

2263
                if (i->install_mode != INSTALL_MODE_REGULAR)
583✔
2264
                        continue;
6✔
2265

2266
                q = install_info_apply(ctx->scope, file_flags, i, lp, config_path, changes, n_changes);
577✔
2267
                if (r >= 0) {
577✔
2268
                        if (q < 0)
577✔
2269
                                r = q;
2270
                        else
2271
                                r += q;
577✔
2272
                }
2273
        }
2274

2275
        return r;
2276
}
2277

2278
static int install_context_mark_for_removal(
25✔
2279
                InstallContext *ctx,
2280
                const LookupPaths *lp,
2281
                Set **remove_symlinks_to,
2282
                const char *config_path,
2283
                InstallChange **changes,
2284
                size_t *n_changes) {
2285

2286
        InstallInfo *i;
25✔
2287
        int r;
25✔
2288

2289
        assert(ctx);
25✔
2290
        assert(lp);
25✔
2291
        assert(config_path);
25✔
2292

2293
        /* Marks all items for removal */
2294

2295
        if (ordered_hashmap_isempty(ctx->will_process))
25✔
2296
                return 0;
2297

2298
        r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &install_info_hash_ops);
21✔
2299
        if (r < 0)
21✔
2300
                return r;
2301

2302
        while ((i = ordered_hashmap_first(ctx->will_process))) {
62✔
2303

2304
                r = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name);
41✔
2305
                if (r < 0)
41✔
2306
                        return r;
2307

2308
                r = install_info_traverse(ctx, lp, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
41✔
2309
                if (r == -ENOLINK) {
41✔
2310
                        log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
×
2311
                        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_DANGLING, i->path ?: i->name, NULL);
×
2312
                        if (r < 0)
×
2313
                                return r;
2314
                } else if (r == -ENOENT) {
41✔
2315
                        if (i->auxiliary)  /* some unit specified in Also= or similar is missing */
×
2316
                                log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
×
2317
                        else {
2318
                                log_debug_errno(r, "Unit %s not found, removing name.", i->name);
×
2319
                                r = install_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
×
2320
                                /* In case there's no unit, we still want to remove any leftover symlink, even if
2321
                                 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2322
                                if (r != -ENOENT)
×
2323
                                        return r;
2324
                        }
2325
                } else if (r < 0) {
41✔
2326
                        log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
×
2327
                        int k = install_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
×
2328
                        if (k != r)
×
2329
                                return k;
2330
                } else if (i->install_mode == INSTALL_MODE_MASKED) {
41✔
2331
                        log_debug("Unit file %s is masked, ignoring.", i->name);
×
2332
                        r = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_MASKED, i->path ?: i->name, NULL);
×
2333
                        if (r < 0)
×
2334
                                return r;
2335
                        continue;
×
2336
                } else if (i->install_mode != INSTALL_MODE_REGULAR) {
41✔
2337
                        log_debug("Unit %s has install mode %s, ignoring.",
2✔
2338
                                  i->name, install_mode_to_string(i->install_mode) ?: "invalid");
2339
                        continue;
2✔
2340
                }
2341

2342
                r = mark_symlink_for_removal(remove_symlinks_to, i->name);
39✔
2343
                if (r < 0)
39✔
2344
                        return r;
2345
        }
2346

2347
        return 0;
2348
}
2349

2350
int unit_file_mask(
11✔
2351
                RuntimeScope scope,
2352
                UnitFileFlags flags,
2353
                const char *root_dir,
2354
                char * const *names,
2355
                InstallChange **changes,
2356
                size_t *n_changes) {
2357

2358
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
11✔
2359
        const char *config_path;
11✔
2360
        int r;
11✔
2361

2362
        assert(scope >= 0);
11✔
2363
        assert(scope < _RUNTIME_SCOPE_MAX);
11✔
2364

2365
        r = lookup_paths_init(&lp, scope, 0, root_dir);
11✔
2366
        if (r < 0)
11✔
2367
                return r;
2368

2369
        config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
11✔
2370
        if (!config_path)
11✔
2371
                return -ENXIO;
2372

2373
        r = 0;
2374

2375
        STRV_FOREACH(name, names) {
22✔
2376
                _cleanup_free_ char *path = NULL;
11✔
2377

2378
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) {
11✔
2379
                        RET_GATHER(r, -EINVAL);
×
2380
                        continue;
×
2381
                }
2382

2383
                path = path_make_absolute(*name, config_path);
11✔
2384
                if (!path)
11✔
2385
                        return -ENOMEM;
×
2386

2387
                RET_GATHER(r, create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes));
11✔
2388
        }
2389

2390
        return r;
2391
}
2392

2393
int unit_file_unmask(
9✔
2394
                RuntimeScope scope,
2395
                UnitFileFlags flags,
2396
                const char *root_dir,
2397
                char * const *names,
2398
                InstallChange **changes,
2399
                size_t *n_changes) {
2400

2401
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
9✔
2402
        _cleanup_set_free_ Set *remove_symlinks_to = NULL;
9✔
2403
        _cleanup_strv_free_ char **todo = NULL;
×
2404
        const char *config_path;
9✔
2405
        size_t n_todo = 0;
9✔
2406
        int r, q;
9✔
2407

2408
        assert(scope >= 0);
9✔
2409
        assert(scope < _RUNTIME_SCOPE_MAX);
9✔
2410

2411
        r = lookup_paths_init(&lp, scope, 0, root_dir);
9✔
2412
        if (r < 0)
9✔
2413
                return r;
2414

2415
        config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
9✔
2416
        if (!config_path)
9✔
2417
                return -ENXIO;
2418

2419
        bool dry_run = flags & UNIT_FILE_DRY_RUN;
9✔
2420

2421
        STRV_FOREACH(name, names) {
18✔
2422
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
9✔
2423
                        return -EINVAL;
9✔
2424

2425
                /* If root_dir is set, we don't care about kernel command line or generators.
2426
                 * But if it is not set, we need to check for interference. */
2427
                if (!root_dir) {
9✔
2428
                        _cleanup_(install_info_clear) InstallInfo info = {
×
2429
                                .name = *name,  /* We borrow *name temporarily… */
5✔
2430
                                .install_mode = _INSTALL_MODE_INVALID,
2431
                        };
2432

2433
                        r = unit_file_search(NULL, &info, &lp, 0);
5✔
2434
                        if (r < 0) {
5✔
2435
                                if (r != -ENOENT)
×
2436
                                        log_debug_errno(r, "Failed to look up unit %s, ignoring: %m", info.name);
×
2437
                        } else if (info.install_mode == INSTALL_MODE_MASKED &&
7✔
2438
                                   path_is_generator(&lp, info.path)) {
2✔
2439
                                r = install_changes_add(changes, n_changes,
×
2440
                                                        INSTALL_CHANGE_IS_MASKED_GENERATOR, info.name, info.path);
×
2441
                                if (r < 0)
×
2442
                                        return r;
×
2443
                        }
2444

2445
                        TAKE_PTR(info.name);  /* … and give it back here */
5✔
2446
                }
2447

2448
                _cleanup_free_ char *path = path_make_absolute(*name, config_path);
18✔
2449
                if (!path)
9✔
2450
                        return -ENOMEM;
2451

2452
                r = null_or_empty_path(path);
9✔
2453
                if (r == -ENOENT)
9✔
2454
                        continue;
4✔
2455
                if (r < 0)
5✔
2456
                        return r;
2457
                if (r == 0)
5✔
2458
                        continue;
×
2459

2460
                if (!GREEDY_REALLOC0(todo, n_todo + 2))
5✔
2461
                        return -ENOMEM;
2462

2463
                todo[n_todo] = strdup(*name);
5✔
2464
                if (!todo[n_todo])
5✔
2465
                        return -ENOMEM;
2466

2467
                n_todo++;
5✔
2468
        }
2469

2470
        strv_uniq(todo);
9✔
2471

2472
        r = 0;
9✔
2473
        STRV_FOREACH(i, todo) {
14✔
2474
                _cleanup_free_ char *path = NULL;
5✔
2475
                const char *rp;
5✔
2476

2477
                path = path_make_absolute(*i, config_path);
5✔
2478
                if (!path)
5✔
2479
                        return -ENOMEM;
2480

2481
                if (!dry_run && unlink(path) < 0) {
5✔
2482
                        if (errno != ENOENT)
×
2483
                                RET_GATHER(r, install_changes_add(changes, n_changes, -errno, path, NULL));
×
2484

2485
                        continue;
×
2486
                }
2487

2488
                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, path, NULL);
5✔
2489
                if (q < 0)
5✔
2490
                        return q;
2491

2492
                rp = skip_root(lp.root_dir, path);
5✔
2493
                q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
5✔
2494
                if (q < 0)
5✔
2495
                        return q;
2496
        }
2497

2498
        RET_GATHER(r, remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes));
9✔
2499

2500
        return r;
2501
}
2502

2503
int unit_file_link(
1✔
2504
                RuntimeScope scope,
2505
                UnitFileFlags flags,
2506
                const char *root_dir,
2507
                char * const *files,
2508
                InstallChange **changes,
2509
                size_t *n_changes) {
2510

2511
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
1✔
2512
        _cleanup_ordered_hashmap_free_ OrderedHashmap *todo = NULL;
1✔
2513
        const char *config_path;
1✔
2514
        int r;
1✔
2515

2516
        assert(scope >= 0);
1✔
2517
        assert(scope < _RUNTIME_SCOPE_MAX);
1✔
2518
        assert(changes);
1✔
2519
        assert(n_changes);
1✔
2520

2521
        r = lookup_paths_init(&lp, scope, 0, root_dir);
1✔
2522
        if (r < 0)
1✔
2523
                return r;
2524

2525
        config_path = FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
1✔
2526
        if (!config_path)
1✔
2527
                return -ENXIO;
2528

2529
        STRV_FOREACH(file, files) {
2✔
2530
                _cleanup_free_ char *fn = NULL, *path = NULL, *full = NULL;
1✔
2531

2532
                if (ordered_hashmap_contains(todo, *file))
1✔
2533
                        continue;
×
2534

2535
                if (!path_is_absolute(*file))
1✔
2536
                        return install_changes_add(changes, n_changes, -EINVAL, *file, NULL);
×
2537

2538
                r = path_extract_filename(*file, &fn);
1✔
2539
                if (r < 0)
1✔
2540
                        return install_changes_add(changes, n_changes, r, *file, NULL);
×
2541

2542
                if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
1✔
2543
                        return install_changes_add(changes, n_changes, -EUCLEAN, *file, NULL);
×
2544

2545
                full = path_join(lp.root_dir, *file);
1✔
2546
                if (!full)
1✔
2547
                        return -ENOMEM;
2548

2549
                r = verify_regular_at(AT_FDCWD, full, /* follow= */ false);
1✔
2550
                if (r < 0)
1✔
2551
                        return install_changes_add(changes, n_changes, r, *file, NULL);
×
2552

2553
                r = in_search_path(&lp, *file);
1✔
2554
                if (r < 0)
1✔
2555
                        return install_changes_add(changes, n_changes, r, *file, NULL);
×
2556
                if (r > 0)
1✔
2557
                        /* A silent noop if the file is already in the search path. */
2558
                        continue;
×
2559

2560
                if (underneath_search_path(&lp, *file))
1✔
2561
                        return install_changes_add(changes, n_changes, -ETXTBSY, *file, NULL);
×
2562

2563
                path = strdup(*file);
1✔
2564
                if (!path)
1✔
2565
                        return -ENOMEM;
2566

2567
                r = ordered_hashmap_ensure_put(&todo, &path_hash_ops_free_free, path, fn);
1✔
2568
                if (r < 0)
1✔
2569
                        return r;
2570
                assert(r > 0);
1✔
2571

2572
                TAKE_PTR(path);
1✔
2573
                TAKE_PTR(fn);
1✔
2574
        }
2575

2576
        r = 0;
1✔
2577

2578
        const char *fn, *path;
1✔
2579
        ORDERED_HASHMAP_FOREACH_KEY(fn, path, todo) {
2✔
2580
                _cleanup_free_ char *new_path = NULL;
1✔
2581

2582
                new_path = path_make_absolute(fn, config_path);
1✔
2583
                if (!new_path)
1✔
2584
                        return -ENOMEM;
×
2585

2586
                RET_GATHER(r, create_symlink(&lp, path, new_path, FLAGS_SET(flags, UNIT_FILE_FORCE), changes, n_changes));
1✔
2587
        }
2588

2589
        return r;
1✔
2590
}
2591

2592
static int path_shall_revert(const LookupPaths *lp, const char *path) {
1✔
2593
        int r;
1✔
2594

2595
        assert(lp);
1✔
2596
        assert(path);
1✔
2597

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

2600
        r = path_is_config(lp, path, true);
1✔
2601
        if (r != 0)
1✔
2602
                return r;
2603

2604
        r = path_is_control(lp, path);
×
2605
        if (r != 0)
×
2606
                return r;
2607

2608
        return path_is_transient(lp, path);
×
2609
}
2610

2611
int unit_file_revert(
4✔
2612
                RuntimeScope scope,
2613
                const char *root_dir,
2614
                char * const *names,
2615
                InstallChange **changes,
2616
                size_t *n_changes) {
2617

2618
        _cleanup_set_free_ Set *remove_symlinks_to = NULL;
4✔
2619
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
4✔
2620
        _cleanup_strv_free_ char **todo = NULL;
4✔
2621
        size_t n_todo = 0;
4✔
2622
        int r, q;
4✔
2623

2624
        /* Puts a unit file back into vendor state. This means:
2625
         *
2626
         * a) we remove all drop-in snippets added by the user ("config"), add to transient units
2627
         *    ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
2628
         *    generated ("generated").
2629
         *
2630
         * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
2631
         *    (i.e. in "config", but not in "transient" or "control" or even "generated").
2632
         *
2633
         * We remove all that in both the runtime and the persistent directories, if that applies.
2634
         */
2635

2636
        r = lookup_paths_init(&lp, scope, 0, root_dir);
4✔
2637
        if (r < 0)
4✔
2638
                return r;
2639

2640
        STRV_FOREACH(name, names) {
8✔
2641
                bool has_vendor = false;
4✔
2642

2643
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
4✔
2644
                        return -EINVAL;
2645

2646
                STRV_FOREACH(p, lp.search_path) {
52✔
2647
                        _cleanup_free_ char *path = NULL, *dropin = NULL;
48✔
2648
                        struct stat st;
48✔
2649

2650
                        path = path_make_absolute(*name, *p);
48✔
2651
                        if (!path)
48✔
2652
                                return -ENOMEM;
2653

2654
                        r = RET_NERRNO(lstat(path, &st));
48✔
2655
                        if (r < 0) {
42✔
2656
                                if (r != -ENOENT)
42✔
2657
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2658
                        } else if (S_ISREG(st.st_mode)) {
6✔
2659
                                /* Check if there's a vendor version */
2660
                                r = path_is_vendor_or_generator(&lp, path);
5✔
2661
                                if (r < 0)
5✔
2662
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2663
                                if (r > 0)
5✔
2664
                                        has_vendor = true;
4✔
2665
                        }
2666

2667
                        dropin = strjoin(path, ".d");
48✔
2668
                        if (!dropin)
48✔
2669
                                return -ENOMEM;
2670

2671
                        r = RET_NERRNO(lstat(dropin, &st));
48✔
2672
                        if (r < 0) {
47✔
2673
                                if (r != -ENOENT)
47✔
2674
                                        return install_changes_add(changes, n_changes, r, dropin, NULL);
×
2675
                        } else if (S_ISDIR(st.st_mode)) {
1✔
2676
                                /* Remove the drop-ins */
2677
                                r = path_shall_revert(&lp, dropin);
1✔
2678
                                if (r < 0)
1✔
2679
                                        return install_changes_add(changes, n_changes, r, dropin, NULL);
×
2680
                                if (r > 0) {
1✔
2681
                                        if (!GREEDY_REALLOC0(todo, n_todo + 2))
1✔
2682
                                                return -ENOMEM;
2683

2684
                                        todo[n_todo++] = TAKE_PTR(dropin);
1✔
2685
                                }
2686
                        }
2687
                }
2688

2689
                if (!has_vendor)
4✔
2690
                        continue;
×
2691

2692
                /* OK, there's a vendor version, hence drop all configuration versions */
2693
                STRV_FOREACH(p, lp.search_path) {
52✔
2694
                        _cleanup_free_ char *path = NULL;
48✔
2695
                        struct stat st;
48✔
2696

2697
                        path = path_make_absolute(*name, *p);
48✔
2698
                        if (!path)
48✔
2699
                                return -ENOMEM;
2700

2701
                        r = RET_NERRNO(lstat(path, &st));
48✔
2702
                        if (r < 0) {
42✔
2703
                                if (r != -ENOENT)
42✔
2704
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2705
                        } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
6✔
2706
                                r = path_is_config(&lp, path, true);
6✔
2707
                                if (r < 0)
6✔
2708
                                        return install_changes_add(changes, n_changes, r, path, NULL);
×
2709
                                if (r > 0) {
6✔
2710
                                        if (!GREEDY_REALLOC0(todo, n_todo + 2))
2✔
2711
                                                return -ENOMEM;
2712

2713
                                        todo[n_todo++] = TAKE_PTR(path);
2✔
2714
                                }
2715
                        }
2716
                }
2717
        }
2718

2719
        strv_uniq(todo);
4✔
2720

2721
        r = 0;
4✔
2722
        STRV_FOREACH(i, todo) {
7✔
2723
                _cleanup_strv_free_ char **fs = NULL;
3✔
2724
                const char *rp;
3✔
2725

2726
                (void) get_files_in_directory(*i, &fs);
3✔
2727

2728
                q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
3✔
2729
                if (q < 0 && q != -ENOENT && r >= 0) {
3✔
2730
                        r = q;
×
2731
                        continue;
×
2732
                }
2733

2734
                STRV_FOREACH(j, fs) {
4✔
2735
                        _cleanup_free_ char *t = NULL;
1✔
2736

2737
                        t = path_join(*i, *j);
1✔
2738
                        if (!t)
1✔
2739
                                return -ENOMEM;
2740

2741
                        q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, t, NULL);
1✔
2742
                        if (q < 0)
1✔
2743
                                return q;
2744
                }
2745

2746
                q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, *i, NULL);
3✔
2747
                if (q < 0)
3✔
2748
                        return q;
2749

2750
                rp = skip_root(lp.root_dir, *i);
3✔
2751
                q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
3✔
2752
                if (q < 0)
3✔
2753
                        return q;
2754
        }
2755

2756
        q = remove_marked_symlinks(remove_symlinks_to, lp.runtime_config, &lp, false, changes, n_changes);
4✔
2757
        if (r >= 0)
4✔
2758
                r = q;
4✔
2759

2760
        q = remove_marked_symlinks(remove_symlinks_to, lp.persistent_config, &lp, false, changes, n_changes);
4✔
2761
        if (r >= 0)
4✔
2762
                r = q;
4✔
2763

2764
        return r;
2765
}
2766

2767
int unit_file_add_dependency(
1✔
2768
                RuntimeScope scope,
2769
                UnitFileFlags file_flags,
2770
                const char *root_dir,
2771
                char * const *names,
2772
                const char *target,
2773
                UnitDependency dep,
2774
                InstallChange **changes,
2775
                size_t *n_changes) {
2776

2777
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
1✔
2778
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
1✔
2779
        InstallInfo *info, *target_info;
1✔
2780
        const char *config_path;
1✔
2781
        int r;
1✔
2782

2783
        assert(scope >= 0);
1✔
2784
        assert(scope < _RUNTIME_SCOPE_MAX);
1✔
2785
        assert(target);
1✔
2786
        assert(IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES));
1✔
2787

2788
        if (!unit_name_is_valid(target, UNIT_NAME_ANY))
1✔
2789
                return install_changes_add(changes, n_changes, -EUCLEAN, target, NULL);
×
2790

2791
        r = lookup_paths_init(&lp, scope, 0, root_dir);
1✔
2792
        if (r < 0)
1✔
2793
                return r;
2794

2795
        config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
1✔
2796
        if (!config_path)
1✔
2797
                return -ENXIO;
2798

2799
        r = install_info_discover_and_check(&ctx, &lp, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
1✔
2800
                                            &target_info, changes, n_changes);
2801
        if (r < 0)
1✔
2802
                return r;
2803

2804
        assert(target_info->install_mode == INSTALL_MODE_REGULAR);
1✔
2805

2806
        STRV_FOREACH(name, names) {
2✔
2807
                char ***l;
1✔
2808

2809
                r = install_info_discover_and_check(&ctx, &lp, *name,
1✔
2810
                                                    SEARCH_FOLLOW_CONFIG_SYMLINKS,
2811
                                                    &info, changes, n_changes);
2812
                if (r < 0)
1✔
2813
                        return r;
2814

2815
                assert(info->install_mode == INSTALL_MODE_REGULAR);
1✔
2816

2817
                /* We didn't actually load anything from the unit
2818
                 * file, but instead just add in our new symlink to
2819
                 * create. */
2820

2821
                if (dep == UNIT_WANTS)
1✔
2822
                        l = &info->wanted_by;
1✔
2823
                else if (dep == UNIT_REQUIRES)
×
2824
                        l = &info->required_by;
×
2825
                else
2826
                        l = &info->upheld_by;
×
2827

2828
                strv_free(*l);
1✔
2829
                *l = strv_new(target_info->name);
1✔
2830
                if (!*l)
1✔
2831
                        return -ENOMEM;
2832
        }
2833

2834
        return install_context_apply(&ctx, &lp, file_flags, config_path,
1✔
2835
                                     SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
2836
}
2837

2838
static int do_unit_file_enable(
27✔
2839
                const LookupPaths *lp,
2840
                RuntimeScope scope,
2841
                UnitFileFlags flags,
2842
                const char *config_path,
2843
                char * const *names_or_paths,
2844
                InstallChange **changes,
2845
                size_t *n_changes) {
2846

2847
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
27✔
2848
        InstallInfo *info;
27✔
2849
        int r;
27✔
2850

2851
        STRV_FOREACH(name, names_or_paths) {
53✔
2852
                r = install_info_discover_and_check(&ctx, lp, *name,
27✔
2853
                                                    SEARCH_LOAD | SEARCH_FOLLOW_CONFIG_SYMLINKS,
2854
                                                    &info, changes, n_changes);
2855
                if (r < 0)
27✔
2856
                        return r;
2857

2858
                assert(info->install_mode == INSTALL_MODE_REGULAR);
26✔
2859
        }
2860

2861
        /* This will return the number of symlink rules that were
2862
           supposed to be created, not the ones actually created. This
2863
           is useful to determine whether the passed units had any
2864
           installation data at all. */
2865

2866
        return install_context_apply(&ctx, lp, flags, config_path,
26✔
2867
                                     SEARCH_LOAD, changes, n_changes);
2868
}
2869

2870
int unit_file_enable(
25✔
2871
                RuntimeScope scope,
2872
                UnitFileFlags flags,
2873
                const char *root_dir,
2874
                char * const *names_or_paths,
2875
                InstallChange **changes,
2876
                size_t *n_changes) {
2877

2878
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
25✔
2879
        int r;
25✔
2880

2881
        assert(scope >= 0);
25✔
2882
        assert(scope < _RUNTIME_SCOPE_MAX);
25✔
2883

2884
        r = lookup_paths_init(&lp, scope, 0, root_dir);
25✔
2885
        if (r < 0)
25✔
2886
                return r;
2887

2888
        const char *config_path = config_path_from_flags(&lp, flags);
25✔
2889
        if (!config_path)
25✔
2890
                return -ENXIO;
2891

2892
        return do_unit_file_enable(&lp, scope, flags, config_path, names_or_paths, changes, n_changes);
25✔
2893
}
2894

2895
static int do_unit_file_disable(
15✔
2896
                const LookupPaths *lp,
2897
                RuntimeScope scope,
2898
                UnitFileFlags flags,
2899
                const char *config_path,
2900
                char * const *names,
2901
                InstallChange **changes,
2902
                size_t *n_changes) {
2903

2904
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
15✔
2905
        bool has_install_info = false;
15✔
2906
        int r;
15✔
2907

2908
        assert(changes);
15✔
2909
        assert(n_changes);
15✔
2910

2911
        STRV_FOREACH(name, names) {
30✔
2912
                InstallInfo *info;
15✔
2913

2914
                if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
15✔
2915
                        return install_changes_add(changes, n_changes, -EUCLEAN, *name, NULL);
×
2916

2917
                r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, &info);
15✔
2918
                if (r >= 0)
15✔
2919
                        r = install_info_traverse(&ctx, lp, info, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
15✔
2920
                if (r < 0) {
15✔
2921
                        r = install_changes_add(changes, n_changes, r, *name, NULL);
×
2922
                        /* In case there's no unit, we still want to remove any leftover symlink, even if
2923
                         * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2924
                        if (r != -ENOENT)
×
2925
                                return r;
2926
                }
2927

2928
                /* If we enable multiple units, some with install info and others without,
2929
                 * the "empty [Install] section" warning is not shown. Let's make the behavior
2930
                 * of disable align with that. */
2931
                has_install_info = has_install_info || install_info_has_rules(info) || install_info_has_also(info);
17✔
2932
        }
2933

2934
        _cleanup_set_free_ Set *remove_symlinks_to = NULL;
15✔
2935
        r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes);
15✔
2936
        if (r >= 0)
15✔
2937
                r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
15✔
2938
        if (r < 0)
15✔
2939
                return r;
2940

2941
        /* The warning is shown only if it's a no-op */
2942
        return install_changes_have_modification(*changes, *n_changes) || has_install_info;
15✔
2943
}
2944

2945
int unit_file_disable(
13✔
2946
                RuntimeScope scope,
2947
                UnitFileFlags flags,
2948
                const char *root_dir,
2949
                char * const *files,
2950
                InstallChange **changes,
2951
                size_t *n_changes) {
2952

2953
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
13✔
2954
        int r;
13✔
2955

2956
        assert(scope >= 0);
13✔
2957
        assert(scope < _RUNTIME_SCOPE_MAX);
13✔
2958

2959
        r = lookup_paths_init(&lp, scope, 0, root_dir);
13✔
2960
        if (r < 0)
13✔
2961
                return r;
2962

2963
        const char *config_path = config_path_from_flags(&lp, flags);
13✔
2964
        if (!config_path)
13✔
2965
                return -ENXIO;
2966

2967
        return do_unit_file_disable(&lp, scope, flags, config_path, files, changes, n_changes);
13✔
2968
}
2969

2970
static int normalize_linked_files(
2✔
2971
                RuntimeScope scope,
2972
                const LookupPaths *lp,
2973
                char * const *names_or_paths,
2974
                char ***ret_names,
2975
                char ***ret_files) {
2976

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

2981
        _cleanup_strv_free_ char **files = NULL, **names = NULL;
2✔
2982
        int r;
2✔
2983

2984
        STRV_FOREACH(a, names_or_paths) {
4✔
2985
                _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
×
2986
                InstallInfo *i = NULL;
2✔
2987
                _cleanup_free_ char *n = NULL;
2✔
2988

2989
                r = path_extract_filename(*a, &n);
2✔
2990
                if (r < 0)
2✔
2991
                        return r;
2992
                if (r == O_DIRECTORY)
2✔
2993
                        return log_debug_errno(SYNTHETIC_ERRNO(EISDIR),
×
2994
                                               "Unexpected path to a directory \"%s\", refusing.", *a);
2995

2996
                if (!is_path(*a) && !unit_name_is_valid(*a, UNIT_NAME_INSTANCE)) {
2✔
2997
                        r = install_info_discover(&ctx, lp, n, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i, NULL, NULL);
2✔
2998
                        if (r < 0)
2✔
2999
                                log_debug_errno(r, "Failed to discover unit \"%s\", operating on name: %m", n);
×
3000
                }
3001

3002
                r = strv_consume(&names, TAKE_PTR(n));
2✔
3003
                if (r < 0)
2✔
3004
                        return r;
3005

3006
                const char *p = NULL;
2✔
3007
                if (i && i->path && i->root)
2✔
3008
                        /* Use startswith here, because we know that paths are normalized, and
3009
                         * path_startswith() would give us a relative path, but we need an absolute path
3010
                         * relative to i->root.
3011
                         *
3012
                         * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
3013
                         * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
3014
                         * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
3015
                         */
3016
                        p = startswith(i->path, i->root);
1✔
3017

3018
                r = strv_extend(&files, p ?: *a);
2✔
3019
                if (r < 0)
2✔
3020
                        return r;
3021
        }
3022

3023
        *ret_names = TAKE_PTR(names);
2✔
3024
        *ret_files = TAKE_PTR(files);
2✔
3025
        return 0;
2✔
3026
}
3027

3028
int unit_file_reenable(
2✔
3029
                RuntimeScope scope,
3030
                UnitFileFlags flags,
3031
                const char *root_dir,
3032
                char * const *names_or_paths,
3033
                InstallChange **changes,
3034
                size_t *n_changes) {
3035

3036
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
2✔
3037
        _cleanup_strv_free_ char **names = NULL, **files = NULL;
2✔
3038
        int r;
2✔
3039

3040
        assert(scope >= 0);
2✔
3041
        assert(scope < _RUNTIME_SCOPE_MAX);
2✔
3042

3043
        r = lookup_paths_init(&lp, scope, 0, root_dir);
2✔
3044
        if (r < 0)
2✔
3045
                return r;
3046

3047
        const char *config_path = config_path_from_flags(&lp, flags);
2✔
3048
        if (!config_path)
2✔
3049
                return -ENXIO;
3050

3051
        r = normalize_linked_files(scope, &lp, names_or_paths, &names, &files);
2✔
3052
        if (r < 0)
2✔
3053
                return r;
3054

3055
        /* First, we invoke the disable command with only the basename... */
3056
        r = do_unit_file_disable(&lp, scope, flags, config_path, names, changes, n_changes);
2✔
3057
        if (r < 0)
2✔
3058
                return r;
3059

3060
        /* But the enable command with the full name */
3061
        return do_unit_file_enable(&lp, scope, flags, config_path, files, changes, n_changes);
2✔
3062
}
3063

3064
int unit_file_set_default(
4✔
3065
                RuntimeScope scope,
3066
                UnitFileFlags flags,
3067
                const char *root_dir,
3068
                const char *name,
3069
                InstallChange **changes,
3070
                size_t *n_changes) {
3071

3072
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
4✔
3073
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
4✔
3074
        InstallInfo *info;
4✔
3075
        const char *new_path;
4✔
3076
        int r;
4✔
3077

3078
        assert(scope >= 0);
4✔
3079
        assert(scope < _RUNTIME_SCOPE_MAX);
4✔
3080
        assert(name);
4✔
3081

3082
        if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
4✔
3083
                return -EINVAL;
3084
        if (streq(name, SPECIAL_DEFAULT_TARGET))
4✔
3085
                return -EINVAL;
3086

3087
        r = lookup_paths_init(&lp, scope, 0, root_dir);
4✔
3088
        if (r < 0)
4✔
3089
                return r;
3090

3091
        r = install_info_discover_and_check(&ctx, &lp, name, 0, &info, changes, n_changes);
4✔
3092
        if (r < 0)
4✔
3093
                return r;
3094

3095
        new_path = strjoina(lp.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
15✔
3096
        return create_symlink(&lp, info->path, new_path, flags & UNIT_FILE_FORCE, changes, n_changes);
3✔
3097
}
3098

3099
int unit_file_get_default(
9✔
3100
                RuntimeScope scope,
3101
                const char *root_dir,
3102
                char **ret) {
3103

3104
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
9✔
3105
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
9✔
3106
        InstallInfo *info;
9✔
3107
        int r;
9✔
3108

3109
        assert(scope >= 0);
9✔
3110
        assert(scope < _RUNTIME_SCOPE_MAX);
9✔
3111
        assert(ret);
9✔
3112

3113
        r = lookup_paths_init(&lp, scope, 0, root_dir);
9✔
3114
        if (r < 0)
9✔
3115
                return r;
3116

3117
        r = install_info_discover(&ctx, &lp, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
9✔
3118
                                  &info, NULL, NULL);
3119
        if (r < 0)
9✔
3120
                return r;
3121

3122
        return strdup_to(ret, info->name);
7✔
3123
}
3124

3125
int unit_file_lookup_state(
3,218✔
3126
                RuntimeScope scope,
3127
                const LookupPaths *lp,
3128
                const char *name,
3129
                UnitFileState *ret) {
3130

3131
        _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
3,218✔
3132
        InstallInfo *info;
3,218✔
3133
        UnitFileState state;
3,218✔
3134
        int r;
3,218✔
3135

3136
        assert(lp);
3,218✔
3137
        assert(name);
3,218✔
3138

3139
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
3,218✔
3140
                return -EINVAL;
3141

3142
        r = install_info_discover(&ctx, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3,218✔
3143
                                  &info, NULL, NULL);
3144
        if (r < 0)
3,218✔
3145
                return log_debug_errno(r, "Failed to discover unit %s: %m", name);
42✔
3146

3147
        assert(IN_SET(info->install_mode, INSTALL_MODE_REGULAR, INSTALL_MODE_MASKED));
3,176✔
3148
        log_debug("Found unit %s at %s (%s)", name, strna(info->path),
3,196✔
3149
                  info->install_mode == INSTALL_MODE_REGULAR ? "regular file" : "mask");
3150

3151
        /* Shortcut things, if the caller just wants to know if this unit exists. */
3152
        if (!ret)
3,176✔
3153
                return 0;
3154

3155
        switch (info->install_mode) {
3,168✔
3156

3157
        case INSTALL_MODE_MASKED:
26✔
3158
                r = path_is_runtime(lp, info->path, true);
26✔
3159
                if (r < 0)
26✔
3160
                        return r;
3161

3162
                state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
26✔
3163
                break;
26✔
3164

3165
        case INSTALL_MODE_REGULAR:
3,142✔
3166
                /* Check if the name we were querying is actually an alias */
3167
                if (!path_equal_filename(name, info->path) && !unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
3,142✔
3168
                        state = UNIT_FILE_ALIAS;
72✔
3169
                        break;
72✔
3170
                }
3171

3172
                r = path_is_generator(lp, info->path);
3,070✔
3173
                if (r < 0)
3,070✔
3174
                        return r;
3175
                if (r > 0) {
3,070✔
3176
                        state = UNIT_FILE_GENERATED;
9✔
3177
                        break;
9✔
3178
                }
3179

3180
                r = path_is_transient(lp, info->path);
3,061✔
3181
                if (r < 0)
3,061✔
3182
                        return r;
3183
                if (r > 0) {
3,061✔
3184
                        state = UNIT_FILE_TRANSIENT;
14✔
3185
                        break;
14✔
3186
                }
3187

3188
                /* Check if any of the Alias= symlinks have been created.
3189
                 * We ignore other aliases, and only check those that would
3190
                 * be created by systemctl enable for this unit. */
3191
                r = find_symlinks_in_scope(scope, lp, info, true, &state);
3,047✔
3192
                if (r < 0)
3,047✔
3193
                        return r;
3194
                if (r > 0)
3,047✔
3195
                        break;
3196

3197
                /* Check if the file is known under other names. If it is,
3198
                 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
3199
                r = find_symlinks_in_scope(scope, lp, info, false, &state);
2,820✔
3200
                if (r < 0)
2,820✔
3201
                        return r;
3202
                if (r > 0)
2,820✔
3203
                        state = UNIT_FILE_INDIRECT;
12✔
3204
                else {
3205
                        if (install_info_has_rules(info))
2,808✔
3206
                                state = UNIT_FILE_DISABLED;
324✔
3207
                        else if (install_info_has_also(info))
2,484✔
3208
                                state = UNIT_FILE_INDIRECT;
28✔
3209
                        else
3210
                                state = UNIT_FILE_STATIC;
2,456✔
3211
                }
3212

3213
                break;
3214

3215
        default:
×
3216
                assert_not_reached();
×
3217
        }
3218

3219
        *ret = state;
3,168✔
3220
        return 0;
3,168✔
3221
}
3222

3223
int unit_file_get_state(
679✔
3224
                RuntimeScope scope,
3225
                const char *root_dir,
3226
                const char *name,
3227
                UnitFileState *ret) {
3228

3229
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
679✔
3230
        int r;
679✔
3231

3232
        assert(scope >= 0);
679✔
3233
        assert(scope < _RUNTIME_SCOPE_MAX);
679✔
3234
        assert(name);
679✔
3235

3236
        r = lookup_paths_init(&lp, scope, 0, root_dir);
679✔
3237
        if (r < 0)
679✔
3238
                return r;
3239

3240
        return unit_file_lookup_state(scope, &lp, name, ret);
679✔
3241
}
3242

3243
int unit_file_exists_full(
1,700✔
3244
                RuntimeScope scope,
3245
                const LookupPaths *lp,
3246
                SearchFlags flags,
3247
                const char *name,
3248
                char **ret_path) {
3249

3250
        _cleanup_(install_context_done) InstallContext c = {
1,700✔
3251
                .scope = scope,
3252
        };
3253
        int r;
1,700✔
3254

3255
        assert(lp);
1,700✔
3256
        assert(name);
1,700✔
3257

3258
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
1,700✔
3259
                return -EINVAL;
3260

3261
        InstallInfo *info = NULL;
1,700✔
3262
        r = install_info_discover(
3,400✔
3263
                        &c,
3264
                        lp,
3265
                        name,
3266
                        flags,
3267
                        ret_path ? &info : NULL,
3268
                        /* changes= */ NULL,
3269
                        /* n_changes= */ NULL);
3270
        if (r == -ENOENT) {
1,700✔
3271
                if (ret_path)
1,122✔
3272
                        *ret_path = NULL;
×
3273
                return 0;
3274
        }
3275
        if (r < 0)
578✔
3276
                return r;
3277

3278
        if (ret_path) {
578✔
3279
                assert(info);
×
3280

3281
                r = strdup_to(ret_path, info->path);
×
3282
                if (r < 0)
×
3283
                        return r;
×
3284
        }
3285

3286
        return 1;
3287
}
3288

3289
static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
13,234✔
3290
        _cleanup_strv_free_ char **instances = NULL;
×
3291
        _cleanup_free_ char *unit_name = NULL;
13,234✔
3292
        int r;
13,234✔
3293

3294
        assert(pattern);
13,234✔
3295
        assert(out_instances);
13,234✔
3296
        assert(out_unit_name);
13,234✔
3297

3298
        r = extract_first_word(&pattern, &unit_name, NULL, EXTRACT_RETAIN_ESCAPE);
13,234✔
3299
        if (r < 0)
13,234✔
3300
                return r;
3301

3302
        /* We handle the instances logic when unit name is extracted */
3303
        if (pattern) {
13,234✔
3304
                /* We only create instances when a rule of templated unit
3305
                 * is seen. A rule like enable foo@.service a b c will
3306
                 * result in an array of (a, b, c) as instance names */
3307
                if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
2✔
3308
                        return -EINVAL;
3309

3310
                instances = strv_split(pattern, WHITESPACE);
2✔
3311
                if (!instances)
2✔
3312
                        return -ENOMEM;
3313

3314
                *out_instances = TAKE_PTR(instances);
2✔
3315
        }
3316

3317
        *out_unit_name = TAKE_PTR(unit_name);
13,234✔
3318

3319
        return 0;
13,234✔
3320
}
3321

3322
static int presets_find_config(RuntimeScope scope, const char *root_dir, char ***ret) {
485✔
3323
        static const char* const initrd_dirs[] = { CONF_PATHS("systemd/initrd-preset"), NULL };
485✔
3324
        static const char* const system_dirs[] = { CONF_PATHS("systemd/system-preset"), NULL };
485✔
3325
        static const char* const user_dirs[] = { CONF_PATHS("systemd/user-preset"), NULL };
485✔
3326
        const char* const* dirs;
485✔
3327
        int r;
485✔
3328

3329
        assert(scope >= 0);
485✔
3330
        assert(scope < _RUNTIME_SCOPE_MAX);
485✔
3331

3332
        if (scope == RUNTIME_SCOPE_SYSTEM) {
485✔
3333
                r = chase_and_access("/etc/initrd-release", root_dir, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
446✔
3334
                if (r < 0 && r != -ENOENT)
446✔
3335
                        return r;
3336

3337
                /* Make sure that we fall back to the system preset directories if we're operating on a root
3338
                 * directory without initrd preset directories. This makes sure that we don't regress when
3339
                 * using a newer systemctl to operate on a root directory with an older version of systemd
3340
                 * installed that doesn't yet known about initrd preset directories. */
3341
                if (r >= 0)
446✔
3342
                        STRV_FOREACH(d, initrd_dirs) {
×
3343
                                r = chase_and_access(*d, root_dir, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
×
3344
                                if (r >= 0)
×
3345
                                        return conf_files_list_strv(ret, ".preset", root_dir, 0, initrd_dirs);
×
3346
                                if (r != -ENOENT)
×
3347
                                        return r;
3348
                        }
3349

3350
                dirs = system_dirs;
3351
        } else if (IN_SET(scope, RUNTIME_SCOPE_GLOBAL, RUNTIME_SCOPE_USER))
39✔
3352
                dirs = user_dirs;
3353
        else
3354
                assert_not_reached();
×
3355

3356
        return conf_files_list_strv(ret, ".preset", root_dir, 0, dirs);
485✔
3357
}
3358

3359
static int read_presets(RuntimeScope scope, const char *root_dir, UnitFilePresets *presets) {
485✔
3360
        _cleanup_(unit_file_presets_done) UnitFilePresets ps = {};
485✔
3361
        _cleanup_strv_free_ char **files = NULL;
×
3362
        int r;
485✔
3363

3364
        assert(scope >= 0);
485✔
3365
        assert(scope < _RUNTIME_SCOPE_MAX);
485✔
3366
        assert(presets);
485✔
3367

3368
        r = presets_find_config(scope, root_dir, &files);
485✔
3369
        if (r < 0)
485✔
3370
                return r;
3371

3372
        STRV_FOREACH(p, files) {
2,291✔
3373
                _cleanup_fclose_ FILE *f = NULL;
2,291✔
3374
                int n = 0;
1,806✔
3375

3376
                f = fopen(*p, "re");
1,806✔
3377
                if (!f) {
1,806✔
3378
                        if (errno == ENOENT)
×
3379
                                continue;
×
3380

3381
                        return -errno;
×
3382
                }
3383

3384
                for (;;) {
58,500✔
3385
                        _cleanup_free_ char *line = NULL;
58,500✔
3386
                        _cleanup_(unit_file_preset_rule_done) UnitFilePresetRule rule = {};
58,500✔
3387
                        const char *parameter;
58,500✔
3388

3389
                        r = read_stripped_line(f, LONG_LINE_MAX, &line);
58,500✔
3390
                        if (r < 0)
58,500✔
3391
                                return r;
3392
                        if (r == 0)
58,500✔
3393
                                break;
3394

3395
                        n++;
56,694✔
3396

3397
                        if (isempty(line))
56,694✔
3398
                                continue;
11,034✔
3399
                        if (strchr(COMMENTS, line[0]))
45,660✔
3400
                                continue;
15,321✔
3401

3402
                        if ((parameter = first_word(line, "enable"))) {
30,339✔
3403
                                char *unit_name;
13,234✔
3404
                                char **instances = NULL;
13,234✔
3405

3406
                                /* Unit_name will remain the same as parameter when no instances are specified */
3407
                                r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
13,234✔
3408
                                if (r < 0) {
13,234✔
3409
                                        log_syntax(NULL, LOG_WARNING, *p, n, r, "Couldn't parse line '%s'. Ignoring.", line);
×
3410
                                        continue;
×
3411
                                }
3412

3413
                                rule = (UnitFilePresetRule) {
13,234✔
3414
                                        .pattern = unit_name,
3415
                                        .action = PRESET_ENABLE,
3416
                                        .instances = instances,
3417
                                };
3418

3419
                        } else if ((parameter = first_word(line, "disable"))) {
17,105✔
3420
                                char *pattern;
17,097✔
3421

3422
                                pattern = strdup(parameter);
17,097✔
3423
                                if (!pattern)
17,097✔
3424
                                        return -ENOMEM;
3425

3426
                                rule = (UnitFilePresetRule) {
17,097✔
3427
                                        .pattern = pattern,
3428
                                        .action = PRESET_DISABLE,
3429
                                };
3430

3431
                        } else if ((parameter = first_word(line, "ignore"))) {
8✔
3432
                                char *pattern;
4✔
3433

3434
                                pattern = strdup(parameter);
4✔
3435
                                if (!pattern)
4✔
3436
                                        return -ENOMEM;
3437

3438
                                rule = (UnitFilePresetRule) {
4✔
3439
                                        .pattern = pattern,
3440
                                        .action = PRESET_IGNORE,
3441
                                };
3442
                        }
3443

3444
                        if (rule.action != 0) {
30,339✔
3445
                                if (!GREEDY_REALLOC(ps.rules, ps.n_rules + 1))
30,335✔
3446
                                        return -ENOMEM;
3447

3448
                                ps.rules[ps.n_rules++] = TAKE_STRUCT(rule);
30,335✔
3449
                                continue;
30,335✔
3450
                        }
3451

3452
                        log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
4✔
3453
                }
3454
        }
3455

3456
        ps.initialized = true;
485✔
3457
        *presets = TAKE_STRUCT(ps);
485✔
3458

3459
        return 0;
485✔
3460
}
3461

3462
static int pattern_match_multiple_instances(
665,445✔
3463
                        const UnitFilePresetRule rule,
3464
                        const char *unit_name,
3465
                        char ***ret) {
3466

3467
        _cleanup_free_ char *templated_name = NULL;
665,445✔
3468
        int r;
665,445✔
3469

3470
        assert(unit_name);
665,445✔
3471

3472
        /* If no ret is needed or the rule itself does not have instances
3473
         * initialized, we return not matching */
3474
        if (!ret || !rule.instances)
665,445✔
3475
                return 0;
3476

3477
        r = unit_name_template(unit_name, &templated_name);
4✔
3478
        if (r < 0)
4✔
3479
                return r;
3480
        if (!streq(rule.pattern, templated_name))
2✔
3481
                return 0;
3482

3483
        /* Compose a list of specified instances when unit name is a template  */
3484
        if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
2✔
3485
                _cleanup_strv_free_ char **out_strv = NULL;
1✔
3486

3487
                STRV_FOREACH(iter, rule.instances) {
4✔
3488
                        _cleanup_free_ char *name = NULL;
3✔
3489

3490
                        r = unit_name_replace_instance(unit_name, *iter, &name);
3✔
3491
                        if (r < 0)
3✔
3492
                                return r;
3493

3494
                        r = strv_consume(&out_strv, TAKE_PTR(name));
3✔
3495
                        if (r < 0)
3✔
3496
                                return r;
3497
                }
3498

3499
                *ret = TAKE_PTR(out_strv);
1✔
3500
                return 1;
1✔
3501
        } else {
3502
                /* We now know the input unit name is an instance name */
3503
                _cleanup_free_ char *instance_name = NULL;
1✔
3504

3505
                r = unit_name_to_instance(unit_name, &instance_name);
1✔
3506
                if (r < 0)
1✔
3507
                        return r;
3508

3509
                if (strv_contains(rule.instances, instance_name))
1✔
3510
                        return 1;
3511
        }
3512
        return 0;
×
3513
}
3514

3515
static int query_presets(const char *name, const UnitFilePresets *presets, char ***instance_name_list) {
10,398✔
3516
        PresetAction action = PRESET_UNKNOWN;
10,398✔
3517

3518
        assert(name);
10,398✔
3519
        assert(presets);
10,398✔
3520
        POINTER_MAY_BE_NULL(instance_name_list);
10,398✔
3521

3522
        if (!unit_name_is_valid(name, UNIT_NAME_ANY))
10,398✔
3523
                return -EINVAL;
3524

3525
        FOREACH_ARRAY(i, presets->rules, presets->n_rules)
665,482✔
3526
                if (pattern_match_multiple_instances(*i, name, instance_name_list) > 0 ||
1,330,888✔
3527
                    fnmatch(i->pattern, name, FNM_NOESCAPE) == 0) {
665,443✔
3528
                        action = i->action;
10,361✔
3529
                        break;
10,361✔
3530
                }
3531

3532
        switch (action) {
10,398✔
3533

3534
        case PRESET_UNKNOWN:
3535
                log_debug("Preset files don't specify rule for %s. Enabling.", name);
37✔
3536
                return PRESET_ENABLE;
3537

3538
        case PRESET_ENABLE:
514✔
3539
                if (instance_name_list && *instance_name_list)
514✔
3540
                        STRV_FOREACH(s, *instance_name_list)
4✔
3541
                                log_debug("Preset files say enable %s.", *s);
3✔
3542
                else
3543
                        log_debug("Preset files say enable %s.", name);
513✔
3544
                return PRESET_ENABLE;
3545

3546
        case PRESET_DISABLE:
3547
                log_debug("Preset files say disable %s.", name);
9,845✔
3548
                return PRESET_DISABLE;
3549

3550
        case PRESET_IGNORE:
3551
                log_debug("Preset files say ignore %s.", name);
2✔
3552
                return PRESET_IGNORE;
3553

3554
        default:
×
3555
                assert_not_reached();
×
3556
        }
3557
}
3558

3559
PresetAction unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
770✔
3560
        _cleanup_(unit_file_presets_done) UnitFilePresets tmp = {};
770✔
3561
        int r;
770✔
3562

3563
        if (!cached)
770✔
3564
                cached = &tmp;
450✔
3565
        if (!cached->initialized) {
770✔
3566
                r = read_presets(scope, root_dir, cached);
455✔
3567
                if (r < 0)
455✔
3568
                        return r;
3569
        }
3570

3571
        return query_presets(name, cached, NULL);
770✔
3572
}
3573

3574
static int execute_preset(
29✔
3575
                UnitFileFlags file_flags,
3576
                InstallContext *plus,
3577
                InstallContext *minus,
3578
                const LookupPaths *lp,
3579
                const char *config_path,
3580
                char * const *files,
3581
                UnitFilePresetMode mode,
3582
                InstallChange **changes,
3583
                size_t *n_changes) {
3584

3585
        int r;
29✔
3586

3587
        assert(plus);
29✔
3588
        assert(minus);
29✔
3589
        assert(lp);
29✔
3590
        assert(config_path);
29✔
3591

3592
        if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
29✔
3593
                _cleanup_set_free_ Set *remove_symlinks_to = NULL;
×
3594

3595
                r = install_context_mark_for_removal(minus, lp, &remove_symlinks_to, config_path, changes, n_changes);
10✔
3596
                if (r < 0)
10✔
3597
                        return r;
×
3598

3599
                r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, false, changes, n_changes);
10✔
3600
        } else
3601
                r = 0;
3602

3603
        if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
10✔
3604
                int q;
28✔
3605

3606
                /* Returns number of symlinks that where supposed to be installed. */
3607
                q = install_context_apply(plus, lp,
56✔
3608
                                          file_flags | UNIT_FILE_IGNORE_AUXILIARY_FAILURE,
28✔
3609
                                          config_path,
3610
                                          SEARCH_LOAD, changes, n_changes);
3611
                if (r >= 0) {
28✔
3612
                        if (q < 0)
28✔
3613
                                r = q;
3614
                        else
3615
                                r += q;
28✔
3616
                }
3617
        }
3618

3619
        return r;
3620
}
3621

3622
static int preset_prepare_one(
10,205✔
3623
                RuntimeScope scope,
3624
                InstallContext *plus,
3625
                InstallContext *minus,
3626
                LookupPaths *lp,
3627
                const char *name,
3628
                const UnitFilePresets *presets,
3629
                InstallChange **changes,
3630
                size_t *n_changes) {
3631

3632
        _cleanup_(install_context_done) InstallContext tmp = { .scope = scope };
10,205✔
3633
        _cleanup_strv_free_ char **instance_name_list = NULL;
×
3634
        InstallInfo *info;
10,205✔
3635
        int r;
10,205✔
3636

3637
        if (install_info_find(plus, name) || install_info_find(minus, name))
10,205✔
3638
                return 0;
3639

3640
        r = install_info_discover(&tmp, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
10,115✔
3641
                                  &info, changes, n_changes);
3642
        if (r < 0)
10,115✔
3643
                return r;
3644

3645
        if (!streq(name, info->name)) {
10,114✔
3646
                log_debug("Skipping %s because it is an alias for %s.", name, info->name);
486✔
3647
                return 0;
3648
        }
3649

3650
        r = query_presets(name, presets, &instance_name_list);
9,628✔
3651
        if (r < 0)
9,628✔
3652
                return r;
3653

3654
        if (r == PRESET_ENABLE) {
9,628✔
3655
                if (instance_name_list)
437✔
3656
                        STRV_FOREACH(s, instance_name_list) {
4✔
3657
                                r = install_info_discover_and_check(plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3✔
3658
                                                                    &info, changes, n_changes);
3659
                                if (r < 0)
3✔
3660
                                        return r;
3661
                        }
3662
                else {
3663
                        r = install_info_discover_and_check(plus, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
10,205✔
3664
                                                            &info, changes, n_changes);
3665
                        if (r < 0)
3666
                                return r;
3667
                }
3668

3669
        } else if (r == PRESET_DISABLE)
9,191✔
3670
                r = install_info_discover(minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
9,189✔
3671
                                          &info, changes, n_changes);
3672

3673
        return r;
3674
}
3675

3676
int unit_file_preset(
9✔
3677
                RuntimeScope scope,
3678
                UnitFileFlags file_flags,
3679
                const char *root_dir,
3680
                char * const *names,
3681
                UnitFilePresetMode mode,
3682
                InstallChange **changes,
3683
                size_t *n_changes) {
3684

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

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

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

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

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

3707
        STRV_FOREACH(name, names) {
18✔
3708
                r = preset_prepare_one(scope, &plus, &minus, &lp, *name, &presets, changes, n_changes);
9✔
3709
                if (r < 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r))
9✔
3710
                        return r;
3711
        }
3712

3713
        return execute_preset(file_flags, &plus, &minus, &lp, config_path, names, mode, changes, n_changes);
9✔
3714
}
3715

3716
int unit_file_preset_all(
21✔
3717
                RuntimeScope scope,
3718
                UnitFileFlags file_flags,
3719
                const char *root_dir,
3720
                UnitFilePresetMode mode,
3721
                InstallChange **changes,
3722
                size_t *n_changes) {
3723

3724
        _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
21✔
3725
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
21✔
3726
        _cleanup_(unit_file_presets_done) UnitFilePresets presets = {};
×
3727
        const char *config_path = NULL;
21✔
3728
        int r;
21✔
3729

3730
        assert(scope >= 0);
21✔
3731
        assert(scope < _RUNTIME_SCOPE_MAX);
21✔
3732
        assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
21✔
3733

3734
        r = lookup_paths_init(&lp, scope, 0, root_dir);
21✔
3735
        if (r < 0)
21✔
3736
                return r;
3737

3738
        config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
21✔
3739
        if (!config_path)
21✔
3740
                return -ENXIO;
3741

3742
        r = read_presets(scope, root_dir, &presets);
21✔
3743
        if (r < 0)
21✔
3744
                return r;
3745

3746
        STRV_FOREACH(i, lp.search_path) {
297✔
3747
                _cleanup_closedir_ DIR *d = NULL;
297✔
3748

3749
                d = opendir(*i);
277✔
3750
                if (!d) {
277✔
3751
                        if (errno == ENOENT)
151✔
3752
                                continue;
151✔
3753

3754
                        return log_debug_errno(errno, "Failed to opendir %s: %m", *i);
×
3755
                }
3756

3757
                FOREACH_DIRENT(de, d,
16,642✔
3758
                               return log_debug_errno(errno, "Failed to read directory %s: %m", *i)) {
3759

3760
                        if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
16,266✔
3761
                                continue;
6,070✔
3762

3763
                        if (!IN_SET(de->d_type, DT_LNK, DT_REG))
10,196✔
3764
                                continue;
×
3765

3766
                        r = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes);
10,196✔
3767
                        if (r < 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r))
10,196✔
3768
                                return r;
3769
                }
3770
        }
3771

3772
        return execute_preset(file_flags, &plus, &minus, &lp, config_path, NULL, mode, changes, n_changes);
20✔
3773
}
3774

3775
static UnitFileList* unit_file_list_free(UnitFileList *f) {
964✔
3776
        if (!f)
964✔
3777
                return NULL;
3778

3779
        free(f->path);
964✔
3780
        return mfree(f);
964✔
3781
}
3782

3783
DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free);
964✔
3784

3785
DEFINE_PRIVATE_HASH_OPS_FULL(unit_file_list_hash_ops_free_free,
1,928✔
3786
                             char, string_hash_func, string_compare_func, free,
3787
                             UnitFileList, unit_file_list_free);
3788

3789
int unit_file_get_list(
4✔
3790
                RuntimeScope scope,
3791
                const char *root_dir,
3792
                char * const *states,
3793
                char * const *patterns,
3794
                Hashmap **ret) {
3795

3796
        _cleanup_(lookup_paths_done) LookupPaths lp = {};
4✔
3797
        _cleanup_hashmap_free_ Hashmap *h = NULL;
×
3798
        int r;
4✔
3799

3800
        assert(scope >= 0);
4✔
3801
        assert(scope < _RUNTIME_SCOPE_MAX);
4✔
3802
        assert(ret);
4✔
3803

3804
        r = lookup_paths_init(&lp, scope, 0, root_dir);
4✔
3805
        if (r < 0)
4✔
3806
                return r;
3807

3808
        STRV_FOREACH(dirname, lp.search_path) {
52✔
3809
                _cleanup_closedir_ DIR *d = NULL;
52✔
3810

3811
                d = opendir(*dirname);
48✔
3812
                if (!d) {
48✔
3813
                        if (errno == ENOENT)
27✔
3814
                                continue;
27✔
3815
                        if (IN_SET(errno, ENOTDIR, EACCES)) {
×
3816
                                log_debug_errno(errno, "Failed to open \"%s\": %m", *dirname);
×
3817
                                continue;
×
3818
                        }
3819

3820
                        return -errno;
×
3821
                }
3822

3823
                FOREACH_DIRENT(de, d, return -errno) {
1,634✔
3824
                        if (!IN_SET(de->d_type, DT_LNK, DT_REG))
1,571✔
3825
                                continue;
607✔
3826

3827
                        if (hashmap_contains(h, de->d_name))
1,421✔
3828
                                continue;
10✔
3829

3830
                        if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
1,411✔
3831
                                continue;
×
3832

3833
                        if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
1,411✔
3834
                                continue;
447✔
3835

3836
                        UnitFileState state;
964✔
3837

3838
                        r = unit_file_lookup_state(scope, &lp, de->d_name, &state);
964✔
3839
                        if (r < 0)
964✔
3840
                                state = UNIT_FILE_BAD;
×
3841

3842
                        if (!strv_isempty(states) &&
964✔
3843
                            !strv_contains(states, unit_file_state_to_string(state)))
×
3844
                                continue;
×
3845

3846
                        _cleanup_(unit_file_list_freep) UnitFileList *f = new(UnitFileList, 1);
964✔
3847
                        if (!f)
964✔
3848
                                return -ENOMEM;
3849

3850
                        *f = (UnitFileList) {
1,928✔
3851
                                .path = path_make_absolute(de->d_name, *dirname),
964✔
3852
                                .state = state,
3853
                        };
3854
                        if (!f->path)
964✔
3855
                                return -ENOMEM;
3856

3857
                        _cleanup_free_ char *unit_name = strdup(de->d_name);
964✔
3858
                        if (!unit_name)
964✔
3859
                                return -ENOMEM;
3860

3861
                        r = hashmap_ensure_put(&h, &unit_file_list_hash_ops_free_free, unit_name, f);
964✔
3862
                        if (r < 0)
964✔
3863
                                return r;
3864
                        assert(r > 0);
964✔
3865

3866
                        TAKE_PTR(unit_name);
964✔
3867
                        TAKE_PTR(f);
964✔
3868
                }
3869
        }
3870

3871
        *ret = TAKE_PTR(h);
4✔
3872
        return 0;
4✔
3873
}
3874

3875
static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
3876
        [UNIT_FILE_ENABLED]         = "enabled",
3877
        [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
3878
        [UNIT_FILE_LINKED]          = "linked",
3879
        [UNIT_FILE_LINKED_RUNTIME]  = "linked-runtime",
3880
        [UNIT_FILE_ALIAS]           = "alias",
3881
        [UNIT_FILE_MASKED]          = "masked",
3882
        [UNIT_FILE_MASKED_RUNTIME]  = "masked-runtime",
3883
        [UNIT_FILE_STATIC]          = "static",
3884
        [UNIT_FILE_DISABLED]        = "disabled",
3885
        [UNIT_FILE_INDIRECT]        = "indirect",
3886
        [UNIT_FILE_GENERATED]       = "generated",
3887
        [UNIT_FILE_TRANSIENT]       = "transient",
3888
        [UNIT_FILE_BAD]             = "bad",
3889
};
3890

3891
DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
6,720✔
3892

3893
static const char* const install_change_type_table[_INSTALL_CHANGE_TYPE_MAX] = {
3894
        [INSTALL_CHANGE_SYMLINK]                 = "symlink",
3895
        [INSTALL_CHANGE_UNLINK]                  = "unlink",
3896
        [INSTALL_CHANGE_IS_MASKED]               = "masked",
3897
        [INSTALL_CHANGE_IS_MASKED_GENERATOR]     = "masked by generator",
3898
        [INSTALL_CHANGE_IS_DANGLING]             = "dangling",
3899
        [INSTALL_CHANGE_DESTINATION_NOT_PRESENT] = "destination not present",
3900
        [INSTALL_CHANGE_AUXILIARY_FAILED]        = "auxiliary unit failed",
3901
};
3902

3903
DEFINE_STRING_TABLE_LOOKUP(install_change_type, InstallChangeType);
129✔
3904

3905
static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MODE_MAX] = {
3906
        [UNIT_FILE_PRESET_FULL]         = "full",
3907
        [UNIT_FILE_PRESET_ENABLE_ONLY]  = "enable-only",
3908
        [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
3909
};
3910

3911
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