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