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