chiark / gitweb /
unit: introduce ConditionFileIsExecutable= and use it where we check for a binary...
[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 > 18)
531                         c -= 18;
532                 else
533                         c = 0;
534
535                 printf("\t Devices:\n");
536
537
538                 show_sysfs(i->id, "\t\t  ", c);
539         }
540 }
541
542 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
543         assert(name);
544         assert(iter);
545         assert(i);
546
547         switch (dbus_message_iter_get_arg_type(iter)) {
548
549         case DBUS_TYPE_STRING: {
550                 const char *s;
551
552                 dbus_message_iter_get_basic(iter, &s);
553
554                 if (!isempty(s)) {
555                         if (streq(name, "Id"))
556                                 i->id = s;
557                         else if (streq(name, "Name"))
558                                 i->name = s;
559                         else if (streq(name, "ControlGroupPath"))
560                                 i->control_group = s;
561                         else if (streq(name, "TTY"))
562                                 i->tty = s;
563                         else if (streq(name, "Display"))
564                                 i->display = s;
565                         else if (streq(name, "RemoteHost"))
566                                 i->remote_host = s;
567                         else if (streq(name, "RemoteUser"))
568                                 i->remote_user = s;
569                         else if (streq(name, "Service"))
570                                 i->service = s;
571                         else if (streq(name, "Type"))
572                                 i->type = s;
573                 }
574                 break;
575         }
576
577         case DBUS_TYPE_UINT32: {
578                 uint32_t u;
579
580                 dbus_message_iter_get_basic(iter, &u);
581
582                 if (streq(name, "VTNr"))
583                         i->vtnr = (int) u;
584                 else if (streq(name, "Leader"))
585                         i->leader = (pid_t) u;
586
587                 break;
588         }
589
590         case DBUS_TYPE_BOOLEAN: {
591                 dbus_bool_t b;
592
593                 dbus_message_iter_get_basic(iter, &b);
594
595                 if (streq(name, "Remote"))
596                         i->remote = b;
597                 else if (streq(name, "Active"))
598                         i->active = b;
599
600                 break;
601         }
602
603         case DBUS_TYPE_UINT64: {
604                 uint64_t u;
605
606                 dbus_message_iter_get_basic(iter, &u);
607
608                 if (streq(name, "Timestamp"))
609                         i->timestamp = (usec_t) u;
610
611                 break;
612         }
613
614         case DBUS_TYPE_STRUCT: {
615                 DBusMessageIter sub;
616
617                 dbus_message_iter_recurse(iter, &sub);
618
619                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
620                         uint32_t u;
621
622                         dbus_message_iter_get_basic(&sub, &u);
623                         i->uid = (uid_t) u;
624
625                 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
626                         const char *s;
627
628                         dbus_message_iter_get_basic(&sub, &s);
629
630                         if (!isempty(s))
631                                 i->seat = s;
632                 }
633
634                 break;
635         }
636         }
637
638         return 0;
639 }
640
641 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
642         assert(name);
643         assert(iter);
644         assert(i);
645
646         switch (dbus_message_iter_get_arg_type(iter)) {
647
648         case DBUS_TYPE_STRING: {
649                 const char *s;
650
651                 dbus_message_iter_get_basic(iter, &s);
652
653                 if (!isempty(s)) {
654                         if (streq(name, "Name"))
655                                 i->name = s;
656                         else if (streq(name, "ControlGroupPath"))
657                                 i->control_group = s;
658                         else if (streq(name, "State"))
659                                 i->state = s;
660                 }
661                 break;
662         }
663
664         case DBUS_TYPE_UINT32: {
665                 uint32_t u;
666
667                 dbus_message_iter_get_basic(iter, &u);
668
669                 if (streq(name, "UID"))
670                         i->uid = (uid_t) u;
671
672                 break;
673         }
674
675         case DBUS_TYPE_UINT64: {
676                 uint64_t u;
677
678                 dbus_message_iter_get_basic(iter, &u);
679
680                 if (streq(name, "Timestamp"))
681                         i->timestamp = (usec_t) u;
682
683                 break;
684         }
685
686         case DBUS_TYPE_STRUCT: {
687                 DBusMessageIter sub;
688
689                 dbus_message_iter_recurse(iter, &sub);
690
691                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
692                         const char *s;
693
694                         dbus_message_iter_get_basic(&sub, &s);
695
696                         if (!isempty(s))
697                                 i->display = s;
698                 }
699
700                 break;
701         }
702
703         case DBUS_TYPE_ARRAY: {
704
705                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
706                         DBusMessageIter sub, sub2;
707
708                         dbus_message_iter_recurse(iter, &sub);
709                         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
710                                 const char *id;
711                                 const char *path;
712
713                                 dbus_message_iter_recurse(&sub, &sub2);
714
715                                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
716                                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
717                                         char **l;
718
719                                         l = strv_append(i->sessions, id);
720                                         if (!l)
721                                                 return -ENOMEM;
722
723                                         strv_free(i->sessions);
724                                         i->sessions = l;
725                                 }
726
727                                 dbus_message_iter_next(&sub);
728                         }
729
730                         return 0;
731                 }
732         }
733         }
734
735         return 0;
736 }
737
738 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
739         assert(name);
740         assert(iter);
741         assert(i);
742
743         switch (dbus_message_iter_get_arg_type(iter)) {
744
745         case DBUS_TYPE_STRING: {
746                 const char *s;
747
748                 dbus_message_iter_get_basic(iter, &s);
749
750                 if (!isempty(s)) {
751                         if (streq(name, "Id"))
752                                 i->id = s;
753                 }
754                 break;
755         }
756
757         case DBUS_TYPE_STRUCT: {
758                 DBusMessageIter sub;
759
760                 dbus_message_iter_recurse(iter, &sub);
761
762                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
763                         const char *s;
764
765                         dbus_message_iter_get_basic(&sub, &s);
766
767                         if (!isempty(s))
768                                 i->active_session = s;
769                 }
770
771                 break;
772         }
773
774         case DBUS_TYPE_ARRAY: {
775
776                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
777                         DBusMessageIter sub, sub2;
778
779                         dbus_message_iter_recurse(iter, &sub);
780                         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
781                                 const char *id;
782                                 const char *path;
783
784                                 dbus_message_iter_recurse(&sub, &sub2);
785
786                                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
787                                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
788                                         char **l;
789
790                                         l = strv_append(i->sessions, id);
791                                         if (!l)
792                                                 return -ENOMEM;
793
794                                         strv_free(i->sessions);
795                                         i->sessions = l;
796                                 }
797
798                                 dbus_message_iter_next(&sub);
799                         }
800
801                         return 0;
802                 }
803         }
804         }
805
806         return 0;
807 }
808
809 static int print_property(const char *name, DBusMessageIter *iter) {
810         assert(name);
811         assert(iter);
812
813         if (arg_property && !strv_find(arg_property, name))
814                 return 0;
815
816         switch (dbus_message_iter_get_arg_type(iter)) {
817
818         case DBUS_TYPE_STRUCT: {
819                 DBusMessageIter sub;
820
821                 dbus_message_iter_recurse(iter, &sub);
822
823                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
824                     (streq(name, "Display") || streq(name, "ActiveSession"))) {
825                         const char *s;
826
827                         dbus_message_iter_get_basic(&sub, &s);
828
829                         if (arg_all || !isempty(s))
830                                 printf("%s=%s\n", name, s);
831                         return 0;
832                 }
833                 break;
834         }
835
836         case DBUS_TYPE_ARRAY:
837
838                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
839                         DBusMessageIter sub, sub2;
840                         bool found = false;
841
842                         dbus_message_iter_recurse(iter, &sub);
843                         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
844                                 const char *id;
845                                 const char *path;
846
847                                 dbus_message_iter_recurse(&sub, &sub2);
848
849                                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
850                                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
851                                         if (found)
852                                                 printf(" %s", id);
853                                         else {
854                                                 printf("%s=%s", name, id);
855                                                 found = true;
856                                         }
857                                 }
858
859                                 dbus_message_iter_next(&sub);
860                         }
861
862                         if (!found && arg_all)
863                                 printf("%s=\n", name);
864                         else if (found)
865                                 printf("\n");
866
867                         return 0;
868                 }
869
870                 break;
871         }
872
873         if (generic_print_property(name, iter, arg_all) > 0)
874                 return 0;
875
876         if (arg_all)
877                 printf("%s=[unprintable]\n", name);
878
879         return 0;
880 }
881
882 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
883         DBusMessage *m = NULL, *reply = NULL;
884         const char *interface = "";
885         int r;
886         DBusError error;
887         DBusMessageIter iter, sub, sub2, sub3;
888         SessionStatusInfo session_info;
889         UserStatusInfo user_info;
890         SeatStatusInfo seat_info;
891
892         assert(bus);
893         assert(path);
894         assert(new_line);
895
896         zero(session_info);
897         zero(user_info);
898         zero(seat_info);
899
900         dbus_error_init(&error);
901
902         m = dbus_message_new_method_call(
903                         "org.freedesktop.login1",
904                         path,
905                         "org.freedesktop.DBus.Properties",
906                         "GetAll");
907         if (!m) {
908                 log_error("Could not allocate message.");
909                 r = -ENOMEM;
910                 goto finish;
911         }
912
913         if (!dbus_message_append_args(m,
914                                       DBUS_TYPE_STRING, &interface,
915                                       DBUS_TYPE_INVALID)) {
916                 log_error("Could not append arguments to message.");
917                 r = -ENOMEM;
918                 goto finish;
919         }
920
921         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
922         if (!reply) {
923                 log_error("Failed to issue method call: %s", bus_error_message(&error));
924                 r = -EIO;
925                 goto finish;
926         }
927
928         if (!dbus_message_iter_init(reply, &iter) ||
929             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
930             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
931                 log_error("Failed to parse reply.");
932                 r = -EIO;
933                 goto finish;
934         }
935
936         dbus_message_iter_recurse(&iter, &sub);
937
938         if (*new_line)
939                 printf("\n");
940
941         *new_line = true;
942
943         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
944                 const char *name;
945
946                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
947                         log_error("Failed to parse reply.");
948                         r = -EIO;
949                         goto finish;
950                 }
951
952                 dbus_message_iter_recurse(&sub, &sub2);
953
954                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
955                         log_error("Failed to parse reply.");
956                         r = -EIO;
957                         goto finish;
958                 }
959
960                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
961                         log_error("Failed to parse reply.");
962                         r = -EIO;
963                         goto finish;
964                 }
965
966                 dbus_message_iter_recurse(&sub2, &sub3);
967
968                 if (show_properties)
969                         r = print_property(name, &sub3);
970                 else if (strstr(verb, "session"))
971                         r = status_property_session(name, &sub3, &session_info);
972                 else if (strstr(verb, "user"))
973                         r = status_property_user(name, &sub3, &user_info);
974                 else
975                         r = status_property_seat(name, &sub3, &seat_info);
976
977                 if (r < 0) {
978                         log_error("Failed to parse reply.");
979                         r = -EIO;
980                         goto finish;
981                 }
982
983                 dbus_message_iter_next(&sub);
984         }
985
986         if (!show_properties) {
987                 if (strstr(verb, "session"))
988                         print_session_status_info(&session_info);
989                 else if (strstr(verb, "user"))
990                         print_user_status_info(&user_info);
991                 else
992                         print_seat_status_info(&seat_info);
993         }
994
995         strv_free(seat_info.sessions);
996         strv_free(user_info.sessions);
997
998         r = 0;
999
1000 finish:
1001         if (m)
1002                 dbus_message_unref(m);
1003
1004         if (reply)
1005                 dbus_message_unref(reply);
1006
1007         dbus_error_free(&error);
1008
1009         return r;
1010 }
1011
1012 static int show(DBusConnection *bus, char **args, unsigned n) {
1013         DBusMessage *m = NULL, *reply = NULL;
1014         int r, ret = 0;
1015         DBusError error;
1016         unsigned i;
1017         bool show_properties, new_line = false;
1018
1019         assert(bus);
1020         assert(args);
1021
1022         dbus_error_init(&error);
1023
1024         show_properties = !strstr(args[0], "status");
1025
1026         if (show_properties)
1027                 pager_open_if_enabled();
1028
1029         if (show_properties && n <= 1) {
1030                 /* If not argument is specified inspect the manager
1031                  * itself */
1032
1033                 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1034                 goto finish;
1035         }
1036
1037         for (i = 1; i < n; i++) {
1038                 const char *path = NULL;
1039
1040                 if (strstr(args[0], "session")) {
1041
1042                         m = dbus_message_new_method_call(
1043                                         "org.freedesktop.login1",
1044                                         "/org/freedesktop/login1",
1045                                         "org.freedesktop.login1.Manager",
1046                                         "GetSession");
1047                         if (!m) {
1048                                 log_error("Could not allocate message.");
1049                                 ret = -ENOMEM;
1050                                 goto finish;
1051                         }
1052
1053                         if (!dbus_message_append_args(m,
1054                                                       DBUS_TYPE_STRING, &args[i],
1055                                                       DBUS_TYPE_INVALID)) {
1056                                 log_error("Could not append arguments to message.");
1057                                 ret = -ENOMEM;
1058                                 goto finish;
1059                         }
1060
1061                 } else if (strstr(args[0], "user")) {
1062                         uint32_t uid;
1063
1064                         if (safe_atou(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                         if (!dbus_message_append_args(m,
1089                                                       DBUS_TYPE_UINT32, &uid,
1090                                                       DBUS_TYPE_INVALID)) {
1091                                 log_error("Could not append arguments to message.");
1092                                 ret = -ENOMEM;
1093                                 goto finish;
1094                         }
1095                 } else {
1096
1097                         m = dbus_message_new_method_call(
1098                                         "org.freedesktop.login1",
1099                                         "/org/freedesktop/login1",
1100                                         "org.freedesktop.login1.Manager",
1101                                         "GetSeat");
1102                         if (!m) {
1103                                 log_error("Could not allocate message.");
1104                                 ret = -ENOMEM;
1105                                 goto finish;
1106                         }
1107
1108                         if (!dbus_message_append_args(m,
1109                                                       DBUS_TYPE_STRING, &args[i],
1110                                                       DBUS_TYPE_INVALID)) {
1111                                 log_error("Could not append arguments to message.");
1112                                 ret = -ENOMEM;
1113                                 goto finish;
1114                         }
1115                 }
1116
1117                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1118                 if (!reply) {
1119                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1120                         ret = -EIO;
1121                         goto finish;
1122                 }
1123
1124                 if (!dbus_message_get_args(reply, &error,
1125                                            DBUS_TYPE_OBJECT_PATH, &path,
1126                                            DBUS_TYPE_INVALID)) {
1127                         log_error("Failed to parse reply: %s", bus_error_message(&error));
1128                         ret = -EIO;
1129                         goto finish;
1130                 }
1131
1132                 r = show_one(args[0], bus, path, show_properties, &new_line);
1133                 if (r != 0)
1134                         ret = r;
1135
1136                 dbus_message_unref(m);
1137                 dbus_message_unref(reply);
1138                 m = reply = NULL;
1139         }
1140
1141 finish:
1142         if (m)
1143                 dbus_message_unref(m);
1144
1145         if (reply)
1146                 dbus_message_unref(reply);
1147
1148         dbus_error_free(&error);
1149
1150         return ret;
1151 }
1152
1153 static int activate(DBusConnection *bus, char **args, unsigned n) {
1154         DBusMessage *m = NULL, *reply = NULL;
1155         int ret = 0;
1156         DBusError error;
1157         unsigned i;
1158
1159         assert(bus);
1160         assert(args);
1161
1162         dbus_error_init(&error);
1163
1164         for (i = 1; i < n; i++) {
1165                 m = dbus_message_new_method_call(
1166                                 "org.freedesktop.login1",
1167                                 "/org/freedesktop/login1",
1168                                 "org.freedesktop.login1.Manager",
1169                                 "ActivateSession");
1170                 if (!m) {
1171                         log_error("Could not allocate message.");
1172                         ret = -ENOMEM;
1173                         goto finish;
1174                 }
1175
1176                 if (!dbus_message_append_args(m,
1177                                               DBUS_TYPE_STRING, &args[i],
1178                                               DBUS_TYPE_INVALID)) {
1179                         log_error("Could not append arguments to message.");
1180                         ret = -ENOMEM;
1181                         goto finish;
1182                 }
1183
1184                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1185                 if (!reply) {
1186                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1187                         ret = -EIO;
1188                         goto finish;
1189                 }
1190
1191                 dbus_message_unref(m);
1192                 dbus_message_unref(reply);
1193                 m = reply = NULL;
1194         }
1195
1196 finish:
1197         if (m)
1198                 dbus_message_unref(m);
1199
1200         if (reply)
1201                 dbus_message_unref(reply);
1202
1203         dbus_error_free(&error);
1204
1205         return ret;
1206 }
1207
1208 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1209         return 0;
1210 }
1211
1212 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1213         return 0;
1214 }
1215
1216 static int attach(DBusConnection *bus, char **args, unsigned n) {
1217         return 0;
1218 }
1219
1220 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1221         return 0;
1222 }
1223
1224 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1225         return 0;
1226 }
1227
1228 static int help(void) {
1229
1230         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1231                "Send control commands to or query the login manager.\n\n"
1232                "  -h --help           Show this help\n"
1233                "     --version        Show package version\n"
1234                "  -p --property=NAME  Show only properties by this name\n"
1235                "  -a --all            Show all properties, including empty ones\n"
1236                "     --kill-who=WHO   Who to send signal to\n"
1237                "  -s --signal=SIGNAL  Which signal to send\n"
1238                "  -H --host=[USER@]HOST\n"
1239                "                      Show information for remote host\n"
1240                "  -P --privileged     Acquire privileges before execution\n"
1241                "     --no-pager       Do not pipe output into a pager\n\n"
1242                "Commands:\n"
1243                "  list-sessions                   List sessions\n"
1244                "  session-status [ID...]          Show session status\n"
1245                "  show-session [ID...]            Show property of one or more sessions\n"
1246                "  activate [ID]                   Activate a session\n"
1247                "  lock-session [ID...]            Screen lock one or more sessions\n"
1248                "  terminate-session [ID...]       Terminate one or more sessions\n"
1249                "  kill-session [ID...]            Send signal to processes of a session\n"
1250                "  list-users                      List users\n"
1251                "  user-status [USER...]           Show user status\n"
1252                "  show-user [USER...]             Show property of one or more users\n"
1253                "  enable-linger [USER...]         Enable linger state of one or more users\n"
1254                "  disable-linger [USER...]        Disable linger state of one or more users\n"
1255                "  terminate-user [USER...]        Terminate all sessions of one or more users\n"
1256                "  kill-user [USER...]             Send signal to processes of a user\n"
1257                "  list-seats                      List seats\n"
1258                "  seat-status [NAME...]           Show seat status\n"
1259                "  show-seat [NAME...]             Show property of one or more seats\n"
1260                "  attach [NAME] [DEVICE...]       Attach one or more devices to a seat\n"
1261                "  flush-devices                   Flush all device associations\n"
1262                "  terminate-seat [NAME...]        Terminate all sessions on one or more seats\n"
1263                "  kill-seat [NAME...]             Send signal to processes of sessions on a seat\n",
1264                program_invocation_short_name);
1265
1266         return 0;
1267 }
1268
1269 static int parse_argv(int argc, char *argv[]) {
1270
1271         enum {
1272                 ARG_VERSION = 0x100,
1273                 ARG_NO_PAGER,
1274                 ARG_KILL_WHO
1275         };
1276
1277         static const struct option options[] = {
1278                 { "help",      no_argument,       NULL, 'h'           },
1279                 { "version",   no_argument,       NULL, ARG_VERSION   },
1280                 { "property",  required_argument, NULL, 'p'           },
1281                 { "all",       no_argument,       NULL, 'a'           },
1282                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
1283                 { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
1284                 { "signal",    required_argument, NULL, 's'           },
1285                 { "host",      required_argument, NULL, 'H'           },
1286                 { "privileged",no_argument,       NULL, 'P'           },
1287                 { NULL,        0,                 NULL, 0             }
1288         };
1289
1290         int c;
1291
1292         assert(argc >= 0);
1293         assert(argv);
1294
1295         while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1296
1297                 switch (c) {
1298
1299                 case 'h':
1300                         help();
1301                         return 0;
1302
1303                 case ARG_VERSION:
1304                         puts(PACKAGE_STRING);
1305                         puts(DISTRIBUTION);
1306                         puts(SYSTEMD_FEATURES);
1307                         return 0;
1308
1309                 case 'p': {
1310                         char **l;
1311
1312                         l = strv_append(arg_property, optarg);
1313                         if (!l)
1314                                 return -ENOMEM;
1315
1316                         strv_free(arg_property);
1317                         arg_property = l;
1318
1319                         /* If the user asked for a particular
1320                          * property, show it to him, even if it is
1321                          * empty. */
1322                         arg_all = true;
1323                         break;
1324                 }
1325
1326                 case 'a':
1327                         arg_all = true;
1328                         break;
1329
1330                 case ARG_NO_PAGER:
1331                         arg_no_pager = true;
1332                         break;
1333
1334                 case ARG_KILL_WHO:
1335                         arg_kill_who = optarg;
1336                         break;
1337
1338                 case 's':
1339                         arg_signal = signal_from_string_try_harder(optarg);
1340                         if (arg_signal < 0) {
1341                                 log_error("Failed to parse signal string %s.", optarg);
1342                                 return -EINVAL;
1343                         }
1344                         break;
1345
1346                 case 'P':
1347                         arg_transport = TRANSPORT_POLKIT;
1348                         break;
1349
1350                 case 'H':
1351                         arg_transport = TRANSPORT_SSH;
1352                         arg_host = optarg;
1353                         break;
1354
1355                 case '?':
1356                         return -EINVAL;
1357
1358                 default:
1359                         log_error("Unknown option code %c", c);
1360                         return -EINVAL;
1361                 }
1362         }
1363
1364         return 1;
1365 }
1366
1367 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1368
1369         static const struct {
1370                 const char* verb;
1371                 const enum {
1372                         MORE,
1373                         LESS,
1374                         EQUAL
1375                 } argc_cmp;
1376                 const int argc;
1377                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1378         } verbs[] = {
1379                 { "list-sessions",         LESS,   1, list_sessions    },
1380                 { "session-status",        MORE,   2, show             },
1381                 { "show-session",          MORE,   1, show             },
1382                 { "activate",              EQUAL,  2, activate         },
1383                 { "lock-session",          MORE,   2, activate         },
1384                 { "terminate-session",     MORE,   2, activate         },
1385                 { "kill-session",          MORE,   2, kill_session     },
1386                 { "list-users",            EQUAL,  1, list_users       },
1387                 { "user-status",           MORE,   2, show             },
1388                 { "show-user",             MORE,   1, show             },
1389                 { "enable-linger",         MORE,   2, enable_linger    },
1390                 { "disable-linger",        MORE,   2, enable_linger    },
1391                 { "terminate-user",        MORE,   2, enable_linger    },
1392                 { "kill-user",             MORE,   2, kill_session     },
1393                 { "list-seats",            EQUAL,  1, list_seats       },
1394                 { "seat-status",           MORE,   2, show             },
1395                 { "show-seat",             MORE,   1, show             },
1396                 { "attach",                MORE,   3, attach           },
1397                 { "flush-devices",         EQUAL,  1, flush_devices    },
1398                 { "terminate-seat",        MORE,   2, terminate_seat   },
1399                 { "kill-seat",             MORE,   2, kill_session     },
1400         };
1401
1402         int left;
1403         unsigned i;
1404
1405         assert(argc >= 0);
1406         assert(argv);
1407         assert(error);
1408
1409         left = argc - optind;
1410
1411         if (left <= 0)
1412                 /* Special rule: no arguments means "list-sessions" */
1413                 i = 0;
1414         else {
1415                 if (streq(argv[optind], "help")) {
1416                         help();
1417                         return 0;
1418                 }
1419
1420                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1421                         if (streq(argv[optind], verbs[i].verb))
1422                                 break;
1423
1424                 if (i >= ELEMENTSOF(verbs)) {
1425                         log_error("Unknown operation %s", argv[optind]);
1426                         return -EINVAL;
1427                 }
1428         }
1429
1430         switch (verbs[i].argc_cmp) {
1431
1432         case EQUAL:
1433                 if (left != verbs[i].argc) {
1434                         log_error("Invalid number of arguments.");
1435                         return -EINVAL;
1436                 }
1437
1438                 break;
1439
1440         case MORE:
1441                 if (left < verbs[i].argc) {
1442                         log_error("Too few arguments.");
1443                         return -EINVAL;
1444                 }
1445
1446                 break;
1447
1448         case LESS:
1449                 if (left > verbs[i].argc) {
1450                         log_error("Too many arguments.");
1451                         return -EINVAL;
1452                 }
1453
1454                 break;
1455
1456         default:
1457                 assert_not_reached("Unknown comparison operator.");
1458         }
1459
1460         if (!bus) {
1461                 log_error("Failed to get D-Bus connection: %s", error->message);
1462                 return -EIO;
1463         }
1464
1465         return verbs[i].dispatch(bus, argv + optind, left);
1466 }
1467
1468 int main(int argc, char*argv[]) {
1469         int r, retval = EXIT_FAILURE;
1470         DBusConnection *bus = NULL;
1471         DBusError error;
1472
1473         dbus_error_init(&error);
1474
1475         log_parse_environment();
1476         log_open();
1477
1478         r = parse_argv(argc, argv);
1479         if (r < 0)
1480                 goto finish;
1481         else if (r == 0) {
1482                 retval = EXIT_SUCCESS;
1483                 goto finish;
1484         }
1485
1486         if (arg_transport == TRANSPORT_NORMAL)
1487                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1488         else if (arg_transport == TRANSPORT_POLKIT)
1489                 bus_connect_system_polkit(&bus, &error);
1490         else if (arg_transport == TRANSPORT_SSH)
1491                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1492         else
1493                 assert_not_reached("Uh, invalid transport...");
1494
1495         r = loginctl_main(bus, argc, argv, &error);
1496         retval = r < 0 ? EXIT_FAILURE : r;
1497
1498 finish:
1499         if (bus) {
1500                 dbus_connection_flush(bus);
1501                 dbus_connection_close(bus);
1502                 dbus_connection_unref(bus);
1503         }
1504
1505         dbus_error_free(&error);
1506         dbus_shutdown();
1507
1508         strv_free(arg_property);
1509
1510         pager_close();
1511
1512         return retval;
1513 }