chiark / gitweb /
loginctl: correctly show session IDs on session-status
[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 } SessionStatusInfo;
275
276 typedef struct UserStatusInfo {
277         uid_t uid;
278         const char *name;
279         usec_t timestamp;
280         const char *state;
281         char **sessions;
282         const char *display;
283         const char *slice;
284 } UserStatusInfo;
285
286 typedef struct SeatStatusInfo {
287         const char *id;
288         const char *active_session;
289         char **sessions;
290 } SeatStatusInfo;
291
292 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
293         const char *contents;
294         int r;
295
296         r = sd_bus_message_peek_type(m, NULL, &contents);
297         if (r < 0)
298                 return r;
299
300         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
301         if (r < 0)
302                 return r;
303
304         if (contents[0] == 's' || contents[0] == 'o') {
305                 const char *s;
306                 char **p = (char **) userdata;
307
308                 r = sd_bus_message_read_basic(m, contents[0], &s);
309                 if (r < 0)
310                         return r;
311
312                 free(*p);
313                 *p = strdup(s);
314
315                 if (!*p)
316                         return -ENOMEM;
317         } else {
318                 r = sd_bus_message_read_basic(m, contents[0], userdata);
319                 if (r < 0)
320                         return r;
321         }
322
323         r = sd_bus_message_skip(m, contents+1);
324         if (r < 0)
325                 return r;
326
327         r = sd_bus_message_exit_container(m);
328         if (r < 0)
329                 return r;
330
331         return 0;
332 }
333
334 static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
335         const char *name;
336         int r;
337
338         assert(bus);
339         assert(m);
340
341         r = sd_bus_message_enter_container(m, 'a', "(so)");
342         if (r < 0)
343                 return r;
344
345         while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
346                 r = strv_extend(userdata, name);
347                 if (r < 0)
348                         return r;
349         }
350         if (r < 0)
351                 return r;
352
353         return sd_bus_message_exit_container(m);
354 }
355
356 static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
357
358         static const struct bus_properties_map map[]  = {
359                 { "Id",         "s", NULL, offsetof(SessionStatusInfo, id) },
360                 { "Name",       "s", NULL, offsetof(SessionStatusInfo, name) },
361                 { "TTY",        "s", NULL, offsetof(SessionStatusInfo, tty) },
362                 { "Display",    "s", NULL, offsetof(SessionStatusInfo, display) },
363                 { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
364                 { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
365                 { "Service",    "s", NULL, offsetof(SessionStatusInfo, service) },
366                 { "Type",       "s", NULL, offsetof(SessionStatusInfo, type) },
367                 { "Class",      "s", NULL, offsetof(SessionStatusInfo, class) },
368                 { "Scope",      "s", NULL, offsetof(SessionStatusInfo, scope) },
369                 { "State",      "s", NULL, offsetof(SessionStatusInfo, state) },
370                 { "VTNr",       "u", NULL, offsetof(SessionStatusInfo, vtnr) },
371                 { "Leader",     "u", NULL, offsetof(SessionStatusInfo, leader) },
372                 { "Remote",     "b", NULL, offsetof(SessionStatusInfo, remote) },
373                 { "Timestamp",  "t", NULL, offsetof(SessionStatusInfo, timestamp) },
374                 { "User",       "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
375                 { "Seat",       "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) },
376                 {}
377         };
378
379         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
380         char since2[FORMAT_TIMESTAMP_MAX], *s2;
381         SessionStatusInfo i = {};
382         int r;
383
384         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
385         if (r < 0) {
386                 log_error("Could not get properties: %s", strerror(-r));
387                 return r;
388         }
389
390         if (*new_line)
391                 printf("\n");
392
393         *new_line = true;
394
395         printf("%s - ", strna(i.id));
396
397         if (i.name)
398                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
399         else
400                 printf("%u\n", (unsigned) i.uid);
401
402         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
403         s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
404
405         if (s1)
406                 printf("\t   Since: %s; %s\n", s2, s1);
407         else if (s2)
408                 printf("\t   Since: %s\n", s2);
409
410         if (i.leader > 0) {
411                 _cleanup_free_ char *t = NULL;
412
413                 printf("\t  Leader: %u", (unsigned) i.leader);
414
415                 get_process_comm(i.leader, &t);
416                 if (t)
417                         printf(" (%s)", t);
418
419                 printf("\n");
420         }
421
422         if (!isempty(i.seat)) {
423                 printf("\t    Seat: %s", i.seat);
424
425                 if (i.vtnr > 0)
426                         printf("; vc%i", i.vtnr);
427
428                 printf("\n");
429         }
430
431         if (i.tty)
432                 printf("\t     TTY: %s\n", i.tty);
433         else if (i.display)
434                 printf("\t Display: %s\n", i.display);
435
436         if (i.remote_host && i.remote_user)
437                 printf("\t  Remote: %s@%s\n", i.remote_user, i.remote_host);
438         else if (i.remote_host)
439                 printf("\t  Remote: %s\n", i.remote_host);
440         else if (i.remote_user)
441                 printf("\t  Remote: user %s\n", i.remote_user);
442         else if (i.remote)
443                 printf("\t  Remote: Yes\n");
444
445         if (i.service) {
446                 printf("\t Service: %s", i.service);
447
448                 if (i.type)
449                         printf("; type %s", i.type);
450
451                 if (i.class)
452                         printf("; class %s", i.class);
453
454                 printf("\n");
455         } else if (i.type) {
456                 printf("\t    Type: %s\n", i.type);
457
458                 if (i.class)
459                         printf("; class %s", i.class);
460         } else if (i.class)
461                 printf("\t   Class: %s\n", i.class);
462
463         if (i.state)
464                 printf("\t   State: %s\n", i.state);
465
466         if (i.scope) {
467                 printf("\t    Unit: %s\n", i.scope);
468                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
469         }
470
471         return 0;
472 }
473
474 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
475
476         static const struct bus_properties_map map[]  = {
477                 { "Name",       "s",     NULL, offsetof(UserStatusInfo, name) },
478                 { "Slice",      "s",     NULL, offsetof(UserStatusInfo, slice) },
479                 { "State",      "s",     NULL, offsetof(UserStatusInfo, state) },
480                 { "UID",        "u",     NULL, offsetof(UserStatusInfo, uid) },
481                 { "Timestamp",  "t",     NULL, offsetof(UserStatusInfo, timestamp) },
482                 { "Display",    "(so)",  prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
483                 { "Sessions",   "a(so)", prop_map_sessions_strv,   offsetof(UserStatusInfo, sessions) },
484                 {}
485         };
486
487         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
488         char since2[FORMAT_TIMESTAMP_MAX], *s2;
489         UserStatusInfo i = {};
490         int r;
491
492         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
493         if (r < 0) {
494                 log_error("Could not get properties: %s", strerror(-r));
495                 goto finish;
496         }
497
498         if (*new_line)
499                 printf("\n");
500
501         *new_line = true;
502
503         if (i.name)
504                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
505         else
506                 printf("%u\n", (unsigned) i.uid);
507
508         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
509         s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
510
511         if (s1)
512                 printf("\t   Since: %s; %s\n", s2, s1);
513         else if (s2)
514                 printf("\t   Since: %s\n", s2);
515
516         if (!isempty(i.state))
517                 printf("\t   State: %s\n", i.state);
518
519         if (!strv_isempty(i.sessions)) {
520                 char **l;
521                 printf("\tSessions:");
522
523                 STRV_FOREACH(l, i.sessions) {
524                         if (streq_ptr(*l, i.display))
525                                 printf(" *%s", *l);
526                         else
527                                 printf(" %s", *l);
528                 }
529
530                 printf("\n");
531         }
532
533         if (i.slice) {
534                 printf("\t    Unit: %s\n", i.slice);
535                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
536         }
537
538 finish:
539         strv_free(i.sessions);
540
541         return r;
542 }
543
544 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
545
546         static const struct bus_properties_map map[]  = {
547                 { "Id",            "s",     NULL, offsetof(SeatStatusInfo, id) },
548                 { "ActiveSession", "(so)",  prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
549                 { "Sessions",      "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
550                 {}
551         };
552
553         SeatStatusInfo i = {};
554         int r;
555
556         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
557         if (r < 0) {
558                 log_error("Could not get properties: %s", strerror(-r));
559                 goto finish;
560         }
561
562         if (*new_line)
563                 printf("\n");
564
565         *new_line = true;
566
567         printf("%s\n", strna(i.id));
568
569         if (!strv_isempty(i.sessions)) {
570                 char **l;
571                 printf("\tSessions:");
572
573                 STRV_FOREACH(l, i.sessions) {
574                         if (streq_ptr(*l, i.active_session))
575                                 printf(" *%s", *l);
576                         else
577                                 printf(" %s", *l);
578                 }
579
580                 printf("\n");
581         }
582
583         if (arg_transport == BUS_TRANSPORT_LOCAL) {
584                 unsigned c;
585
586                 c = columns();
587                 if (c > 21)
588                         c -= 21;
589                 else
590                         c = 0;
591
592                 printf("\t Devices:\n");
593
594                 show_sysfs(i.id, "\t\t  ", c);
595         }
596
597 finish:
598         strv_free(i.sessions);
599
600         return r;
601 }
602
603 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
604         int r;
605
606         if (*new_line)
607                 printf("\n");
608
609         *new_line = true;
610
611         r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
612         if (r < 0)
613                 log_error("Could not get properties: %s", strerror(-r));
614
615         return r;
616 }
617
618 static int show_session(sd_bus *bus, char **args, unsigned n) {
619         bool properties, new_line = false;
620         unsigned i;
621         int r;
622
623         assert(bus);
624         assert(args);
625
626         properties = !strstr(args[0], "status");
627
628         pager_open_if_enabled();
629
630         if (properties && n <= 1) {
631                 /* If not argument is specified inspect the manager
632                  * itself */
633                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
634         }
635
636         for (i = 1; i < n; i++) {
637                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
638                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
639                 const char *path = NULL;
640
641                 r = sd_bus_call_method(
642                                 bus,
643                                 "org.freedesktop.login1",
644                                 "/org/freedesktop/login1",
645                                 "org.freedesktop.login1.Manager",
646                                 "GetSession",
647                                 &error, &reply,
648                                 "s", args[i]);
649                 if (r < 0) {
650                         log_error("Failed to get session: %s", bus_error_message(&error, r));
651                         return r;
652                 }
653
654                 r = sd_bus_message_read(reply, "o", &path);
655                 if (r < 0)
656                         return bus_log_parse_error(r);
657
658                 if (properties)
659                         r = show_properties(bus, path, &new_line);
660                 else
661                         r = print_session_status_info(bus, path, &new_line);
662
663                 if (r < 0)
664                         return r;
665         }
666
667         return 0;
668 }
669
670 static int show_user(sd_bus *bus, char **args, unsigned n) {
671         bool properties, new_line = false;
672         unsigned i;
673         int r;
674
675         assert(bus);
676         assert(args);
677
678         properties = !strstr(args[0], "status");
679
680         pager_open_if_enabled();
681
682         if (properties && n <= 1) {
683                 /* If not argument is specified inspect the manager
684                  * itself */
685                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
686         }
687
688         for (i = 1; i < n; i++) {
689                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
690                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
691                 const char *path = NULL;
692                 uid_t uid;
693
694                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
695                 if (r < 0) {
696                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
697                         return r;
698                 }
699
700                 r = sd_bus_call_method(
701                                 bus,
702                                 "org.freedesktop.login1",
703                                 "/org/freedesktop/login1",
704                                 "org.freedesktop.login1.Manager",
705                                 "GetUser",
706                                 &error, &reply,
707                                 "u", (uint32_t) uid);
708                 if (r < 0) {
709                         log_error("Failed to get user: %s", bus_error_message(&error, r));
710                         return r;
711                 }
712
713                 r = sd_bus_message_read(reply, "o", &path);
714                 if (r < 0)
715                         return bus_log_parse_error(r);
716
717                 if (properties)
718                         r = show_properties(bus, path, &new_line);
719                 else
720                         r = print_user_status_info(bus, path, &new_line);
721
722                 if (r < 0)
723                         return r;
724         }
725
726         return 0;
727 }
728
729 static int show_seat(sd_bus *bus, char **args, unsigned n) {
730         bool properties, new_line = false;
731         unsigned i;
732         int r;
733
734         assert(bus);
735         assert(args);
736
737         properties = !strstr(args[0], "status");
738
739         pager_open_if_enabled();
740
741         if (properties && n <= 1) {
742                 /* If not argument is specified inspect the manager
743                  * itself */
744                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
745         }
746
747         for (i = 1; i < n; i++) {
748                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
749                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
750                 const char *path = NULL;
751
752                 r = sd_bus_call_method(
753                                 bus,
754                                 "org.freedesktop.login1",
755                                 "/org/freedesktop/login1",
756                                 "org.freedesktop.login1.Manager",
757                                 "GetSeat",
758                                 &error, &reply,
759                                 "s", args[i]);
760                 if (r < 0) {
761                         log_error("Failed to get seat: %s", bus_error_message(&error, r));
762                         return r;
763                 }
764
765                 r = sd_bus_message_read(reply, "o", &path);
766                 if (r < 0)
767                         return bus_log_parse_error(r);
768
769                 if (properties)
770                         r = show_properties(bus, path, &new_line);
771                 else
772                         r = print_seat_status_info(bus, path, &new_line);
773
774                 if (r < 0)
775                         return r;
776         }
777
778         return 0;
779 }
780
781 static int activate(sd_bus *bus, char **args, unsigned n) {
782         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
783         unsigned i;
784         int r;
785
786         assert(args);
787
788         for (i = 1; i < n; i++) {
789
790                 r = sd_bus_call_method (
791                                 bus,
792                                 "org.freedesktop.login1",
793                                 "/org/freedesktop/login1",
794                                 "org.freedesktop.login1.Manager",
795                                 streq(args[0], "lock-session")      ? "LockSession" :
796                                 streq(args[0], "unlock-session")    ? "UnlockSession" :
797                                 streq(args[0], "terminate-session") ? "TerminateSession" :
798                                                                       "ActivateSession",
799                                 &error, NULL,
800                                 "s", args[i]);
801                 if (r < 0) {
802                         log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
803                         return r;
804                 }
805         }
806
807         return 0;
808 }
809
810 static int kill_session(sd_bus *bus, char **args, unsigned n) {
811         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
812         unsigned i;
813         int r;
814
815         assert(args);
816
817         if (!arg_kill_who)
818                 arg_kill_who = "all";
819
820         for (i = 1; i < n; i++) {
821
822                 r = sd_bus_call_method (
823                         bus,
824                         "org.freedesktop.login1",
825                         "/org/freedesktop/login1",
826                         "org.freedesktop.login1.Manager",
827                         "KillSession",
828                         &error, NULL,
829                         "ssi", args[i], arg_kill_who, arg_signal);
830                 if (r < 0) {
831                         log_error("Could not kill session: %s", bus_error_message(&error, -r));
832                         return r;
833                 }
834         }
835
836         return 0;
837 }
838
839 static int enable_linger(sd_bus *bus, char **args, unsigned n) {
840         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
841         unsigned i;
842         bool b;
843         int r;
844
845         assert(args);
846
847         polkit_agent_open_if_enabled();
848
849         b = streq(args[0], "enable-linger");
850
851         for (i = 1; i < n; i++) {
852                 uid_t uid;
853
854                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
855                 if (r < 0) {
856                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
857                         return r;
858                 }
859
860                 r = sd_bus_call_method (
861                         bus,
862                         "org.freedesktop.login1",
863                         "/org/freedesktop/login1",
864                         "org.freedesktop.login1.Manager",
865                         "SetUserLinger",
866                         &error, NULL,
867                         "ubb", (uint32_t) uid, b, true);
868                 if (r < 0) {
869                         log_error("Could not enable linger: %s", bus_error_message(&error, -r));
870                         return r;
871                 }
872         }
873
874         return 0;
875 }
876
877 static int terminate_user(sd_bus *bus, char **args, unsigned n) {
878         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
879         unsigned i;
880         int r;
881
882         assert(args);
883
884         for (i = 1; i < n; i++) {
885                 uid_t uid;
886
887                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
888                 if (r < 0) {
889                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
890                         return r;
891                 }
892
893                 r = sd_bus_call_method (
894                         bus,
895                         "org.freedesktop.login1",
896                         "/org/freedesktop/login1",
897                         "org.freedesktop.login1.Manager",
898                         "TerminateUser",
899                         &error, NULL,
900                         "u", (uint32_t) uid);
901                 if (r < 0) {
902                         log_error("Could not terminate user: %s", bus_error_message(&error, -r));
903                         return r;
904                 }
905         }
906
907         return 0;
908 }
909
910 static int kill_user(sd_bus *bus, char **args, unsigned n) {
911         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
912         unsigned i;
913         int r;
914
915         assert(args);
916
917         if (!arg_kill_who)
918                 arg_kill_who = "all";
919
920         for (i = 1; i < n; i++) {
921                 uid_t uid;
922
923                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
924                 if (r < 0) {
925                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
926                         return r;
927                 }
928
929                 r = sd_bus_call_method (
930                         bus,
931                         "org.freedesktop.login1",
932                         "/org/freedesktop/login1",
933                         "org.freedesktop.login1.Manager",
934                         "KillUser",
935                         &error, NULL,
936                         "ui", (uint32_t) uid, arg_signal);
937                 if (r < 0) {
938                         log_error("Could not kill user: %s", bus_error_message(&error, -r));
939                         return r;
940                 }
941         }
942
943         return 0;
944 }
945
946 static int attach(sd_bus *bus, char **args, unsigned n) {
947         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
948         unsigned i;
949         int r;
950
951         assert(args);
952
953         polkit_agent_open_if_enabled();
954
955         for (i = 2; i < n; i++) {
956
957                 r = sd_bus_call_method (
958                         bus,
959                         "org.freedesktop.login1",
960                         "/org/freedesktop/login1",
961                         "org.freedesktop.login1.Manager",
962                         "AttachDevice",
963                         &error, NULL,
964                         "ssb", args[1], args[i], true);
965
966                 if (r < 0) {
967                         log_error("Could not attach device: %s", bus_error_message(&error, -r));
968                         return r;
969                 }
970         }
971
972         return 0;
973 }
974
975 static int flush_devices(sd_bus *bus, char **args, unsigned n) {
976         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
977         int r;
978
979         assert(args);
980
981         polkit_agent_open_if_enabled();
982
983         r = sd_bus_call_method (
984                         bus,
985                         "org.freedesktop.login1",
986                         "/org/freedesktop/login1",
987                         "org.freedesktop.login1.Manager",
988                         "FlushDevices",
989                         &error, NULL,
990                         "b", true);
991         if (r < 0)
992                 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
993
994         return r;
995 }
996
997 static int lock_sessions(sd_bus *bus, char **args, unsigned n) {
998         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
999         int r;
1000
1001         assert(args);
1002
1003         r = sd_bus_call_method (
1004                         bus,
1005                         "org.freedesktop.login1",
1006                         "/org/freedesktop/login1",
1007                         "org.freedesktop.login1.Manager",
1008                         streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1009                         &error, NULL,
1010                         NULL);
1011         if (r < 0)
1012                 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1013
1014         return r;
1015 }
1016
1017 static int terminate_seat(sd_bus *bus, char **args, unsigned n) {
1018         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1019         unsigned i;
1020         int r;
1021
1022         assert(args);
1023
1024         for (i = 1; i < n; i++) {
1025
1026                 r = sd_bus_call_method (
1027                         bus,
1028                         "org.freedesktop.login1",
1029                         "/org/freedesktop/login1",
1030                         "org.freedesktop.login1.Manager",
1031                         "TerminateSeat",
1032                         &error, NULL,
1033                         "s", args[i]);
1034                 if (r < 0) {
1035                         log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1036                         return r;
1037                 }
1038         }
1039
1040         return 0;
1041 }
1042
1043 static int help(void) {
1044
1045         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1046                "Send control commands to or query the login manager.\n\n"
1047                "  -h --help              Show this help\n"
1048                "     --version           Show package version\n"
1049                "     --no-pager          Do not pipe output into a pager\n"
1050                "     --no-legend         Do not show the headers and footers\n"
1051                "     --no-ask-password   Don't prompt for password\n"
1052                "  -H --host=[USER@]HOST  Operate on remote host\n"
1053                "  -M --machine=CONTAINER Operate on local container\n"
1054                "  -p --property=NAME     Show only properties by this name\n"
1055                "  -a --all               Show all properties, including empty ones\n"
1056                "  -l --full              Do not ellipsize output\n"
1057                "     --kill-who=WHO      Who to send signal to\n"
1058                "  -s --signal=SIGNAL     Which signal to send\n\n"
1059                "Commands:\n"
1060                "  list-sessions            List sessions\n"
1061                "  session-status ID...     Show session status\n"
1062                "  show-session [ID...]     Show properties of sessions or the manager\n"
1063                "  activate ID              Activate a session\n"
1064                "  lock-session ID...       Screen lock one or more sessions\n"
1065                "  unlock-session ID...     Screen unlock one or more sessions\n"
1066                "  lock-sessions            Screen lock all current sessions\n"
1067                "  unlock-sessions          Screen unlock all current sessions\n"
1068                "  terminate-session ID...  Terminate one or more sessions\n"
1069                "  kill-session ID...       Send signal to processes of a session\n"
1070                "  list-users               List users\n"
1071                "  user-status USER...      Show user status\n"
1072                "  show-user [USER...]      Show properties of users or the manager\n"
1073                "  enable-linger USER...    Enable linger state of one or more users\n"
1074                "  disable-linger USER...   Disable linger state of one or more users\n"
1075                "  terminate-user USER...   Terminate all sessions of one or more users\n"
1076                "  kill-user USER...        Send signal to processes of a user\n"
1077                "  list-seats               List seats\n"
1078                "  seat-status NAME...      Show seat status\n"
1079                "  show-seat NAME...        Show properties of one or more seats\n"
1080                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
1081                "  flush-devices            Flush all device associations\n"
1082                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n",
1083                program_invocation_short_name);
1084
1085         return 0;
1086 }
1087
1088 static int parse_argv(int argc, char *argv[]) {
1089
1090         enum {
1091                 ARG_VERSION = 0x100,
1092                 ARG_NO_PAGER,
1093                 ARG_NO_LEGEND,
1094                 ARG_KILL_WHO,
1095                 ARG_NO_ASK_PASSWORD,
1096         };
1097
1098         static const struct option options[] = {
1099                 { "help",            no_argument,       NULL, 'h'                 },
1100                 { "version",         no_argument,       NULL, ARG_VERSION         },
1101                 { "property",        required_argument, NULL, 'p'                 },
1102                 { "all",             no_argument,       NULL, 'a'                 },
1103                 { "full",            no_argument,       NULL, 'l'                 },
1104                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1105                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1106                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1107                 { "signal",          required_argument, NULL, 's'                 },
1108                 { "host",            required_argument, NULL, 'H'                 },
1109                 { "machine",         required_argument, NULL, 'M'                 },
1110                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
1111                 {}
1112         };
1113
1114         int c, r;
1115
1116         assert(argc >= 0);
1117         assert(argv);
1118
1119         while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
1120
1121                 switch (c) {
1122
1123                 case 'h':
1124                         return help();
1125
1126                 case ARG_VERSION:
1127                         puts(PACKAGE_STRING);
1128                         puts(SYSTEMD_FEATURES);
1129                         return 0;
1130
1131                 case 'p': {
1132                         r = strv_extend(&arg_property, optarg);
1133                         if (r < 0)
1134                                 return log_oom();
1135
1136                         /* If the user asked for a particular
1137                          * property, show it to him, even if it is
1138                          * empty. */
1139                         arg_all = true;
1140                         break;
1141                 }
1142
1143                 case 'a':
1144                         arg_all = true;
1145                         break;
1146
1147                 case 'l':
1148                         arg_full = true;
1149                         break;
1150
1151                 case ARG_NO_PAGER:
1152                         arg_no_pager = true;
1153                         break;
1154
1155                 case ARG_NO_LEGEND:
1156                         arg_legend = false;
1157                         break;
1158
1159                 case ARG_NO_ASK_PASSWORD:
1160                         arg_ask_password = false;
1161                         break;
1162
1163                 case ARG_KILL_WHO:
1164                         arg_kill_who = optarg;
1165                         break;
1166
1167                 case 's':
1168                         arg_signal = signal_from_string_try_harder(optarg);
1169                         if (arg_signal < 0) {
1170                                 log_error("Failed to parse signal string %s.", optarg);
1171                                 return -EINVAL;
1172                         }
1173                         break;
1174
1175                 case 'H':
1176                         arg_transport = BUS_TRANSPORT_REMOTE;
1177                         arg_host = optarg;
1178                         break;
1179
1180                 case 'M':
1181                         arg_transport = BUS_TRANSPORT_CONTAINER;
1182                         arg_host = optarg;
1183                         break;
1184
1185                 case '?':
1186                         return -EINVAL;
1187
1188                 default:
1189                         assert_not_reached("Unhandled option");
1190                 }
1191         }
1192
1193         return 1;
1194 }
1195
1196 static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
1197
1198         static const struct {
1199                 const char* verb;
1200                 const enum {
1201                         MORE,
1202                         LESS,
1203                         EQUAL
1204                 } argc_cmp;
1205                 const int argc;
1206                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1207         } verbs[] = {
1208                 { "list-sessions",         LESS,   1, list_sessions     },
1209                 { "session-status",        MORE,   2, show_session      },
1210                 { "show-session",          MORE,   1, show_session      },
1211                 { "activate",              EQUAL,  2, activate          },
1212                 { "lock-session",          MORE,   2, activate          },
1213                 { "unlock-session",        MORE,   2, activate          },
1214                 { "lock-sessions",         EQUAL,  1, lock_sessions     },
1215                 { "unlock-sessions",       EQUAL,  1, lock_sessions     },
1216                 { "terminate-session",     MORE,   2, activate          },
1217                 { "kill-session",          MORE,   2, kill_session      },
1218                 { "list-users",            EQUAL,  1, list_users        },
1219                 { "user-status",           MORE,   2, show_user         },
1220                 { "show-user",             MORE,   1, show_user         },
1221                 { "enable-linger",         MORE,   2, enable_linger     },
1222                 { "disable-linger",        MORE,   2, enable_linger     },
1223                 { "terminate-user",        MORE,   2, terminate_user    },
1224                 { "kill-user",             MORE,   2, kill_user         },
1225                 { "list-seats",            EQUAL,  1, list_seats        },
1226                 { "seat-status",           MORE,   2, show_seat         },
1227                 { "show-seat",             MORE,   1, show_seat         },
1228                 { "attach",                MORE,   3, attach            },
1229                 { "flush-devices",         EQUAL,  1, flush_devices     },
1230                 { "terminate-seat",        MORE,   2, terminate_seat    },
1231         };
1232
1233         int left;
1234         unsigned i;
1235
1236         assert(argc >= 0);
1237         assert(argv);
1238
1239         left = argc - optind;
1240
1241         if (left <= 0)
1242                 /* Special rule: no arguments means "list-sessions" */
1243                 i = 0;
1244         else {
1245                 if (streq(argv[optind], "help")) {
1246                         help();
1247                         return 0;
1248                 }
1249
1250                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1251                         if (streq(argv[optind], verbs[i].verb))
1252                                 break;
1253
1254                 if (i >= ELEMENTSOF(verbs)) {
1255                         log_error("Unknown operation %s", argv[optind]);
1256                         return -EINVAL;
1257                 }
1258         }
1259
1260         switch (verbs[i].argc_cmp) {
1261
1262         case EQUAL:
1263                 if (left != verbs[i].argc) {
1264                         log_error("Invalid number of arguments.");
1265                         return -EINVAL;
1266                 }
1267
1268                 break;
1269
1270         case MORE:
1271                 if (left < verbs[i].argc) {
1272                         log_error("Too few arguments.");
1273                         return -EINVAL;
1274                 }
1275
1276                 break;
1277
1278         case LESS:
1279                 if (left > verbs[i].argc) {
1280                         log_error("Too many arguments.");
1281                         return -EINVAL;
1282                 }
1283
1284                 break;
1285
1286         default:
1287                 assert_not_reached("Unknown comparison operator.");
1288         }
1289
1290         return verbs[i].dispatch(bus, argv + optind, left);
1291 }
1292
1293 int main(int argc, char *argv[]) {
1294         _cleanup_bus_unref_ sd_bus *bus = NULL;
1295         int r;
1296
1297         setlocale(LC_ALL, "");
1298         log_parse_environment();
1299         log_open();
1300
1301         r = parse_argv(argc, argv);
1302         if (r <= 0)
1303                 goto finish;
1304
1305         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1306         if (r < 0) {
1307                 log_error("Failed to create bus connection: %s", strerror(-r));
1308                 goto finish;
1309         }
1310
1311         r = loginctl_main(bus, argc, argv);
1312
1313 finish:
1314         pager_close();
1315
1316         strv_free(arg_property);
1317
1318         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1319 }