chiark / gitweb /
f15e0315d9f212c8feb32d74d727b0662fb00c19
[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];
496         char since2[FORMAT_TIMESTAMP_MAX];
497         const char *s1, *s2;
498         SessionStatusInfo i = {};
499         int r;
500
501         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
502         if (r < 0)
503                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
504
505         if (*new_line)
506                 printf("\n");
507
508         *new_line = true;
509
510         printf("%s - ", strna(i.id));
511
512         if (i.name)
513                 printf("%s (%"PRIu32")\n", i.name, i.uid);
514         else
515                 printf("%"PRIu32"\n", i.uid);
516
517         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
518         s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
519
520         if (s1)
521                 printf("\t   Since: %s; %s\n", s2, s1);
522         else if (s2)
523                 printf("\t   Since: %s\n", s2);
524
525         if (i.leader > 0) {
526                 _cleanup_free_ char *t = NULL;
527
528                 printf("\t  Leader: %"PRIu32, i.leader);
529
530                 get_process_comm(i.leader, &t);
531                 if (t)
532                         printf(" (%s)", t);
533
534                 printf("\n");
535         }
536
537         if (!isempty(i.seat)) {
538                 printf("\t    Seat: %s", i.seat);
539
540                 if (i.vtnr > 0)
541                         printf("; vc%u", i.vtnr);
542
543                 printf("\n");
544         }
545
546         if (i.tty)
547                 printf("\t     TTY: %s\n", i.tty);
548         else if (i.display)
549                 printf("\t Display: %s\n", i.display);
550
551         if (i.remote_host && i.remote_user)
552                 printf("\t  Remote: %s@%s\n", i.remote_user, i.remote_host);
553         else if (i.remote_host)
554                 printf("\t  Remote: %s\n", i.remote_host);
555         else if (i.remote_user)
556                 printf("\t  Remote: user %s\n", i.remote_user);
557         else if (i.remote)
558                 printf("\t  Remote: Yes\n");
559
560         if (i.service) {
561                 printf("\t Service: %s", i.service);
562
563                 if (i.type)
564                         printf("; type %s", i.type);
565
566                 if (i.class)
567                         printf("; class %s", i.class);
568
569                 printf("\n");
570         } else if (i.type) {
571                 printf("\t    Type: %s", i.type);
572
573                 if (i.class)
574                         printf("; class %s", i.class);
575
576                 printf("\n");
577         } else if (i.class)
578                 printf("\t   Class: %s\n", i.class);
579
580         if (!isempty(i.desktop))
581                 printf("\t Desktop: %s\n", i.desktop);
582
583         if (i.state)
584                 printf("\t   State: %s\n", i.state);
585
586         if (i.scope) {
587                 printf("\t    Unit: %s\n", i.scope);
588 #if 0 /// UNNEEDED by elogind
589                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
590
591                 if (arg_transport == BUS_TRANSPORT_LOCAL) {
592
593                         show_journal_by_unit(
594                                         stdout,
595                                         i.scope,
596                                         arg_output,
597                                         0,
598                                         i.timestamp.monotonic,
599                                         arg_lines,
600                                         0,
601                                         get_output_flags() | OUTPUT_BEGIN_NEWLINE,
602                                         SD_JOURNAL_LOCAL_ONLY,
603                                         true,
604                                         NULL);
605                 }
606 #endif // 0
607         }
608
609         return 0;
610 }
611
612 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
613
614         static const struct bus_properties_map map[]  = {
615                 { "Name",               "s",     NULL,                     offsetof(UserStatusInfo, name)                },
616                 { "Linger",             "b",     NULL,                     offsetof(UserStatusInfo, linger)              },
617                 { "Slice",              "s",     NULL,                     offsetof(UserStatusInfo, slice)               },
618                 { "State",              "s",     NULL,                     offsetof(UserStatusInfo, state)               },
619                 { "UID",                "u",     NULL,                     offsetof(UserStatusInfo, uid)                 },
620                 { "Timestamp",          "t",     NULL,                     offsetof(UserStatusInfo, timestamp.realtime)  },
621                 { "TimestampMonotonic", "t",     NULL,                     offsetof(UserStatusInfo, timestamp.monotonic) },
622                 { "Display",            "(so)",  prop_map_first_of_struct, offsetof(UserStatusInfo, display)             },
623                 { "Sessions",           "a(so)", prop_map_sessions_strv,   offsetof(UserStatusInfo, sessions)            },
624                 {}
625         };
626
627         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
628         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
629         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX];
630         char since2[FORMAT_TIMESTAMP_MAX];
631         const char *s1, *s2;
632         _cleanup_(user_status_info_clear) UserStatusInfo i = {};
633         int r;
634
635         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
636         if (r < 0)
637                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
638
639         if (*new_line)
640                 printf("\n");
641
642         *new_line = true;
643
644         if (i.name)
645                 printf("%s (%"PRIu32")\n", i.name, i.uid);
646         else
647                 printf("%"PRIu32"\n", i.uid);
648
649         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp.realtime);
650         s2 = format_timestamp(since2, sizeof(since2), i.timestamp.realtime);
651
652         if (s1)
653                 printf("\t   Since: %s; %s\n", s2, s1);
654         else if (s2)
655                 printf("\t   Since: %s\n", s2);
656
657         if (!isempty(i.state))
658                 printf("\t   State: %s\n", i.state);
659
660         if (!strv_isempty(i.sessions)) {
661                 char **l;
662                 printf("\tSessions:");
663
664                 STRV_FOREACH(l, i.sessions)
665                         printf(" %s%s",
666                                streq_ptr(*l, i.display) ? "*" : "",
667                                *l);
668
669                 printf("\n");
670         }
671
672         printf("\t  Linger: %s\n", yes_no(i.linger));
673
674         if (i.slice) {
675                 printf("\t    Unit: %s\n", i.slice);
676 #if 0 /// UNNEEDED by elogind
677                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
678
679                 show_journal_by_unit(
680                                 stdout,
681                                 i.slice,
682                                 arg_output,
683                                 0,
684                                 i.timestamp.monotonic,
685                                 arg_lines,
686                                 0,
687                                 get_output_flags() | OUTPUT_BEGIN_NEWLINE,
688                                 SD_JOURNAL_LOCAL_ONLY,
689                                 true,
690                                 NULL);
691 #endif // 0
692         }
693
694         return 0;
695 }
696
697 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
698
699         static const struct bus_properties_map map[]  = {
700                 { "Id",            "s",     NULL, offsetof(SeatStatusInfo, id) },
701                 { "ActiveSession", "(so)",  prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
702                 { "Sessions",      "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
703                 {}
704         };
705
706         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
707         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
708         _cleanup_(seat_status_info_clear) SeatStatusInfo i = {};
709         int r;
710
711         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, 0, &error, &m, &i);
712         if (r < 0)
713                 return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
714
715         if (*new_line)
716                 printf("\n");
717
718         *new_line = true;
719
720         printf("%s\n", strna(i.id));
721
722         if (!strv_isempty(i.sessions)) {
723                 char **l;
724                 printf("\tSessions:");
725
726                 STRV_FOREACH(l, i.sessions) {
727                         if (streq_ptr(*l, i.active_session))
728                                 printf(" *%s", *l);
729                         else
730                                 printf(" %s", *l);
731                 }
732
733                 printf("\n");
734         }
735
736         if (arg_transport == BUS_TRANSPORT_LOCAL) {
737                 unsigned c;
738
739                 c = columns();
740                 if (c > 21)
741                         c -= 21;
742                 else
743                         c = 0;
744
745                 printf("\t Devices:\n");
746
747                 show_sysfs(i.id, "\t\t  ", c, get_output_flags());
748         }
749
750         return 0;
751 }
752
753 #define property(name, fmt, ...)                                        \
754         do {                                                            \
755                 if (value)                                              \
756                         printf(fmt "\n", __VA_ARGS__);                  \
757                 else                                                    \
758                         printf("%s=" fmt "\n", name, __VA_ARGS__);      \
759         } while (0)
760
761 static int print_property(const char *name, sd_bus_message *m, bool value, bool all) {
762         char type;
763         const char *contents;
764         int r;
765
766         assert(name);
767         assert(m);
768
769         r = sd_bus_message_peek_type(m, &type, &contents);
770         if (r < 0)
771                 return r;
772
773         switch (type) {
774
775         case SD_BUS_TYPE_STRUCT:
776
777                 if (contents[0] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) {
778                         const char *s;
779
780                         r = sd_bus_message_read(m, "(so)", &s, NULL);
781                         if (r < 0)
782                                 return bus_log_parse_error(r);
783
784                         if (all || !isempty(s))
785                                 property(name, "%s", s);
786
787                         return 1;
788
789                 } else if (contents[0] == SD_BUS_TYPE_UINT32 && streq(name, "User")) {
790                         uint32_t uid;
791
792                         r = sd_bus_message_read(m, "(uo)", &uid, NULL);
793                         if (r < 0)
794                                 return bus_log_parse_error(r);
795
796                         if (!uid_is_valid(uid)) {
797                                 log_error("Invalid user ID: " UID_FMT, uid);
798                                 return -EINVAL;
799                         }
800
801                         property(name, UID_FMT, uid);
802                         return 1;
803                 }
804                 break;
805
806         case SD_BUS_TYPE_ARRAY:
807
808                 if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) {
809                         const char *s;
810                         bool space = false;
811
812                         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)");
813                         if (r < 0)
814                                 return bus_log_parse_error(r);
815
816                         if (!value)
817                                 printf("%s=", name);
818
819                         while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) {
820                                 printf("%s%s", space ? " " : "", s);
821                                 space = true;
822                         }
823
824                         if (space || !value)
825                                 printf("\n");
826
827                         if (r < 0)
828                                 return bus_log_parse_error(r);
829
830                         r = sd_bus_message_exit_container(m);
831                         if (r < 0)
832                                 return bus_log_parse_error(r);
833
834                         return 1;
835                 }
836                 break;
837         }
838
839         return 0;
840 }
841
842 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
843         int r;
844
845         assert(bus);
846         assert(path);
847         assert(new_line);
848
849         if (*new_line)
850                 printf("\n");
851
852         *new_line = true;
853
854         r = bus_print_all_properties(bus, "org.freedesktop.login1", path, print_property, arg_property, arg_value, arg_all, NULL);
855         if (r < 0)
856                 return bus_log_parse_error(r);
857
858         return 0;
859 }
860
861 static int show_session(int argc, char *argv[], void *userdata) {
862         bool properties, new_line = false;
863         sd_bus *bus = userdata;
864         int r, i;
865         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
866         _cleanup_free_ char *path = NULL;
867
868         assert(bus);
869         assert(argv);
870
871         properties = !strstr(argv[0], "status");
872
873         (void) pager_open(arg_no_pager, false);
874
875         if (argc <= 1) {
876                 const char *session, *p = "/org/freedesktop/login1/session/self";
877
878                 if (properties)
879                         /* If no argument is specified inspect the manager itself */
880                         return show_properties(bus, "/org/freedesktop/login1", &new_line);
881
882                 /* And in the pretty case, show data of the calling session */
883                 session = getenv("XDG_SESSION_ID");
884                 if (session) {
885                         r = get_session_path(bus, session, &error, &path);
886                         if (r < 0) {
887                                 log_error("Failed to get session path: %s", bus_error_message(&error, r));
888                                 return r;
889                         }
890                         p = path;
891                 }
892
893                 return print_session_status_info(bus, p, &new_line);
894         }
895
896         for (i = 1; i < argc; i++) {
897                 r = get_session_path(bus, argv[i], &error, &path);
898                 if (r < 0) {
899                         log_error("Failed to get session path: %s", bus_error_message(&error, r));
900                         return r;
901                 }
902
903                 if (properties)
904                         r = show_properties(bus, path, &new_line);
905                 else
906                         r = print_session_status_info(bus, path, &new_line);
907
908                 if (r < 0)
909                         return r;
910         }
911
912         return 0;
913 }
914
915 static int show_user(int argc, char *argv[], void *userdata) {
916         bool properties, new_line = false;
917         sd_bus *bus = userdata;
918         int r, i;
919
920         assert(bus);
921         assert(argv);
922
923         properties = !strstr(argv[0], "status");
924
925         (void) pager_open(arg_no_pager, false);
926
927         if (argc <= 1) {
928                 /* If not argument is specified inspect the manager
929                  * itself */
930                 if (properties)
931                         return show_properties(bus, "/org/freedesktop/login1", &new_line);
932
933                 return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line);
934         }
935
936         for (i = 1; i < argc; i++) {
937                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
938                 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
939                 const char *path = NULL;
940                 uid_t uid;
941
942                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
943                 if (r < 0)
944                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
945
946                 r = sd_bus_call_method(
947                                 bus,
948                                 "org.freedesktop.login1",
949                                 "/org/freedesktop/login1",
950                                 "org.freedesktop.login1.Manager",
951                                 "GetUser",
952                                 &error, &reply,
953                                 "u", (uint32_t) uid);
954                 if (r < 0) {
955                         log_error("Failed to get user: %s", bus_error_message(&error, r));
956                         return r;
957                 }
958
959                 r = sd_bus_message_read(reply, "o", &path);
960                 if (r < 0)
961                         return bus_log_parse_error(r);
962
963                 if (properties)
964                         r = show_properties(bus, path, &new_line);
965                 else
966                         r = print_user_status_info(bus, path, &new_line);
967
968                 if (r < 0)
969                         return r;
970         }
971
972         return 0;
973 }
974
975 static int show_seat(int argc, char *argv[], void *userdata) {
976         bool properties, new_line = false;
977         sd_bus *bus = userdata;
978         int r, i;
979
980         assert(bus);
981         assert(argv);
982
983         properties = !strstr(argv[0], "status");
984
985         (void) pager_open(arg_no_pager, false);
986
987         if (argc <= 1) {
988                 /* If not argument is specified inspect the manager
989                  * itself */
990                 if (properties)
991                         return show_properties(bus, "/org/freedesktop/login1", &new_line);
992
993                 return print_seat_status_info(bus, "/org/freedesktop/login1/seat/self", &new_line);
994         }
995
996         for (i = 1; i < argc; i++) {
997                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
998                 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
999                 const char *path = NULL;
1000
1001                 r = sd_bus_call_method(
1002                                 bus,
1003                                 "org.freedesktop.login1",
1004                                 "/org/freedesktop/login1",
1005                                 "org.freedesktop.login1.Manager",
1006                                 "GetSeat",
1007                                 &error, &reply,
1008                                 "s", argv[i]);
1009                 if (r < 0) {
1010                         log_error("Failed to get seat: %s", bus_error_message(&error, r));
1011                         return r;
1012                 }
1013
1014                 r = sd_bus_message_read(reply, "o", &path);
1015                 if (r < 0)
1016                         return bus_log_parse_error(r);
1017
1018                 if (properties)
1019                         r = show_properties(bus, path, &new_line);
1020                 else
1021                         r = print_seat_status_info(bus, path, &new_line);
1022
1023                 if (r < 0)
1024                         return r;
1025         }
1026
1027         return 0;
1028 }
1029
1030 static int activate(int argc, char *argv[], void *userdata) {
1031         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1032         sd_bus *bus = userdata;
1033         char *short_argv[3];
1034         int r, i;
1035
1036         assert(bus);
1037         assert(argv);
1038
1039         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1040
1041         if (argc < 2) {
1042                 /* No argument? Let's either use $XDG_SESSION_ID (if specified), or an empty
1043                  * session name, in which case logind will try to guess our session. */
1044
1045                 short_argv[0] = argv[0];
1046                 short_argv[1] = getenv("XDG_SESSION_ID") ?: (char*) "";
1047                 short_argv[2] = NULL;
1048
1049                 argv = short_argv;
1050                 argc = 2;
1051         }
1052
1053         for (i = 1; i < argc; i++) {
1054
1055                 r = sd_bus_call_method(
1056                                 bus,
1057                                 "org.freedesktop.login1",
1058                                 "/org/freedesktop/login1",
1059                                 "org.freedesktop.login1.Manager",
1060                                 streq(argv[0], "lock-session")      ? "LockSession" :
1061                                 streq(argv[0], "unlock-session")    ? "UnlockSession" :
1062                                 streq(argv[0], "terminate-session") ? "TerminateSession" :
1063                                                                       "ActivateSession",
1064                                 &error, NULL,
1065                                 "s", argv[i]);
1066                 if (r < 0) {
1067                         log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1068                         return r;
1069                 }
1070         }
1071
1072         return 0;
1073 }
1074
1075 static int kill_session(int argc, char *argv[], void *userdata) {
1076         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1077         sd_bus *bus = userdata;
1078         int r, i;
1079
1080         assert(bus);
1081         assert(argv);
1082
1083         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1084
1085         if (!arg_kill_who)
1086                 arg_kill_who = "all";
1087
1088         for (i = 1; i < argc; i++) {
1089
1090                 r = sd_bus_call_method(
1091                         bus,
1092                         "org.freedesktop.login1",
1093                         "/org/freedesktop/login1",
1094                         "org.freedesktop.login1.Manager",
1095                         "KillSession",
1096                         &error, NULL,
1097                         "ssi", argv[i], arg_kill_who, arg_signal);
1098                 if (r < 0) {
1099                         log_error("Could not kill session: %s", bus_error_message(&error, -r));
1100                         return r;
1101                 }
1102         }
1103
1104         return 0;
1105 }
1106
1107 static int enable_linger(int argc, char *argv[], void *userdata) {
1108         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1109         sd_bus *bus = userdata;
1110         char* short_argv[3];
1111         bool b;
1112         int r, i;
1113
1114         assert(bus);
1115         assert(argv);
1116
1117         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1118
1119         b = streq(argv[0], "enable-linger");
1120
1121         if (argc < 2) {
1122                 /* No argument? Let's use an empty user name,
1123                  * then logind will use our user. */
1124
1125                 short_argv[0] = argv[0];
1126                 short_argv[1] = (char*) "";
1127                 short_argv[2] = NULL;
1128                 argv = short_argv;
1129                 argc = 2;
1130         }
1131
1132         for (i = 1; i < argc; i++) {
1133                 uid_t uid;
1134
1135                 if (isempty(argv[i]))
1136                         uid = UID_INVALID;
1137                 else {
1138                         r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
1139                         if (r < 0)
1140                                 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1141                 }
1142
1143                 r = sd_bus_call_method(
1144                         bus,
1145                         "org.freedesktop.login1",
1146                         "/org/freedesktop/login1",
1147                         "org.freedesktop.login1.Manager",
1148                         "SetUserLinger",
1149                         &error, NULL,
1150                         "ubb", (uint32_t) uid, b, true);
1151                 if (r < 0) {
1152                         log_error("Could not enable linger: %s", bus_error_message(&error, -r));
1153                         return r;
1154                 }
1155         }
1156
1157         return 0;
1158 }
1159
1160 static int terminate_user(int argc, char *argv[], void *userdata) {
1161         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1162         sd_bus *bus = userdata;
1163         int r, i;
1164
1165         assert(bus);
1166         assert(argv);
1167
1168         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1169
1170         for (i = 1; i < argc; i++) {
1171                 uid_t uid;
1172
1173                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
1174                 if (r < 0)
1175                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1176
1177                 r = sd_bus_call_method(
1178                         bus,
1179                         "org.freedesktop.login1",
1180                         "/org/freedesktop/login1",
1181                         "org.freedesktop.login1.Manager",
1182                         "TerminateUser",
1183                         &error, NULL,
1184                         "u", (uint32_t) uid);
1185                 if (r < 0) {
1186                         log_error("Could not terminate user: %s", bus_error_message(&error, -r));
1187                         return r;
1188                 }
1189         }
1190
1191         return 0;
1192 }
1193
1194 static int kill_user(int argc, char *argv[], void *userdata) {
1195         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1196         sd_bus *bus = userdata;
1197         int r, i;
1198
1199         assert(bus);
1200         assert(argv);
1201
1202         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1203
1204         if (!arg_kill_who)
1205                 arg_kill_who = "all";
1206
1207         for (i = 1; i < argc; i++) {
1208                 uid_t uid;
1209
1210                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
1211                 if (r < 0)
1212                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
1213
1214                 r = sd_bus_call_method(
1215                         bus,
1216                         "org.freedesktop.login1",
1217                         "/org/freedesktop/login1",
1218                         "org.freedesktop.login1.Manager",
1219                         "KillUser",
1220                         &error, NULL,
1221                         "ui", (uint32_t) uid, arg_signal);
1222                 if (r < 0) {
1223                         log_error("Could not kill user: %s", bus_error_message(&error, -r));
1224                         return r;
1225                 }
1226         }
1227
1228         return 0;
1229 }
1230
1231 static int attach(int argc, char *argv[], void *userdata) {
1232         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1233         sd_bus *bus = userdata;
1234         int r, i;
1235
1236         assert(bus);
1237         assert(argv);
1238
1239         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1240
1241         for (i = 2; i < argc; i++) {
1242
1243                 r = sd_bus_call_method(
1244                         bus,
1245                         "org.freedesktop.login1",
1246                         "/org/freedesktop/login1",
1247                         "org.freedesktop.login1.Manager",
1248                         "AttachDevice",
1249                         &error, NULL,
1250                         "ssb", argv[1], argv[i], true);
1251
1252                 if (r < 0) {
1253                         log_error("Could not attach device: %s", bus_error_message(&error, -r));
1254                         return r;
1255                 }
1256         }
1257
1258         return 0;
1259 }
1260
1261 static int flush_devices(int argc, char *argv[], void *userdata) {
1262         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1263         sd_bus *bus = userdata;
1264         int r;
1265
1266         assert(bus);
1267         assert(argv);
1268
1269         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1270
1271         r = sd_bus_call_method(
1272                         bus,
1273                         "org.freedesktop.login1",
1274                         "/org/freedesktop/login1",
1275                         "org.freedesktop.login1.Manager",
1276                         "FlushDevices",
1277                         &error, NULL,
1278                         "b", true);
1279         if (r < 0)
1280                 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
1281
1282         return r;
1283 }
1284
1285 static int lock_sessions(int argc, char *argv[], void *userdata) {
1286         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1287         sd_bus *bus = userdata;
1288         int r;
1289
1290         assert(bus);
1291         assert(argv);
1292
1293         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1294
1295         r = sd_bus_call_method(
1296                         bus,
1297                         "org.freedesktop.login1",
1298                         "/org/freedesktop/login1",
1299                         "org.freedesktop.login1.Manager",
1300                         streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1301                         &error, NULL,
1302                         NULL);
1303         if (r < 0)
1304                 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1305
1306         return r;
1307 }
1308
1309 static int terminate_seat(int argc, char *argv[], void *userdata) {
1310         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1311         sd_bus *bus = userdata;
1312         int r, i;
1313
1314         assert(bus);
1315         assert(argv);
1316
1317         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
1318
1319         for (i = 1; i < argc; i++) {
1320
1321                 r = sd_bus_call_method(
1322                         bus,
1323                         "org.freedesktop.login1",
1324                         "/org/freedesktop/login1",
1325                         "org.freedesktop.login1.Manager",
1326                         "TerminateSeat",
1327                         &error, NULL,
1328                         "s", argv[i]);
1329                 if (r < 0) {
1330                         log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1331                         return r;
1332                 }
1333         }
1334
1335         return 0;
1336 }
1337
1338 static int help(int argc, char *argv[], void *userdata) {
1339
1340         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1341                "Send control commands to or query the login manager.\n\n"
1342                "  -h --help                Show this help\n"
1343                "     --version             Show package version\n"
1344                "     --no-pager            Do not pipe output into a pager\n"
1345 #if 1 /// elogind supports --no-wall and --dry-run
1346                "     --no-wall             Do not print any wall message\n"
1347                "     --dry-run             Only print what would be done\n"
1348                "  -q --quiet               Suppress output\n"
1349 #endif // 1
1350                "     --no-legend           Do not show the headers and footers\n"
1351                "     --no-ask-password     Don't prompt for password\n"
1352                "  -H --host=[USER@]HOST    Operate on remote host\n"
1353                "  -M --machine=CONTAINER   Operate on local container\n"
1354                "  -p --property=NAME       Show only properties by this name\n"
1355                "  -a --all                 Show all properties, including empty ones\n"
1356                "     --value               When showing properties, only print the value\n"
1357                "  -l --full                Do not ellipsize output\n"
1358                "     --kill-who=WHO        Who to send signal to\n"
1359                "  -s --signal=SIGNAL       Which signal to send\n"
1360 #if 0 /// UNNEEDED by elogind
1361                "  -n --lines=INTEGER       Number of journal entries to show\n"
1362                "  -o --output=STRING       Change journal output mode (short, short-precise,\n"
1363                "                             short-iso, short-iso-precise, short-full,\n"
1364                "                             short-monotonic, short-unix, verbose, export,\n"
1365                "                             json, json-pretty, json-sse, cat)\n"
1366 #else
1367                 /// elogind can cancel shutdowns and allows to ignore inhibitors
1368                "  -c                       Cancel a pending shutdown or reboot\n"
1369                "  -i --ignore-inhibitors   When shutting down or sleeping, ignore inhibitors\n\n"
1370 #endif // 0
1371                "Session Commands:\n"
1372 #if 0 /// elogind has "list" as a shorthand for "list-sessions"
1373                "  list-sessions            List sessions\n"
1374 #else
1375                "  list[-sessions]          List sessions (default command)\n"
1376 #endif // 0
1377                "  session-status [ID...]   Show session status\n"
1378                "  show-session [ID...]     Show properties of sessions or the manager\n"
1379                "  activate [ID]            Activate a session\n"
1380                "  lock-session [ID...]     Screen lock one or more sessions\n"
1381                "  unlock-session [ID...]   Screen unlock one or more sessions\n"
1382                "  lock-sessions            Screen lock all current sessions\n"
1383                "  unlock-sessions          Screen unlock all current sessions\n"
1384                "  terminate-session ID...  Terminate one or more sessions\n"
1385                "  kill-session ID...       Send signal to processes of a session\n\n"
1386                "User Commands:\n"
1387                "  list-users               List users\n"
1388                "  user-status [USER...]    Show user status\n"
1389                "  show-user [USER...]      Show properties of users or the manager\n"
1390                "  enable-linger [USER...]  Enable linger state of one or more users\n"
1391                "  disable-linger [USER...] Disable linger state of one or more users\n"
1392                "  terminate-user USER...   Terminate all sessions of one or more users\n"
1393                "  kill-user USER...        Send signal to processes of a user\n\n"
1394                "Seat Commands:\n"
1395                "  list-seats               List seats\n"
1396                "  seat-status [NAME...]    Show seat status\n"
1397                "  show-seat [NAME...]      Show properties of seats or the manager\n"
1398                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
1399                "  flush-devices            Flush all device associations\n"
1400 #if 0 /// elogind adds some system commands to loginctl
1401                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n"
1402 #else
1403                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n\n"
1404                "System Commands:\n"
1405                "  poweroff [TIME] [WALL...] Turn off the machine\n"
1406                "  reboot   [TIME] [WALL...] Reboot the machine\n"
1407                "  suspend                   Suspend the machine to memory\n"
1408                "  hibernate                 Suspend the machine to disk\n"
1409                "  hybrid-sleep              Suspend the machine to memory and disk\n"
1410 #endif // 0
1411                , program_invocation_short_name);
1412
1413         return 0;
1414 }
1415
1416 static int parse_argv(int argc, char *argv[]) {
1417
1418         enum {
1419                 ARG_VERSION = 0x100,
1420                 ARG_VALUE,
1421                 ARG_NO_PAGER,
1422 #if 1 /// elogind supports --no-wall and --dry-run
1423                 ARG_NO_WALL,
1424                 ARG_DRY_RUN,
1425 #endif // 1
1426                 ARG_NO_LEGEND,
1427                 ARG_KILL_WHO,
1428                 ARG_NO_ASK_PASSWORD,
1429         };
1430
1431         static const struct option options[] = {
1432                 { "help",            no_argument,       NULL, 'h'                 },
1433                 { "version",         no_argument,       NULL, ARG_VERSION         },
1434                 { "property",        required_argument, NULL, 'p'                 },
1435                 { "all",             no_argument,       NULL, 'a'                 },
1436                 { "value",           no_argument,       NULL, ARG_VALUE           },
1437                 { "full",            no_argument,       NULL, 'l'                 },
1438                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1439 #if 1 /// elogind supports --no-wall, --dry-run and --quiet
1440                 { "no-wall",         no_argument,       NULL, ARG_NO_WALL         },
1441                 { "dry-run",         no_argument,       NULL, ARG_DRY_RUN         },
1442                 { "quiet",           no_argument,       NULL, 'q'                 },
1443 #endif // 1
1444                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1445                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1446                 { "signal",          required_argument, NULL, 's'                 },
1447                 { "host",            required_argument, NULL, 'H'                 },
1448                 { "machine",         required_argument, NULL, 'M'                 },
1449                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
1450 #if 0 /// UNNEEDED by elogind
1451                 { "lines",           required_argument, NULL, 'n'                 },
1452                 { "output",          required_argument, NULL, 'o'                 },
1453 #else
1454                 /// elogind allows to ignore inhibitors for system commands.
1455                 { "ignore-inhibitors", no_argument,     NULL, 'i'                 },
1456 #endif // 0
1457                 {}
1458         };
1459
1460         int c, r;
1461
1462         assert(argc >= 0);
1463         assert(argv);
1464
1465 #if 0 /// elogind adds some system commands to loginctl
1466         while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
1467 #else
1468         while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:ci", options, NULL)) >= 0)
1469 #endif // 0
1470
1471                 switch (c) {
1472
1473                 case 'h':
1474                         help(0, NULL, NULL);
1475                         return 0;
1476
1477                 case ARG_VERSION:
1478                         return version();
1479
1480                 case 'p': {
1481                         r = strv_extend(&arg_property, optarg);
1482                         if (r < 0)
1483                                 return log_oom();
1484
1485                         /* If the user asked for a particular
1486                          * property, show it to him, even if it is
1487                          * empty. */
1488                         arg_all = true;
1489                         break;
1490                 }
1491
1492                 case 'a':
1493                         arg_all = true;
1494                         break;
1495
1496                 case ARG_VALUE:
1497                         arg_value = true;
1498                         break;
1499
1500                 case 'l':
1501                         arg_full = true;
1502                         break;
1503
1504 #if 0 /// UNNEEDED by elogind
1505                 case 'n':
1506                         if (safe_atou(optarg, &arg_lines) < 0) {
1507                                 log_error("Failed to parse lines '%s'", optarg);
1508                                 return -EINVAL;
1509                         }
1510                         break;
1511
1512                 case 'o':
1513                         if (streq(optarg, "help")) {
1514                                 DUMP_STRING_TABLE(output_mode, OutputMode, _OUTPUT_MODE_MAX);
1515                                 return 0;
1516                         }
1517
1518                         arg_output = output_mode_from_string(optarg);
1519                         if (arg_output < 0) {
1520                                 log_error("Unknown output '%s'.", optarg);
1521                                 return -EINVAL;
1522                         }
1523                         break;
1524 #endif // 0
1525
1526                 case ARG_NO_PAGER:
1527                         arg_no_pager = true;
1528                         break;
1529 #if 1 /// elogind supports --no-wall, -dry-run and --quiet
1530                 case ARG_NO_WALL:
1531                         arg_no_wall = true;
1532                         break;
1533
1534                 case ARG_DRY_RUN:
1535                         arg_dry_run = true;
1536                         break;
1537
1538                 case 'q':
1539                         arg_quiet = true;
1540                         break;
1541
1542 #endif // 1
1543
1544                 case ARG_NO_LEGEND:
1545                         arg_legend = false;
1546                         break;
1547
1548                 case ARG_NO_ASK_PASSWORD:
1549                         arg_ask_password = false;
1550                         break;
1551
1552                 case ARG_KILL_WHO:
1553                         arg_kill_who = optarg;
1554                         break;
1555
1556                 case 's':
1557                         if (streq(optarg, "help")) {
1558                                 DUMP_STRING_TABLE(signal, int, _NSIG);
1559                                 return 0;
1560                         }
1561
1562                         arg_signal = signal_from_string(optarg);
1563                         if (arg_signal < 0) {
1564                                 log_error("Failed to parse signal string %s.", optarg);
1565                                 return -EINVAL;
1566                         }
1567                         break;
1568
1569                 case 'H':
1570                         arg_transport = BUS_TRANSPORT_REMOTE;
1571                         arg_host = optarg;
1572                         break;
1573
1574                 case 'M':
1575                         arg_transport = BUS_TRANSPORT_MACHINE;
1576                         arg_host = optarg;
1577                         break;
1578 #if 1 /// elogind can cancel shutdowns and allows to ignore inhibitors
1579                 case 'c':
1580                         arg_action = ACTION_CANCEL_SHUTDOWN;
1581                         break;
1582
1583                 case 'i':
1584                         arg_ignore_inhibitors = true;
1585                         break;
1586 #endif // 1
1587                 case '?':
1588                         return -EINVAL;
1589
1590                 default:
1591                         assert_not_reached("Unhandled option");
1592                 }
1593
1594         return 1;
1595 }
1596
1597 static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
1598
1599         static const Verb verbs[] = {
1600                 { "help",              VERB_ANY, VERB_ANY, 0,            help              },
1601 #if 0 /// elogind has "list" as a shorthand for "list-sessions"
1602                 { "list-sessions",     VERB_ANY, 1,        VERB_DEFAULT, list_sessions     },
1603 #else
1604                 { "list",              VERB_ANY, 1,        VERB_DEFAULT, list_sessions     },
1605                 { "list-sessions",     VERB_ANY, 1,        0,            list_sessions     },
1606 #endif // 0
1607                 { "session-status",    VERB_ANY, VERB_ANY, 0,            show_session      },
1608                 { "show-session",      VERB_ANY, VERB_ANY, 0,            show_session      },
1609                 { "activate",          VERB_ANY, 2,        0,            activate          },
1610                 { "lock-session",      VERB_ANY, VERB_ANY, 0,            activate          },
1611                 { "unlock-session",    VERB_ANY, VERB_ANY, 0,            activate          },
1612                 { "lock-sessions",     VERB_ANY, 1,        0,            lock_sessions     },
1613                 { "unlock-sessions",   VERB_ANY, 1,        0,            lock_sessions     },
1614                 { "terminate-session", 2,        VERB_ANY, 0,            activate          },
1615                 { "kill-session",      2,        VERB_ANY, 0,            kill_session      },
1616                 { "list-users",        VERB_ANY, 1,        0,            list_users        },
1617                 { "user-status",       VERB_ANY, VERB_ANY, 0,            show_user         },
1618                 { "show-user",         VERB_ANY, VERB_ANY, 0,            show_user         },
1619                 { "enable-linger",     VERB_ANY, VERB_ANY, 0,            enable_linger     },
1620                 { "disable-linger",    VERB_ANY, VERB_ANY, 0,            enable_linger     },
1621                 { "terminate-user",    2,        VERB_ANY, 0,            terminate_user    },
1622                 { "kill-user",         2,        VERB_ANY, 0,            kill_user         },
1623                 { "list-seats",        VERB_ANY, 1,        0,            list_seats        },
1624                 { "seat-status",       VERB_ANY, VERB_ANY, 0,            show_seat         },
1625                 { "show-seat",         VERB_ANY, VERB_ANY, 0,            show_seat         },
1626                 { "attach",            3,        VERB_ANY, 0,            attach            },
1627                 { "flush-devices",     VERB_ANY, 1,        0,            flush_devices     },
1628                 { "terminate-seat",    2,        VERB_ANY, 0,            terminate_seat    },
1629 #if 1 /// elogind adds some system commands to loginctl
1630                 { "poweroff",          VERB_ANY, VERB_ANY, 0,            start_special     },
1631                 { "reboot",            VERB_ANY, VERB_ANY, 0,            start_special     },
1632                 { "suspend",           VERB_ANY, 1,        0,            start_special     },
1633                 { "hibernate",         VERB_ANY, 1,        0,            start_special     },
1634                 { "hybrid-sleep",      VERB_ANY, 1,        0,            start_special     },
1635                 { "cancel-shutdown",   VERB_ANY, 1,        0,            start_special     },
1636 #endif // 1
1637                 {}
1638         };
1639
1640 #if 1 /// elogind can do shutdown and allows its cancellation
1641         if ((argc == optind) && (ACTION_CANCEL_SHUTDOWN == arg_action))
1642                 return elogind_cancel_shutdown(bus);
1643 #endif // 1
1644         return dispatch_verb(argc, argv, verbs, bus);
1645 }
1646
1647 int main(int argc, char *argv[]) {
1648         sd_bus *bus = NULL;
1649         int r;
1650
1651         setlocale(LC_ALL, "");
1652         elogind_set_program_name(argv[0]);
1653         log_parse_environment();
1654         log_open();
1655         sigbus_install();
1656
1657         r = parse_argv(argc, argv);
1658         if (r <= 0)
1659                 goto finish;
1660
1661         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
1662         if (r < 0) {
1663                 log_error_errno(r, "Failed to create bus connection: %m");
1664                 goto finish;
1665         }
1666
1667         sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1668
1669         r = loginctl_main(argc, argv, bus);
1670
1671 finish:
1672         /* make sure we terminate the bus connection first, and then close the
1673          * pager, see issue #3543 for the details. */
1674         sd_bus_flush_close_unref(bus);
1675         pager_close();
1676 #if 0 /// elogind does that in elogind_cleanup()
1677         polkit_agent_close();
1678 #endif // 0
1679
1680         strv_free(arg_property);
1681
1682 #if 1 /// elogind has some own cleanups to do
1683         elogind_cleanup();
1684 #endif // 1
1685         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1686 }