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