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