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