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