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