]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/loginctl.c
tree-wide: drop license boilerplate
[thirdparty/systemd.git] / src / login / loginctl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
abca4822
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
abca4822
LP
6***/
7
abca4822 8#include <errno.h>
abca4822 9#include <getopt.h>
a9cdc94f 10#include <locale.h>
3f6fd1ba
LP
11#include <string.h>
12#include <unistd.h>
abca4822 13
f8f14b36 14#include "sd-bus.h"
3f6fd1ba 15
b5efdb8a 16#include "alloc-util.h"
f8f14b36 17#include "bus-error.h"
a0e27019 18#include "bus-unit-util.h"
3f6fd1ba
LP
19#include "bus-util.h"
20#include "cgroup-show.h"
21#include "cgroup-util.h"
abca4822 22#include "log.h"
3f6fd1ba 23#include "logs-show.h"
abca4822
LP
24#include "macro.h"
25#include "pager.h"
6bedfcbb 26#include "parse-util.h"
3f6fd1ba 27#include "process-util.h"
9e29521e 28#include "sigbus.h"
3f6fd1ba
LP
29#include "signal-util.h"
30#include "spawn-polkit-agent.h"
a4c279f8 31#include "strv.h"
a4c279f8 32#include "sysfs-show.h"
288a74cc 33#include "terminal-util.h"
3f6fd1ba 34#include "unit-name.h"
b1d4f8e1 35#include "user-util.h"
3f6fd1ba
LP
36#include "util.h"
37#include "verbs.h"
abca4822 38
a4c279f8
LP
39static char **arg_property = NULL;
40static bool arg_all = false;
f4046fe0 41static bool arg_value = false;
9bdbc2e2 42static bool arg_full = false;
abca4822 43static bool arg_no_pager = false;
841aa8c0 44static bool arg_legend = true;
a4c279f8
LP
45static const char *arg_kill_who = NULL;
46static int arg_signal = SIGTERM;
f8f14b36 47static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 48static char *arg_host = NULL;
079dac08 49static bool arg_ask_password = true;
3c756001
LP
50static unsigned arg_lines = 10;
51static OutputMode arg_output = OUTPUT_SHORT;
abca4822 52
3c756001
LP
53static OutputFlags get_output_flags(void) {
54
55 return
56 arg_all * OUTPUT_SHOW_ALL |
459b9f9f 57 (arg_full || !on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
40c9fe4c 58 colors_enabled() * OUTPUT_COLOR;
3c756001
LP
59}
60
5611ddeb
ZJS
61static int get_session_path(sd_bus *bus, const char *session_id, sd_bus_error *error, char **path) {
62 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
63 int r;
64 char *ans;
65
66 r = sd_bus_call_method(
67 bus,
68 "org.freedesktop.login1",
69 "/org/freedesktop/login1",
70 "org.freedesktop.login1.Manager",
71 "GetSession",
72 error, &reply,
73 "s", session_id);
74 if (r < 0)
75 return r;
76
77 r = sd_bus_message_read(reply, "o", &ans);
78 if (r < 0)
79 return r;
80
81 ans = strdup(ans);
82 if (!ans)
83 return -ENOMEM;
84
85 *path = ans;
86 return 0;
87}
88
f7621db0 89static int list_sessions(int argc, char *argv[], void *userdata) {
4afd3348
LP
90 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
91 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
f8f14b36 92 const char *id, *user, *seat, *object;
f7621db0 93 sd_bus *bus = userdata;
abca4822 94 unsigned k = 0;
f8f14b36
SP
95 uint32_t uid;
96 int r;
abca4822 97
f7621db0
LP
98 assert(bus);
99 assert(argv);
100
ee5324aa 101 (void) pager_open(arg_no_pager, false);
abca4822 102
f8f14b36 103 r = sd_bus_call_method(
2a3613b1 104 bus,
abca4822
LP
105 "org.freedesktop.login1",
106 "/org/freedesktop/login1",
107 "org.freedesktop.login1.Manager",
2a3613b1 108 "ListSessions",
f8f14b36
SP
109 &error, &reply,
110 "");
111 if (r < 0) {
112 log_error("Failed to list sessions: %s", bus_error_message(&error, r));
4654e558 113 return r;
abca4822
LP
114 }
115
f8f14b36
SP
116 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
117 if (r < 0)
5b30bef8 118 return bus_log_parse_error(r);
abca4822 119
841aa8c0 120 if (arg_legend)
5611ddeb 121 printf("%10s %10s %-16s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT", "TTY");
abca4822 122
f8f14b36 123 while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) {
5611ddeb
ZJS
124 _cleanup_(sd_bus_error_free) sd_bus_error error2 = SD_BUS_ERROR_NULL;
125 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply2 = NULL;
126 _cleanup_free_ char *path = NULL;
127 const char *tty = NULL;
128
129 r = get_session_path(bus, id, &error2, &path);
130 if (r < 0)
131 log_warning("Failed to get session path: %s", bus_error_message(&error, r));
132 else {
133 r = sd_bus_get_property(
134 bus,
135 "org.freedesktop.login1",
136 path,
137 "org.freedesktop.login1.Session",
138 "TTY",
139 &error2,
140 &reply2,
141 "s");
142 if (r < 0)
143 log_warning("Failed to get TTY for session %s: %s",
144 id, bus_error_message(&error2, r));
145 else {
146 r = sd_bus_message_read(reply2, "s", &tty);
147 if (r < 0)
148 return bus_log_parse_error(r);
149 }
150 }
151
152 printf("%10s %10"PRIu32" %-16s %-16s %-16s\n", id, uid, user, seat, strna(tty));
abca4822 153 k++;
abca4822 154 }
f8f14b36 155 if (r < 0)
5b30bef8 156 return bus_log_parse_error(r);
abca4822 157
841aa8c0
ZJS
158 if (arg_legend)
159 printf("\n%u sessions listed.\n", k);
abca4822 160
4654e558 161 return 0;
abca4822
LP
162}
163
f7621db0 164static int list_users(int argc, char *argv[], void *userdata) {
4afd3348
LP
165 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
166 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
f8f14b36 167 const char *user, *object;
f7621db0 168 sd_bus *bus = userdata;
abca4822 169 unsigned k = 0;
f8f14b36
SP
170 uint32_t uid;
171 int r;
abca4822 172
f7621db0
LP
173 assert(bus);
174 assert(argv);
175
ee5324aa 176 (void) pager_open(arg_no_pager, false);
abca4822 177
f8f14b36 178 r = sd_bus_call_method(
2a3613b1 179 bus,
abca4822
LP
180 "org.freedesktop.login1",
181 "/org/freedesktop/login1",
182 "org.freedesktop.login1.Manager",
2a3613b1 183 "ListUsers",
f8f14b36
SP
184 &error, &reply,
185 "");
186 if (r < 0) {
187 log_error("Failed to list users: %s", bus_error_message(&error, r));
4654e558 188 return r;
abca4822
LP
189 }
190
f8f14b36
SP
191 r = sd_bus_message_enter_container(reply, 'a', "(uso)");
192 if (r < 0)
5b30bef8 193 return bus_log_parse_error(r);
abca4822 194
841aa8c0
ZJS
195 if (arg_legend)
196 printf("%10s %-16s\n", "UID", "USER");
abca4822 197
f8f14b36 198 while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) {
e8f215d3 199 printf("%10"PRIu32" %-16s\n", uid, user);
abca4822 200 k++;
abca4822 201 }
f8f14b36 202 if (r < 0)
5b30bef8 203 return bus_log_parse_error(r);
abca4822 204
841aa8c0
ZJS
205 if (arg_legend)
206 printf("\n%u users listed.\n", k);
abca4822 207
4654e558 208 return 0;
abca4822
LP
209}
210
f7621db0 211static int list_seats(int argc, char *argv[], void *userdata) {
4afd3348
LP
212 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
213 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
f8f14b36 214 const char *seat, *object;
f7621db0 215 sd_bus *bus = userdata;
abca4822 216 unsigned k = 0;
f8f14b36 217 int r;
f7621db0
LP
218 assert(bus);
219 assert(argv);
220
ee5324aa 221 (void) pager_open(arg_no_pager, false);
abca4822 222
f8f14b36 223 r = sd_bus_call_method(
2a3613b1 224 bus,
abca4822
LP
225 "org.freedesktop.login1",
226 "/org/freedesktop/login1",
227 "org.freedesktop.login1.Manager",
2a3613b1 228 "ListSeats",
f8f14b36
SP
229 &error, &reply,
230 "");
231 if (r < 0) {
232 log_error("Failed to list seats: %s", bus_error_message(&error, r));
4654e558 233 return r;
abca4822
LP
234 }
235
f8f14b36
SP
236 r = sd_bus_message_enter_container(reply, 'a', "(so)");
237 if (r < 0)
5b30bef8 238 return bus_log_parse_error(r);
abca4822 239
841aa8c0
ZJS
240 if (arg_legend)
241 printf("%-16s\n", "SEAT");
abca4822 242
f8f14b36 243 while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) {
abca4822 244 printf("%-16s\n", seat);
abca4822 245 k++;
abca4822 246 }
f8f14b36 247 if (r < 0)
5b30bef8 248 return bus_log_parse_error(r);
abca4822 249
841aa8c0
ZJS
250 if (arg_legend)
251 printf("\n%u seats listed.\n", k);
abca4822 252
4654e558 253 return 0;
abca4822
LP
254}
255
f8f14b36 256static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
bc06be75 257 _cleanup_free_ char *cgroup = NULL;
4afd3348 258 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
aa1936ea 259 unsigned c;
a0e27019 260 int r;
aa1936ea
LP
261
262 assert(bus);
263 assert(unit);
264
bc06be75 265 r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
f8f14b36 266 if (r < 0)
bc06be75 267 return r;
aa1936ea 268
9d127096
LP
269 if (isempty(cgroup))
270 return 0;
271
aa1936ea
LP
272 c = columns();
273 if (c > 18)
274 c -= 18;
275 else
276 c = 0;
277
a0e27019
LP
278 r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
279 if (r == -EBADR) {
280
281 if (arg_transport == BUS_TRANSPORT_REMOTE)
282 return 0;
283
284 /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
285
286 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
287 return 0;
288
0ff308c8 289 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
a0e27019
LP
290 } else if (r < 0)
291 return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
292
aa1936ea
LP
293 return 0;
294}
295
a4c279f8 296typedef struct SessionStatusInfo {
f37f8a61 297 const char *id;
a4c279f8 298 uid_t uid;
f37f8a61 299 const char *name;
3c756001 300 struct dual_timestamp timestamp;
92bd5ff3 301 unsigned int vtnr;
f37f8a61
YW
302 const char *seat;
303 const char *tty;
304 const char *display;
305 bool remote;
306 const char *remote_host;
307 const char *remote_user;
308 const char *service;
a4c279f8 309 pid_t leader;
f37f8a61
YW
310 const char *type;
311 const char *class;
312 const char *state;
313 const char *scope;
314 const char *desktop;
a4c279f8
LP
315} SessionStatusInfo;
316
317typedef struct UserStatusInfo {
318 uid_t uid;
f37f8a61
YW
319 bool linger;
320 const char *name;
3c756001 321 struct dual_timestamp timestamp;
f37f8a61 322 const char *state;
a4c279f8 323 char **sessions;
f37f8a61
YW
324 const char *display;
325 const char *slice;
a4c279f8
LP
326} UserStatusInfo;
327
328typedef struct SeatStatusInfo {
f37f8a61
YW
329 const char *id;
330 const char *active_session;
a4c279f8
LP
331 char **sessions;
332} SeatStatusInfo;
333
e7e55dbd
DH
334static void user_status_info_clear(UserStatusInfo *info) {
335 if (info) {
e7e55dbd 336 strv_free(info->sessions);
e7e55dbd
DH
337 zero(*info);
338 }
339}
340
341static void seat_status_info_clear(SeatStatusInfo *info) {
342 if (info) {
e7e55dbd
DH
343 strv_free(info->sessions);
344 zero(*info);
345 }
346}
347
f8f14b36
SP
348static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
349 const char *contents;
350 int r;
351
352 r = sd_bus_message_peek_type(m, NULL, &contents);
353 if (r < 0)
354 return r;
355
356 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
357 if (r < 0)
358 return r;
359
f37f8a61
YW
360 r = sd_bus_message_read_basic(m, contents[0], userdata);
361 if (r < 0)
362 return r;
f8f14b36
SP
363
364 r = sd_bus_message_skip(m, contents+1);
365 if (r < 0)
366 return r;
367
368 r = sd_bus_message_exit_container(m);
369 if (r < 0)
370 return r;
371
372 return 0;
373}
374
375static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
376 const char *name;
377 int r;
378
379 assert(bus);
380 assert(m);
381
382 r = sd_bus_message_enter_container(m, 'a', "(so)");
383 if (r < 0)
384 return r;
385
386 while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
387 r = strv_extend(userdata, name);
388 if (r < 0)
389 return r;
390 }
391 if (r < 0)
392 return r;
393
394 return sd_bus_message_exit_container(m);
395}
396
495cb9bb 397static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
f8f14b36
SP
398
399 static const struct bus_properties_map map[] = {
3c756001
LP
400 { "Id", "s", NULL, offsetof(SessionStatusInfo, id) },
401 { "Name", "s", NULL, offsetof(SessionStatusInfo, name) },
402 { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) },
403 { "Display", "s", NULL, offsetof(SessionStatusInfo, display) },
404 { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
405 { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
406 { "Service", "s", NULL, offsetof(SessionStatusInfo, service) },
407 { "Desktop", "s", NULL, offsetof(SessionStatusInfo, desktop) },
408 { "Type", "s", NULL, offsetof(SessionStatusInfo, type) },
409 { "Class", "s", NULL, offsetof(SessionStatusInfo, class) },
410 { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) },
411 { "State", "s", NULL, offsetof(SessionStatusInfo, state) },
412 { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) },
413 { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) },
414 { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) },
415 { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp.realtime) },
416 { "TimestampMonotonic", "t", NULL, offsetof(SessionStatusInfo, timestamp.monotonic) },
417 { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
418 { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) },
f8f14b36
SP
419 {}
420 };
421
f9e0eefc 422 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 423 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
9185c8e6 424 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
a4c279f8 425 char since2[FORMAT_TIMESTAMP_MAX], *s2;
f37f8a61 426 SessionStatusInfo i = {};
f8f14b36
SP
427 int r;
428
a7e4861c 429 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
f647962d 430 if (r < 0)
f9e0eefc 431 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
495cb9bb
DH
432
433 if (*new_line)
434 printf("\n");
435
436 *new_line = true;
a4c279f8 437
f8f14b36 438 printf("%s - ", strna(i.id));
a4c279f8 439
f8f14b36 440 if (i.name)
e8f215d3 441 printf("%s (%"PRIu32")\n", i.name, i.uid);
a4c279f8 442 else
e8f215d3 443 printf("%"PRIu32"\n", i.uid);
a4c279f8 444
3c756001
LP
445 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
446 s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
a4c279f8
LP
447
448 if (s1)
449 printf("\t Since: %s; %s\n", s2, s1);
450 else if (s2)
451 printf("\t Since: %s\n", s2);
452
f8f14b36 453 if (i.leader > 0) {
9444b1f2 454 _cleanup_free_ char *t = NULL;
a4c279f8 455
e8f215d3 456 printf("\t Leader: %"PRIu32, i.leader);
a4c279f8 457
f8f14b36 458 get_process_comm(i.leader, &t);
9444b1f2 459 if (t)
a4c279f8 460 printf(" (%s)", t);
a4c279f8
LP
461
462 printf("\n");
463 }
464
016284c3 465 if (!isempty(i.seat)) {
f8f14b36 466 printf("\t Seat: %s", i.seat);
a4c279f8 467
f8f14b36 468 if (i.vtnr > 0)
c4ef0548 469 printf("; vc%u", i.vtnr);
a4c279f8
LP
470
471 printf("\n");
472 }
473
f8f14b36
SP
474 if (i.tty)
475 printf("\t TTY: %s\n", i.tty);
476 else if (i.display)
477 printf("\t Display: %s\n", i.display);
478
479 if (i.remote_host && i.remote_user)
480 printf("\t Remote: %s@%s\n", i.remote_user, i.remote_host);
481 else if (i.remote_host)
482 printf("\t Remote: %s\n", i.remote_host);
483 else if (i.remote_user)
484 printf("\t Remote: user %s\n", i.remote_user);
485 else if (i.remote)
a4c279f8
LP
486 printf("\t Remote: Yes\n");
487
f8f14b36
SP
488 if (i.service) {
489 printf("\t Service: %s", i.service);
a4c279f8 490
f8f14b36
SP
491 if (i.type)
492 printf("; type %s", i.type);
a4c279f8 493
f8f14b36
SP
494 if (i.class)
495 printf("; class %s", i.class);
55efac6c 496
a4c279f8 497 printf("\n");
f8f14b36 498 } else if (i.type) {
91d53e2b 499 printf("\t Type: %s", i.type);
a4c279f8 500
f8f14b36
SP
501 if (i.class)
502 printf("; class %s", i.class);
91d53e2b
MM
503
504 printf("\n");
f8f14b36
SP
505 } else if (i.class)
506 printf("\t Class: %s\n", i.class);
55efac6c 507
a4cd87e9
LP
508 if (!isempty(i.desktop))
509 printf("\t Desktop: %s\n", i.desktop);
510
f8f14b36
SP
511 if (i.state)
512 printf("\t State: %s\n", i.state);
a4c279f8 513
f8f14b36
SP
514 if (i.scope) {
515 printf("\t Unit: %s\n", i.scope);
516 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
3c756001
LP
517
518 if (arg_transport == BUS_TRANSPORT_LOCAL) {
519
520 show_journal_by_unit(
521 stdout,
522 i.scope,
523 arg_output,
524 0,
525 i.timestamp.monotonic,
526 arg_lines,
527 0,
528 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
529 SD_JOURNAL_LOCAL_ONLY,
530 true,
531 NULL);
532 }
a4c279f8 533 }
f8f14b36
SP
534
535 return 0;
a4c279f8
LP
536}
537
495cb9bb 538static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
f8f14b36
SP
539
540 static const struct bus_properties_map map[] = {
3c756001 541 { "Name", "s", NULL, offsetof(UserStatusInfo, name) },
26e00f0e 542 { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) },
3c756001
LP
543 { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
544 { "State", "s", NULL, offsetof(UserStatusInfo, state) },
545 { "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
546 { "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp.realtime) },
547 { "TimestampMonotonic", "t", NULL, offsetof(UserStatusInfo, timestamp.monotonic) },
548 { "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
549 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) },
f8f14b36
SP
550 {}
551 };
552
f9e0eefc 553 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 554 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
9185c8e6 555 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
a4c279f8 556 char since2[FORMAT_TIMESTAMP_MAX], *s2;
e7e55dbd 557 _cleanup_(user_status_info_clear) UserStatusInfo i = {};
f8f14b36 558 int r;
a4c279f8 559
a7e4861c 560 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
e7e55dbd 561 if (r < 0)
f9e0eefc 562 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
495cb9bb
DH
563
564 if (*new_line)
565 printf("\n");
566
567 *new_line = true;
f8f14b36
SP
568
569 if (i.name)
e8f215d3 570 printf("%s (%"PRIu32")\n", i.name, i.uid);
a4c279f8 571 else
e8f215d3 572 printf("%"PRIu32"\n", i.uid);
a4c279f8 573
3c756001
LP
574 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
575 s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
a4c279f8
LP
576
577 if (s1)
578 printf("\t Since: %s; %s\n", s2, s1);
579 else if (s2)
580 printf("\t Since: %s\n", s2);
581
f8f14b36
SP
582 if (!isempty(i.state))
583 printf("\t State: %s\n", i.state);
a4c279f8 584
f8f14b36 585 if (!strv_isempty(i.sessions)) {
a4c279f8
LP
586 char **l;
587 printf("\tSessions:");
588
26e00f0e
ZJS
589 STRV_FOREACH(l, i.sessions)
590 printf(" %s%s",
591 streq_ptr(*l, i.display) ? "*" : "",
592 *l);
a4c279f8
LP
593
594 printf("\n");
595 }
596
26e00f0e
ZJS
597 printf("\t Linger: %s\n", yes_no(i.linger));
598
f8f14b36
SP
599 if (i.slice) {
600 printf("\t Unit: %s\n", i.slice);
601 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
3c756001
LP
602
603 show_journal_by_unit(
604 stdout,
605 i.slice,
606 arg_output,
607 0,
608 i.timestamp.monotonic,
609 arg_lines,
610 0,
611 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
612 SD_JOURNAL_LOCAL_ONLY,
613 true,
614 NULL);
a4c279f8 615 }
f8f14b36 616
e7e55dbd 617 return 0;
a4c279f8
LP
618}
619
495cb9bb 620static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
a4c279f8 621
f8f14b36
SP
622 static const struct bus_properties_map map[] = {
623 { "Id", "s", NULL, offsetof(SeatStatusInfo, id) },
46e65dcc
LP
624 { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
625 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
f8f14b36
SP
626 {}
627 };
a4c279f8 628
f9e0eefc 629 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f37f8a61 630 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
e7e55dbd 631 _cleanup_(seat_status_info_clear) SeatStatusInfo i = {};
f8f14b36
SP
632 int r;
633
a7e4861c 634 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, 0, &error, &m, &i);
e7e55dbd 635 if (r < 0)
f9e0eefc 636 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
495cb9bb
DH
637
638 if (*new_line)
639 printf("\n");
640
641 *new_line = true;
f8f14b36
SP
642
643 printf("%s\n", strna(i.id));
644
645 if (!strv_isempty(i.sessions)) {
a4c279f8
LP
646 char **l;
647 printf("\tSessions:");
648
f8f14b36
SP
649 STRV_FOREACH(l, i.sessions) {
650 if (streq_ptr(*l, i.active_session))
a4c279f8
LP
651 printf(" *%s", *l);
652 else
653 printf(" %s", *l);
654 }
655
656 printf("\n");
657 }
658
f8f14b36 659 if (arg_transport == BUS_TRANSPORT_LOCAL) {
a4c279f8
LP
660 unsigned c;
661
662 c = columns();
88e3dc90
LP
663 if (c > 21)
664 c -= 21;
a4c279f8
LP
665 else
666 c = 0;
667
668 printf("\t Devices:\n");
669
3850319b 670 show_sysfs(i.id, "\t\t ", c, get_output_flags());
a4c279f8 671 }
a4c279f8 672
e7e55dbd 673 return 0;
a4c279f8
LP
674}
675
f4046fe0
ZJS
676#define property(name, fmt, ...) \
677 do { \
07636114 678 if (value) \
f4046fe0
ZJS
679 printf(fmt "\n", __VA_ARGS__); \
680 else \
681 printf("%s=" fmt "\n", name, __VA_ARGS__); \
508f63b4 682 } while (0)
f4046fe0 683
07636114
YW
684static int print_property(const char *name, sd_bus_message *m, bool value, bool all) {
685 char type;
686 const char *contents;
2a998c74
LN
687 int r;
688
689 assert(name);
690 assert(m);
2a998c74 691
07636114
YW
692 r = sd_bus_message_peek_type(m, &type, &contents);
693 if (r < 0)
694 return r;
2a998c74 695
07636114 696 switch (type) {
2a998c74 697
07636114 698 case SD_BUS_TYPE_STRUCT:
2a998c74 699
07636114 700 if (contents[0] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) {
2a998c74
LN
701 const char *s;
702
703 r = sd_bus_message_read(m, "(so)", &s, NULL);
704 if (r < 0)
705 return bus_log_parse_error(r);
706
07636114 707 if (all || !isempty(s))
f4046fe0 708 property(name, "%s", s);
2a998c74 709
07636114 710 return 1;
2a998c74 711
07636114 712 } else if (contents[0] == SD_BUS_TYPE_UINT32 && streq(name, "User")) {
2a998c74
LN
713 uint32_t uid;
714
715 r = sd_bus_message_read(m, "(uo)", &uid, NULL);
716 if (r < 0)
717 return bus_log_parse_error(r);
718
c077529b 719 if (!uid_is_valid(uid)) {
2a998c74
LN
720 log_error("Invalid user ID: " UID_FMT, uid);
721 return -EINVAL;
722 }
723
f4046fe0 724 property(name, UID_FMT, uid);
07636114 725 return 1;
2a998c74 726 }
2a998c74
LN
727 break;
728
729 case SD_BUS_TYPE_ARRAY:
730
07636114 731 if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) {
2a998c74
LN
732 const char *s;
733 bool space = false;
734
735 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)");
736 if (r < 0)
737 return bus_log_parse_error(r);
738
07636114 739 if (!value)
f4046fe0 740 printf("%s=", name);
2a998c74
LN
741
742 while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
743 printf("%s%s", space ? " " : "", s);
744 space = true;
745 }
746
07636114 747 if (space || !value)
f4046fe0 748 printf("\n");
2a998c74
LN
749
750 if (r < 0)
751 return bus_log_parse_error(r);
752
753 r = sd_bus_message_exit_container(m);
754 if (r < 0)
755 return bus_log_parse_error(r);
756
07636114 757 return 1;
2a998c74 758 }
2a998c74
LN
759 break;
760 }
761
2a998c74
LN
762 return 0;
763}
764
97aa7b47
DH
765static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
766 int r;
767
2a998c74
LN
768 assert(bus);
769 assert(path);
770 assert(new_line);
771
97aa7b47
DH
772 if (*new_line)
773 printf("\n");
774
775 *new_line = true;
776
07636114 777 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, print_property, arg_property, arg_value, arg_all, NULL);
2a998c74
LN
778 if (r < 0)
779 return bus_log_parse_error(r);
780
781 return 0;
97aa7b47
DH
782}
783
f7621db0 784static int show_session(int argc, char *argv[], void *userdata) {
97aa7b47 785 bool properties, new_line = false;
f7621db0
LP
786 sd_bus *bus = userdata;
787 int r, i;
9cf8e208
ZJS
788 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
789 _cleanup_free_ char *path = NULL;
a4c279f8 790
f8f14b36 791 assert(bus);
f7621db0 792 assert(argv);
a4c279f8 793
f7621db0 794 properties = !strstr(argv[0], "status");
a4c279f8 795
ee5324aa 796 (void) pager_open(arg_no_pager, false);
a4c279f8 797
86e1f46f 798 if (argc <= 1) {
9cf8e208
ZJS
799 const char *session, *p = "/org/freedesktop/login1/session/self";
800
86e1f46f 801 if (properties)
9cf8e208 802 /* If no argument is specified inspect the manager itself */
86e1f46f
LP
803 return show_properties(bus, "/org/freedesktop/login1", &new_line);
804
805 /* And in the pretty case, show data of the calling session */
9cf8e208
ZJS
806 session = getenv("XDG_SESSION_ID");
807 if (session) {
808 r = get_session_path(bus, session, &error, &path);
809 if (r < 0) {
810 log_error("Failed to get session path: %s", bus_error_message(&error, r));
811 return r;
812 }
813 p = path;
814 }
815
816 return print_session_status_info(bus, p, &new_line);
a4c279f8
LP
817 }
818
f7621db0 819 for (i = 1; i < argc; i++) {
b0d08b05 820 r = get_session_path(bus, argv[i], &error, &path);
f8f14b36 821 if (r < 0) {
5611ddeb 822 log_error("Failed to get session path: %s", bus_error_message(&error, r));
f8f14b36 823 return r;
a4c279f8 824 }
a4c279f8 825
97aa7b47
DH
826 if (properties)
827 r = show_properties(bus, path, &new_line);
f8f14b36 828 else
495cb9bb
DH
829 r = print_session_status_info(bus, path, &new_line);
830
831 if (r < 0)
f8f14b36 832 return r;
a4c279f8
LP
833 }
834
a4c279f8
LP
835 return 0;
836}
837
f7621db0 838static int show_user(int argc, char *argv[], void *userdata) {
97aa7b47 839 bool properties, new_line = false;
f7621db0
LP
840 sd_bus *bus = userdata;
841 int r, i;
a4c279f8 842
f8f14b36 843 assert(bus);
f7621db0 844 assert(argv);
a4c279f8 845
f7621db0 846 properties = !strstr(argv[0], "status");
a4c279f8 847
ee5324aa 848 (void) pager_open(arg_no_pager, false);
a4c279f8 849
86e1f46f 850 if (argc <= 1) {
f8f14b36
SP
851 /* If not argument is specified inspect the manager
852 * itself */
86e1f46f
LP
853 if (properties)
854 return show_properties(bus, "/org/freedesktop/login1", &new_line);
855
856 return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line);
f8f14b36 857 }
a4c279f8 858
f7621db0 859 for (i = 1; i < argc; i++) {
4afd3348
LP
860 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
861 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
f8f14b36
SP
862 const char *path = NULL;
863 uid_t uid;
a4c279f8 864
f7621db0 865 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
f647962d 866 if (r < 0)
f7621db0 867 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
a4c279f8 868
f8f14b36
SP
869 r = sd_bus_call_method(
870 bus,
871 "org.freedesktop.login1",
872 "/org/freedesktop/login1",
873 "org.freedesktop.login1.Manager",
874 "GetUser",
875 &error, &reply,
876 "u", (uint32_t) uid);
877 if (r < 0) {
878 log_error("Failed to get user: %s", bus_error_message(&error, r));
879 return r;
a4c279f8
LP
880 }
881
f8f14b36
SP
882 r = sd_bus_message_read(reply, "o", &path);
883 if (r < 0)
5b30bef8 884 return bus_log_parse_error(r);
a4c279f8 885
97aa7b47
DH
886 if (properties)
887 r = show_properties(bus, path, &new_line);
9444b1f2 888 else
495cb9bb
DH
889 r = print_user_status_info(bus, path, &new_line);
890
891 if (r < 0)
f8f14b36 892 return r;
a4c279f8
LP
893 }
894
f8f14b36 895 return 0;
a4c279f8
LP
896}
897
f7621db0 898static int show_seat(int argc, char *argv[], void *userdata) {
97aa7b47 899 bool properties, new_line = false;
f7621db0
LP
900 sd_bus *bus = userdata;
901 int r, i;
a4c279f8
LP
902
903 assert(bus);
f7621db0 904 assert(argv);
a4c279f8 905
f7621db0 906 properties = !strstr(argv[0], "status");
a4c279f8 907
ee5324aa 908 (void) pager_open(arg_no_pager, false);
a4c279f8 909
86e1f46f 910 if (argc <= 1) {
a4c279f8
LP
911 /* If not argument is specified inspect the manager
912 * itself */
86e1f46f
LP
913 if (properties)
914 return show_properties(bus, "/org/freedesktop/login1", &new_line);
915
916 return print_seat_status_info(bus, "/org/freedesktop/login1/seat/self", &new_line);
a4c279f8
LP
917 }
918
f7621db0 919 for (i = 1; i < argc; i++) {
4afd3348
LP
920 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
921 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
a4c279f8
LP
922 const char *path = NULL;
923
f8f14b36
SP
924 r = sd_bus_call_method(
925 bus,
926 "org.freedesktop.login1",
927 "/org/freedesktop/login1",
928 "org.freedesktop.login1.Manager",
929 "GetSeat",
930 &error, &reply,
f7621db0 931 "s", argv[i]);
f8f14b36
SP
932 if (r < 0) {
933 log_error("Failed to get seat: %s", bus_error_message(&error, r));
934 return r;
a4c279f8 935 }
9444b1f2 936
f8f14b36
SP
937 r = sd_bus_message_read(reply, "o", &path);
938 if (r < 0)
5b30bef8 939 return bus_log_parse_error(r);
a4c279f8 940
97aa7b47
DH
941 if (properties)
942 r = show_properties(bus, path, &new_line);
f8f14b36 943 else
495cb9bb
DH
944 r = print_seat_status_info(bus, path, &new_line);
945
946 if (r < 0)
f8f14b36 947 return r;
a4c279f8
LP
948 }
949
f8f14b36 950 return 0;
a4c279f8
LP
951}
952
f7621db0 953static int activate(int argc, char *argv[], void *userdata) {
4afd3348 954 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0 955 sd_bus *bus = userdata;
2fbcde74 956 char *short_argv[3];
f7621db0 957 int r, i;
24310c11 958
f7621db0
LP
959 assert(bus);
960 assert(argv);
24310c11 961
8a4b13c5 962 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 963
906b76b2 964 if (argc < 2) {
bdb07fa5
ZJS
965 /* No argument? Let's either use $XDG_SESSION_ID (if specified), or an empty
966 * session name, in which case logind will try to guess our session. */
906b76b2 967
2fbcde74 968 short_argv[0] = argv[0];
bdb07fa5 969 short_argv[1] = getenv("XDG_SESSION_ID") ?: (char*) "";
2fbcde74
LP
970 short_argv[2] = NULL;
971
972 argv = short_argv;
906b76b2
LP
973 argc = 2;
974 }
975
f7621db0 976 for (i = 1; i < argc; i++) {
f8440af5 977
c529695e 978 r = sd_bus_call_method(
2a3613b1 979 bus,
24310c11
LP
980 "org.freedesktop.login1",
981 "/org/freedesktop/login1",
982 "org.freedesktop.login1.Manager",
f7621db0
LP
983 streq(argv[0], "lock-session") ? "LockSession" :
984 streq(argv[0], "unlock-session") ? "UnlockSession" :
985 streq(argv[0], "terminate-session") ? "TerminateSession" :
2a3613b1 986 "ActivateSession",
f8f14b36 987 &error, NULL,
f7621db0 988 "s", argv[i]);
f8f14b36
SP
989 if (r < 0) {
990 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
991 return r;
992 }
24310c11
LP
993 }
994
f8f14b36 995 return 0;
a4c279f8
LP
996}
997
f7621db0 998static int kill_session(int argc, char *argv[], void *userdata) {
4afd3348 999 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0
LP
1000 sd_bus *bus = userdata;
1001 int r, i;
de07ab16 1002
f7621db0
LP
1003 assert(bus);
1004 assert(argv);
de07ab16 1005
8a4b13c5 1006 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1007
de07ab16
LP
1008 if (!arg_kill_who)
1009 arg_kill_who = "all";
1010
f7621db0 1011 for (i = 1; i < argc; i++) {
4654e558 1012
c529695e 1013 r = sd_bus_call_method(
4654e558
ZJS
1014 bus,
1015 "org.freedesktop.login1",
1016 "/org/freedesktop/login1",
1017 "org.freedesktop.login1.Manager",
1018 "KillSession",
f8f14b36 1019 &error, NULL,
f7621db0 1020 "ssi", argv[i], arg_kill_who, arg_signal);
f8f14b36
SP
1021 if (r < 0) {
1022 log_error("Could not kill session: %s", bus_error_message(&error, -r));
4654e558 1023 return r;
f8f14b36 1024 }
de07ab16
LP
1025 }
1026
4654e558 1027 return 0;
a4c279f8
LP
1028}
1029
f7621db0 1030static int enable_linger(int argc, char *argv[], void *userdata) {
4afd3348 1031 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0 1032 sd_bus *bus = userdata;
2fbcde74 1033 char* short_argv[3];
f8f14b36 1034 bool b;
f7621db0 1035 int r, i;
88e3dc90 1036
f7621db0
LP
1037 assert(bus);
1038 assert(argv);
88e3dc90 1039
8a4b13c5 1040 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6bb92a16 1041
f7621db0 1042 b = streq(argv[0], "enable-linger");
88e3dc90 1043
906b76b2 1044 if (argc < 2) {
545f779f
AJ
1045 /* No argument? Let's use an empty user name,
1046 * then logind will use our user. */
bdb07fa5 1047
2fbcde74 1048 short_argv[0] = argv[0];
545f779f 1049 short_argv[1] = (char*) "";
2fbcde74
LP
1050 short_argv[2] = NULL;
1051 argv = short_argv;
906b76b2
LP
1052 argc = 2;
1053 }
1054
f7621db0 1055 for (i = 1; i < argc; i++) {
ddd88763 1056 uid_t uid;
88e3dc90 1057
906b76b2
LP
1058 if (isempty(argv[i]))
1059 uid = UID_INVALID;
1060 else {
1061 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
1062 if (r < 0)
1063 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1064 }
88e3dc90 1065
c529695e 1066 r = sd_bus_call_method(
4654e558
ZJS
1067 bus,
1068 "org.freedesktop.login1",
1069 "/org/freedesktop/login1",
1070 "org.freedesktop.login1.Manager",
1071 "SetUserLinger",
f8f14b36
SP
1072 &error, NULL,
1073 "ubb", (uint32_t) uid, b, true);
1074 if (r < 0) {
1075 log_error("Could not enable linger: %s", bus_error_message(&error, -r));
4654e558 1076 return r;
f8f14b36 1077 }
88e3dc90
LP
1078 }
1079
4654e558 1080 return 0;
88e3dc90
LP
1081}
1082
f7621db0 1083static int terminate_user(int argc, char *argv[], void *userdata) {
4afd3348 1084 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0
LP
1085 sd_bus *bus = userdata;
1086 int r, i;
88e3dc90 1087
f7621db0
LP
1088 assert(bus);
1089 assert(argv);
88e3dc90 1090
8a4b13c5 1091 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1092
f7621db0 1093 for (i = 1; i < argc; i++) {
ddd88763 1094 uid_t uid;
88e3dc90 1095
f7621db0 1096 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
f647962d 1097 if (r < 0)
f7621db0 1098 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
88e3dc90 1099
c529695e 1100 r = sd_bus_call_method(
4654e558
ZJS
1101 bus,
1102 "org.freedesktop.login1",
1103 "/org/freedesktop/login1",
1104 "org.freedesktop.login1.Manager",
1105 "TerminateUser",
f8f14b36
SP
1106 &error, NULL,
1107 "u", (uint32_t) uid);
1108 if (r < 0) {
1109 log_error("Could not terminate user: %s", bus_error_message(&error, -r));
4654e558 1110 return r;
f8f14b36 1111 }
88e3dc90
LP
1112 }
1113
4654e558 1114 return 0;
a4c279f8
LP
1115}
1116
f7621db0 1117static int kill_user(int argc, char *argv[], void *userdata) {
4afd3348 1118 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0
LP
1119 sd_bus *bus = userdata;
1120 int r, i;
de07ab16 1121
f7621db0
LP
1122 assert(bus);
1123 assert(argv);
de07ab16 1124
8a4b13c5 1125 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1126
de07ab16
LP
1127 if (!arg_kill_who)
1128 arg_kill_who = "all";
1129
f7621db0 1130 for (i = 1; i < argc; i++) {
ddd88763 1131 uid_t uid;
de07ab16 1132
f7621db0 1133 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
f647962d 1134 if (r < 0)
f7621db0 1135 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
de07ab16 1136
c529695e 1137 r = sd_bus_call_method(
4654e558
ZJS
1138 bus,
1139 "org.freedesktop.login1",
1140 "/org/freedesktop/login1",
1141 "org.freedesktop.login1.Manager",
1142 "KillUser",
f8f14b36
SP
1143 &error, NULL,
1144 "ui", (uint32_t) uid, arg_signal);
1145 if (r < 0) {
1146 log_error("Could not kill user: %s", bus_error_message(&error, -r));
4654e558 1147 return r;
f8f14b36 1148 }
de07ab16
LP
1149 }
1150
4654e558 1151 return 0;
de07ab16
LP
1152}
1153
f7621db0 1154static int attach(int argc, char *argv[], void *userdata) {
4afd3348 1155 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0
LP
1156 sd_bus *bus = userdata;
1157 int r, i;
88e3dc90 1158
f7621db0
LP
1159 assert(bus);
1160 assert(argv);
88e3dc90 1161
8a4b13c5 1162 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6bb92a16 1163
f7621db0 1164 for (i = 2; i < argc; i++) {
4654e558 1165
c529695e 1166 r = sd_bus_call_method(
4654e558
ZJS
1167 bus,
1168 "org.freedesktop.login1",
1169 "/org/freedesktop/login1",
1170 "org.freedesktop.login1.Manager",
1171 "AttachDevice",
f8f14b36 1172 &error, NULL,
f7621db0 1173 "ssb", argv[1], argv[i], true);
f8f14b36
SP
1174
1175 if (r < 0) {
1176 log_error("Could not attach device: %s", bus_error_message(&error, -r));
4654e558 1177 return r;
f8f14b36 1178 }
88e3dc90
LP
1179 }
1180
4654e558 1181 return 0;
a4c279f8
LP
1182}
1183
f7621db0 1184static int flush_devices(int argc, char *argv[], void *userdata) {
4afd3348 1185 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0 1186 sd_bus *bus = userdata;
f8f14b36 1187 int r;
88e3dc90 1188
f7621db0
LP
1189 assert(bus);
1190 assert(argv);
88e3dc90 1191
8a4b13c5 1192 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
6bb92a16 1193
c529695e 1194 r = sd_bus_call_method(
2a3613b1 1195 bus,
88e3dc90
LP
1196 "org.freedesktop.login1",
1197 "/org/freedesktop/login1",
1198 "org.freedesktop.login1.Manager",
2a3613b1 1199 "FlushDevices",
f8f14b36
SP
1200 &error, NULL,
1201 "b", true);
1202 if (r < 0)
1203 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
1204
1205 return r;
a4c279f8
LP
1206}
1207
f7621db0 1208static int lock_sessions(int argc, char *argv[], void *userdata) {
4afd3348 1209 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0 1210 sd_bus *bus = userdata;
f8f14b36 1211 int r;
b6160029 1212
f7621db0
LP
1213 assert(bus);
1214 assert(argv);
7212a8a9 1215
8a4b13c5 1216 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1217
f7621db0 1218 r = sd_bus_call_method(
2a3613b1 1219 bus,
7212a8a9
LP
1220 "org.freedesktop.login1",
1221 "/org/freedesktop/login1",
1222 "org.freedesktop.login1.Manager",
f7621db0 1223 streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
f8f14b36
SP
1224 &error, NULL,
1225 NULL);
1226 if (r < 0)
1227 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1228
1229 return r;
7212a8a9
LP
1230}
1231
f7621db0 1232static int terminate_seat(int argc, char *argv[], void *userdata) {
4afd3348 1233 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
f7621db0
LP
1234 sd_bus *bus = userdata;
1235 int r, i;
88e3dc90 1236
f7621db0
LP
1237 assert(bus);
1238 assert(argv);
88e3dc90 1239
8a4b13c5 1240 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
079dac08 1241
f7621db0 1242 for (i = 1; i < argc; i++) {
4654e558 1243
f7621db0 1244 r = sd_bus_call_method(
4654e558
ZJS
1245 bus,
1246 "org.freedesktop.login1",
1247 "/org/freedesktop/login1",
1248 "org.freedesktop.login1.Manager",
1249 "TerminateSeat",
f8f14b36 1250 &error, NULL,
f7621db0 1251 "s", argv[i]);
f8f14b36
SP
1252 if (r < 0) {
1253 log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
4654e558 1254 return r;
f8f14b36 1255 }
88e3dc90
LP
1256 }
1257
4654e558 1258 return 0;
a4c279f8
LP
1259}
1260
f7621db0 1261static int help(int argc, char *argv[], void *userdata) {
079dac08 1262
abca4822
LP
1263 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1264 "Send control commands to or query the login manager.\n\n"
01c51934
LP
1265 " -h --help Show this help\n"
1266 " --version Show package version\n"
1267 " --no-pager Do not pipe output into a pager\n"
1268 " --no-legend Do not show the headers and footers\n"
1269 " --no-ask-password Don't prompt for password\n"
1270 " -H --host=[USER@]HOST Operate on remote host\n"
1271 " -M --machine=CONTAINER Operate on local container\n"
1272 " -p --property=NAME Show only properties by this name\n"
1273 " -a --all Show all properties, including empty ones\n"
f4046fe0 1274 " --value When showing properties, only print the value\n"
01c51934
LP
1275 " -l --full Do not ellipsize output\n"
1276 " --kill-who=WHO Who to send signal to\n"
3c756001
LP
1277 " -s --signal=SIGNAL Which signal to send\n"
1278 " -n --lines=INTEGER Number of journal entries to show\n"
7e563bfc
IW
1279 " -o --output=STRING Change journal output mode (short, short-precise,\n"
1280 " short-iso, short-iso-precise, short-full,\n"
1281 " short-monotonic, short-unix, verbose, export,\n"
1282 " json, json-pretty, json-sse, cat)\n"
2520f939 1283 "Session Commands:\n"
4f8f66cb 1284 " list-sessions List sessions\n"
86e1f46f 1285 " session-status [ID...] Show session status\n"
4f8f66cb 1286 " show-session [ID...] Show properties of sessions or the manager\n"
906b76b2
LP
1287 " activate [ID] Activate a session\n"
1288 " lock-session [ID...] Screen lock one or more sessions\n"
1289 " unlock-session [ID...] Screen unlock one or more sessions\n"
4f8f66cb
ZJS
1290 " lock-sessions Screen lock all current sessions\n"
1291 " unlock-sessions Screen unlock all current sessions\n"
1292 " terminate-session ID... Terminate one or more sessions\n"
2520f939
LP
1293 " kill-session ID... Send signal to processes of a session\n\n"
1294 "User Commands:\n"
4f8f66cb 1295 " list-users List users\n"
86e1f46f 1296 " user-status [USER...] Show user status\n"
4f8f66cb 1297 " show-user [USER...] Show properties of users or the manager\n"
906b76b2
LP
1298 " enable-linger [USER...] Enable linger state of one or more users\n"
1299 " disable-linger [USER...] Disable linger state of one or more users\n"
4f8f66cb 1300 " terminate-user USER... Terminate all sessions of one or more users\n"
2520f939
LP
1301 " kill-user USER... Send signal to processes of a user\n\n"
1302 "Seat Commands:\n"
4f8f66cb 1303 " list-seats List seats\n"
86e1f46f
LP
1304 " seat-status [NAME...] Show seat status\n"
1305 " show-seat [NAME...] Show properties of seats or the manager\n"
4f8f66cb
ZJS
1306 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1307 " flush-devices Flush all device associations\n"
601185b4
ZJS
1308 " terminate-seat NAME... Terminate all sessions on one or more seats\n"
1309 , program_invocation_short_name);
f7621db0
LP
1310
1311 return 0;
abca4822
LP
1312}
1313
1314static int parse_argv(int argc, char *argv[]) {
1315
1316 enum {
1317 ARG_VERSION = 0x100,
f4046fe0 1318 ARG_VALUE,
a4c279f8 1319 ARG_NO_PAGER,
841aa8c0 1320 ARG_NO_LEGEND,
6bb92a16 1321 ARG_KILL_WHO,
9bdbc2e2 1322 ARG_NO_ASK_PASSWORD,
abca4822
LP
1323 };
1324
1325 static const struct option options[] = {
6d0274f1
LP
1326 { "help", no_argument, NULL, 'h' },
1327 { "version", no_argument, NULL, ARG_VERSION },
1328 { "property", required_argument, NULL, 'p' },
1329 { "all", no_argument, NULL, 'a' },
f4046fe0 1330 { "value", no_argument, NULL, ARG_VALUE },
422fa650 1331 { "full", no_argument, NULL, 'l' },
6d0274f1 1332 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
841aa8c0 1333 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
6d0274f1
LP
1334 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1335 { "signal", required_argument, NULL, 's' },
1336 { "host", required_argument, NULL, 'H' },
f8f14b36 1337 { "machine", required_argument, NULL, 'M' },
6d0274f1 1338 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
3c756001
LP
1339 { "lines", required_argument, NULL, 'n' },
1340 { "output", required_argument, NULL, 'o' },
eb9da376 1341 {}
abca4822
LP
1342 };
1343
1c3051eb 1344 int c, r;
abca4822
LP
1345
1346 assert(argc >= 0);
1347 assert(argv);
1348
3c756001 1349 while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
abca4822
LP
1350
1351 switch (c) {
1352
1353 case 'h':
f7621db0 1354 help(0, NULL, NULL);
601185b4 1355 return 0;
abca4822
LP
1356
1357 case ARG_VERSION:
3f6fd1ba 1358 return version();
abca4822 1359
a4c279f8 1360 case 'p': {
1c3051eb
DH
1361 r = strv_extend(&arg_property, optarg);
1362 if (r < 0)
1363 return log_oom();
a4c279f8
LP
1364
1365 /* If the user asked for a particular
1366 * property, show it to him, even if it is
1367 * empty. */
1368 arg_all = true;
1369 break;
1370 }
1371
1372 case 'a':
1373 arg_all = true;
1374 break;
1375
f4046fe0
ZJS
1376 case ARG_VALUE:
1377 arg_value = true;
1378 break;
1379
422fa650
ZJS
1380 case 'l':
1381 arg_full = true;
1382 break;
1383
3c756001
LP
1384 case 'n':
1385 if (safe_atou(optarg, &arg_lines) < 0) {
1386 log_error("Failed to parse lines '%s'", optarg);
1387 return -EINVAL;
1388 }
1389 break;
1390
1391 case 'o':
1392 arg_output = output_mode_from_string(optarg);
1393 if (arg_output < 0) {
1394 log_error("Unknown output '%s'.", optarg);
1395 return -EINVAL;
1396 }
1397 break;
1398
abca4822
LP
1399 case ARG_NO_PAGER:
1400 arg_no_pager = true;
1401 break;
1402
841aa8c0
ZJS
1403 case ARG_NO_LEGEND:
1404 arg_legend = false;
1405 break;
1406
6bb92a16
LP
1407 case ARG_NO_ASK_PASSWORD:
1408 arg_ask_password = false;
5d5e98eb 1409 break;
6bb92a16 1410
a4c279f8
LP
1411 case ARG_KILL_WHO:
1412 arg_kill_who = optarg;
1413 break;
1414
1415 case 's':
1416 arg_signal = signal_from_string_try_harder(optarg);
1417 if (arg_signal < 0) {
1418 log_error("Failed to parse signal string %s.", optarg);
1419 return -EINVAL;
1420 }
1421 break;
1422
f8f14b36
SP
1423 case 'H':
1424 arg_transport = BUS_TRANSPORT_REMOTE;
1425 arg_host = optarg;
abca4822
LP
1426 break;
1427
f8f14b36 1428 case 'M':
de33fc62 1429 arg_transport = BUS_TRANSPORT_MACHINE;
f8f14b36 1430 arg_host = optarg;
abca4822
LP
1431 break;
1432
1433 case '?':
1434 return -EINVAL;
1435
1436 default:
eb9da376 1437 assert_not_reached("Unhandled option");
abca4822 1438 }
abca4822
LP
1439
1440 return 1;
1441}
1442
f7621db0
LP
1443static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
1444
1445 static const Verb verbs[] = {
1446 { "help", VERB_ANY, VERB_ANY, 0, help },
1447 { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions },
86e1f46f 1448 { "session-status", VERB_ANY, VERB_ANY, 0, show_session },
f7621db0 1449 { "show-session", VERB_ANY, VERB_ANY, 0, show_session },
906b76b2
LP
1450 { "activate", VERB_ANY, 2, 0, activate },
1451 { "lock-session", VERB_ANY, VERB_ANY, 0, activate },
1452 { "unlock-session", VERB_ANY, VERB_ANY, 0, activate },
f7621db0
LP
1453 { "lock-sessions", VERB_ANY, 1, 0, lock_sessions },
1454 { "unlock-sessions", VERB_ANY, 1, 0, lock_sessions },
1455 { "terminate-session", 2, VERB_ANY, 0, activate },
1456 { "kill-session", 2, VERB_ANY, 0, kill_session },
1457 { "list-users", VERB_ANY, 1, 0, list_users },
86e1f46f 1458 { "user-status", VERB_ANY, VERB_ANY, 0, show_user },
f7621db0 1459 { "show-user", VERB_ANY, VERB_ANY, 0, show_user },
906b76b2
LP
1460 { "enable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
1461 { "disable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
f7621db0
LP
1462 { "terminate-user", 2, VERB_ANY, 0, terminate_user },
1463 { "kill-user", 2, VERB_ANY, 0, kill_user },
1464 { "list-seats", VERB_ANY, 1, 0, list_seats },
86e1f46f
LP
1465 { "seat-status", VERB_ANY, VERB_ANY, 0, show_seat },
1466 { "show-seat", VERB_ANY, VERB_ANY, 0, show_seat },
f7621db0
LP
1467 { "attach", 3, VERB_ANY, 0, attach },
1468 { "flush-devices", VERB_ANY, 1, 0, flush_devices },
1469 { "terminate-seat", 2, VERB_ANY, 0, terminate_seat },
1470 {}
abca4822
LP
1471 };
1472
f7621db0 1473 return dispatch_verb(argc, argv, verbs, bus);
abca4822
LP
1474}
1475
f8f14b36 1476int main(int argc, char *argv[]) {
a3c56345 1477 sd_bus *bus = NULL;
f8f14b36 1478 int r;
abca4822 1479
a9cdc94f 1480 setlocale(LC_ALL, "");
abca4822
LP
1481 log_parse_environment();
1482 log_open();
9e29521e 1483 sigbus_install();
abca4822
LP
1484
1485 r = parse_argv(argc, argv);
f8f14b36 1486 if (r <= 0)
abca4822 1487 goto finish;
f8f14b36 1488
266f3e26 1489 r = bus_connect_transport(arg_transport, arg_host, false, &bus);
f8f14b36 1490 if (r < 0) {
da927ba9 1491 log_error_errno(r, "Failed to create bus connection: %m");
abca4822
LP
1492 goto finish;
1493 }
1494
c529695e
LP
1495 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1496
f7621db0 1497 r = loginctl_main(argc, argv, bus);
abca4822
LP
1498
1499finish:
0a84daa5
FB
1500 /* make sure we terminate the bus connection first, and then close the
1501 * pager, see issue #3543 for the details. */
a3c56345 1502 sd_bus_flush_close_unref(bus);
f8f14b36 1503 pager_close();
f7621db0 1504 polkit_agent_close();
abca4822 1505
a4c279f8
LP
1506 strv_free(arg_property);
1507
f8f14b36 1508 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
abca4822 1509}