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

systemd / systemd / 21121026098

18 Jan 2026 06:15PM UTC coverage: 72.736% (+0.2%) from 72.561%
21121026098

push

github

YHNdnzj
cryptenroll,cryptsetup,shutdown: only call mlockall if we have CAP_IPC_LOCK

Calling mlockall in an unprivileged process most notably had the effect
of making systemd-cryptenroll OOM while trying to open a normal-sized
argon2 keyslot due to it hitting RLIMIT_MEMLOCK.

9 of 14 new or added lines in 4 files covered. (64.29%)

1479 existing lines in 62 files now uncovered.

310610 of 427035 relevant lines covered (72.74%)

1127441.3 hits per line

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

66.5
/src/systemctl/systemctl-enable.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "sd-bus.h"
4

5
#include "alloc-util.h"
6
#include "bus-error.h"
7
#include "bus-locator.h"
8
#include "bus-unit-util.h"
9
#include "bus-util.h"
10
#include "glyph-util.h"
11
#include "install.h"
12
#include "log.h"
13
#include "path-lookup.h"
14
#include "path-util.h"
15
#include "string-util.h"
16
#include "strv-fundamental.h"
17
#include "strv.h"
18
#include "systemctl.h"
19
#include "systemctl-daemon-reload.h"
20
#include "systemctl-enable.h"
21
#include "systemctl-start-unit.h"
22
#include "systemctl-util.h"
23
#include "unit-name.h"
24
#include "verbs.h"
25

26
static int normalize_link_paths(char **paths) {
×
27
        int r;
×
28

29
        STRV_FOREACH(u, paths) {
×
30
                if (path_is_absolute(*u))
×
31
                        continue;
×
32

33
                if (!isempty(arg_root))
×
34
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
35
                                               "Non-absolute paths are not allowed when --root= is used: %s",
36
                                               *u);
37

38
                if (!is_path(*u))
×
39
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
40
                                               "Link argument must contain at least one directory separator.\n"
41
                                               "If you intended to link a file in the current directory, try './%s' instead.",
42
                                               *u);
43

44
                char *normalized_path;
×
45

46
                r = path_make_absolute_cwd(*u, &normalized_path);
×
47
                if (r < 0)
×
48
                        return log_error_errno(r, "Failed to normalize path '%s': %m", *u);
×
49

50
                path_simplify(normalized_path);
×
51

52
                free_and_replace(*u, normalized_path);
×
53
        }
54

55
        return 0;
56
}
57

58
static int normalize_names(char **names) {
15✔
59
        bool was_path = false;
15✔
60
        int r;
15✔
61

62
        STRV_FOREACH(u, names) {
30✔
63
                if (!is_path(*u))
15✔
64
                        continue;
15✔
65

66
                char *fn;
×
67

68
                r = path_extract_filename(*u, &fn);
×
69
                if (r < 0)
×
70
                        return log_error_errno(r, "Failed to extract file name from '%s': %m", *u);
×
71

72
                free_and_replace(*u, fn);
×
73

74
                was_path = true;
×
75
        }
76

77
        if (was_path)
15✔
78
                log_warning("Warning: Can't execute disable on the unit file path. Proceeding with the unit name.");
×
79

80
        return 0;
81
}
82

83
int verb_enable(int argc, char *argv[], void *userdata) {
71✔
84
        const char *verb = ASSERT_PTR(argv[0]);
71✔
85
        _cleanup_strv_free_ char **names = NULL;
71✔
86
        int carries_install_info = -1;
71✔
87
        bool ignore_carries_install_info = arg_quiet || arg_no_warn;
71✔
88
        sd_bus *bus = NULL;
71✔
89
        int r;
71✔
90

91
        if (streq(verb, "preset") && should_bypass("SYSTEMD_PRESET"))
71✔
92
                return 0;
93

94
        const char *operation = strjoina("to ", verb);
355✔
95
        r = mangle_names(operation, ASSERT_PTR(strv_skip(argv, 1)), &names);
71✔
96
        if (r < 0)
71✔
97
                return r;
98

99
        if (streq(verb, "disable"))
71✔
100
                r = normalize_names(names);
15✔
101
        else if (streq(verb, "link"))
56✔
102
                r = normalize_link_paths(names);
×
103
        else
104
                r = 0;
105
        if (r < 0)
15✔
106
                return r;
107

108
        if (install_client_side() == INSTALL_CLIENT_SIDE_NO) {
71✔
109
                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
106✔
110
                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
54✔
111
                bool expect_carries_install_info = false;
54✔
112
                bool send_runtime = true, send_force = true, send_preset_mode = false;
54✔
113
                const char *method, *warn_trigger_operation = NULL;
54✔
114
                bool warn_trigger_ignore_masked = true; /* suppress "used uninitialized" warning */
54✔
115

116
                if (STR_IN_SET(verb, "mask", "unmask")) {
54✔
117
                        _cleanup_(lookup_paths_done) LookupPaths lp = {};
23✔
118

119
                        r = lookup_paths_init_or_warn(&lp, arg_runtime_scope, 0, arg_root);
23✔
120
                        if (r < 0)
23✔
121
                                return r;
122

123
                        STRV_FOREACH(name, names) {
49✔
124
                                r = unit_exists(&lp, *name);
26✔
125
                                if (r < 0)
26✔
126
                                        return r;
127
                                if (r == 0)
26✔
UNCOV
128
                                        log_notice("Unit %s does not exist, proceeding anyway.", *name);
×
129
                        }
130
                }
131

132
                r = acquire_bus(BUS_MANAGER, &bus);
54✔
133
                if (r < 0)
54✔
134
                        return r;
135

136
                polkit_agent_open_maybe();
54✔
137

138
                if (streq(verb, "enable")) {
54✔
139
                        method = "EnableUnitFiles";
140
                        expect_carries_install_info = true;
141
                } else if (streq(verb, "disable")) {
42✔
142
                        method = "DisableUnitFilesWithFlagsAndInstallInfo";
143
                        expect_carries_install_info = true;
144
                        send_force = false;
145

146
                        warn_trigger_operation = "Disabling";
147
                        warn_trigger_ignore_masked = true;
148
                } else if (streq(verb, "reenable")) {
30✔
149
                        method = "ReenableUnitFiles";
150
                        expect_carries_install_info = true;
151
                } else if (streq(verb, "link"))
29✔
152
                        method = "LinkUnitFiles";
153
                else if (streq(verb, "preset")) {
29✔
154

155
                        if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
3✔
156
                                method = "PresetUnitFilesWithMode";
157
                                send_preset_mode = true;
158
                        } else
159
                                method = "PresetUnitFiles";
1✔
160

161
                        expect_carries_install_info = true;
162
                        ignore_carries_install_info = true;
163
                } else if (streq(verb, "mask")) {
26✔
164
                        method = "MaskUnitFiles";
165

166
                        warn_trigger_operation = "Masking";
167
                        warn_trigger_ignore_masked = false;
168
                } else if (streq(verb, "unmask")) {
15✔
169
                        method = "UnmaskUnitFiles";
170
                        send_force = false;
171
                } else if (streq(verb, "revert")) {
3✔
172
                        method = "RevertUnitFiles";
173
                        send_runtime = send_force = false;
174
                } else
UNCOV
175
                        assert_not_reached();
×
176

177
                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
54✔
178
                if (r < 0)
54✔
UNCOV
179
                        return bus_log_create_error(r);
×
180

181
                r = sd_bus_message_append_strv(m, names);
54✔
182
                if (r < 0)
54✔
UNCOV
183
                        return bus_log_create_error(r);
×
184

185
                if (send_preset_mode) {
54✔
186
                        r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
2✔
187
                        if (r < 0)
2✔
188
                                return bus_log_create_error(r);
×
189
                }
190

191
                if (send_runtime) {
54✔
192
                        if (streq(method, "DisableUnitFilesWithFlagsAndInstallInfo"))
51✔
193
                                r = sd_bus_message_append(m, "t", arg_runtime ? (uint64_t) UNIT_FILE_RUNTIME : UINT64_C(0));
23✔
194
                        else
195
                                r = sd_bus_message_append(m, "b", arg_runtime);
39✔
196
                        if (r < 0)
51✔
197
                                return bus_log_create_error(r);
×
198
                }
199

200
                if (send_force) {
54✔
201
                        r = sd_bus_message_append(m, "b", arg_force);
27✔
202
                        if (r < 0)
27✔
UNCOV
203
                                return bus_log_create_error(r);
×
204
                }
205

206
                r = sd_bus_call(bus, m, 0, &error, &reply);
54✔
207
                if (r < 0)
54✔
208
                        return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r));
2✔
209

210
                if (expect_carries_install_info) {
52✔
211
                        r = sd_bus_message_read(reply, "b", &carries_install_info);
26✔
212
                        if (r < 0)
26✔
UNCOV
213
                                return bus_log_parse_error(r);
×
214
                }
215

216
                r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet);
52✔
217
                if (r < 0)
52✔
218
                        return r;
219

220
                /* Try to reload if enabled */
221
                if (!arg_no_reload) {
52✔
222
                        r = daemon_reload(ACTION_RELOAD, /* graceful= */ false);
52✔
223
                        if (r < 0)
52✔
224
                                return r;
225
                }
226

227
                if (warn_trigger_operation && !arg_quiet && !arg_no_warn)
52✔
228
                        STRV_FOREACH(unit, names)
42✔
229
                                warn_triggering_units(bus, *unit, warn_trigger_operation, warn_trigger_ignore_masked);
21✔
230
        } else {
231
                UnitFileFlags flags;
17✔
232
                InstallChange *changes = NULL;
17✔
233
                size_t n_changes = 0;
17✔
234

235
                CLEANUP_ARRAY(changes, n_changes, install_changes_free);
17✔
236

237
                flags = unit_file_flags_from_args();
17✔
238

239
                if (streq(verb, "enable")) {
17✔
240
                        r = unit_file_enable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
241
                        carries_install_info = r;
3✔
242
                } else if (streq(verb, "disable")) {
14✔
243
                        r = unit_file_disable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
244
                        carries_install_info = r;
3✔
245
                } else if (streq(verb, "reenable")) {
11✔
246
                        r = unit_file_reenable(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
1✔
247
                        carries_install_info = r;
1✔
248
                } else if (streq(verb, "link"))
10✔
UNCOV
249
                        r = unit_file_link(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
×
250
                else if (streq(verb, "preset"))
10✔
251
                        r = unit_file_preset(arg_runtime_scope, flags, arg_root, names, arg_preset_mode, &changes, &n_changes);
3✔
252
                else if (streq(verb, "mask"))
7✔
253
                        r = unit_file_mask(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
254
                else if (streq(verb, "unmask"))
4✔
255
                        r = unit_file_unmask(arg_runtime_scope, flags, arg_root, names, &changes, &n_changes);
3✔
256
                else if (streq(verb, "revert"))
1✔
257
                        r = unit_file_revert(arg_runtime_scope, arg_root, names, &changes, &n_changes);
1✔
258
                else
UNCOV
259
                        assert_not_reached();
×
260

261
                install_changes_dump(r, verb, changes, n_changes, arg_quiet);
17✔
262
                if (r < 0)
17✔
UNCOV
263
                        return r;
×
264
        }
265

266
        if (carries_install_info == 0 && !ignore_carries_install_info)
69✔
UNCOV
267
                log_notice("The unit files have no installation config (WantedBy=, RequiredBy=, UpheldBy=,\n"
×
268
                           "Also=, or Alias= settings in the [Install] section, and DefaultInstance= for\n"
269
                           "template units). This means they are not meant to be enabled or disabled using systemctl.\n"
270
                           " \n" /* trick: the space is needed so that the line does not get stripped from output */
271
                           "Possible reasons for having these kinds of units are:\n"
272
                           "%1$s A unit may be statically enabled by being symlinked from another unit's\n"
273
                           "  .wants/, .requires/, or .upholds/ directory.\n"
274
                           "%1$s A unit's purpose may be to act as a helper for some other unit which has\n"
275
                           "  a requirement dependency on it.\n"
276
                           "%1$s A unit may be started when needed via activation (socket, path, timer,\n"
277
                           "  D-Bus, udev, scripted systemctl call, ...).\n"
278
                           "%1$s In case of template units, the unit is meant to be enabled with some\n"
279
                           "  instance name specified.",
280
                           glyph(GLYPH_BULLET));
281

282
        if (streq(verb, "disable") && arg_runtime_scope == RUNTIME_SCOPE_USER && !arg_quiet && !arg_no_warn) {
69✔
283
                /* If some of the units are disabled in user scope but still enabled in global scope,
284
                 * we emit a warning for that. */
285

286
                /* No strv_free here, strings are owned by 'names' */
UNCOV
287
                _cleanup_free_ char **enabled_in_global_scope = NULL;
×
288

UNCOV
289
                STRV_FOREACH(name, names) {
×
UNCOV
290
                        UnitFileState state;
×
291

UNCOV
292
                        r = unit_file_get_state(RUNTIME_SCOPE_GLOBAL, arg_root, *name, &state);
×
UNCOV
293
                        if (r == -ENOENT)
×
UNCOV
294
                                continue;
×
UNCOV
295
                        if (r < 0)
×
296
                                return log_error_errno(r, "Failed to get unit file state for %s: %m", *name);
×
297

298
                        if (IN_SET(state, UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME)) {
×
299
                                r = strv_push(&enabled_in_global_scope, *name);
×
UNCOV
300
                                if (r < 0)
×
301
                                        return log_oom();
×
302
                        }
303
                }
304

305
                if (!strv_isempty(enabled_in_global_scope)) {
×
UNCOV
306
                        _cleanup_free_ char *joined = NULL;
×
307

308
                        joined = strv_join(enabled_in_global_scope, ", ");
×
309
                        if (!joined)
×
310
                                return log_oom();
×
311

UNCOV
312
                        log_notice("The following unit files have been enabled in global scope. This means\n"
×
313
                                   "they will still be started automatically after a successful disablement\n"
314
                                   "in user scope:\n"
315
                                   "%s",
316
                                   joined);
317
                }
318
        }
319

320
        if (arg_now) {
69✔
321
                _cleanup_strv_free_ char **new_args = NULL;
12✔
322
                const char *start_verb;
12✔
323
                bool accept_path, prohibit_templates, dead_ok = false;
12✔
324

325
                if (streq(verb, "enable")) {
12✔
326
                        start_verb = "start";
327
                        accept_path = true;
328
                        prohibit_templates = true;
329
                } else if (STR_IN_SET(verb, "disable", "mask")) {
8✔
330
                        start_verb = "stop";
331
                        accept_path = false;
332
                        prohibit_templates = false;
333
                        dead_ok = true;  /* If the service is not running anyway, no need to stop it. */
UNCOV
334
                } else if (streq(verb, "reenable")) {
×
335
                        /* Note that we use try-restart here. This matches the semantics of reenable better,
336
                         * and allows us to glob template units. */
337
                        start_verb = "try-restart";
338
                        accept_path = true;
339
                        prohibit_templates = false;
340
                } else
UNCOV
341
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
342
                                               "--now can only be used with verb enable, disable, reenable, or mask.");
343

344
                switch (install_client_side()) {
12✔
345
                case INSTALL_CLIENT_SIDE_NO:
346
                        break;
UNCOV
347
                case INSTALL_CLIENT_SIDE_OVERRIDE:
×
348
                case INSTALL_CLIENT_SIDE_OFFLINE:
349
                case INSTALL_CLIENT_SIDE_NOT_BOOTED:
350
                        if (!dead_ok)
×
UNCOV
351
                                log_warning("Cannot %s unit with --now when systemd is not running, ignoring.", start_verb);
×
UNCOV
352
                        return 0;
×
353
                case INSTALL_CLIENT_SIDE_ARG_ROOT:
UNCOV
354
                        return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "--now cannot be used with --root=.");
×
355
                case INSTALL_CLIENT_SIDE_GLOBAL_SCOPE:
356
                        return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "--now cannot be used with --global.");
×
357
                }
358

359
                assert(bus);
12✔
360

361
                if (strv_extend(&new_args, start_verb) < 0)
12✔
UNCOV
362
                        return log_oom();
×
363

364
                STRV_FOREACH(name, names) {
28✔
365
                        _cleanup_free_ char *fn = NULL;
16✔
366
                        const char *unit_name;
16✔
367

368
                        if (accept_path) {
16✔
369
                                /* 'enable' and 'reenable' accept path to unit files, so extract it first. */
370

371
                                r = path_extract_filename(*name, &fn);
8✔
372
                                if (r < 0)
8✔
UNCOV
373
                                        return log_error_errno(r, "Failed to extract filename of '%s': %m", *name);
×
374

375
                                unit_name = fn;
8✔
376
                        } else
377
                                unit_name = *name;
8✔
378

379
                        if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
16✔
380
                                char *globbed;
1✔
381

382
                                if (prohibit_templates) {
1✔
383
                                        /* Skip template units when enabling. Globbing doesn't make sense
384
                                         * since the semantics would be altered (we're operating on
385
                                         * DefaultInstance= when enabling), and starting template unit
386
                                         * is not supported anyway. */
UNCOV
387
                                        log_warning("Template unit is not supported by %s --now, skipping: %s",
×
388
                                                    verb, unit_name);
UNCOV
389
                                        continue;
×
390
                                }
391

392
                                assert(!STR_IN_SET(start_verb, "start", "restart"));
1✔
393

394
                                r = unit_name_replace_instance_full(unit_name, "*", /* accept_glob= */ true, &globbed);
1✔
395
                                if (r < 0)
1✔
396
                                        return log_error_errno(r, "Failed to glob unit name '%s': %m", unit_name);
×
397

398
                                r = strv_consume(&new_args, globbed);
1✔
399
                        } else
400
                                r = strv_extend(&new_args, unit_name);
15✔
401
                        if (r < 0)
16✔
UNCOV
402
                                return log_oom();
×
403
                }
404

405
                return verb_start(strv_length(new_args), new_args, userdata);
12✔
406
        }
407

408
        return 0;
409
}
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