chiark / gitweb /
util: improve signal_to_string, signal_from_string
[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                         r = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1067                         if (r < 0) {
1068                                 log_error("User %s unknown.", args[i]);
1069                                 r = -ENOENT;
1070                                 goto finish;
1071                         }
1072
1073                         m = dbus_message_new_method_call(
1074                                         "org.freedesktop.login1",
1075                                         "/org/freedesktop/login1",
1076                                         "org.freedesktop.login1.Manager",
1077                                         "GetUser");
1078                         if (!m) {
1079                                 log_error("Could not allocate message.");
1080                                 ret = -ENOMEM;
1081                                 goto finish;
1082                         }
1083
1084                         u = (uint32_t) uid;
1085                         if (!dbus_message_append_args(m,
1086                                                       DBUS_TYPE_UINT32, &u,
1087                                                       DBUS_TYPE_INVALID)) {
1088                                 log_error("Could not append arguments to message.");
1089                                 ret = -ENOMEM;
1090                                 goto finish;
1091                         }
1092                 } else {
1093
1094                         m = dbus_message_new_method_call(
1095                                         "org.freedesktop.login1",
1096                                         "/org/freedesktop/login1",
1097                                         "org.freedesktop.login1.Manager",
1098                                         "GetSeat");
1099                         if (!m) {
1100                                 log_error("Could not allocate message.");
1101                                 ret = -ENOMEM;
1102                                 goto finish;
1103                         }
1104
1105                         if (!dbus_message_append_args(m,
1106                                                       DBUS_TYPE_STRING, &args[i],
1107                                                       DBUS_TYPE_INVALID)) {
1108                                 log_error("Could not append arguments to message.");
1109                                 ret = -ENOMEM;
1110                                 goto finish;
1111                         }
1112                 }
1113
1114                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1115                 if (!reply) {
1116                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1117                         ret = -EIO;
1118                         goto finish;
1119                 }
1120
1121                 if (!dbus_message_get_args(reply, &error,
1122                                            DBUS_TYPE_OBJECT_PATH, &path,
1123                                            DBUS_TYPE_INVALID)) {
1124                         log_error("Failed to parse reply: %s", bus_error_message(&error));
1125                         ret = -EIO;
1126                         goto finish;
1127                 }
1128
1129                 r = show_one(args[0], bus, path, show_properties, &new_line);
1130                 if (r != 0)
1131                         ret = r;
1132
1133                 dbus_message_unref(m);
1134                 dbus_message_unref(reply);
1135                 m = reply = NULL;
1136         }
1137
1138 finish:
1139         if (m)
1140                 dbus_message_unref(m);
1141
1142         if (reply)
1143                 dbus_message_unref(reply);
1144
1145         dbus_error_free(&error);
1146
1147         return ret;
1148 }
1149
1150 static int activate(DBusConnection *bus, char **args, unsigned n) {
1151         DBusMessage *m = NULL;
1152         int ret = 0;
1153         DBusError error;
1154         unsigned i;
1155
1156         assert(bus);
1157         assert(args);
1158
1159         dbus_error_init(&error);
1160
1161         for (i = 1; i < n; i++) {
1162                 DBusMessage *reply;
1163
1164                 m = dbus_message_new_method_call(
1165                                 "org.freedesktop.login1",
1166                                 "/org/freedesktop/login1",
1167                                 "org.freedesktop.login1.Manager",
1168                                 streq(args[0], "lock-session")      ? "LockSession" :
1169                                 streq(args[0], "unlock-session")    ? "UnlockSession" :
1170                                 streq(args[0], "terminate-session") ? "TerminateSession" :
1171                                                                       "ActivateSession");
1172                 if (!m) {
1173                         log_error("Could not allocate message.");
1174                         ret = -ENOMEM;
1175                         goto finish;
1176                 }
1177
1178                 if (!dbus_message_append_args(m,
1179                                               DBUS_TYPE_STRING, &args[i],
1180                                               DBUS_TYPE_INVALID)) {
1181                         log_error("Could not append arguments to message.");
1182                         ret = -ENOMEM;
1183                         goto finish;
1184                 }
1185
1186                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1187                 if (!reply) {
1188                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1189                         ret = -EIO;
1190                         goto finish;
1191                 }
1192
1193                 dbus_message_unref(m);
1194                 dbus_message_unref(reply);
1195                 m = reply = NULL;
1196         }
1197
1198 finish:
1199         if (m)
1200                 dbus_message_unref(m);
1201
1202         dbus_error_free(&error);
1203
1204         return ret;
1205 }
1206
1207 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1208         DBusMessage *m = NULL;
1209         int ret = 0;
1210         DBusError error;
1211         unsigned i;
1212
1213         assert(bus);
1214         assert(args);
1215
1216         dbus_error_init(&error);
1217
1218         if (!arg_kill_who)
1219                 arg_kill_who = "all";
1220
1221         for (i = 1; i < n; i++) {
1222                 DBusMessage *reply;
1223
1224                 m = dbus_message_new_method_call(
1225                                 "org.freedesktop.login1",
1226                                 "/org/freedesktop/login1",
1227                                 "org.freedesktop.login1.Manager",
1228                                 "KillSession");
1229                 if (!m) {
1230                         log_error("Could not allocate message.");
1231                         ret = -ENOMEM;
1232                         goto finish;
1233                 }
1234
1235                 if (!dbus_message_append_args(m,
1236                                               DBUS_TYPE_STRING, &args[i],
1237                                               DBUS_TYPE_STRING, &arg_kill_who,
1238                                               DBUS_TYPE_INT32, arg_signal,
1239                                               DBUS_TYPE_INVALID)) {
1240                         log_error("Could not append arguments to message.");
1241                         ret = -ENOMEM;
1242                         goto finish;
1243                 }
1244
1245                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1246                 if (!reply) {
1247                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1248                         ret = -EIO;
1249                         goto finish;
1250                 }
1251
1252                 dbus_message_unref(m);
1253                 dbus_message_unref(reply);
1254                 m = reply = NULL;
1255         }
1256
1257 finish:
1258         if (m)
1259                 dbus_message_unref(m);
1260
1261         dbus_error_free(&error);
1262
1263         return ret;
1264 }
1265
1266 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1267         DBusMessage *m = NULL;
1268         int ret = 0;
1269         DBusError error;
1270         unsigned i;
1271         dbus_bool_t b, interactive = true;
1272
1273         assert(bus);
1274         assert(args);
1275
1276         dbus_error_init(&error);
1277
1278         b = streq(args[0], "enable-linger");
1279
1280         for (i = 1; i < n; i++) {
1281                 DBusMessage *reply;
1282                 uint32_t u;
1283                 uid_t uid;
1284
1285                 m = dbus_message_new_method_call(
1286                                 "org.freedesktop.login1",
1287                                 "/org/freedesktop/login1",
1288                                 "org.freedesktop.login1.Manager",
1289                                 "SetUserLinger");
1290                 if (!m) {
1291                         log_error("Could not allocate message.");
1292                         ret = -ENOMEM;
1293                         goto finish;
1294                 }
1295
1296                 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1297                 if (ret < 0) {
1298                         log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1299                         goto finish;
1300                 }
1301
1302                 u = (uint32_t) uid;
1303                 if (!dbus_message_append_args(m,
1304                                               DBUS_TYPE_UINT32, &u,
1305                                               DBUS_TYPE_BOOLEAN, &b,
1306                                               DBUS_TYPE_BOOLEAN, &interactive,
1307                                               DBUS_TYPE_INVALID)) {
1308                         log_error("Could not append arguments to message.");
1309                         ret = -ENOMEM;
1310                         goto finish;
1311                 }
1312
1313                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1314                 if (!reply) {
1315                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1316                         ret = -EIO;
1317                         goto finish;
1318                 }
1319
1320                 dbus_message_unref(m);
1321                 dbus_message_unref(reply);
1322                 m = reply = NULL;
1323         }
1324
1325         ret = 0;
1326
1327 finish:
1328         if (m)
1329                 dbus_message_unref(m);
1330
1331         dbus_error_free(&error);
1332
1333         return ret;
1334 }
1335
1336 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1337         DBusMessage *m = NULL;
1338         int ret = 0;
1339         DBusError error;
1340         unsigned i;
1341
1342         assert(bus);
1343         assert(args);
1344
1345         dbus_error_init(&error);
1346
1347         for (i = 1; i < n; i++) {
1348                 uint32_t u;
1349                 uid_t uid;
1350                 DBusMessage *reply;
1351
1352                 m = dbus_message_new_method_call(
1353                                 "org.freedesktop.login1",
1354                                 "/org/freedesktop/login1",
1355                                 "org.freedesktop.login1.Manager",
1356                                 "TerminateUser");
1357                 if (!m) {
1358                         log_error("Could not allocate message.");
1359                         ret = -ENOMEM;
1360                         goto finish;
1361                 }
1362
1363                 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1364                 if (ret < 0) {
1365                         log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1366                         goto finish;
1367                 }
1368
1369                 u = (uint32_t) uid;
1370                 if (!dbus_message_append_args(m,
1371                                               DBUS_TYPE_UINT32, &u,
1372                                               DBUS_TYPE_INVALID)) {
1373                         log_error("Could not append arguments to message.");
1374                         ret = -ENOMEM;
1375                         goto finish;
1376                 }
1377
1378                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1379                 if (!reply) {
1380                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1381                         ret = -EIO;
1382                         goto finish;
1383                 }
1384
1385                 dbus_message_unref(m);
1386                 dbus_message_unref(reply);
1387                 m = reply = NULL;
1388         }
1389
1390         ret = 0;
1391
1392 finish:
1393         if (m)
1394                 dbus_message_unref(m);
1395
1396         dbus_error_free(&error);
1397
1398         return ret;
1399 }
1400
1401 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1402         DBusMessage *m = NULL;
1403         int ret = 0;
1404         DBusError error;
1405         unsigned i;
1406
1407         assert(bus);
1408         assert(args);
1409
1410         dbus_error_init(&error);
1411
1412         if (!arg_kill_who)
1413                 arg_kill_who = "all";
1414
1415         for (i = 1; i < n; i++) {
1416                 DBusMessage *reply;
1417                 uid_t uid;
1418                 uint32_t u;
1419
1420                 m = dbus_message_new_method_call(
1421                                 "org.freedesktop.login1",
1422                                 "/org/freedesktop/login1",
1423                                 "org.freedesktop.login1.Manager",
1424                                 "KillUser");
1425                 if (!m) {
1426                         log_error("Could not allocate message.");
1427                         ret = -ENOMEM;
1428                         goto finish;
1429                 }
1430
1431                 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1432                 if (ret < 0) {
1433                         log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1434                         goto finish;
1435                 }
1436
1437                 u = (uint32_t) uid;
1438                 if (!dbus_message_append_args(m,
1439                                               DBUS_TYPE_UINT32, &u,
1440                                               DBUS_TYPE_INT32, arg_signal,
1441                                               DBUS_TYPE_INVALID)) {
1442                         log_error("Could not append arguments to message.");
1443                         ret = -ENOMEM;
1444                         goto finish;
1445                 }
1446
1447                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1448                 if (!reply) {
1449                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1450                         ret = -EIO;
1451                         goto finish;
1452                 }
1453
1454                 dbus_message_unref(m);
1455                 dbus_message_unref(reply);
1456                 m = reply = NULL;
1457         }
1458
1459         ret = 0;
1460
1461 finish:
1462         if (m)
1463                 dbus_message_unref(m);
1464
1465         dbus_error_free(&error);
1466
1467         return ret;
1468 }
1469
1470 static int attach(DBusConnection *bus, char **args, unsigned n) {
1471         DBusMessage *m = NULL;
1472         int ret = 0;
1473         DBusError error;
1474         unsigned i;
1475         dbus_bool_t interactive = true;
1476
1477         assert(bus);
1478         assert(args);
1479
1480         dbus_error_init(&error);
1481
1482         for (i = 2; i < n; i++) {
1483                 DBusMessage *reply;
1484
1485                 m = dbus_message_new_method_call(
1486                                 "org.freedesktop.login1",
1487                                 "/org/freedesktop/login1",
1488                                 "org.freedesktop.login1.Manager",
1489                                 "AttachDevice");
1490                 if (!m) {
1491                         log_error("Could not allocate message.");
1492                         ret = -ENOMEM;
1493                         goto finish;
1494                 }
1495
1496                 if (!dbus_message_append_args(m,
1497                                               DBUS_TYPE_STRING, &args[1],
1498                                               DBUS_TYPE_STRING, &args[i],
1499                                               DBUS_TYPE_BOOLEAN, &interactive,
1500                                               DBUS_TYPE_INVALID)) {
1501                         log_error("Could not append arguments to message.");
1502                         ret = -ENOMEM;
1503                         goto finish;
1504                 }
1505
1506                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1507                 if (!reply) {
1508                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1509                         ret = -EIO;
1510                         goto finish;
1511                 }
1512
1513                 dbus_message_unref(m);
1514                 dbus_message_unref(reply);
1515                 m = reply = NULL;
1516         }
1517
1518 finish:
1519         if (m)
1520                 dbus_message_unref(m);
1521
1522         dbus_error_free(&error);
1523
1524         return ret;
1525 }
1526
1527 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1528         DBusMessage *m = NULL, *reply = NULL;
1529         int ret = 0;
1530         DBusError error;
1531         dbus_bool_t interactive = true;
1532
1533         assert(bus);
1534         assert(args);
1535
1536         dbus_error_init(&error);
1537
1538         m = dbus_message_new_method_call(
1539                         "org.freedesktop.login1",
1540                         "/org/freedesktop/login1",
1541                         "org.freedesktop.login1.Manager",
1542                         "FlushDevices");
1543         if (!m) {
1544                 log_error("Could not allocate message.");
1545                 ret = -ENOMEM;
1546                 goto finish;
1547         }
1548
1549         if (!dbus_message_append_args(m,
1550                                       DBUS_TYPE_BOOLEAN, &interactive,
1551                                       DBUS_TYPE_INVALID)) {
1552                 log_error("Could not append arguments to message.");
1553                 ret = -ENOMEM;
1554                 goto finish;
1555         }
1556
1557         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1558         if (!reply) {
1559                 log_error("Failed to issue method call: %s", bus_error_message(&error));
1560                 ret = -EIO;
1561                 goto finish;
1562         }
1563
1564 finish:
1565         if (m)
1566                 dbus_message_unref(m);
1567
1568         if (reply)
1569                 dbus_message_unref(reply);
1570
1571         dbus_error_free(&error);
1572
1573         return ret;
1574 }
1575
1576 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1577         DBusMessage *m = NULL;
1578         int ret = 0;
1579         DBusError error;
1580         unsigned i;
1581
1582         assert(bus);
1583         assert(args);
1584
1585         dbus_error_init(&error);
1586
1587         for (i = 1; i < n; i++) {
1588                 DBusMessage *reply;
1589
1590                 m = dbus_message_new_method_call(
1591                                 "org.freedesktop.login1",
1592                                 "/org/freedesktop/login1",
1593                                 "org.freedesktop.login1.Manager",
1594                                 "TerminateSeat");
1595                 if (!m) {
1596                         log_error("Could not allocate message.");
1597                         ret = -ENOMEM;
1598                         goto finish;
1599                 }
1600
1601                 if (!dbus_message_append_args(m,
1602                                               DBUS_TYPE_STRING, &args[i],
1603                                               DBUS_TYPE_INVALID)) {
1604                         log_error("Could not append arguments to message.");
1605                         ret = -ENOMEM;
1606                         goto finish;
1607                 }
1608
1609                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1610                 if (!reply) {
1611                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1612                         ret = -EIO;
1613                         goto finish;
1614                 }
1615
1616                 dbus_message_unref(m);
1617                 dbus_message_unref(reply);
1618                 m = reply = NULL;
1619         }
1620
1621 finish:
1622         if (m)
1623                 dbus_message_unref(m);
1624
1625         dbus_error_free(&error);
1626
1627         return ret;
1628 }
1629
1630 static int help(void) {
1631
1632         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1633                "Send control commands to or query the login manager.\n\n"
1634                "  -h --help           Show this help\n"
1635                "     --version        Show package version\n"
1636                "  -p --property=NAME  Show only properties by this name\n"
1637                "  -a --all            Show all properties, including empty ones\n"
1638                "     --kill-who=WHO   Who to send signal to\n"
1639                "  -s --signal=SIGNAL  Which signal to send\n"
1640                "  -H --host=[USER@]HOST\n"
1641                "                      Show information for remote host\n"
1642                "  -P --privileged     Acquire privileges before execution\n"
1643                "     --no-pager       Do not pipe output into a pager\n\n"
1644                "Commands:\n"
1645                "  list-sessions                   List sessions\n"
1646                "  session-status [ID...]          Show session status\n"
1647                "  show-session [ID...]            Show properties of one or more sessions\n"
1648                "  activate [ID]                   Activate a session\n"
1649                "  lock-session [ID...]            Screen lock one or more sessions\n"
1650                "  unlock-session [ID...]          Screen unlock one or more sessions\n"
1651                "  terminate-session [ID...]       Terminate one or more sessions\n"
1652                "  kill-session [ID...]            Send signal to processes of a session\n"
1653                "  list-users                      List users\n"
1654                "  user-status [USER...]           Show user status\n"
1655                "  show-user [USER...]             Show properties of one or more users\n"
1656                "  enable-linger [USER...]         Enable linger state of one or more users\n"
1657                "  disable-linger [USER...]        Disable linger state of one or more users\n"
1658                "  terminate-user [USER...]        Terminate all sessions of one or more users\n"
1659                "  kill-user [USER...]             Send signal to processes of a user\n"
1660                "  list-seats                      List seats\n"
1661                "  seat-status [NAME...]           Show seat status\n"
1662                "  show-seat [NAME...]             Show properties of one or more seats\n"
1663                "  attach [NAME] [DEVICE...]       Attach one or more devices to a seat\n"
1664                "  flush-devices                   Flush all device associations\n"
1665                "  terminate-seat [NAME...]        Terminate all sessions on one or more seats\n",
1666                program_invocation_short_name);
1667
1668         return 0;
1669 }
1670
1671 static int parse_argv(int argc, char *argv[]) {
1672
1673         enum {
1674                 ARG_VERSION = 0x100,
1675                 ARG_NO_PAGER,
1676                 ARG_KILL_WHO
1677         };
1678
1679         static const struct option options[] = {
1680                 { "help",      no_argument,       NULL, 'h'           },
1681                 { "version",   no_argument,       NULL, ARG_VERSION   },
1682                 { "property",  required_argument, NULL, 'p'           },
1683                 { "all",       no_argument,       NULL, 'a'           },
1684                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
1685                 { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
1686                 { "signal",    required_argument, NULL, 's'           },
1687                 { "host",      required_argument, NULL, 'H'           },
1688                 { "privileged",no_argument,       NULL, 'P'           },
1689                 { NULL,        0,                 NULL, 0             }
1690         };
1691
1692         int c;
1693
1694         assert(argc >= 0);
1695         assert(argv);
1696
1697         while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1698
1699                 switch (c) {
1700
1701                 case 'h':
1702                         help();
1703                         return 0;
1704
1705                 case ARG_VERSION:
1706                         puts(PACKAGE_STRING);
1707                         puts(DISTRIBUTION);
1708                         puts(SYSTEMD_FEATURES);
1709                         return 0;
1710
1711                 case 'p': {
1712                         char **l;
1713
1714                         l = strv_append(arg_property, optarg);
1715                         if (!l)
1716                                 return -ENOMEM;
1717
1718                         strv_free(arg_property);
1719                         arg_property = l;
1720
1721                         /* If the user asked for a particular
1722                          * property, show it to him, even if it is
1723                          * empty. */
1724                         arg_all = true;
1725                         break;
1726                 }
1727
1728                 case 'a':
1729                         arg_all = true;
1730                         break;
1731
1732                 case ARG_NO_PAGER:
1733                         arg_no_pager = true;
1734                         break;
1735
1736                 case ARG_KILL_WHO:
1737                         arg_kill_who = optarg;
1738                         break;
1739
1740                 case 's':
1741                         arg_signal = signal_from_string_try_harder(optarg);
1742                         if (arg_signal < 0) {
1743                                 log_error("Failed to parse signal string %s.", optarg);
1744                                 return -EINVAL;
1745                         }
1746                         break;
1747
1748                 case 'P':
1749                         arg_transport = TRANSPORT_POLKIT;
1750                         break;
1751
1752                 case 'H':
1753                         arg_transport = TRANSPORT_SSH;
1754                         arg_host = optarg;
1755                         break;
1756
1757                 case '?':
1758                         return -EINVAL;
1759
1760                 default:
1761                         log_error("Unknown option code %c", c);
1762                         return -EINVAL;
1763                 }
1764         }
1765
1766         return 1;
1767 }
1768
1769 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1770
1771         static const struct {
1772                 const char* verb;
1773                 const enum {
1774                         MORE,
1775                         LESS,
1776                         EQUAL
1777                 } argc_cmp;
1778                 const int argc;
1779                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1780         } verbs[] = {
1781                 { "list-sessions",         LESS,   1, list_sessions    },
1782                 { "session-status",        MORE,   2, show             },
1783                 { "show-session",          MORE,   1, show             },
1784                 { "activate",              EQUAL,  2, activate         },
1785                 { "lock-session",          MORE,   2, activate         },
1786                 { "unlock-session",        MORE,   2, activate         },
1787                 { "terminate-session",     MORE,   2, activate         },
1788                 { "kill-session",          MORE,   2, kill_session     },
1789                 { "list-users",            EQUAL,  1, list_users       },
1790                 { "user-status",           MORE,   2, show             },
1791                 { "show-user",             MORE,   1, show             },
1792                 { "enable-linger",         MORE,   2, enable_linger    },
1793                 { "disable-linger",        MORE,   2, enable_linger    },
1794                 { "terminate-user",        MORE,   2, terminate_user   },
1795                 { "kill-user",             MORE,   2, kill_user        },
1796                 { "list-seats",            EQUAL,  1, list_seats       },
1797                 { "seat-status",           MORE,   2, show             },
1798                 { "show-seat",             MORE,   1, show             },
1799                 { "attach",                MORE,   3, attach           },
1800                 { "flush-devices",         EQUAL,  1, flush_devices    },
1801                 { "terminate-seat",        MORE,   2, terminate_seat   },
1802         };
1803
1804         int left;
1805         unsigned i;
1806
1807         assert(argc >= 0);
1808         assert(argv);
1809         assert(error);
1810
1811         left = argc - optind;
1812
1813         if (left <= 0)
1814                 /* Special rule: no arguments means "list-sessions" */
1815                 i = 0;
1816         else {
1817                 if (streq(argv[optind], "help")) {
1818                         help();
1819                         return 0;
1820                 }
1821
1822                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1823                         if (streq(argv[optind], verbs[i].verb))
1824                                 break;
1825
1826                 if (i >= ELEMENTSOF(verbs)) {
1827                         log_error("Unknown operation %s", argv[optind]);
1828                         return -EINVAL;
1829                 }
1830         }
1831
1832         switch (verbs[i].argc_cmp) {
1833
1834         case EQUAL:
1835                 if (left != verbs[i].argc) {
1836                         log_error("Invalid number of arguments.");
1837                         return -EINVAL;
1838                 }
1839
1840                 break;
1841
1842         case MORE:
1843                 if (left < verbs[i].argc) {
1844                         log_error("Too few arguments.");
1845                         return -EINVAL;
1846                 }
1847
1848                 break;
1849
1850         case LESS:
1851                 if (left > verbs[i].argc) {
1852                         log_error("Too many arguments.");
1853                         return -EINVAL;
1854                 }
1855
1856                 break;
1857
1858         default:
1859                 assert_not_reached("Unknown comparison operator.");
1860         }
1861
1862         if (!bus) {
1863                 log_error("Failed to get D-Bus connection: %s", error->message);
1864                 return -EIO;
1865         }
1866
1867         return verbs[i].dispatch(bus, argv + optind, left);
1868 }
1869
1870 int main(int argc, char*argv[]) {
1871         int r, retval = EXIT_FAILURE;
1872         DBusConnection *bus = NULL;
1873         DBusError error;
1874
1875         dbus_error_init(&error);
1876
1877         log_parse_environment();
1878         log_open();
1879
1880         r = parse_argv(argc, argv);
1881         if (r < 0)
1882                 goto finish;
1883         else if (r == 0) {
1884                 retval = EXIT_SUCCESS;
1885                 goto finish;
1886         }
1887
1888         if (arg_transport == TRANSPORT_NORMAL)
1889                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1890         else if (arg_transport == TRANSPORT_POLKIT)
1891                 bus_connect_system_polkit(&bus, &error);
1892         else if (arg_transport == TRANSPORT_SSH)
1893                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1894         else
1895                 assert_not_reached("Uh, invalid transport...");
1896
1897         r = loginctl_main(bus, argc, argv, &error);
1898         retval = r < 0 ? EXIT_FAILURE : r;
1899
1900 finish:
1901         if (bus) {
1902                 dbus_connection_flush(bus);
1903                 dbus_connection_close(bus);
1904                 dbus_connection_unref(bus);
1905         }
1906
1907         dbus_error_free(&error);
1908         dbus_shutdown();
1909
1910         strv_free(arg_property);
1911
1912         pager_close();
1913
1914         return retval;
1915 }