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