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