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