chiark / gitweb /
update NEWS
[elogind.git] / src / login / loginctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <getopt.h>
26 #include <pwd.h>
27 #include <locale.h>
28
29 #include "sd-bus.h"
30 #include "bus-util.h"
31 #include "bus-error.h"
32 #include "log.h"
33 #include "util.h"
34 #include "macro.h"
35 #include "pager.h"
36 #include "build.h"
37 #include "strv.h"
38 #include "unit-name.h"
39 #include "sysfs-show.h"
40 #include "cgroup-show.h"
41 #include "cgroup-util.h"
42 #include "spawn-polkit-agent.h"
43
44 static char **arg_property = NULL;
45 static bool arg_all = false;
46 static bool arg_full = false;
47 static bool arg_no_pager = false;
48 static bool arg_legend = true;
49 static const char *arg_kill_who = NULL;
50 static int arg_signal = SIGTERM;
51 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
52 static bool arg_ask_password = true;
53 static char *arg_host = NULL;
54
55 static void pager_open_if_enabled(void) {
56
57         if (arg_no_pager)
58                 return;
59
60         pager_open(false);
61 }
62
63 static void polkit_agent_open_if_enabled(void) {
64
65         /* Open the polkit agent as a child process if necessary */
66
67         if (!arg_ask_password)
68                 return;
69
70         if (arg_transport != BUS_TRANSPORT_LOCAL)
71                 return;
72
73         polkit_agent_open();
74 }
75
76 static int list_sessions(sd_bus *bus, char **args, unsigned n) {
77         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
78         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
79         const char *id, *user, *seat, *object;
80         unsigned k = 0;
81         uint32_t uid;
82         int r;
83
84         pager_open_if_enabled();
85
86         r = sd_bus_call_method(
87                         bus,
88                         "org.freedesktop.login1",
89                         "/org/freedesktop/login1",
90                         "org.freedesktop.login1.Manager",
91                         "ListSessions",
92                         &error, &reply,
93                         "");
94         if (r < 0) {
95                 log_error("Failed to list sessions: %s", bus_error_message(&error, r));
96                 return r;
97         }
98
99         r = sd_bus_message_enter_container(reply, 'a', "(susso)");
100         if (r < 0)
101                 return bus_log_parse_error(r);
102
103         if (arg_legend)
104                 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
105
106         while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) {
107                 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
108                 k++;
109         }
110         if (r < 0)
111                 return bus_log_parse_error(r);
112
113         if (arg_legend)
114                 printf("\n%u sessions listed.\n", k);
115
116         return 0;
117 }
118
119 static int list_users(sd_bus *bus, char **args, unsigned n) {
120         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
121         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
122         const char *user, *object;
123         unsigned k = 0;
124         uint32_t uid;
125         int r;
126
127         pager_open_if_enabled();
128
129         r = sd_bus_call_method(
130                         bus,
131                         "org.freedesktop.login1",
132                         "/org/freedesktop/login1",
133                         "org.freedesktop.login1.Manager",
134                         "ListUsers",
135                         &error, &reply,
136                         "");
137         if (r < 0) {
138                 log_error("Failed to list users: %s", bus_error_message(&error, r));
139                 return r;
140         }
141
142         r = sd_bus_message_enter_container(reply, 'a', "(uso)");
143         if (r < 0)
144                 return bus_log_parse_error(r);
145
146         if (arg_legend)
147                 printf("%10s %-16s\n", "UID", "USER");
148
149         while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) {
150                 printf("%10u %-16s\n", (unsigned) uid, user);
151                 k++;
152         }
153         if (r < 0)
154                 return bus_log_parse_error(r);
155
156         if (arg_legend)
157                 printf("\n%u users listed.\n", k);
158
159         return 0;
160 }
161
162 static int list_seats(sd_bus *bus, char **args, unsigned n) {
163         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
164         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
165         const char *seat, *object;
166         unsigned k = 0;
167         int r;
168
169         pager_open_if_enabled();
170
171         r = sd_bus_call_method(
172                         bus,
173                         "org.freedesktop.login1",
174                         "/org/freedesktop/login1",
175                         "org.freedesktop.login1.Manager",
176                         "ListSeats",
177                         &error, &reply,
178                         "");
179         if (r < 0) {
180                 log_error("Failed to list seats: %s", bus_error_message(&error, r));
181                 return r;
182         }
183
184         r = sd_bus_message_enter_container(reply, 'a', "(so)");
185         if (r < 0)
186                 return bus_log_parse_error(r);
187
188         if (arg_legend)
189                 printf("%-16s\n", "SEAT");
190
191         while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) {
192                 printf("%-16s\n", seat);
193                 k++;
194         }
195         if (r < 0)
196                 return bus_log_parse_error(r);
197
198         if (arg_legend)
199                 printf("\n%u seats listed.\n", k);
200
201         return 0;
202 }
203
204 static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
205         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
206         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
207         _cleanup_free_ char *path = NULL;
208         const char *cgroup;
209         int r, output_flags;
210         unsigned c;
211
212         assert(bus);
213         assert(unit);
214
215         if (arg_transport != BUS_TRANSPORT_LOCAL)
216                 return 0;
217
218         path = unit_dbus_path_from_name(unit);
219         if (!path)
220                 return -ENOMEM;
221
222         r = sd_bus_get_property(
223                         bus,
224                         "org.freedesktop.systemd1",
225                         path,
226                         interface,
227                         "ControlGroup",
228                         &error, &reply, "s");
229         if (r < 0)
230                 return r;
231
232         r = sd_bus_message_read(reply, "s", &cgroup);
233         if (r < 0)
234                 return r;
235
236         if (isempty(cgroup))
237                 return 0;
238
239         if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
240                 return 0;
241
242         output_flags =
243                 arg_all * OUTPUT_SHOW_ALL |
244                 arg_full * OUTPUT_FULL_WIDTH;
245
246         c = columns();
247         if (c > 18)
248                 c -= 18;
249         else
250                 c = 0;
251
252         show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, false, &leader, leader > 0, output_flags);
253         return 0;
254 }
255
256 typedef struct SessionStatusInfo {
257         const char *id;
258         uid_t uid;
259         const char *name;
260         usec_t timestamp;
261         unsigned int vtnr;
262         const char *seat;
263         const char *tty;
264         const char *display;
265         bool remote;
266         const char *remote_host;
267         const char *remote_user;
268         const char *service;
269         pid_t leader;
270         const char *type;
271         const char *class;
272         const char *state;
273         const char *scope;
274         const char *desktop;
275 } SessionStatusInfo;
276
277 typedef struct UserStatusInfo {
278         uid_t uid;
279         const char *name;
280         usec_t timestamp;
281         const char *state;
282         char **sessions;
283         const char *display;
284         const char *slice;
285 } UserStatusInfo;
286
287 typedef struct SeatStatusInfo {
288         const char *id;
289         const char *active_session;
290         char **sessions;
291 } SeatStatusInfo;
292
293 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
294         const char *contents;
295         int r;
296
297         r = sd_bus_message_peek_type(m, NULL, &contents);
298         if (r < 0)
299                 return r;
300
301         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
302         if (r < 0)
303                 return r;
304
305         if (contents[0] == 's' || contents[0] == 'o') {
306                 const char *s;
307                 char **p = (char **) userdata;
308
309                 r = sd_bus_message_read_basic(m, contents[0], &s);
310                 if (r < 0)
311                         return r;
312
313                 free(*p);
314                 *p = strdup(s);
315
316                 if (!*p)
317                         return -ENOMEM;
318         } else {
319                 r = sd_bus_message_read_basic(m, contents[0], userdata);
320                 if (r < 0)
321                         return r;
322         }
323
324         r = sd_bus_message_skip(m, contents+1);
325         if (r < 0)
326                 return r;
327
328         r = sd_bus_message_exit_container(m);
329         if (r < 0)
330                 return r;
331
332         return 0;
333 }
334
335 static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
336         const char *name;
337         int r;
338
339         assert(bus);
340         assert(m);
341
342         r = sd_bus_message_enter_container(m, 'a', "(so)");
343         if (r < 0)
344                 return r;
345
346         while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
347                 r = strv_extend(userdata, name);
348                 if (r < 0)
349                         return r;
350         }
351         if (r < 0)
352                 return r;
353
354         return sd_bus_message_exit_container(m);
355 }
356
357 static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
358
359         static const struct bus_properties_map map[]  = {
360                 { "Id",         "s", NULL, offsetof(SessionStatusInfo, id) },
361                 { "Name",       "s", NULL, offsetof(SessionStatusInfo, name) },
362                 { "TTY",        "s", NULL, offsetof(SessionStatusInfo, tty) },
363                 { "Display",    "s", NULL, offsetof(SessionStatusInfo, display) },
364                 { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
365                 { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
366                 { "Service",    "s", NULL, offsetof(SessionStatusInfo, service) },
367                 { "Desktop",    "s", NULL, offsetof(SessionStatusInfo, desktop) },
368                 { "Type",       "s", NULL, offsetof(SessionStatusInfo, type) },
369                 { "Class",      "s", NULL, offsetof(SessionStatusInfo, class) },
370                 { "Scope",      "s", NULL, offsetof(SessionStatusInfo, scope) },
371                 { "State",      "s", NULL, offsetof(SessionStatusInfo, state) },
372                 { "VTNr",       "u", NULL, offsetof(SessionStatusInfo, vtnr) },
373                 { "Leader",     "u", NULL, offsetof(SessionStatusInfo, leader) },
374                 { "Remote",     "b", NULL, offsetof(SessionStatusInfo, remote) },
375                 { "Timestamp",  "t", NULL, offsetof(SessionStatusInfo, timestamp) },
376                 { "User",       "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
377                 { "Seat",       "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) },
378                 {}
379         };
380
381         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
382         char since2[FORMAT_TIMESTAMP_MAX], *s2;
383         SessionStatusInfo i = {};
384         int r;
385
386         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
387         if (r < 0) {
388                 log_error("Could not get properties: %s", strerror(-r));
389                 return r;
390         }
391
392         if (*new_line)
393                 printf("\n");
394
395         *new_line = true;
396
397         printf("%s - ", strna(i.id));
398
399         if (i.name)
400                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
401         else
402                 printf("%u\n", (unsigned) i.uid);
403
404         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
405         s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
406
407         if (s1)
408                 printf("\t   Since: %s; %s\n", s2, s1);
409         else if (s2)
410                 printf("\t   Since: %s\n", s2);
411
412         if (i.leader > 0) {
413                 _cleanup_free_ char *t = NULL;
414
415                 printf("\t  Leader: %u", (unsigned) i.leader);
416
417                 get_process_comm(i.leader, &t);
418                 if (t)
419                         printf(" (%s)", t);
420
421                 printf("\n");
422         }
423
424         if (!isempty(i.seat)) {
425                 printf("\t    Seat: %s", i.seat);
426
427                 if (i.vtnr > 0)
428                         printf("; vc%i", i.vtnr);
429
430                 printf("\n");
431         }
432
433         if (i.tty)
434                 printf("\t     TTY: %s\n", i.tty);
435         else if (i.display)
436                 printf("\t Display: %s\n", i.display);
437
438         if (i.remote_host && i.remote_user)
439                 printf("\t  Remote: %s@%s\n", i.remote_user, i.remote_host);
440         else if (i.remote_host)
441                 printf("\t  Remote: %s\n", i.remote_host);
442         else if (i.remote_user)
443                 printf("\t  Remote: user %s\n", i.remote_user);
444         else if (i.remote)
445                 printf("\t  Remote: Yes\n");
446
447         if (i.service) {
448                 printf("\t Service: %s", i.service);
449
450                 if (i.type)
451                         printf("; type %s", i.type);
452
453                 if (i.class)
454                         printf("; class %s", i.class);
455
456                 printf("\n");
457         } else if (i.type) {
458                 printf("\t    Type: %s", i.type);
459
460                 if (i.class)
461                         printf("; class %s", i.class);
462
463                 printf("\n");
464         } else if (i.class)
465                 printf("\t   Class: %s\n", i.class);
466
467         if (!isempty(i.desktop))
468                 printf("\t Desktop: %s\n", i.desktop);
469
470         if (i.state)
471                 printf("\t   State: %s\n", i.state);
472
473         if (i.scope) {
474                 printf("\t    Unit: %s\n", i.scope);
475                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
476         }
477
478         return 0;
479 }
480
481 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
482
483         static const struct bus_properties_map map[]  = {
484                 { "Name",       "s",     NULL, offsetof(UserStatusInfo, name) },
485                 { "Slice",      "s",     NULL, offsetof(UserStatusInfo, slice) },
486                 { "State",      "s",     NULL, offsetof(UserStatusInfo, state) },
487                 { "UID",        "u",     NULL, offsetof(UserStatusInfo, uid) },
488                 { "Timestamp",  "t",     NULL, offsetof(UserStatusInfo, timestamp) },
489                 { "Display",    "(so)",  prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
490                 { "Sessions",   "a(so)", prop_map_sessions_strv,   offsetof(UserStatusInfo, sessions) },
491                 {}
492         };
493
494         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
495         char since2[FORMAT_TIMESTAMP_MAX], *s2;
496         UserStatusInfo i = {};
497         int r;
498
499         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
500         if (r < 0) {
501                 log_error("Could not get properties: %s", strerror(-r));
502                 goto finish;
503         }
504
505         if (*new_line)
506                 printf("\n");
507
508         *new_line = true;
509
510         if (i.name)
511                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
512         else
513                 printf("%u\n", (unsigned) i.uid);
514
515         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
516         s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
517
518         if (s1)
519                 printf("\t   Since: %s; %s\n", s2, s1);
520         else if (s2)
521                 printf("\t   Since: %s\n", s2);
522
523         if (!isempty(i.state))
524                 printf("\t   State: %s\n", i.state);
525
526         if (!strv_isempty(i.sessions)) {
527                 char **l;
528                 printf("\tSessions:");
529
530                 STRV_FOREACH(l, i.sessions) {
531                         if (streq_ptr(*l, i.display))
532                                 printf(" *%s", *l);
533                         else
534                                 printf(" %s", *l);
535                 }
536
537                 printf("\n");
538         }
539
540         if (i.slice) {
541                 printf("\t    Unit: %s\n", i.slice);
542                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
543         }
544
545 finish:
546         strv_free(i.sessions);
547
548         return r;
549 }
550
551 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
552
553         static const struct bus_properties_map map[]  = {
554                 { "Id",            "s",     NULL, offsetof(SeatStatusInfo, id) },
555                 { "ActiveSession", "(so)",  prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
556                 { "Sessions",      "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
557                 {}
558         };
559
560         SeatStatusInfo i = {};
561         int r;
562
563         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
564         if (r < 0) {
565                 log_error("Could not get properties: %s", strerror(-r));
566                 goto finish;
567         }
568
569         if (*new_line)
570                 printf("\n");
571
572         *new_line = true;
573
574         printf("%s\n", strna(i.id));
575
576         if (!strv_isempty(i.sessions)) {
577                 char **l;
578                 printf("\tSessions:");
579
580                 STRV_FOREACH(l, i.sessions) {
581                         if (streq_ptr(*l, i.active_session))
582                                 printf(" *%s", *l);
583                         else
584                                 printf(" %s", *l);
585                 }
586
587                 printf("\n");
588         }
589
590         if (arg_transport == BUS_TRANSPORT_LOCAL) {
591                 unsigned c;
592
593                 c = columns();
594                 if (c > 21)
595                         c -= 21;
596                 else
597                         c = 0;
598
599                 printf("\t Devices:\n");
600
601                 show_sysfs(i.id, "\t\t  ", c);
602         }
603
604 finish:
605         strv_free(i.sessions);
606
607         return r;
608 }
609
610 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
611         int r;
612
613         if (*new_line)
614                 printf("\n");
615
616         *new_line = true;
617
618         r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
619         if (r < 0)
620                 log_error("Could not get properties: %s", strerror(-r));
621
622         return r;
623 }
624
625 static int show_session(sd_bus *bus, char **args, unsigned n) {
626         bool properties, new_line = false;
627         unsigned i;
628         int r;
629
630         assert(bus);
631         assert(args);
632
633         properties = !strstr(args[0], "status");
634
635         pager_open_if_enabled();
636
637         if (properties && n <= 1) {
638                 /* If not argument is specified inspect the manager
639                  * itself */
640                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
641         }
642
643         for (i = 1; i < n; i++) {
644                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
645                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
646                 const char *path = NULL;
647
648                 r = sd_bus_call_method(
649                                 bus,
650                                 "org.freedesktop.login1",
651                                 "/org/freedesktop/login1",
652                                 "org.freedesktop.login1.Manager",
653                                 "GetSession",
654                                 &error, &reply,
655                                 "s", args[i]);
656                 if (r < 0) {
657                         log_error("Failed to get session: %s", bus_error_message(&error, r));
658                         return r;
659                 }
660
661                 r = sd_bus_message_read(reply, "o", &path);
662                 if (r < 0)
663                         return bus_log_parse_error(r);
664
665                 if (properties)
666                         r = show_properties(bus, path, &new_line);
667                 else
668                         r = print_session_status_info(bus, path, &new_line);
669
670                 if (r < 0)
671                         return r;
672         }
673
674         return 0;
675 }
676
677 static int show_user(sd_bus *bus, char **args, unsigned n) {
678         bool properties, new_line = false;
679         unsigned i;
680         int r;
681
682         assert(bus);
683         assert(args);
684
685         properties = !strstr(args[0], "status");
686
687         pager_open_if_enabled();
688
689         if (properties && n <= 1) {
690                 /* If not argument is specified inspect the manager
691                  * itself */
692                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
693         }
694
695         for (i = 1; i < n; i++) {
696                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
697                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
698                 const char *path = NULL;
699                 uid_t uid;
700
701                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
702                 if (r < 0) {
703                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
704                         return r;
705                 }
706
707                 r = sd_bus_call_method(
708                                 bus,
709                                 "org.freedesktop.login1",
710                                 "/org/freedesktop/login1",
711                                 "org.freedesktop.login1.Manager",
712                                 "GetUser",
713                                 &error, &reply,
714                                 "u", (uint32_t) uid);
715                 if (r < 0) {
716                         log_error("Failed to get user: %s", bus_error_message(&error, r));
717                         return r;
718                 }
719
720                 r = sd_bus_message_read(reply, "o", &path);
721                 if (r < 0)
722                         return bus_log_parse_error(r);
723
724                 if (properties)
725                         r = show_properties(bus, path, &new_line);
726                 else
727                         r = print_user_status_info(bus, path, &new_line);
728
729                 if (r < 0)
730                         return r;
731         }
732
733         return 0;
734 }
735
736 static int show_seat(sd_bus *bus, char **args, unsigned n) {
737         bool properties, new_line = false;
738         unsigned i;
739         int r;
740
741         assert(bus);
742         assert(args);
743
744         properties = !strstr(args[0], "status");
745
746         pager_open_if_enabled();
747
748         if (properties && n <= 1) {
749                 /* If not argument is specified inspect the manager
750                  * itself */
751                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
752         }
753
754         for (i = 1; i < n; i++) {
755                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
756                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
757                 const char *path = NULL;
758
759                 r = sd_bus_call_method(
760                                 bus,
761                                 "org.freedesktop.login1",
762                                 "/org/freedesktop/login1",
763                                 "org.freedesktop.login1.Manager",
764                                 "GetSeat",
765                                 &error, &reply,
766                                 "s", args[i]);
767                 if (r < 0) {
768                         log_error("Failed to get seat: %s", bus_error_message(&error, r));
769                         return r;
770                 }
771
772                 r = sd_bus_message_read(reply, "o", &path);
773                 if (r < 0)
774                         return bus_log_parse_error(r);
775
776                 if (properties)
777                         r = show_properties(bus, path, &new_line);
778                 else
779                         r = print_seat_status_info(bus, path, &new_line);
780
781                 if (r < 0)
782                         return r;
783         }
784
785         return 0;
786 }
787
788 static int activate(sd_bus *bus, char **args, unsigned n) {
789         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
790         unsigned i;
791         int r;
792
793         assert(args);
794
795         for (i = 1; i < n; i++) {
796
797                 r = sd_bus_call_method (
798                                 bus,
799                                 "org.freedesktop.login1",
800                                 "/org/freedesktop/login1",
801                                 "org.freedesktop.login1.Manager",
802                                 streq(args[0], "lock-session")      ? "LockSession" :
803                                 streq(args[0], "unlock-session")    ? "UnlockSession" :
804                                 streq(args[0], "terminate-session") ? "TerminateSession" :
805                                                                       "ActivateSession",
806                                 &error, NULL,
807                                 "s", args[i]);
808                 if (r < 0) {
809                         log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
810                         return r;
811                 }
812         }
813
814         return 0;
815 }
816
817 static int kill_session(sd_bus *bus, char **args, unsigned n) {
818         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
819         unsigned i;
820         int r;
821
822         assert(args);
823
824         if (!arg_kill_who)
825                 arg_kill_who = "all";
826
827         for (i = 1; i < n; i++) {
828
829                 r = sd_bus_call_method (
830                         bus,
831                         "org.freedesktop.login1",
832                         "/org/freedesktop/login1",
833                         "org.freedesktop.login1.Manager",
834                         "KillSession",
835                         &error, NULL,
836                         "ssi", args[i], arg_kill_who, arg_signal);
837                 if (r < 0) {
838                         log_error("Could not kill session: %s", bus_error_message(&error, -r));
839                         return r;
840                 }
841         }
842
843         return 0;
844 }
845
846 static int enable_linger(sd_bus *bus, char **args, unsigned n) {
847         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
848         unsigned i;
849         bool b;
850         int r;
851
852         assert(args);
853
854         polkit_agent_open_if_enabled();
855
856         b = streq(args[0], "enable-linger");
857
858         for (i = 1; i < n; i++) {
859                 uid_t uid;
860
861                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
862                 if (r < 0) {
863                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
864                         return r;
865                 }
866
867                 r = sd_bus_call_method (
868                         bus,
869                         "org.freedesktop.login1",
870                         "/org/freedesktop/login1",
871                         "org.freedesktop.login1.Manager",
872                         "SetUserLinger",
873                         &error, NULL,
874                         "ubb", (uint32_t) uid, b, true);
875                 if (r < 0) {
876                         log_error("Could not enable linger: %s", bus_error_message(&error, -r));
877                         return r;
878                 }
879         }
880
881         return 0;
882 }
883
884 static int terminate_user(sd_bus *bus, char **args, unsigned n) {
885         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
886         unsigned i;
887         int r;
888
889         assert(args);
890
891         for (i = 1; i < n; i++) {
892                 uid_t uid;
893
894                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
895                 if (r < 0) {
896                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
897                         return r;
898                 }
899
900                 r = sd_bus_call_method (
901                         bus,
902                         "org.freedesktop.login1",
903                         "/org/freedesktop/login1",
904                         "org.freedesktop.login1.Manager",
905                         "TerminateUser",
906                         &error, NULL,
907                         "u", (uint32_t) uid);
908                 if (r < 0) {
909                         log_error("Could not terminate user: %s", bus_error_message(&error, -r));
910                         return r;
911                 }
912         }
913
914         return 0;
915 }
916
917 static int kill_user(sd_bus *bus, char **args, unsigned n) {
918         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
919         unsigned i;
920         int r;
921
922         assert(args);
923
924         if (!arg_kill_who)
925                 arg_kill_who = "all";
926
927         for (i = 1; i < n; i++) {
928                 uid_t uid;
929
930                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
931                 if (r < 0) {
932                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
933                         return r;
934                 }
935
936                 r = sd_bus_call_method (
937                         bus,
938                         "org.freedesktop.login1",
939                         "/org/freedesktop/login1",
940                         "org.freedesktop.login1.Manager",
941                         "KillUser",
942                         &error, NULL,
943                         "ui", (uint32_t) uid, arg_signal);
944                 if (r < 0) {
945                         log_error("Could not kill user: %s", bus_error_message(&error, -r));
946                         return r;
947                 }
948         }
949
950         return 0;
951 }
952
953 static int attach(sd_bus *bus, char **args, unsigned n) {
954         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
955         unsigned i;
956         int r;
957
958         assert(args);
959
960         polkit_agent_open_if_enabled();
961
962         for (i = 2; i < n; i++) {
963
964                 r = sd_bus_call_method (
965                         bus,
966                         "org.freedesktop.login1",
967                         "/org/freedesktop/login1",
968                         "org.freedesktop.login1.Manager",
969                         "AttachDevice",
970                         &error, NULL,
971                         "ssb", args[1], args[i], true);
972
973                 if (r < 0) {
974                         log_error("Could not attach device: %s", bus_error_message(&error, -r));
975                         return r;
976                 }
977         }
978
979         return 0;
980 }
981
982 static int flush_devices(sd_bus *bus, char **args, unsigned n) {
983         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
984         int r;
985
986         assert(args);
987
988         polkit_agent_open_if_enabled();
989
990         r = sd_bus_call_method (
991                         bus,
992                         "org.freedesktop.login1",
993                         "/org/freedesktop/login1",
994                         "org.freedesktop.login1.Manager",
995                         "FlushDevices",
996                         &error, NULL,
997                         "b", true);
998         if (r < 0)
999                 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
1000
1001         return r;
1002 }
1003
1004 static int lock_sessions(sd_bus *bus, char **args, unsigned n) {
1005         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1006         int r;
1007
1008         assert(args);
1009
1010         r = sd_bus_call_method (
1011                         bus,
1012                         "org.freedesktop.login1",
1013                         "/org/freedesktop/login1",
1014                         "org.freedesktop.login1.Manager",
1015                         streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1016                         &error, NULL,
1017                         NULL);
1018         if (r < 0)
1019                 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1020
1021         return r;
1022 }
1023
1024 static int terminate_seat(sd_bus *bus, char **args, unsigned n) {
1025         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1026         unsigned i;
1027         int r;
1028
1029         assert(args);
1030
1031         for (i = 1; i < n; i++) {
1032
1033                 r = sd_bus_call_method (
1034                         bus,
1035                         "org.freedesktop.login1",
1036                         "/org/freedesktop/login1",
1037                         "org.freedesktop.login1.Manager",
1038                         "TerminateSeat",
1039                         &error, NULL,
1040                         "s", args[i]);
1041                 if (r < 0) {
1042                         log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1043                         return r;
1044                 }
1045         }
1046
1047         return 0;
1048 }
1049
1050 static void help(void) {
1051         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1052                "Send control commands to or query the login manager.\n\n"
1053                "  -h --help              Show this help\n"
1054                "     --version           Show package version\n"
1055                "     --no-pager          Do not pipe output into a pager\n"
1056                "     --no-legend         Do not show the headers and footers\n"
1057                "     --no-ask-password   Don't prompt for password\n"
1058                "  -H --host=[USER@]HOST  Operate on remote host\n"
1059                "  -M --machine=CONTAINER Operate on local container\n"
1060                "  -p --property=NAME     Show only properties by this name\n"
1061                "  -a --all               Show all properties, including empty ones\n"
1062                "  -l --full              Do not ellipsize output\n"
1063                "     --kill-who=WHO      Who to send signal to\n"
1064                "  -s --signal=SIGNAL     Which signal to send\n\n"
1065                "Commands:\n"
1066                "  list-sessions            List sessions\n"
1067                "  session-status ID...     Show session status\n"
1068                "  show-session [ID...]     Show properties of sessions or the manager\n"
1069                "  activate ID              Activate a session\n"
1070                "  lock-session ID...       Screen lock one or more sessions\n"
1071                "  unlock-session ID...     Screen unlock one or more sessions\n"
1072                "  lock-sessions            Screen lock all current sessions\n"
1073                "  unlock-sessions          Screen unlock all current sessions\n"
1074                "  terminate-session ID...  Terminate one or more sessions\n"
1075                "  kill-session ID...       Send signal to processes of a session\n"
1076                "  list-users               List users\n"
1077                "  user-status USER...      Show user status\n"
1078                "  show-user [USER...]      Show properties of users or the manager\n"
1079                "  enable-linger USER...    Enable linger state of one or more users\n"
1080                "  disable-linger USER...   Disable linger state of one or more users\n"
1081                "  terminate-user USER...   Terminate all sessions of one or more users\n"
1082                "  kill-user USER...        Send signal to processes of a user\n"
1083                "  list-seats               List seats\n"
1084                "  seat-status NAME...      Show seat status\n"
1085                "  show-seat NAME...        Show properties of one or more seats\n"
1086                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
1087                "  flush-devices            Flush all device associations\n"
1088                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n"
1089                , program_invocation_short_name);
1090 }
1091
1092 static int parse_argv(int argc, char *argv[]) {
1093
1094         enum {
1095                 ARG_VERSION = 0x100,
1096                 ARG_NO_PAGER,
1097                 ARG_NO_LEGEND,
1098                 ARG_KILL_WHO,
1099                 ARG_NO_ASK_PASSWORD,
1100         };
1101
1102         static const struct option options[] = {
1103                 { "help",            no_argument,       NULL, 'h'                 },
1104                 { "version",         no_argument,       NULL, ARG_VERSION         },
1105                 { "property",        required_argument, NULL, 'p'                 },
1106                 { "all",             no_argument,       NULL, 'a'                 },
1107                 { "full",            no_argument,       NULL, 'l'                 },
1108                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1109                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1110                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1111                 { "signal",          required_argument, NULL, 's'                 },
1112                 { "host",            required_argument, NULL, 'H'                 },
1113                 { "machine",         required_argument, NULL, 'M'                 },
1114                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
1115                 {}
1116         };
1117
1118         int c, r;
1119
1120         assert(argc >= 0);
1121         assert(argv);
1122
1123         while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1124
1125                 switch (c) {
1126
1127                 case 'h':
1128                         help();
1129                         return 0;
1130
1131                 case ARG_VERSION:
1132                         puts(PACKAGE_STRING);
1133                         puts(SYSTEMD_FEATURES);
1134                         return 0;
1135
1136                 case 'p': {
1137                         r = strv_extend(&arg_property, optarg);
1138                         if (r < 0)
1139                                 return log_oom();
1140
1141                         /* If the user asked for a particular
1142                          * property, show it to him, even if it is
1143                          * empty. */
1144                         arg_all = true;
1145                         break;
1146                 }
1147
1148                 case 'a':
1149                         arg_all = true;
1150                         break;
1151
1152                 case 'l':
1153                         arg_full = true;
1154                         break;
1155
1156                 case ARG_NO_PAGER:
1157                         arg_no_pager = true;
1158                         break;
1159
1160                 case ARG_NO_LEGEND:
1161                         arg_legend = false;
1162                         break;
1163
1164                 case ARG_NO_ASK_PASSWORD:
1165                         arg_ask_password = false;
1166                         break;
1167
1168                 case ARG_KILL_WHO:
1169                         arg_kill_who = optarg;
1170                         break;
1171
1172                 case 's':
1173                         arg_signal = signal_from_string_try_harder(optarg);
1174                         if (arg_signal < 0) {
1175                                 log_error("Failed to parse signal string %s.", optarg);
1176                                 return -EINVAL;
1177                         }
1178                         break;
1179
1180                 case 'H':
1181                         arg_transport = BUS_TRANSPORT_REMOTE;
1182                         arg_host = optarg;
1183                         break;
1184
1185                 case 'M':
1186                         arg_transport = BUS_TRANSPORT_CONTAINER;
1187                         arg_host = optarg;
1188                         break;
1189
1190                 case '?':
1191                         return -EINVAL;
1192
1193                 default:
1194                         assert_not_reached("Unhandled option");
1195                 }
1196
1197         return 1;
1198 }
1199
1200 static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
1201
1202         static const struct {
1203                 const char* verb;
1204                 const enum {
1205                         MORE,
1206                         LESS,
1207                         EQUAL
1208                 } argc_cmp;
1209                 const int argc;
1210                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1211         } verbs[] = {
1212                 { "list-sessions",         LESS,   1, list_sessions     },
1213                 { "session-status",        MORE,   2, show_session      },
1214                 { "show-session",          MORE,   1, show_session      },
1215                 { "activate",              EQUAL,  2, activate          },
1216                 { "lock-session",          MORE,   2, activate          },
1217                 { "unlock-session",        MORE,   2, activate          },
1218                 { "lock-sessions",         EQUAL,  1, lock_sessions     },
1219                 { "unlock-sessions",       EQUAL,  1, lock_sessions     },
1220                 { "terminate-session",     MORE,   2, activate          },
1221                 { "kill-session",          MORE,   2, kill_session      },
1222                 { "list-users",            EQUAL,  1, list_users        },
1223                 { "user-status",           MORE,   2, show_user         },
1224                 { "show-user",             MORE,   1, show_user         },
1225                 { "enable-linger",         MORE,   2, enable_linger     },
1226                 { "disable-linger",        MORE,   2, enable_linger     },
1227                 { "terminate-user",        MORE,   2, terminate_user    },
1228                 { "kill-user",             MORE,   2, kill_user         },
1229                 { "list-seats",            EQUAL,  1, list_seats        },
1230                 { "seat-status",           MORE,   2, show_seat         },
1231                 { "show-seat",             MORE,   1, show_seat         },
1232                 { "attach",                MORE,   3, attach            },
1233                 { "flush-devices",         EQUAL,  1, flush_devices     },
1234                 { "terminate-seat",        MORE,   2, terminate_seat    },
1235         };
1236
1237         int left;
1238         unsigned i;
1239
1240         assert(argc >= 0);
1241         assert(argv);
1242
1243         left = argc - optind;
1244
1245         if (left <= 0)
1246                 /* Special rule: no arguments means "list-sessions" */
1247                 i = 0;
1248         else {
1249                 if (streq(argv[optind], "help")) {
1250                         help();
1251                         return 0;
1252                 }
1253
1254                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1255                         if (streq(argv[optind], verbs[i].verb))
1256                                 break;
1257
1258                 if (i >= ELEMENTSOF(verbs)) {
1259                         log_error("Unknown operation %s", argv[optind]);
1260                         return -EINVAL;
1261                 }
1262         }
1263
1264         switch (verbs[i].argc_cmp) {
1265
1266         case EQUAL:
1267                 if (left != verbs[i].argc) {
1268                         log_error("Invalid number of arguments.");
1269                         return -EINVAL;
1270                 }
1271
1272                 break;
1273
1274         case MORE:
1275                 if (left < verbs[i].argc) {
1276                         log_error("Too few arguments.");
1277                         return -EINVAL;
1278                 }
1279
1280                 break;
1281
1282         case LESS:
1283                 if (left > verbs[i].argc) {
1284                         log_error("Too many arguments.");
1285                         return -EINVAL;
1286                 }
1287
1288                 break;
1289
1290         default:
1291                 assert_not_reached("Unknown comparison operator.");
1292         }
1293
1294         return verbs[i].dispatch(bus, argv + optind, left);
1295 }
1296
1297 int main(int argc, char *argv[]) {
1298         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1299         int r;
1300
1301         setlocale(LC_ALL, "");
1302         log_parse_environment();
1303         log_open();
1304
1305         r = parse_argv(argc, argv);
1306         if (r <= 0)
1307                 goto finish;
1308
1309         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1310         if (r < 0) {
1311                 log_error("Failed to create bus connection: %s", strerror(-r));
1312                 goto finish;
1313         }
1314
1315         r = loginctl_main(bus, argc, argv);
1316
1317 finish:
1318         pager_close();
1319
1320         strv_free(arg_property);
1321
1322         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1323 }