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