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