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