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