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