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