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