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