]> git.proxmox.com Git - systemd.git/blame - src/login/loginctl.c
Imported Upstream version 226
[systemd.git] / src / login / loginctl.c
CommitLineData
663996b3
MS
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
663996b3
MS
22#include <unistd.h>
23#include <errno.h>
24#include <string.h>
25#include <getopt.h>
663996b3
MS
26#include <locale.h>
27
60f067b4
JS
28#include "sd-bus.h"
29#include "bus-util.h"
30#include "bus-error.h"
663996b3
MS
31#include "log.h"
32#include "util.h"
33#include "macro.h"
34#include "pager.h"
663996b3
MS
35#include "build.h"
36#include "strv.h"
14228c0d 37#include "unit-name.h"
663996b3 38#include "sysfs-show.h"
e735f4d4 39#include "logs-show.h"
14228c0d
MB
40#include "cgroup-show.h"
41#include "cgroup-util.h"
663996b3 42#include "spawn-polkit-agent.h"
e735f4d4 43#include "verbs.h"
e3bff60a
MP
44#include "process-util.h"
45#include "terminal-util.h"
86f210e9 46#include "signal-util.h"
663996b3
MS
47
48static char **arg_property = NULL;
49static bool arg_all = false;
50static bool arg_full = false;
51static bool arg_no_pager = false;
60f067b4 52static bool arg_legend = true;
663996b3
MS
53static const char *arg_kill_who = NULL;
54static int arg_signal = SIGTERM;
60f067b4 55static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
14228c0d 56static char *arg_host = NULL;
e735f4d4
MP
57static bool arg_ask_password = true;
58static unsigned arg_lines = 10;
59static OutputMode arg_output = OUTPUT_SHORT;
663996b3
MS
60
61static void pager_open_if_enabled(void) {
62
663996b3
MS
63 if (arg_no_pager)
64 return;
65
66 pager_open(false);
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
60f067b4
JS
76 if (arg_transport != BUS_TRANSPORT_LOCAL)
77 return;
78
663996b3
MS
79 polkit_agent_open();
80}
81
e735f4d4
MP
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
91static int list_sessions(int argc, char *argv[], void *userdata) {
60f067b4
JS
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;
e735f4d4 95 sd_bus *bus = userdata;
663996b3 96 unsigned k = 0;
60f067b4
JS
97 uint32_t uid;
98 int r;
663996b3 99
e735f4d4
MP
100 assert(bus);
101 assert(argv);
102
663996b3
MS
103 pager_open_if_enabled();
104
60f067b4 105 r = sd_bus_call_method(
663996b3
MS
106 bus,
107 "org.freedesktop.login1",
108 "/org/freedesktop/login1",
109 "org.freedesktop.login1.Manager",
110 "ListSessions",
60f067b4
JS
111 &error, &reply,
112 "");
113 if (r < 0) {
114 log_error("Failed to list sessions: %s", bus_error_message(&error, r));
663996b3 115 return r;
663996b3
MS
116 }
117
60f067b4
JS
118 r = sd_bus_message_enter_container(reply, 'a', "(susso)");
119 if (r < 0)
120 return bus_log_parse_error(r);
663996b3 121
60f067b4 122 if (arg_legend)
663996b3
MS
123 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
124
60f067b4 125 while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) {
663996b3 126 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
663996b3 127 k++;
663996b3 128 }
60f067b4
JS
129 if (r < 0)
130 return bus_log_parse_error(r);
663996b3 131
60f067b4 132 if (arg_legend)
663996b3
MS
133 printf("\n%u sessions listed.\n", k);
134
135 return 0;
136}
137
e735f4d4 138static int list_users(int argc, char *argv[], void *userdata) {
60f067b4
JS
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;
e735f4d4 142 sd_bus *bus = userdata;
663996b3 143 unsigned k = 0;
60f067b4
JS
144 uint32_t uid;
145 int r;
663996b3 146
e735f4d4
MP
147 assert(bus);
148 assert(argv);
149
663996b3
MS
150 pager_open_if_enabled();
151
60f067b4 152 r = sd_bus_call_method(
663996b3
MS
153 bus,
154 "org.freedesktop.login1",
155 "/org/freedesktop/login1",
156 "org.freedesktop.login1.Manager",
157 "ListUsers",
60f067b4
JS
158 &error, &reply,
159 "");
160 if (r < 0) {
161 log_error("Failed to list users: %s", bus_error_message(&error, r));
663996b3 162 return r;
663996b3
MS
163 }
164
60f067b4
JS
165 r = sd_bus_message_enter_container(reply, 'a', "(uso)");
166 if (r < 0)
167 return bus_log_parse_error(r);
663996b3 168
60f067b4 169 if (arg_legend)
663996b3
MS
170 printf("%10s %-16s\n", "UID", "USER");
171
60f067b4 172 while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) {
663996b3 173 printf("%10u %-16s\n", (unsigned) uid, user);
663996b3 174 k++;
663996b3 175 }
60f067b4
JS
176 if (r < 0)
177 return bus_log_parse_error(r);
663996b3 178
60f067b4 179 if (arg_legend)
663996b3
MS
180 printf("\n%u users listed.\n", k);
181
182 return 0;
183}
184
e735f4d4 185static int list_seats(int argc, char *argv[], void *userdata) {
60f067b4
JS
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;
e735f4d4 189 sd_bus *bus = userdata;
663996b3 190 unsigned k = 0;
60f067b4 191 int r;
663996b3 192
e735f4d4
MP
193 assert(bus);
194 assert(argv);
195
663996b3
MS
196 pager_open_if_enabled();
197
60f067b4 198 r = sd_bus_call_method(
663996b3
MS
199 bus,
200 "org.freedesktop.login1",
201 "/org/freedesktop/login1",
202 "org.freedesktop.login1.Manager",
203 "ListSeats",
60f067b4
JS
204 &error, &reply,
205 "");
206 if (r < 0) {
207 log_error("Failed to list seats: %s", bus_error_message(&error, r));
663996b3 208 return r;
663996b3
MS
209 }
210
60f067b4
JS
211 r = sd_bus_message_enter_container(reply, 'a', "(so)");
212 if (r < 0)
213 return bus_log_parse_error(r);
663996b3 214
60f067b4 215 if (arg_legend)
663996b3
MS
216 printf("%-16s\n", "SEAT");
217
60f067b4 218 while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) {
663996b3 219 printf("%-16s\n", seat);
663996b3 220 k++;
663996b3 221 }
60f067b4
JS
222 if (r < 0)
223 return bus_log_parse_error(r);
663996b3 224
60f067b4 225 if (arg_legend)
663996b3
MS
226 printf("\n%u seats listed.\n", k);
227
228 return 0;
229}
230
60f067b4
JS
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;
14228c0d 234 _cleanup_free_ char *path = NULL;
14228c0d 235 const char *cgroup;
e735f4d4 236 int r;
14228c0d
MB
237 unsigned c;
238
239 assert(bus);
240 assert(unit);
241
60f067b4 242 if (arg_transport != BUS_TRANSPORT_LOCAL)
14228c0d
MB
243 return 0;
244
245 path = unit_dbus_path_from_name(unit);
246 if (!path)
60f067b4 247 return -ENOMEM;
14228c0d 248
60f067b4 249 r = sd_bus_get_property(
14228c0d
MB
250 bus,
251 "org.freedesktop.systemd1",
252 path,
60f067b4
JS
253 interface,
254 "ControlGroup",
255 &error, &reply, "s");
256 if (r < 0)
14228c0d 257 return r;
14228c0d 258
60f067b4
JS
259 r = sd_bus_message_read(reply, "s", &cgroup);
260 if (r < 0)
261 return r;
14228c0d
MB
262
263 if (isempty(cgroup))
264 return 0;
265
d9dfd233 266 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
14228c0d
MB
267 return 0;
268
14228c0d
MB
269 c = columns();
270 if (c > 18)
271 c -= 18;
272 else
273 c = 0;
274
e735f4d4 275 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags());
14228c0d
MB
276 return 0;
277}
278
663996b3 279typedef struct SessionStatusInfo {
86f210e9 280 char *id;
663996b3 281 uid_t uid;
86f210e9 282 char *name;
e735f4d4 283 struct dual_timestamp timestamp;
60f067b4 284 unsigned int vtnr;
86f210e9
MP
285 char *seat;
286 char *tty;
287 char *display;
663996b3 288 bool remote;
86f210e9
MP
289 char *remote_host;
290 char *remote_user;
291 char *service;
663996b3 292 pid_t leader;
86f210e9
MP
293 char *type;
294 char *class;
295 char *state;
296 char *scope;
297 char *desktop;
663996b3
MS
298} SessionStatusInfo;
299
300typedef struct UserStatusInfo {
301 uid_t uid;
86f210e9 302 char *name;
e735f4d4 303 struct dual_timestamp timestamp;
86f210e9 304 char *state;
663996b3 305 char **sessions;
86f210e9
MP
306 char *display;
307 char *slice;
663996b3
MS
308} UserStatusInfo;
309
310typedef struct SeatStatusInfo {
86f210e9
MP
311 char *id;
312 char *active_session;
663996b3
MS
313 char **sessions;
314} SeatStatusInfo;
315
86f210e9
MP
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
60f067b4
JS
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
5fd56512
MP
375 r = free_and_strdup(p, s);
376 if (r < 0)
377 return r;
60f067b4
JS
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
417static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
418
419 static const struct bus_properties_map map[] = {
e735f4d4
MP
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) },
60f067b4
JS
439 {}
440 };
441
663996b3
MS
442 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
443 char since2[FORMAT_TIMESTAMP_MAX], *s2;
86f210e9 444 _cleanup_(session_status_info_clear) SessionStatusInfo i = {};
60f067b4 445 int r;
663996b3 446
60f067b4 447 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
f47781d8
MP
448 if (r < 0)
449 return log_error_errno(r, "Could not get properties: %m");
663996b3 450
60f067b4
JS
451 if (*new_line)
452 printf("\n");
453
454 *new_line = true;
455
456 printf("%s - ", strna(i.id));
457
458 if (i.name)
459 printf("%s (%u)\n", i.name, (unsigned) i.uid);
663996b3 460 else
60f067b4 461 printf("%u\n", (unsigned) i.uid);
663996b3 462
e735f4d4
MP
463 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
464 s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
663996b3
MS
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
60f067b4 471 if (i.leader > 0) {
14228c0d 472 _cleanup_free_ char *t = NULL;
663996b3 473
60f067b4 474 printf("\t Leader: %u", (unsigned) i.leader);
663996b3 475
60f067b4 476 get_process_comm(i.leader, &t);
14228c0d 477 if (t)
663996b3 478 printf(" (%s)", t);
663996b3
MS
479
480 printf("\n");
481 }
482
60f067b4
JS
483 if (!isempty(i.seat)) {
484 printf("\t Seat: %s", i.seat);
663996b3 485
60f067b4 486 if (i.vtnr > 0)
e735f4d4 487 printf("; vc%u", i.vtnr);
663996b3
MS
488
489 printf("\n");
490 }
491
60f067b4
JS
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)
663996b3
MS
504 printf("\t Remote: Yes\n");
505
60f067b4
JS
506 if (i.service) {
507 printf("\t Service: %s", i.service);
508
509 if (i.type)
510 printf("; type %s", i.type);
511
512 if (i.class)
513 printf("; class %s", i.class);
663996b3 514
60f067b4
JS
515 printf("\n");
516 } else if (i.type) {
517 printf("\t Type: %s", i.type);
663996b3 518
60f067b4
JS
519 if (i.class)
520 printf("; class %s", i.class);
663996b3
MS
521
522 printf("\n");
60f067b4
JS
523 } else if (i.class)
524 printf("\t Class: %s\n", i.class);
663996b3 525
60f067b4
JS
526 if (!isempty(i.desktop))
527 printf("\t Desktop: %s\n", i.desktop);
663996b3 528
60f067b4
JS
529 if (i.state)
530 printf("\t State: %s\n", i.state);
663996b3 531
60f067b4
JS
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);
e735f4d4
MP
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 }
663996b3 551 }
60f067b4
JS
552
553 return 0;
663996b3
MS
554}
555
60f067b4
JS
556static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
557
558 static const struct bus_properties_map map[] = {
e735f4d4
MP
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) },
60f067b4
JS
567 {}
568 };
569
663996b3
MS
570 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
571 char since2[FORMAT_TIMESTAMP_MAX], *s2;
86f210e9 572 _cleanup_(user_status_info_clear) UserStatusInfo i = {};
60f067b4
JS
573 int r;
574
575 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
86f210e9
MP
576 if (r < 0)
577 return log_error_errno(r, "Could not get properties: %m");
663996b3 578
60f067b4
JS
579 if (*new_line)
580 printf("\n");
581
582 *new_line = true;
583
584 if (i.name)
585 printf("%s (%u)\n", i.name, (unsigned) i.uid);
663996b3 586 else
60f067b4 587 printf("%u\n", (unsigned) i.uid);
663996b3 588
e735f4d4
MP
589 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
590 s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
663996b3
MS
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
60f067b4
JS
597 if (!isempty(i.state))
598 printf("\t State: %s\n", i.state);
14228c0d 599
60f067b4 600 if (!strv_isempty(i.sessions)) {
663996b3
MS
601 char **l;
602 printf("\tSessions:");
603
60f067b4
JS
604 STRV_FOREACH(l, i.sessions) {
605 if (streq_ptr(*l, i.display))
663996b3
MS
606 printf(" *%s", *l);
607 else
608 printf(" %s", *l);
609 }
610
611 printf("\n");
612 }
613
60f067b4
JS
614 if (i.slice) {
615 printf("\t Unit: %s\n", i.slice);
616 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
e735f4d4
MP
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);
663996b3 630 }
60f067b4 631
86f210e9 632 return 0;
663996b3
MS
633}
634
60f067b4
JS
635static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
636
637 static const struct bus_properties_map map[] = {
638 { "Id", "s", NULL, offsetof(SeatStatusInfo, id) },
639 { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
640 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
641 {}
642 };
643
86f210e9 644 _cleanup_(seat_status_info_clear) SeatStatusInfo i = {};
60f067b4
JS
645 int r;
646
647 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
86f210e9
MP
648 if (r < 0)
649 return log_error_errno(r, "Could not get properties: %m");
60f067b4
JS
650
651 if (*new_line)
652 printf("\n");
663996b3 653
60f067b4
JS
654 *new_line = true;
655
656 printf("%s\n", strna(i.id));
663996b3 657
60f067b4 658 if (!strv_isempty(i.sessions)) {
663996b3
MS
659 char **l;
660 printf("\tSessions:");
661
60f067b4
JS
662 STRV_FOREACH(l, i.sessions) {
663 if (streq_ptr(*l, i.active_session))
663996b3
MS
664 printf(" *%s", *l);
665 else
666 printf(" %s", *l);
667 }
668
669 printf("\n");
670 }
671
60f067b4 672 if (arg_transport == BUS_TRANSPORT_LOCAL) {
663996b3
MS
673 unsigned c;
674
675 c = columns();
676 if (c > 21)
677 c -= 21;
678 else
679 c = 0;
680
681 printf("\t Devices:\n");
682
60f067b4 683 show_sysfs(i.id, "\t\t ", c);
663996b3
MS
684 }
685
86f210e9 686 return 0;
663996b3
MS
687}
688
60f067b4
JS
689static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
690 int r;
663996b3 691
60f067b4
JS
692 if (*new_line)
693 printf("\n");
663996b3 694
60f067b4 695 *new_line = true;
663996b3 696
60f067b4
JS
697 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
698 if (r < 0)
f47781d8 699 log_error_errno(r, "Could not get properties: %m");
663996b3 700
60f067b4 701 return r;
663996b3
MS
702}
703
e735f4d4 704static int show_session(int argc, char *argv[], void *userdata) {
60f067b4 705 bool properties, new_line = false;
e735f4d4
MP
706 sd_bus *bus = userdata;
707 int r, i;
663996b3 708
60f067b4 709 assert(bus);
e735f4d4 710 assert(argv);
663996b3 711
e735f4d4 712 properties = !strstr(argv[0], "status");
663996b3 713
60f067b4 714 pager_open_if_enabled();
663996b3 715
e735f4d4 716 if (argc <= 1) {
60f067b4
JS
717 /* If not argument is specified inspect the manager
718 * itself */
e735f4d4
MP
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);
663996b3
MS
724 }
725
e735f4d4 726 for (i = 1; i < argc; i++) {
60f067b4
JS
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;
663996b3 730
60f067b4
JS
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,
e735f4d4 738 "s", argv[i]);
60f067b4
JS
739 if (r < 0) {
740 log_error("Failed to get session: %s", bus_error_message(&error, r));
741 return r;
663996b3 742 }
663996b3 743
60f067b4
JS
744 r = sd_bus_message_read(reply, "o", &path);
745 if (r < 0)
746 return bus_log_parse_error(r);
663996b3 747
60f067b4
JS
748 if (properties)
749 r = show_properties(bus, path, &new_line);
750 else
751 r = print_session_status_info(bus, path, &new_line);
663996b3 752
60f067b4
JS
753 if (r < 0)
754 return r;
663996b3
MS
755 }
756
663996b3
MS
757 return 0;
758}
759
e735f4d4 760static int show_user(int argc, char *argv[], void *userdata) {
60f067b4 761 bool properties, new_line = false;
e735f4d4
MP
762 sd_bus *bus = userdata;
763 int r, i;
663996b3 764
60f067b4 765 assert(bus);
e735f4d4 766 assert(argv);
663996b3 767
e735f4d4 768 properties = !strstr(argv[0], "status");
663996b3 769
60f067b4 770 pager_open_if_enabled();
663996b3 771
e735f4d4 772 if (argc <= 1) {
60f067b4
JS
773 /* If not argument is specified inspect the manager
774 * itself */
e735f4d4
MP
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);
60f067b4 779 }
663996b3 780
e735f4d4 781 for (i = 1; i < argc; i++) {
60f067b4
JS
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;
663996b3 786
e735f4d4 787 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
f47781d8 788 if (r < 0)
e735f4d4 789 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
663996b3 790
60f067b4
JS
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);
663996b3 799 if (r < 0) {
60f067b4
JS
800 log_error("Failed to get user: %s", bus_error_message(&error, r));
801 return r;
663996b3
MS
802 }
803
60f067b4
JS
804 r = sd_bus_message_read(reply, "o", &path);
805 if (r < 0)
806 return bus_log_parse_error(r);
663996b3 807
60f067b4
JS
808 if (properties)
809 r = show_properties(bus, path, &new_line);
663996b3 810 else
60f067b4 811 r = print_user_status_info(bus, path, &new_line);
663996b3 812
60f067b4
JS
813 if (r < 0)
814 return r;
815 }
663996b3 816
60f067b4 817 return 0;
663996b3
MS
818}
819
e735f4d4 820static int show_seat(int argc, char *argv[], void *userdata) {
60f067b4 821 bool properties, new_line = false;
e735f4d4
MP
822 sd_bus *bus = userdata;
823 int r, i;
663996b3
MS
824
825 assert(bus);
e735f4d4 826 assert(argv);
663996b3 827
e735f4d4 828 properties = !strstr(argv[0], "status");
663996b3
MS
829
830 pager_open_if_enabled();
831
e735f4d4 832 if (argc <= 1) {
663996b3
MS
833 /* If not argument is specified inspect the manager
834 * itself */
e735f4d4
MP
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);
663996b3
MS
839 }
840
e735f4d4 841 for (i = 1; i < argc; i++) {
60f067b4
JS
842 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
843 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
663996b3
MS
844 const char *path = NULL;
845
60f067b4
JS
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,
e735f4d4 853 "s", argv[i]);
60f067b4
JS
854 if (r < 0) {
855 log_error("Failed to get seat: %s", bus_error_message(&error, r));
856 return r;
663996b3 857 }
14228c0d 858
60f067b4
JS
859 r = sd_bus_message_read(reply, "o", &path);
860 if (r < 0)
861 return bus_log_parse_error(r);
663996b3 862
60f067b4
JS
863 if (properties)
864 r = show_properties(bus, path, &new_line);
865 else
866 r = print_seat_status_info(bus, path, &new_line);
663996b3 867
60f067b4
JS
868 if (r < 0)
869 return r;
663996b3
MS
870 }
871
60f067b4 872 return 0;
663996b3
MS
873}
874
e735f4d4 875static int activate(int argc, char *argv[], void *userdata) {
60f067b4 876 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4
MP
877 sd_bus *bus = userdata;
878 char *short_argv[3];
879 int r, i;
880
881 assert(bus);
882 assert(argv);
883
884 polkit_agent_open_if_enabled();
663996b3 885
e735f4d4
MP
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. */
663996b3 890
e735f4d4
MP
891 short_argv[0] = argv[0];
892 short_argv[1] = (char*) "";
893 short_argv[2] = NULL;
894
895 argv = short_argv;
896 argc = 2;
897 }
898
899 for (i = 1; i < argc; i++) {
663996b3 900
e3bff60a 901 r = sd_bus_call_method(
663996b3
MS
902 bus,
903 "org.freedesktop.login1",
904 "/org/freedesktop/login1",
905 "org.freedesktop.login1.Manager",
e735f4d4
MP
906 streq(argv[0], "lock-session") ? "LockSession" :
907 streq(argv[0], "unlock-session") ? "UnlockSession" :
908 streq(argv[0], "terminate-session") ? "TerminateSession" :
663996b3 909 "ActivateSession",
60f067b4 910 &error, NULL,
e735f4d4 911 "s", argv[i]);
60f067b4
JS
912 if (r < 0) {
913 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
914 return r;
915 }
663996b3
MS
916 }
917
60f067b4 918 return 0;
663996b3
MS
919}
920
e735f4d4 921static int kill_session(int argc, char *argv[], void *userdata) {
60f067b4 922 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4
MP
923 sd_bus *bus = userdata;
924 int r, i;
663996b3 925
e735f4d4
MP
926 assert(bus);
927 assert(argv);
928
929 polkit_agent_open_if_enabled();
663996b3
MS
930
931 if (!arg_kill_who)
932 arg_kill_who = "all";
933
e735f4d4 934 for (i = 1; i < argc; i++) {
663996b3 935
e3bff60a 936 r = sd_bus_call_method(
663996b3
MS
937 bus,
938 "org.freedesktop.login1",
939 "/org/freedesktop/login1",
940 "org.freedesktop.login1.Manager",
941 "KillSession",
60f067b4 942 &error, NULL,
e735f4d4 943 "ssi", argv[i], arg_kill_who, arg_signal);
60f067b4
JS
944 if (r < 0) {
945 log_error("Could not kill session: %s", bus_error_message(&error, -r));
663996b3 946 return r;
60f067b4 947 }
663996b3
MS
948 }
949
950 return 0;
951}
952
e735f4d4 953static int enable_linger(int argc, char *argv[], void *userdata) {
60f067b4 954 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4
MP
955 sd_bus *bus = userdata;
956 char* short_argv[3];
60f067b4 957 bool b;
e735f4d4 958 int r, i;
663996b3 959
e735f4d4
MP
960 assert(bus);
961 assert(argv);
663996b3
MS
962
963 polkit_agent_open_if_enabled();
964
e735f4d4
MP
965 b = streq(argv[0], "enable-linger");
966
967 if (argc < 2) {
968 short_argv[0] = argv[0];
969 short_argv[1] = (char*) "";
970 short_argv[2] = NULL;
971 argv = short_argv;
972 argc = 2;
973 }
663996b3 974
e735f4d4 975 for (i = 1; i < argc; i++) {
663996b3 976 uid_t uid;
663996b3 977
e735f4d4
MP
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 }
663996b3 985
e3bff60a 986 r = sd_bus_call_method(
663996b3
MS
987 bus,
988 "org.freedesktop.login1",
989 "/org/freedesktop/login1",
990 "org.freedesktop.login1.Manager",
991 "SetUserLinger",
60f067b4
JS
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));
663996b3 996 return r;
60f067b4 997 }
663996b3
MS
998 }
999
1000 return 0;
1001}
1002
e735f4d4 1003static int terminate_user(int argc, char *argv[], void *userdata) {
60f067b4 1004 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4
MP
1005 sd_bus *bus = userdata;
1006 int r, i;
663996b3 1007
e735f4d4
MP
1008 assert(bus);
1009 assert(argv);
663996b3 1010
e735f4d4
MP
1011 polkit_agent_open_if_enabled();
1012
1013 for (i = 1; i < argc; i++) {
663996b3 1014 uid_t uid;
663996b3 1015
e735f4d4 1016 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
f47781d8 1017 if (r < 0)
e735f4d4 1018 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
663996b3 1019
e3bff60a 1020 r = sd_bus_call_method(
663996b3
MS
1021 bus,
1022 "org.freedesktop.login1",
1023 "/org/freedesktop/login1",
1024 "org.freedesktop.login1.Manager",
1025 "TerminateUser",
60f067b4
JS
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));
663996b3 1030 return r;
60f067b4 1031 }
663996b3
MS
1032 }
1033
1034 return 0;
1035}
1036
e735f4d4 1037static int kill_user(int argc, char *argv[], void *userdata) {
60f067b4 1038 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4
MP
1039 sd_bus *bus = userdata;
1040 int r, i;
663996b3 1041
e735f4d4
MP
1042 assert(bus);
1043 assert(argv);
1044
1045 polkit_agent_open_if_enabled();
663996b3
MS
1046
1047 if (!arg_kill_who)
1048 arg_kill_who = "all";
1049
e735f4d4 1050 for (i = 1; i < argc; i++) {
663996b3 1051 uid_t uid;
663996b3 1052
e735f4d4 1053 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
f47781d8 1054 if (r < 0)
e735f4d4 1055 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
663996b3 1056
e3bff60a 1057 r = sd_bus_call_method(
663996b3
MS
1058 bus,
1059 "org.freedesktop.login1",
1060 "/org/freedesktop/login1",
1061 "org.freedesktop.login1.Manager",
1062 "KillUser",
60f067b4
JS
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));
663996b3 1067 return r;
60f067b4 1068 }
663996b3
MS
1069 }
1070
1071 return 0;
1072}
1073
e735f4d4 1074static int attach(int argc, char *argv[], void *userdata) {
60f067b4 1075 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4
MP
1076 sd_bus *bus = userdata;
1077 int r, i;
663996b3 1078
e735f4d4
MP
1079 assert(bus);
1080 assert(argv);
663996b3
MS
1081
1082 polkit_agent_open_if_enabled();
1083
e735f4d4 1084 for (i = 2; i < argc; i++) {
663996b3 1085
e3bff60a 1086 r = sd_bus_call_method(
663996b3
MS
1087 bus,
1088 "org.freedesktop.login1",
1089 "/org/freedesktop/login1",
1090 "org.freedesktop.login1.Manager",
1091 "AttachDevice",
60f067b4 1092 &error, NULL,
e735f4d4 1093 "ssb", argv[1], argv[i], true);
60f067b4
JS
1094
1095 if (r < 0) {
1096 log_error("Could not attach device: %s", bus_error_message(&error, -r));
663996b3 1097 return r;
60f067b4 1098 }
663996b3
MS
1099 }
1100
1101 return 0;
1102}
1103
e735f4d4 1104static int flush_devices(int argc, char *argv[], void *userdata) {
60f067b4 1105 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4 1106 sd_bus *bus = userdata;
60f067b4 1107 int r;
663996b3 1108
e735f4d4
MP
1109 assert(bus);
1110 assert(argv);
663996b3
MS
1111
1112 polkit_agent_open_if_enabled();
1113
e3bff60a 1114 r = sd_bus_call_method(
663996b3
MS
1115 bus,
1116 "org.freedesktop.login1",
1117 "/org/freedesktop/login1",
1118 "org.freedesktop.login1.Manager",
1119 "FlushDevices",
60f067b4
JS
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;
663996b3
MS
1126}
1127
e735f4d4 1128static int lock_sessions(int argc, char *argv[], void *userdata) {
60f067b4 1129 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4 1130 sd_bus *bus = userdata;
60f067b4 1131 int r;
663996b3 1132
e735f4d4
MP
1133 assert(bus);
1134 assert(argv);
663996b3 1135
e735f4d4
MP
1136 polkit_agent_open_if_enabled();
1137
1138 r = sd_bus_call_method(
663996b3
MS
1139 bus,
1140 "org.freedesktop.login1",
1141 "/org/freedesktop/login1",
1142 "org.freedesktop.login1.Manager",
e735f4d4 1143 streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
60f067b4
JS
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;
663996b3
MS
1150}
1151
e735f4d4 1152static int terminate_seat(int argc, char *argv[], void *userdata) {
60f067b4 1153 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e735f4d4
MP
1154 sd_bus *bus = userdata;
1155 int r, i;
663996b3 1156
e735f4d4
MP
1157 assert(bus);
1158 assert(argv);
1159
1160 polkit_agent_open_if_enabled();
663996b3 1161
e735f4d4 1162 for (i = 1; i < argc; i++) {
663996b3 1163
e735f4d4 1164 r = sd_bus_call_method(
663996b3
MS
1165 bus,
1166 "org.freedesktop.login1",
1167 "/org/freedesktop/login1",
1168 "org.freedesktop.login1.Manager",
1169 "TerminateSeat",
60f067b4 1170 &error, NULL,
e735f4d4 1171 "s", argv[i]);
60f067b4
JS
1172 if (r < 0) {
1173 log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
663996b3 1174 return r;
60f067b4 1175 }
663996b3
MS
1176 }
1177
1178 return 0;
1179}
1180
e735f4d4
MP
1181static int help(int argc, char *argv[], void *userdata) {
1182
663996b3
MS
1183 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1184 "Send control commands to or query the login manager.\n\n"
e735f4d4
MP
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"
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"
1200 "Session Commands:\n"
60f067b4 1201 " list-sessions List sessions\n"
e735f4d4 1202 " session-status [ID...] Show session status\n"
60f067b4 1203 " show-session [ID...] Show properties of sessions or the manager\n"
e735f4d4
MP
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"
60f067b4
JS
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"
e735f4d4
MP
1210 " kill-session ID... Send signal to processes of a session\n\n"
1211 "User Commands:\n"
60f067b4 1212 " list-users List users\n"
e735f4d4 1213 " user-status [USER...] Show user status\n"
60f067b4 1214 " show-user [USER...] Show properties of users or the manager\n"
e735f4d4
MP
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"
60f067b4 1217 " terminate-user USER... Terminate all sessions of one or more users\n"
e735f4d4
MP
1218 " kill-user USER... Send signal to processes of a user\n\n"
1219 "Seat Commands:\n"
60f067b4 1220 " list-seats List seats\n"
e735f4d4
MP
1221 " seat-status [NAME...] Show seat status\n"
1222 " show-seat [NAME...] Show properties of seats or the manager\n"
60f067b4
JS
1223 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1224 " flush-devices Flush all device associations\n"
5eef597e
MP
1225 " terminate-seat NAME... Terminate all sessions on one or more seats\n"
1226 , program_invocation_short_name);
e735f4d4
MP
1227
1228 return 0;
663996b3
MS
1229}
1230
1231static int parse_argv(int argc, char *argv[]) {
1232
1233 enum {
1234 ARG_VERSION = 0x100,
1235 ARG_NO_PAGER,
60f067b4 1236 ARG_NO_LEGEND,
663996b3
MS
1237 ARG_KILL_WHO,
1238 ARG_NO_ASK_PASSWORD,
663996b3
MS
1239 };
1240
1241 static const struct option options[] = {
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' },
14228c0d 1246 { "full", no_argument, NULL, 'l' },
663996b3 1247 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
60f067b4 1248 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
663996b3
MS
1249 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1250 { "signal", required_argument, NULL, 's' },
1251 { "host", required_argument, NULL, 'H' },
60f067b4 1252 { "machine", required_argument, NULL, 'M' },
663996b3 1253 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
e735f4d4
MP
1254 { "lines", required_argument, NULL, 'n' },
1255 { "output", required_argument, NULL, 'o' },
60f067b4 1256 {}
663996b3
MS
1257 };
1258
60f067b4 1259 int c, r;
663996b3
MS
1260
1261 assert(argc >= 0);
1262 assert(argv);
1263
e735f4d4 1264 while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
663996b3
MS
1265
1266 switch (c) {
1267
1268 case 'h':
e735f4d4 1269 help(0, NULL, NULL);
5eef597e 1270 return 0;
663996b3
MS
1271
1272 case ARG_VERSION:
1273 puts(PACKAGE_STRING);
1274 puts(SYSTEMD_FEATURES);
1275 return 0;
1276
1277 case 'p': {
60f067b4
JS
1278 r = strv_extend(&arg_property, optarg);
1279 if (r < 0)
1280 return log_oom();
663996b3
MS
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
14228c0d
MB
1293 case 'l':
1294 arg_full = true;
1295 break;
1296
e735f4d4
MP
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
663996b3
MS
1312 case ARG_NO_PAGER:
1313 arg_no_pager = true;
1314 break;
1315
60f067b4
JS
1316 case ARG_NO_LEGEND:
1317 arg_legend = false;
1318 break;
1319
663996b3
MS
1320 case ARG_NO_ASK_PASSWORD:
1321 arg_ask_password = false;
1322 break;
1323
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
60f067b4
JS
1336 case 'H':
1337 arg_transport = BUS_TRANSPORT_REMOTE;
1338 arg_host = optarg;
663996b3
MS
1339 break;
1340
60f067b4 1341 case 'M':
e735f4d4 1342 arg_transport = BUS_TRANSPORT_MACHINE;
60f067b4 1343 arg_host = optarg;
663996b3
MS
1344 break;
1345
1346 case '?':
1347 return -EINVAL;
1348
1349 default:
60f067b4 1350 assert_not_reached("Unhandled option");
663996b3 1351 }
663996b3
MS
1352
1353 return 1;
1354}
1355
e735f4d4
MP
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 },
1361 { "session-status", VERB_ANY, VERB_ANY, 0, show_session },
1362 { "show-session", VERB_ANY, VERB_ANY, 0, show_session },
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 },
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 },
1371 { "user-status", VERB_ANY, VERB_ANY, 0, show_user },
1372 { "show-user", VERB_ANY, VERB_ANY, 0, show_user },
1373 { "enable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
1374 { "disable-linger", VERB_ANY, VERB_ANY, 0, enable_linger },
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 },
1378 { "seat-status", VERB_ANY, VERB_ANY, 0, show_seat },
1379 { "show-seat", VERB_ANY, VERB_ANY, 0, show_seat },
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 {}
663996b3
MS
1384 };
1385
e735f4d4 1386 return dispatch_verb(argc, argv, verbs, bus);
663996b3
MS
1387}
1388
60f067b4 1389int main(int argc, char *argv[]) {
fb183854 1390 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
60f067b4 1391 int r;
663996b3
MS
1392
1393 setlocale(LC_ALL, "");
1394 log_parse_environment();
1395 log_open();
1396
1397 r = parse_argv(argc, argv);
60f067b4 1398 if (r <= 0)
663996b3 1399 goto finish;
60f067b4
JS
1400
1401 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1402 if (r < 0) {
f47781d8 1403 log_error_errno(r, "Failed to create bus connection: %m");
663996b3
MS
1404 goto finish;
1405 }
1406
e3bff60a
MP
1407 sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1408
e735f4d4 1409 r = loginctl_main(argc, argv, bus);
663996b3
MS
1410
1411finish:
60f067b4 1412 pager_close();
e735f4d4 1413 polkit_agent_close();
663996b3
MS
1414
1415 strv_free(arg_property);
1416
60f067b4 1417 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
663996b3 1418}