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