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