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