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

systemd / systemd / 14119061320

27 Mar 2025 08:38PM UTC coverage: 71.976% (+0.02%) from 71.954%
14119061320

push

github

web-flow
test: Make it possible to run the integration tests standalone (#36868)

Currently, to run the integration tests, it's still necessary to
install various other build tools besides meson: A compiler, gperf,
libcap, ... which we want to avoid in CI systems where we receive
prebuilt systemd packages and only want to test them. Examples are
Debian's autopkgtest CI and Fedora CI. Let's make it possible for
these systems to run the integration tests without having to install
any other build dependency besides meson by extracting the logic
required to run the integration tests with meson into a separate
subdirectory and adding a standalone top-level meson.build file which
can be used to configure a meson tree with as its only purpose running
the integration tests.

Practically, we do the following:
- all the integration test directories and integration-test-wrapper.py
  are moved from test/ to test/integration-tests/.
- All the installation logic is kept out of test/integration-tests/ or
  any of its subdirectories and moved into test/meson.build instead.
- We add test/integration-tests/standalone/meson.build to run the
  integration tests standalone. This meson file includes
  test/integration-tests via a cute symlink hack to trick meson into
  including a parent directory with subdir().
- Documentation is included on how to use the new standalone mode.

296746 of 412283 relevant lines covered (71.98%)

719287.73 hits per line

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

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

3
#include <unistd.h>
4

5
#include "sd-login.h"
6

7
#include "bus-error.h"
8
#include "bus-locator.h"
9
#include "login-util.h"
10
#include "mountpoint-util.h"
11
#include "process-util.h"
12
#include "systemctl-logind.h"
13
#include "systemctl-start-unit.h"
14
#include "systemctl-util.h"
15
#include "systemctl.h"
16
#include "terminal-util.h"
17
#include "user-util.h"
18

19
static int logind_set_wall_message(sd_bus *bus) {
38✔
20
#if ENABLE_LOGIND
21
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
22
        _cleanup_free_ char *m = NULL;
38✔
23
        int r;
38✔
24

25
        assert(bus);
38✔
26

27
        m = strv_join(arg_wall, " ");
38✔
28
        if (!m)
38✔
29
                return log_oom();
×
30

31
        log_debug("%s wall message \"%s\".", arg_dry_run ? "Would set" : "Setting", m);
43✔
32
        if (arg_dry_run)
38✔
33
                return 0;
34

35
        r = bus_call_method(bus, bus_login_mgr, "SetWallMessage", &error, NULL, "sb", m, !arg_no_wall);
25✔
36
        if (r < 0)
25✔
37
                return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r));
×
38
#endif
39
        return 0;
40
}
41

42
/* Ask systemd-logind, which might grant access to unprivileged users through polkit */
43
int logind_reboot(enum action a) {
32✔
44
#if ENABLE_LOGIND
45
        static const char* actions[_ACTION_MAX] = {
32✔
46
                [ACTION_POWEROFF]               = "PowerOff",
47
                [ACTION_REBOOT]                 = "Reboot",
48
                [ACTION_KEXEC]                  = "Reboot",
49
                [ACTION_SOFT_REBOOT]            = "Reboot",
50
                [ACTION_HALT]                   = "Halt",
51
                [ACTION_SUSPEND]                = "Suspend",
52
                [ACTION_HIBERNATE]              = "Hibernate",
53
                [ACTION_HYBRID_SLEEP]           = "HybridSleep",
54
                [ACTION_SUSPEND_THEN_HIBERNATE] = "SuspendThenHibernate",
55
                [ACTION_SLEEP]                  = "Sleep",
56
        };
57

58
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
32✔
59
        uint64_t flags = 0;
32✔
60
        sd_bus *bus;
32✔
61
        int r;
32✔
62

63
        assert(a >= 0);
32✔
64
        assert(a < _ACTION_MAX);
32✔
65

66
        if (!actions[a])
32✔
67
                return -EINVAL;
68

69
        r = acquire_bus_full(BUS_FULL, /* graceful = */ true, &bus);
32✔
70
        if (r < 0)
32✔
71
                return r;
72

73
        polkit_agent_open_maybe();
32✔
74
        (void) logind_set_wall_message(bus);
32✔
75

76
        const char *method_with_flags = a == ACTION_SLEEP ? actions[a] : strjoina(actions[a], "WithFlags");
160✔
77

78
        log_debug("%s org.freedesktop.login1.Manager %s dbus call.",
37✔
79
                  arg_dry_run ? "Would execute" : "Executing", method_with_flags);
80

81
        if (arg_dry_run)
32✔
82
                return 0;
83

84
        SET_FLAG(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS, arg_check_inhibitors > 0);
19✔
85
        SET_FLAG(flags, SD_LOGIND_SKIP_INHIBITORS, arg_check_inhibitors == 0);
19✔
86
        SET_FLAG(flags,
19✔
87
                 SD_LOGIND_REBOOT_VIA_KEXEC,
88
                 a == ACTION_KEXEC || (a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_KEXEC") <= 0));
89
        /* Try to soft-reboot if /run/nextroot/ is a valid OS tree, but only if it's also a mount point.
90
         * Otherwise, if people store new rootfs directly on /run/ tmpfs, 'systemctl reboot' would always
91
         * soft-reboot, as /run/nextroot/ can never go away. */
92
        SET_FLAG(flags,
24✔
93
                 SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP,
94
                 a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0 && path_is_mount_point("/run/nextroot") > 0);
95
        SET_FLAG(flags, SD_LOGIND_SOFT_REBOOT, a == ACTION_SOFT_REBOOT);
19✔
96

97
        r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags);
19✔
98
        if (r < 0 && FLAGS_SET(flags, SD_LOGIND_SKIP_INHIBITORS) &&
19✔
99
                        sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
×
100
                sd_bus_error_free(&error);
×
101
                flags &= ~SD_LOGIND_SKIP_INHIBITORS;
×
102
                r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags);
×
103
        }
104
        if (r < 0 && FLAGS_SET(flags, SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) &&
×
105
                        sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
×
106
                sd_bus_error_free(&error);
×
107
                flags &= ~SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP;
×
108
                r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags);
×
109
        }
110
        if (r >= 0)
×
111
                return 0;
19✔
112
        if (geteuid() == 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED))
×
113
                return log_error_errno(r,
×
114
                                       "The current polkit policy does not allow root to ignore inhibitors without authentication in order to %s.\n"
115
                                       "To allow this action, a new polkit rule is needed.\n"
116
                                       "See " POLKIT_RULES_DIR "/10-systemd-logind-root-ignore-inhibitors.rules.example.",
117
                                       action_table[a].verb);
118
        if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD) || a == ACTION_SLEEP)
×
119
                return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r));
×
120

121
        /* Fall back to original methods in case there is an older version of systemd-logind */
122
        log_debug("Method %s not available: %s. Falling back to %s", method_with_flags, bus_error_message(&error, r), actions[a]);
×
123
        sd_bus_error_free(&error);
×
124

125
        r = bus_call_method(bus, bus_login_mgr, actions[a], &error, NULL, "b", arg_ask_password);
×
126
        if (r < 0)
×
127
                return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r));
×
128

129
        return 0;
130
#else
131
        return -ENOSYS;
132
#endif
133
}
134

135
int logind_check_inhibitors(enum action a) {
43✔
136
#if ENABLE_LOGIND
137
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
43✔
138
        _cleanup_strv_free_ char **sessions = NULL;
×
139
        const char *what, *who, *why, *mode;
43✔
140
        uint32_t uid, pid;
43✔
141
        sd_bus *bus;
43✔
142
        unsigned c = 0;
43✔
143
        int r;
43✔
144

145
        assert(a >= 0);
43✔
146
        assert(a < _ACTION_MAX);
43✔
147

148
        if (arg_check_inhibitors == 0 || arg_force > 0)
43✔
149
                return 0;
150

151
        if (arg_when > 0)
37✔
152
                return 0;
153

154
        if (arg_check_inhibitors < 0 && !on_tty())
37✔
155
                return 0;
156

157
        if (arg_transport != BUS_TRANSPORT_LOCAL)
×
158
                return 0;
159

160
        r = acquire_bus_full(BUS_FULL, /* graceful = */ true, &bus);
×
161
        if ((ERRNO_IS_NEG_DISCONNECT(r) || r == -ENOENT) && geteuid() == 0)
×
162
                return 0; /* When D-Bus is not running (ECONNREFUSED) or D-Bus socket is not created (ENOENT),
163
                           * allow root to force a shutdown. E.g. when running at the emergency console. */
164
        if (r < 0)
×
165
                return r;
166

167
        r = bus_call_method(bus, bus_login_mgr, "ListInhibitors", NULL, &reply, NULL);
×
168
        if (r < 0)
×
169
                /* If logind is not around, then there are no inhibitors... */
170
                return 0;
171

172
        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
×
173
        if (r < 0)
×
174
                return bus_log_parse_error(r);
×
175

176
        while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
×
177
                _cleanup_free_ char *comm = NULL, *user = NULL;
×
178
                _cleanup_strv_free_ char **sv = NULL;
×
179

180
                if (!STR_IN_SET(mode, "block", "block-weak"))
×
181
                        continue;
×
182

183
                if (streq(mode, "block-weak") && (geteuid() == 0 || geteuid() == uid || !on_tty()))
×
184
                        continue;
×
185

186
                sv = strv_split(what, ":");
×
187
                if (!sv)
×
188
                        return log_oom();
×
189

190
                if (!pid_is_valid((pid_t) pid))
×
191
                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Invalid PID "PID_FMT".", (pid_t) pid);
×
192

193
                if (!strv_contains(sv,
×
194
                                   IN_SET(a,
195
                                          ACTION_HALT,
196
                                          ACTION_POWEROFF,
197
                                          ACTION_REBOOT,
198
                                          ACTION_KEXEC) ? "shutdown" : "sleep"))
199
                        continue;
×
200

201
                (void) pid_get_comm(pid, &comm);
×
202
                user = uid_to_name(uid);
×
203

204
                log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".",
×
205
                            who, (pid_t) pid, strna(comm), strna(user), why);
206

207
                c++;
×
208
        }
209
        if (r < 0)
×
210
                return bus_log_parse_error(r);
×
211

212
        r = sd_bus_message_exit_container(reply);
×
213
        if (r < 0)
×
214
                return bus_log_parse_error(r);
×
215

216
        /* root respects inhibitors since v257 but keeps ignoring sessions by default */
217
        if (arg_check_inhibitors < 0 && c == 0 && geteuid() == 0)
×
218
                return 0;
219

220
        /* Check for current sessions */
221
        sd_get_sessions(&sessions);
×
222
        STRV_FOREACH(s, sessions) {
×
223
                _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL;
×
224

225
                if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid())
×
226
                        continue;
×
227

228
                if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user"))
×
229
                        continue;
×
230

231
                if (sd_session_get_type(*s, &type) < 0 || !STR_IN_SET(type, "x11", "wayland", "tty", "mir"))
×
232
                        continue;
×
233

234
                sd_session_get_tty(*s, &tty);
×
235
                sd_session_get_seat(*s, &seat);
×
236
                sd_session_get_service(*s, &service);
×
237
                user = uid_to_name(uid);
×
238

239
                log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty);
×
240
                c++;
×
241
        }
242

243
        if (c <= 0)
×
244
                return 0;
245

246
        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
43✔
247
                               "Please retry operation after closing inhibitors and logging out other users.\n"
248
                               "'systemd-inhibit' can be used to list active inhibitors.\n"
249
                               "Alternatively, ignore inhibitors and users with 'systemctl %s -i'.",
250
                               action_table[a].verb);
251
#else
252
        return 0;
253
#endif
254
}
255

256
int prepare_firmware_setup(void) {
29✔
257

258
        if (!arg_firmware_setup)
29✔
259
                return 0;
29✔
260

261
#if ENABLE_LOGIND
262
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
263
        sd_bus *bus;
×
264
        int r;
×
265

266
        r = acquire_bus(BUS_FULL, &bus);
×
267
        if (r < 0)
×
268
                return r;
269

270
        r = bus_call_method(bus, bus_login_mgr, "SetRebootToFirmwareSetup", &error, NULL, "b", true);
×
271
        if (r < 0)
×
272
                return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r));
×
273

274
        return 0;
275
#else
276
        return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
277
                               "Booting into firmware setup not supported.");
278
#endif
279
}
280

281
int prepare_boot_loader_menu(void) {
29✔
282

283
        if (arg_boot_loader_menu == USEC_INFINITY)
29✔
284
                return 0;
29✔
285

286
#if ENABLE_LOGIND
287
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
288
        sd_bus *bus;
×
289
        int r;
×
290

291
        r = acquire_bus(BUS_FULL, &bus);
×
292
        if (r < 0)
×
293
                return r;
294

295
        r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderMenu", &error, NULL, "t", arg_boot_loader_menu);
×
296
        if (r < 0)
×
297
                return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r));
×
298

299
        return 0;
300
#else
301
        return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
302
                               "Booting into boot loader menu not supported.");
303
#endif
304
}
305

306
int prepare_boot_loader_entry(void) {
29✔
307

308
        if (!arg_boot_loader_entry)
29✔
309
                return 0;
29✔
310

311
#if ENABLE_LOGIND
312
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
313
        sd_bus *bus;
×
314
        int r;
×
315

316
        r = acquire_bus(BUS_FULL, &bus);
×
317
        if (r < 0)
×
318
                return r;
319

320
        r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderEntry", &error, NULL, "s", arg_boot_loader_entry);
×
321
        if (r < 0)
×
322
                return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r));
×
323

324
        return 0;
325
#else
326
        return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
327
                               "Booting into boot loader entry not supported.");
328
#endif
329
}
330

331
int logind_schedule_shutdown(enum action a) {
3✔
332
#if ENABLE_LOGIND
333
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
334
        const char *action;
3✔
335
        sd_bus *bus;
3✔
336
        int r;
3✔
337

338
        assert(a >= 0);
3✔
339
        assert(a < _ACTION_MAX);
3✔
340

341
        r = acquire_bus(BUS_FULL, &bus);
3✔
342
        if (r < 0)
3✔
343
                return r;
344

345
        action = action_table[a].verb;
3✔
346
        if (!action)
3✔
347
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Scheduling not supported for this action.");
×
348

349
        if (arg_dry_run)
3✔
350
                action = strjoina("dry-", action);
×
351

352
        (void) logind_set_wall_message(bus);
3✔
353

354
        r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
3✔
355
        if (r < 0)
3✔
356
                return log_warning_errno(r, "Failed to schedule shutdown: %s", bus_error_message(&error, r));
×
357

358
        if (!arg_quiet)
3✔
359
                logind_show_shutdown();
3✔
360

361
        return 0;
362
#else
363
        return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
364
                               "Cannot schedule shutdown without logind support, proceeding with immediate shutdown.");
365
#endif
366
}
367

368
int logind_cancel_shutdown(void) {
3✔
369
#if ENABLE_LOGIND
370
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
3✔
371
        sd_bus *bus;
3✔
372
        int r;
3✔
373

374
        r = acquire_bus(BUS_FULL, &bus);
3✔
375
        if (r < 0)
3✔
376
                return r;
377

378
        (void) logind_set_wall_message(bus);
3✔
379

380
        r = bus_call_method(bus, bus_login_mgr, "CancelScheduledShutdown", &error, NULL, NULL);
3✔
381
        if (r < 0)
3✔
382
                return log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r));
×
383

384
        return 0;
385
#else
386
        return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
387
                               "Not compiled with logind support, cannot cancel scheduled shutdowns.");
388
#endif
389
}
390

391
int logind_show_shutdown(void) {
4✔
392
#if ENABLE_LOGIND
393
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
394
        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
4✔
395
        sd_bus *bus;
4✔
396
        const char *action, *pretty_action;
4✔
397
        uint64_t elapse;
4✔
398
        int r;
4✔
399

400
        r = acquire_bus(BUS_FULL, &bus);
4✔
401
        if (r < 0)
4✔
402
                return r;
403

404
        r = bus_get_property(bus, bus_login_mgr, "ScheduledShutdown", &error, &reply, "(st)");
4✔
405
        if (r < 0)
4✔
406
                return log_error_errno(r, "Failed to query scheduled shutdown: %s", bus_error_message(&error, r));
×
407

408
        r = sd_bus_message_read(reply, "(st)", &action, &elapse);
4✔
409
        if (r < 0)
4✔
410
                return r;
411

412
        if (isempty(action))
4✔
413
                return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_ERR, SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown.");
×
414

415
        if (STR_IN_SET(action, "halt", "poweroff", "exit"))
4✔
416
                pretty_action = "Shutdown";
417
        else if (streq(action, "kexec"))
2✔
418
                pretty_action = "Reboot via kexec";
419
        else if (streq(action, "reboot"))
2✔
420
                pretty_action = "Reboot";
421
        else /* If we don't recognize the action string, we'll show it as-is */
422
                pretty_action = action;
×
423

424
        if (IN_SET(arg_action, ACTION_SYSTEMCTL, ACTION_SYSTEMCTL_SHOW_SHUTDOWN))
4✔
425
                log_info("%s scheduled for %s, use 'systemctl %s --when=cancel' to cancel.",
×
426
                         pretty_action,
427
                         FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style),
428
                         action);
429
        else
430
                log_info("%s scheduled for %s, use 'shutdown -c' to cancel.",
4✔
431
                         pretty_action,
432
                         FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style));
433

434
        return 0;
435
#else
436
        return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
437
                               "Not compiled with logind support, cannot show scheduled shutdowns.");
438
#endif
439
}
440

441
int help_boot_loader_entry(void) {
×
442
#if ENABLE_LOGIND
443
        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
×
444
        _cleanup_strv_free_ char **l = NULL;
×
445
        sd_bus *bus;
×
446
        int r;
×
447

448
        /* This is called without checking runtime scope and bus transport like we do in parse_argv().
449
         * Loading boot entries is only supported by system scope. Let's gracefully adjust them. */
450
        arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
×
451
        if (arg_transport == BUS_TRANSPORT_CAPSULE) {
×
452
                arg_host = NULL;
×
453
                arg_transport = BUS_TRANSPORT_LOCAL;
×
454
        }
455

456
        r = acquire_bus(BUS_FULL, &bus);
×
457
        if (r < 0)
×
458
                return r;
459

460
        r = bus_get_property_strv(bus, bus_login_mgr, "BootLoaderEntries", &error, &l);
×
461
        if (r < 0)
×
462
                return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r));
×
463

464
        if (strv_isempty(l))
×
465
                return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No boot loader entries discovered.");
×
466

467
        STRV_FOREACH(i, l)
×
468
                puts(*i);
×
469

470
        return 0;
471
#else
472
        return log_error_errno(SYNTHETIC_ERRNO(ENOSYS),
473
                               "Not compiled with logind support, cannot display boot loader entries.");
474
#endif
475
}
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