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