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