chiark / gitweb /
unit: use ESRCH as error when we don't find anybody to kill
[elogind.git] / src / logind-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 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 <errno.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <pwd.h>
26
27 #include "logind.h"
28 #include "dbus-common.h"
29 #include "strv.h"
30 #include "polkit.h"
31
32 #define BUS_MANAGER_INTERFACE                                           \
33         " <interface name=\"org.freedesktop.login1.Manager\">\n"        \
34         "  <method name=\"GetSession\">\n"                              \
35         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
36         "   <arg name=\"session\" type=\"o\" direction=\"out\"/>\n"     \
37         "  </method>\n"                                                 \
38         "  <method name=\"GetUser\">\n"                                 \
39         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
40         "   <arg name=\"user\" type=\"o\" direction=\"out\"/>\n"        \
41         "  </method>\n"                                                 \
42         "  <method name=\"GetSeat\">\n"                                 \
43         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
44         "   <arg name=\"seat\" type=\"o\" direction=\"out\"/>\n"        \
45         "  </method>\n"                                                 \
46         "  <method name=\"ListSessions\">\n"                            \
47         "   <arg name=\"sessions\" type=\"a(susso)\" direction=\"out\"/>\n" \
48         "  </method>\n"                                                 \
49         "  <method name=\"ListUsers\">\n"                               \
50         "   <arg name=\"users\" type=\"a(uso)\" direction=\"out\"/>\n"  \
51         "  </method>\n"                                                 \
52         "  <method name=\"ListSeats\">\n"                               \
53         "   <arg name=\"seats\" type=\"a(so)\" direction=\"out\"/>\n"   \
54         "  </method>\n"                                                 \
55         "  <method name=\"CreateSession\">\n"                           \
56         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
57         "   <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n"       \
58         "   <arg name=\"sevice\" type=\"s\" direction=\"in\"/>\n"       \
59         "   <arg name=\"type\" type=\"s\" direction=\"in\"/>\n"         \
60         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
61         "   <arg name=\"vtnr\" type=\"u\" direction=\"in\"/>\n"         \
62         "   <arg name=\"tty\" type=\"s\" direction=\"in\"/>\n"          \
63         "   <arg name=\"display\" type=\"s\" direction=\"in\"/>\n"      \
64         "   <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n"       \
65         "   <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n"  \
66         "   <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n"  \
67         "   <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
68         "   <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
69         "   <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
70         "   <arg name=\"id\" type=\"s\" direction=\"out\"/>\n"          \
71         "   <arg name=\"path\" type=\"o\" direction=\"out\"/>\n"        \
72         "   <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
73         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
74         "  </method>\n"                                                 \
75         "  <method name=\"ActivateSession\">\n"                         \
76         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
77         "  </method>\n"                                                 \
78         "  <method name=\"LockSession\">\n"                             \
79         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
80         "  </method>\n"                                                 \
81         "  <method name=\"UnlockSession\">\n"                           \
82         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
83         "  </method>\n"                                                 \
84         "  <method name=\"TerminateSession\">\n"                        \
85         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
86         "  </method>\n"                                                 \
87         "  <method name=\"TerminateUser\">\n"                           \
88         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
89         "  </method>\n"                                                 \
90         "  <method name=\"TerminateSeat\">\n"                           \
91         "   <arg name=\"id\" type=\"s\" direction=\"in\"/>\n"           \
92         "  </method>\n"                                                 \
93         "  <method name=\"SetUserLinger\">\n"                           \
94         "   <arg name=\"uid\" type=\"u\" direction=\"in\"/>\n"          \
95         "   <arg name=\"b\" type=\"b\" direction=\"in\"/>\n"            \
96         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
97         "  </method>\n"                                                 \
98         "  <method name=\"AttachDevice\">\n"                            \
99         "   <arg name=\"seat\" type=\"s\" direction=\"in\"/>\n"         \
100         "   <arg name=\"sysfs\" type=\"s\" direction=\"in\"/>\n"        \
101         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
102         "  </method>\n"                                                 \
103         "  <method name=\"FlushDevices\">\n"                            \
104         "   <arg name=\"interactive\" type=\"b\" direction=\"in\"/>\n"  \
105         "  </method>\n"                                                 \
106         "  <signal name=\"SessionNew\">\n"                              \
107         "   <arg name=\"id\" type=\"s\"/>\n"                            \
108         "   <arg name=\"path\" type=\"o\"/>\n"                          \
109         "  </signal>\n"                                                 \
110         "  <signal name=\"SessionRemoved\">\n"                          \
111         "   <arg name=\"id\" type=\"s\"/>\n"                            \
112         "   <arg name=\"path\" type=\"o\"/>\n"                          \
113         "  </signal>\n"                                                 \
114         "  <signal name=\"UserNew\">\n"                                 \
115         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
116         "   <arg name=\"path\" type=\"o\"/>\n"                          \
117         "  </signal>\n"                                                 \
118         "  <signal name=\"UserRemoved\">\n"                             \
119         "   <arg name=\"uid\" type=\"u\"/>\n"                           \
120         "   <arg name=\"path\" type=\"o\"/>\n"                          \
121         "  </signal>\n"                                                 \
122         "  <signal name=\"SeatNew\">\n"                                 \
123         "   <arg name=\"id\" type=\"s\"/>\n"                            \
124         "   <arg name=\"path\" type=\"o\"/>\n"                          \
125         "  </signal>\n"                                                 \
126         "  <signal name=\"SeatRemoved\">\n"                             \
127         "   <arg name=\"id\" type=\"s\"/>\n"                            \
128         "   <arg name=\"path\" type=\"o\"/>\n"                          \
129         "  </signal>\n"                                                 \
130         "  <property name=\"ControlGroupHierarchy\" type=\"s\" access=\"read\"/>\n" \
131         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
132         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
133         "  <property name=\"NAutoVTs\" type=\"u\" access=\"read\"/>\n" \
134         "  <property name=\"KillOnlyUsers\" type=\"as\" access=\"read\"/>\n" \
135         "  <property name=\"KillExcludeUsers\" type=\"as\" access=\"read\"/>\n" \
136         "  <property name=\"KillUserProcesses\" type=\"b\" access=\"read\"/>\n" \
137         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
138         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
139         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
140         " </interface>\n"
141
142 #define INTROSPECTION_BEGIN                                             \
143         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
144         "<node>\n"                                                      \
145         BUS_MANAGER_INTERFACE                                           \
146         BUS_PROPERTIES_INTERFACE                                        \
147         BUS_PEER_INTERFACE                                              \
148         BUS_INTROSPECTABLE_INTERFACE
149
150 #define INTROSPECTION_END                                               \
151         "</node>\n"
152
153 #define INTERFACES_LIST                              \
154         BUS_GENERIC_INTERFACES_LIST                  \
155         "org.freedesktop.login1.Manager\0"
156
157 static int bus_manager_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
158         Manager *m = data;
159         dbus_bool_t b;
160
161         assert(i);
162         assert(property);
163         assert(m);
164
165         b = manager_get_idle_hint(m, NULL) > 0;
166         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
167                 return -ENOMEM;
168
169         return 0;
170 }
171
172 static int bus_manager_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
173         Manager *m = data;
174         dual_timestamp t;
175         uint64_t u;
176
177         assert(i);
178         assert(property);
179         assert(m);
180
181         manager_get_idle_hint(m, &t);
182         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
183
184         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
185                 return -ENOMEM;
186
187         return 0;
188 }
189
190 static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
191         Session *session = NULL;
192         User *user = NULL;
193         const char *type, *seat, *tty, *display, *remote_user, *remote_host, *service;
194         uint32_t uid, leader, audit_id = 0;
195         dbus_bool_t remote, kill_processes;
196         char **controllers = NULL, **reset_controllers = NULL;
197         SessionType t;
198         Seat *s;
199         DBusMessageIter iter;
200         int r;
201         char *id = NULL, *p;
202         uint32_t vtnr = 0;
203         int fifo_fd = -1;
204         DBusMessage *reply = NULL;
205         bool b;
206
207         assert(m);
208         assert(message);
209         assert(_reply);
210
211         if (!dbus_message_iter_init(message, &iter) ||
212             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
213                 return -EINVAL;
214
215         dbus_message_iter_get_basic(&iter, &uid);
216
217         if (!dbus_message_iter_next(&iter) ||
218             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
219                 return -EINVAL;
220
221         dbus_message_iter_get_basic(&iter, &leader);
222
223         if (leader <= 0 ||
224             !dbus_message_iter_next(&iter) ||
225             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
226                 return -EINVAL;
227
228         dbus_message_iter_get_basic(&iter, &service);
229
230         if (!dbus_message_iter_next(&iter) ||
231             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
232                 return -EINVAL;
233
234         dbus_message_iter_get_basic(&iter, &type);
235         t = session_type_from_string(type);
236
237         if (t < 0 ||
238             !dbus_message_iter_next(&iter) ||
239             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
240                 return -EINVAL;
241
242         dbus_message_iter_get_basic(&iter, &seat);
243
244         if (isempty(seat))
245                 s = NULL;
246         else {
247                 s = hashmap_get(m->seats, seat);
248                 if (!s)
249                         return -ENOENT;
250         }
251
252         if (!dbus_message_iter_next(&iter) ||
253             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
254                 return -EINVAL;
255
256         dbus_message_iter_get_basic(&iter, &vtnr);
257
258         if (!dbus_message_iter_next(&iter) ||
259             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
260                 return -EINVAL;
261
262         dbus_message_iter_get_basic(&iter, &tty);
263
264         if (tty_is_vc(tty)) {
265                 int v;
266
267                 if (!s)
268                         s = m->vtconsole;
269                 else if (s != m->vtconsole)
270                         return -EINVAL;
271
272                 v = vtnr_from_tty(tty);
273
274                 if (v <= 0)
275                         return v < 0 ? v : -EINVAL;
276
277                 if (vtnr <= 0)
278                         vtnr = (uint32_t) v;
279                 else if (vtnr != (uint32_t) v)
280                         return -EINVAL;
281
282         } else if (!isempty(tty) && s && seat_is_vtconsole(s))
283                 return -EINVAL;
284
285         if (s) {
286                 if (seat_is_vtconsole(s)) {
287                         if (vtnr <= 0 || vtnr > 63)
288                                 return -EINVAL;
289                 } else {
290                         if (vtnr > 0)
291                                 return -EINVAL;
292                 }
293         }
294
295         if (!dbus_message_iter_next(&iter) ||
296             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
297                 return -EINVAL;
298
299         dbus_message_iter_get_basic(&iter, &display);
300
301         if (!dbus_message_iter_next(&iter) ||
302             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
303                 return -EINVAL;
304
305         dbus_message_iter_get_basic(&iter, &remote);
306
307         if (!dbus_message_iter_next(&iter) ||
308             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
309                 return -EINVAL;
310
311         dbus_message_iter_get_basic(&iter, &remote_user);
312
313         if (!dbus_message_iter_next(&iter) ||
314             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
315                 return -EINVAL;
316
317         dbus_message_iter_get_basic(&iter, &remote_host);
318
319         if (!dbus_message_iter_next(&iter) ||
320             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
321             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
322                 return -EINVAL;
323
324         r = bus_parse_strv_iter(&iter, &controllers);
325         if (r < 0)
326                 return -EINVAL;
327
328         if (strv_contains(controllers, "systemd") ||
329             !dbus_message_iter_next(&iter) ||
330             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
331             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
332                 r = -EINVAL;
333                 goto fail;
334         }
335
336         r = bus_parse_strv_iter(&iter, &reset_controllers);
337         if (r < 0)
338                 goto fail;
339
340         if (strv_contains(reset_controllers, "systemd") ||
341             !dbus_message_iter_next(&iter) ||
342             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
343                 r = -EINVAL;
344                 goto fail;
345         }
346
347         dbus_message_iter_get_basic(&iter, &kill_processes);
348
349         r = manager_add_user_by_uid(m, uid, &user);
350         if (r < 0)
351                 goto fail;
352
353         audit_session_from_pid(leader, &audit_id);
354
355         if (audit_id > 0) {
356                 asprintf(&id, "%lu", (unsigned long) audit_id);
357
358                 if (!id) {
359                         r = -ENOMEM;
360                         goto fail;
361                 }
362
363                 session = hashmap_get(m->sessions, id);
364
365                 if (session) {
366
367                         fifo_fd = session_create_fifo(session);
368                         if (fifo_fd < 0) {
369                                 r = fifo_fd;
370                                 goto fail;
371                         }
372
373                         /* Session already exists, client is probably
374                          * something like "su" which changes uid but
375                          * is still the same audit session */
376
377                         reply = dbus_message_new_method_return(message);
378                         if (!reply) {
379                                 r = -ENOMEM;
380                                 goto fail;
381                         }
382
383                         p = session_bus_path(session);
384                         if (!p) {
385                                 r = -ENOMEM;
386                                 goto fail;
387                         }
388
389                         b = dbus_message_append_args(
390                                         reply,
391                                         DBUS_TYPE_STRING, &session->id,
392                                         DBUS_TYPE_OBJECT_PATH, &p,
393                                         DBUS_TYPE_STRING, &session->user->runtime_path,
394                                         DBUS_TYPE_UNIX_FD, &fifo_fd,
395                                         DBUS_TYPE_INVALID);
396                         free(p);
397
398                         if (!b) {
399                                 r = -ENOMEM;
400                                 goto fail;
401                         }
402
403                         close_nointr_nofail(fifo_fd);
404                         *_reply = reply;
405
406                         return 0;
407                 }
408
409         } else {
410                 do {
411                         free(id);
412                         asprintf(&id, "c%lu", ++m->session_counter);
413
414                         if (!id) {
415                                 r = -ENOMEM;
416                                 goto fail;
417                         }
418
419                 } while (hashmap_get(m->sessions, id));
420         }
421
422         r = manager_add_session(m, user, id, &session);
423         free(id);
424         if (r < 0)
425                 goto fail;
426
427         session->leader = leader;
428         session->audit_id = audit_id;
429         session->type = t;
430         session->remote = remote;
431         session->controllers = controllers;
432         session->reset_controllers = reset_controllers;
433         session->kill_processes = kill_processes;
434         session->vtnr = vtnr;
435
436         controllers = reset_controllers = NULL;
437
438         if (!isempty(tty)) {
439                 session->tty = strdup(tty);
440                 if (!session->tty) {
441                         r = -ENOMEM;
442                         goto fail;
443                 }
444         }
445
446         if (!isempty(display)) {
447                 session->display = strdup(display);
448                 if (!session->display) {
449                         r = -ENOMEM;
450                         goto fail;
451                 }
452         }
453
454         if (!isempty(remote_user)) {
455                 session->remote_user = strdup(remote_user);
456                 if (!session->remote_user) {
457                         r = -ENOMEM;
458                         goto fail;
459                 }
460         }
461
462         if (!isempty(remote_host)) {
463                 session->remote_host = strdup(remote_host);
464                 if (!session->remote_host) {
465                         r = -ENOMEM;
466                         goto fail;
467                 }
468         }
469
470         if (!isempty(service)) {
471                 session->service = strdup(service);
472                 if (!session->service) {
473                         r = -ENOMEM;
474                         goto fail;
475                 }
476         }
477
478         fifo_fd = session_create_fifo(session);
479         if (fifo_fd < 0) {
480                 r = fifo_fd;
481                 goto fail;
482         }
483
484         if (s) {
485                 r = seat_attach_session(s, session);
486                 if (r < 0)
487                         goto fail;
488         }
489
490         r = session_start(session);
491         if (r < 0)
492                 goto fail;
493
494         reply = dbus_message_new_method_return(message);
495         if (!reply) {
496                 r = -ENOMEM;
497                 goto fail;
498         }
499
500         p = session_bus_path(session);
501         if (!p) {
502                 r = -ENOMEM;
503                 goto fail;
504         }
505
506         b = dbus_message_append_args(
507                         reply,
508                         DBUS_TYPE_STRING, &session->id,
509                         DBUS_TYPE_OBJECT_PATH, &p,
510                         DBUS_TYPE_STRING, &session->user->runtime_path,
511                         DBUS_TYPE_UNIX_FD, &fifo_fd,
512                         DBUS_TYPE_INVALID);
513         free(p);
514
515         if (!b) {
516                 r = -ENOMEM;
517                 goto fail;
518         }
519
520         close_nointr_nofail(fifo_fd);
521         *_reply = reply;
522
523         return 0;
524
525 fail:
526         strv_free(controllers);
527         strv_free(reset_controllers);
528
529         if (session)
530                 session_add_to_gc_queue(session);
531
532         if (user)
533                 user_add_to_gc_queue(user);
534
535         if (fifo_fd >= 0)
536                 close_nointr_nofail(fifo_fd);
537
538         if (reply)
539                 dbus_message_unref(reply);
540
541         return r;
542 }
543
544 static bool device_has_tag(struct udev_device *d, const char *tag) {
545         struct udev_list_entry *first, *item;
546
547         assert(d);
548         assert(tag);
549
550         /* FIXME */
551         udev_device_get_is_initialized(d);
552
553         first = udev_device_get_tags_list_entry(d);
554         udev_list_entry_foreach(item, first)
555                 if (streq(udev_list_entry_get_name(item), tag))
556                         return true;
557
558         return false;
559 }
560
561 static int trigger_device(Manager *m, const char *prefix) {
562         struct udev_enumerate *e;
563         struct udev_list_entry *first, *item;
564         int r;
565
566         assert(m);
567
568         e = udev_enumerate_new(m->udev);
569         if (!e) {
570                 r = -ENOMEM;
571                 goto finish;
572         }
573
574         if (udev_enumerate_scan_devices(e) < 0) {
575                 r = -EIO;
576                 goto finish;
577         }
578
579         first = udev_enumerate_get_list_entry(e);
580         udev_list_entry_foreach(item, first) {
581                 char *t;
582                 const char *p;
583
584                 p = udev_list_entry_get_name(item);
585
586                 if (prefix && !path_startswith(p, prefix))
587                         continue;
588
589                 t = strappend(p, "/uevent");
590                 if (!t) {
591                         r = -ENOMEM;
592                         goto finish;
593                 }
594
595                 write_one_line_file(t, "change");
596                 free(t);
597         }
598
599         r = 0;
600
601 finish:
602         if (e)
603                 udev_enumerate_unref(e);
604
605         return r;
606 }
607
608 static int attach_device(Manager *m, const char *seat, const char *sysfs) {
609         struct udev_device *d;
610         char *rule = NULL, *file = NULL;
611         const char *id_for_seat;
612         int r;
613
614         assert(m);
615         assert(seat);
616         assert(sysfs);
617
618         d = udev_device_new_from_syspath(m->udev, sysfs);
619         if (!d)
620                 return -ENODEV;
621
622         if (!device_has_tag(d, "seat")) {
623                 r = -ENODEV;
624                 goto finish;
625         }
626
627         id_for_seat = udev_device_get_property_value(d, "ID_FOR_SEAT");
628         if (!id_for_seat) {
629                 r = -ENODEV;
630                 goto finish;
631         }
632
633         if (asprintf(&file, "/etc/udev/rules.d/72-seat-%s.rules", id_for_seat) < 0) {
634                 r = -ENOMEM;
635                 goto finish;
636         }
637
638         if (asprintf(&rule, "TAG==\"seat\", ENV{ID_FOR_SEAT}==\"%s\", ENV{ID_SEAT}=\"%s\"", id_for_seat, seat) < 0) {
639                 r = -ENOMEM;
640                 goto finish;
641         }
642
643         mkdir_p("/etc/udev/rules.d", 0755);
644         r = write_one_line_file_atomic(file, rule);
645         if (r < 0)
646                 goto finish;
647
648         r = trigger_device(m, sysfs);
649
650 finish:
651         free(rule);
652         free(file);
653
654         if (d)
655                 udev_device_unref(d);
656
657         return r;
658 }
659
660 static int flush_devices(Manager *m) {
661         DIR *d;
662
663         assert(m);
664
665         d = opendir("/etc/udev/rules.d");
666         if (!d) {
667                 if (errno != ENOENT)
668                         log_warning("Failed to open /etc/udev/rules.d: %m");
669         } else {
670                 struct dirent *de;
671
672                 while ((de = readdir(d))) {
673
674                         if (!dirent_is_file(de))
675                                 continue;
676
677                         if (!startswith(de->d_name, "72-seat-"))
678                                 continue;
679
680                         if (!endswith(de->d_name, ".rules"))
681                                 continue;
682
683                         if (unlinkat(dirfd(d), de->d_name, 0) < 0)
684                                 log_warning("Failed to unlink %s: %m", de->d_name);
685                 }
686
687                 closedir(d);
688         }
689
690         return trigger_device(m, NULL);
691 }
692
693 static DBusHandlerResult manager_message_handler(
694                 DBusConnection *connection,
695                 DBusMessage *message,
696                 void *userdata) {
697
698         Manager *m = userdata;
699
700         const BusProperty properties[] = {
701                 { "org.freedesktop.login1.Manager", "ControlGroupHierarchy",  bus_property_append_string,   "s",  m->cgroup_path          },
702                 { "org.freedesktop.login1.Manager", "Controllers",            bus_property_append_strv,     "as", m->controllers          },
703                 { "org.freedesktop.login1.Manager", "ResetControllers",       bus_property_append_strv,     "as", m->reset_controllers    },
704                 { "org.freedesktop.login1.Manager", "NAutoVTs",               bus_property_append_unsigned, "u",  &m->n_autovts           },
705                 { "org.freedesktop.login1.Manager", "KillOnlyUsers",          bus_property_append_strv,     "as", m->kill_only_users      },
706                 { "org.freedesktop.login1.Manager", "KillExcludeUsers",       bus_property_append_strv,     "as", m->kill_exclude_users   },
707                 { "org.freedesktop.login1.Manager", "KillUserProcesses",      bus_property_append_bool,     "b",  &m->kill_user_processes },
708                 { "org.freedesktop.login1.Manager", "IdleHint",               bus_manager_append_idle_hint, "b",  m                       },
709                 { "org.freedesktop.login1.Manager", "IdleSinceHint",          bus_manager_append_idle_hint_since, "t", m                  },
710                 { "org.freedesktop.login1.Manager", "IdleSinceHintMonotonic", bus_manager_append_idle_hint_since, "t", m                  },
711                 { NULL, NULL, NULL, NULL, NULL }
712         };
713
714         DBusError error;
715         DBusMessage *reply = NULL;
716         int r;
717
718         assert(connection);
719         assert(message);
720         assert(m);
721
722         dbus_error_init(&error);
723
724         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSession")) {
725                 const char *name;
726                 char *p;
727                 Session *session;
728                 bool b;
729
730                 if (!dbus_message_get_args(
731                                     message,
732                                     &error,
733                                     DBUS_TYPE_STRING, &name,
734                                     DBUS_TYPE_INVALID))
735                         return bus_send_error_reply(connection, message, &error, -EINVAL);
736
737                 session = hashmap_get(m->sessions, name);
738                 if (!session)
739                         return bus_send_error_reply(connection, message, &error, -ENOENT);
740
741                 reply = dbus_message_new_method_return(message);
742                 if (!reply)
743                         goto oom;
744
745                 p = session_bus_path(session);
746                 if (!p)
747                         goto oom;
748
749                 b = dbus_message_append_args(
750                                 reply,
751                                 DBUS_TYPE_OBJECT_PATH, &p,
752                                 DBUS_TYPE_INVALID);
753                 free(p);
754
755                 if (!b)
756                         goto oom;
757
758         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetUser")) {
759                 uint32_t uid;
760                 char *p;
761                 User *user;
762                 bool b;
763
764                 if (!dbus_message_get_args(
765                                     message,
766                                     &error,
767                                     DBUS_TYPE_UINT32, &uid,
768                                     DBUS_TYPE_INVALID))
769                         return bus_send_error_reply(connection, message, &error, -EINVAL);
770
771                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
772                 if (!user)
773                         return bus_send_error_reply(connection, message, &error, -ENOENT);
774
775                 reply = dbus_message_new_method_return(message);
776                 if (!reply)
777                         goto oom;
778
779                 p = user_bus_path(user);
780                 if (!p)
781                         goto oom;
782
783                 b = dbus_message_append_args(
784                                 reply,
785                                 DBUS_TYPE_OBJECT_PATH, &p,
786                                 DBUS_TYPE_INVALID);
787                 free(p);
788
789                 if (!b)
790                         goto oom;
791
792         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "GetSeat")) {
793                 const char *name;
794                 char *p;
795                 Seat *seat;
796                 bool b;
797
798                 if (!dbus_message_get_args(
799                                     message,
800                                     &error,
801                                     DBUS_TYPE_STRING, &name,
802                                     DBUS_TYPE_INVALID))
803                         return bus_send_error_reply(connection, message, &error, -EINVAL);
804
805                 seat = hashmap_get(m->seats, name);
806                 if (!seat)
807                         return bus_send_error_reply(connection, message, &error, -ENOENT);
808
809                 reply = dbus_message_new_method_return(message);
810                 if (!reply)
811                         goto oom;
812
813                 p = seat_bus_path(seat);
814                 if (!p)
815                         goto oom;
816
817                 b = dbus_message_append_args(
818                                 reply,
819                                 DBUS_TYPE_OBJECT_PATH, &p,
820                                 DBUS_TYPE_INVALID);
821                 free(p);
822
823                 if (!b)
824                         goto oom;
825
826         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSessions")) {
827                 char *p;
828                 Session *session;
829                 Iterator i;
830                 DBusMessageIter iter, sub;
831                 const char *empty = "";
832
833                 reply = dbus_message_new_method_return(message);
834                 if (!reply)
835                         goto oom;
836
837                 dbus_message_iter_init_append(reply, &iter);
838
839                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(susso)", &sub))
840                         goto oom;
841
842                 HASHMAP_FOREACH(session, m->sessions, i) {
843                         DBusMessageIter sub2;
844                         uint32_t uid;
845
846                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
847                                 goto oom;
848
849                         uid = session->user->uid;
850
851                         p = session_bus_path(session);
852                         if (!p)
853                                 goto oom;
854
855                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
856                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
857                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->user->name) ||
858                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, session->seat ? (const char**) &session->seat->id : &empty) ||
859                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
860                                 free(p);
861                                 goto oom;
862                         }
863
864                         free(p);
865
866                         if (!dbus_message_iter_close_container(&sub, &sub2))
867                                 goto oom;
868                 }
869
870                 if (!dbus_message_iter_close_container(&iter, &sub))
871                         goto oom;
872
873         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListUsers")) {
874                 char *p;
875                 User *user;
876                 Iterator i;
877                 DBusMessageIter iter, sub;
878
879                 reply = dbus_message_new_method_return(message);
880                 if (!reply)
881                         goto oom;
882
883                 dbus_message_iter_init_append(reply, &iter);
884
885                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(uso)", &sub))
886                         goto oom;
887
888                 HASHMAP_FOREACH(user, m->users, i) {
889                         DBusMessageIter sub2;
890                         uint32_t uid;
891
892                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
893                                 goto oom;
894
895                         uid = user->uid;
896
897                         p = user_bus_path(user);
898                         if (!p)
899                                 goto oom;
900
901                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_UINT32, &uid) ||
902                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &user->name) ||
903                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
904                                 free(p);
905                                 goto oom;
906                         }
907
908                         free(p);
909
910                         if (!dbus_message_iter_close_container(&sub, &sub2))
911                                 goto oom;
912                 }
913
914                 if (!dbus_message_iter_close_container(&iter, &sub))
915                         goto oom;
916
917         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ListSeats")) {
918                 char *p;
919                 Seat *seat;
920                 Iterator i;
921                 DBusMessageIter iter, sub;
922
923                 reply = dbus_message_new_method_return(message);
924                 if (!reply)
925                         goto oom;
926
927                 dbus_message_iter_init_append(reply, &iter);
928
929                 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(so)", &sub))
930                         goto oom;
931
932                 HASHMAP_FOREACH(seat, m->seats, i) {
933                         DBusMessageIter sub2;
934
935                         if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
936                                 goto oom;
937
938                         p = seat_bus_path(seat);
939                         if (!p)
940                                 goto oom;
941
942                         if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &seat->id) ||
943                             !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
944                                 free(p);
945                                 goto oom;
946                         }
947
948                         free(p);
949
950                         if (!dbus_message_iter_close_container(&sub, &sub2))
951                                 goto oom;
952                 }
953
954                 if (!dbus_message_iter_close_container(&iter, &sub))
955                         goto oom;
956
957         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
958
959                 r = bus_manager_create_session(m, message, &reply);
960                 if (r == -ENOMEM)
961                         goto oom;
962
963                 if (r < 0)
964                         return bus_send_error_reply(connection, message, &error, r);
965
966         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "ActivateSession")) {
967                 const char *name;
968                 Session *session;
969
970                 if (!dbus_message_get_args(
971                                     message,
972                                     &error,
973                                     DBUS_TYPE_STRING, &name,
974                                     DBUS_TYPE_INVALID))
975                         return bus_send_error_reply(connection, message, &error, -EINVAL);
976
977                 session = hashmap_get(m->sessions, name);
978                 if (!session)
979                         return bus_send_error_reply(connection, message, &error, -ENOENT);
980
981                 r = session_activate(session);
982                 if (r < 0)
983                         return bus_send_error_reply(connection, message, NULL, r);
984
985                 reply = dbus_message_new_method_return(message);
986                 if (!reply)
987                         goto oom;
988
989         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "LockSession") ||
990                    dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "UnlockSession")) {
991                 const char *name;
992                 Session *session;
993
994                 if (!dbus_message_get_args(
995                                     message,
996                                     &error,
997                                     DBUS_TYPE_STRING, &name,
998                                     DBUS_TYPE_INVALID))
999                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1000
1001                 session = hashmap_get(m->sessions, name);
1002                 if (!session)
1003                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1004
1005                 if (session_send_lock(session, streq(dbus_message_get_member(message), "LockSession")) < 0)
1006                         goto oom;
1007
1008                 reply = dbus_message_new_method_return(message);
1009                 if (!reply)
1010                         goto oom;
1011
1012         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSession")) {
1013                 const char *name;
1014                 Session *session;
1015
1016                 if (!dbus_message_get_args(
1017                                     message,
1018                                     &error,
1019                                     DBUS_TYPE_STRING, &name,
1020                                     DBUS_TYPE_INVALID))
1021                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1022
1023                 session = hashmap_get(m->sessions, name);
1024                 if (!session)
1025                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1026
1027                 r = session_stop(session);
1028                 if (r < 0)
1029                         return bus_send_error_reply(connection, message, NULL, r);
1030
1031                 reply = dbus_message_new_method_return(message);
1032                 if (!reply)
1033                         goto oom;
1034
1035         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateUser")) {
1036                 uint32_t uid;
1037                 User *user;
1038
1039                 if (!dbus_message_get_args(
1040                                     message,
1041                                     &error,
1042                                     DBUS_TYPE_UINT32, &uid,
1043                                     DBUS_TYPE_INVALID))
1044                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1045
1046                 user = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1047                 if (!user)
1048                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1049
1050                 r = user_stop(user);
1051                 if (r < 0)
1052                         return bus_send_error_reply(connection, message, NULL, r);
1053
1054                 reply = dbus_message_new_method_return(message);
1055                 if (!reply)
1056                         goto oom;
1057
1058         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "TerminateSeat")) {
1059                 const char *name;
1060                 Seat *seat;
1061
1062                 if (!dbus_message_get_args(
1063                                     message,
1064                                     &error,
1065                                     DBUS_TYPE_STRING, &name,
1066                                     DBUS_TYPE_INVALID))
1067                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1068
1069                 seat = hashmap_get(m->seats, name);
1070                 if (!seat)
1071                         return bus_send_error_reply(connection, message, &error, -ENOENT);
1072
1073                 r = seat_stop_sessions(seat);
1074                 if (r < 0)
1075                         return bus_send_error_reply(connection, message, NULL, r);
1076
1077                 reply = dbus_message_new_method_return(message);
1078                 if (!reply)
1079                         goto oom;
1080
1081         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "SetUserLinger")) {
1082                 uint32_t uid;
1083                 struct passwd *pw;
1084                 dbus_bool_t b, interactive;
1085                 char *path;
1086
1087                 if (!dbus_message_get_args(
1088                                     message,
1089                                     &error,
1090                                     DBUS_TYPE_UINT32, &uid,
1091                                     DBUS_TYPE_BOOLEAN, &b,
1092                                     DBUS_TYPE_BOOLEAN, &interactive,
1093                                     DBUS_TYPE_INVALID))
1094                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1095
1096                 errno = 0;
1097                 pw = getpwuid(uid);
1098                 if (!pw)
1099                         return bus_send_error_reply(connection, message, NULL, errno ? -errno : -EINVAL);
1100
1101                 r = verify_polkit(connection, message, "org.freedesktop.login1.set-user-linger", interactive, &error);
1102                 if (r < 0)
1103                         return bus_send_error_reply(connection, message, &error, r);
1104
1105                 r = safe_mkdir("/var/lib/systemd/linger", 0755, 0, 0);
1106                 if (r < 0)
1107                         return bus_send_error_reply(connection, message, &error, r);
1108
1109                 path = strappend("/var/lib/systemd/linger/", pw->pw_name);
1110                 if (!path)
1111                         goto oom;
1112
1113                 if (b) {
1114                         User *u;
1115
1116                         r = touch(path);
1117                         free(path);
1118
1119                         if (r < 0)
1120                                 return bus_send_error_reply(connection, message, &error, r);
1121
1122                         if (manager_add_user_by_uid(m, uid, &u) >= 0)
1123                                 user_start(u);
1124
1125                 } else {
1126                         User *u;
1127
1128                         r = unlink(path);
1129                         free(path);
1130
1131                         if (r < 0 && errno != ENOENT)
1132                                 return bus_send_error_reply(connection, message, &error, -errno);
1133
1134                         u = hashmap_get(m->users, ULONG_TO_PTR((unsigned long) uid));
1135                         if (u)
1136                                 user_add_to_gc_queue(u);
1137                 }
1138
1139                 reply = dbus_message_new_method_return(message);
1140                 if (!reply)
1141                         goto oom;
1142
1143         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "AttachDevice")) {
1144                 const char *sysfs, *seat;
1145                 dbus_bool_t interactive;
1146
1147                 if (!dbus_message_get_args(
1148                                     message,
1149                                     &error,
1150                                     DBUS_TYPE_STRING, &seat,
1151                                     DBUS_TYPE_STRING, &sysfs,
1152                                     DBUS_TYPE_BOOLEAN, &interactive,
1153                                     DBUS_TYPE_INVALID))
1154                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1155
1156                 if (!path_startswith(sysfs, "/sys") || !seat_name_is_valid(seat))
1157                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1158
1159                 r = verify_polkit(connection, message, "org.freedesktop.login1.attach-device", interactive, &error);
1160                 if (r < 0)
1161                         return bus_send_error_reply(connection, message, &error, r);
1162
1163                 r = attach_device(m, seat, sysfs);
1164                 if (r < 0)
1165                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1166
1167                 reply = dbus_message_new_method_return(message);
1168                 if (!reply)
1169                         goto oom;
1170
1171
1172         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "FlushDevices")) {
1173                 dbus_bool_t interactive;
1174
1175                 if (!dbus_message_get_args(
1176                                     message,
1177                                     &error,
1178                                     DBUS_TYPE_BOOLEAN, &interactive,
1179                                     DBUS_TYPE_INVALID))
1180                         return bus_send_error_reply(connection, message, &error, -EINVAL);
1181
1182                 r = verify_polkit(connection, message, "org.freedesktop.login1.flush-devices", interactive, &error);
1183                 if (r < 0)
1184                         return bus_send_error_reply(connection, message, &error, r);
1185
1186                 r = flush_devices(m);
1187                 if (r < 0)
1188                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
1189
1190                 reply = dbus_message_new_method_return(message);
1191                 if (!reply)
1192                         goto oom;
1193
1194         } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1195                 char *introspection = NULL;
1196                 FILE *f;
1197                 Iterator i;
1198                 Session *session;
1199                 Seat *seat;
1200                 User *user;
1201                 size_t size;
1202                 char *p;
1203
1204                 if (!(reply = dbus_message_new_method_return(message)))
1205                         goto oom;
1206
1207                 /* We roll our own introspection code here, instead of
1208                  * relying on bus_default_message_handler() because we
1209                  * need to generate our introspection string
1210                  * dynamically. */
1211
1212                 if (!(f = open_memstream(&introspection, &size)))
1213                         goto oom;
1214
1215                 fputs(INTROSPECTION_BEGIN, f);
1216
1217                 HASHMAP_FOREACH(seat, m->seats, i) {
1218                         p = bus_path_escape(seat->id);
1219
1220                         if (p) {
1221                                 fprintf(f, "<node name=\"seat/%s\"/>", p);
1222                                 free(p);
1223                         }
1224                 }
1225
1226                 HASHMAP_FOREACH(user, m->users, i)
1227                         fprintf(f, "<node name=\"user/%llu\"/>", (unsigned long long) user->uid);
1228
1229                 HASHMAP_FOREACH(session, m->sessions, i) {
1230                         p = bus_path_escape(session->id);
1231
1232                         if (p) {
1233                                 fprintf(f, "<node name=\"session/%s\"/>", p);
1234                                 free(p);
1235                         }
1236                 }
1237
1238                 fputs(INTROSPECTION_END, f);
1239
1240                 if (ferror(f)) {
1241                         fclose(f);
1242                         free(introspection);
1243                         goto oom;
1244                 }
1245
1246                 fclose(f);
1247
1248                 if (!introspection)
1249                         goto oom;
1250
1251                 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
1252                         free(introspection);
1253                         goto oom;
1254                 }
1255
1256                 free(introspection);
1257         } else
1258                 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, properties);
1259
1260         if (reply) {
1261                 if (!dbus_connection_send(connection, reply, NULL))
1262                         goto oom;
1263
1264                 dbus_message_unref(reply);
1265         }
1266
1267         return DBUS_HANDLER_RESULT_HANDLED;
1268
1269 oom:
1270         if (reply)
1271                 dbus_message_unref(reply);
1272
1273         dbus_error_free(&error);
1274
1275         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1276 }
1277
1278 const DBusObjectPathVTable bus_manager_vtable = {
1279         .message_function = manager_message_handler
1280 };
1281
1282 DBusHandlerResult bus_message_filter(
1283                 DBusConnection *connection,
1284                 DBusMessage *message,
1285                 void *userdata) {
1286
1287         Manager *m = userdata;
1288         DBusError error;
1289
1290         assert(m);
1291         assert(connection);
1292         assert(message);
1293
1294         dbus_error_init(&error);
1295
1296         if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
1297                 const char *cgroup;
1298
1299                 if (!dbus_message_get_args(message, &error,
1300                                            DBUS_TYPE_STRING, &cgroup,
1301                                            DBUS_TYPE_INVALID))
1302                         log_error("Failed to parse Released message: %s", bus_error_message(&error));
1303                 else
1304                         manager_cgroup_notify_empty(m, cgroup);
1305         }
1306
1307         dbus_error_free(&error);
1308
1309         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1310 }
1311
1312 int manager_send_changed(Manager *manager, const char *properties) {
1313         DBusMessage *m;
1314         int r = -ENOMEM;
1315
1316         assert(manager);
1317
1318         m = bus_properties_changed_new("/org/freedesktop/login1", "org.freedesktop.login1.Manager", properties);
1319         if (!m)
1320                 goto finish;
1321
1322         if (!dbus_connection_send(manager->bus, m, NULL))
1323                 goto finish;
1324
1325         r = 0;
1326
1327 finish:
1328         if (m)
1329                 dbus_message_unref(m);
1330
1331         return r;
1332 }