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