chiark / gitweb /
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[elogind.git] / src / login / logind-seat-dbus.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2011 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <string.h>
23
24 #include "alloc-util.h"
25 #include "bus-common-errors.h"
26 #include "bus-label.h"
27 #include "bus-util.h"
28 #include "logind-seat.h"
29 #include "logind.h"
30 #include "strv.h"
31 #include "user-util.h"
32 #include "util.h"
33
34 static int property_get_active_session(
35                 sd_bus *bus,
36                 const char *path,
37                 const char *interface,
38                 const char *property,
39                 sd_bus_message *reply,
40                 void *userdata,
41                 sd_bus_error *error) {
42
43         _cleanup_free_ char *p = NULL;
44         Seat *s = userdata;
45
46         assert(bus);
47         assert(reply);
48         assert(s);
49
50         p = s->active ? session_bus_path(s->active) : strdup("/");
51         if (!p)
52                 return -ENOMEM;
53
54         return sd_bus_message_append(reply, "(so)", s->active ? s->active->id : "", p);
55 }
56
57 static int property_get_can_multi_session(
58                 sd_bus *bus,
59                 const char *path,
60                 const char *interface,
61                 const char *property,
62                 sd_bus_message *reply,
63                 void *userdata,
64                 sd_bus_error *error) {
65
66         Seat *s = userdata;
67
68         assert(bus);
69         assert(reply);
70         assert(s);
71
72         return sd_bus_message_append(reply, "b", seat_can_multi_session(s));
73 }
74
75 static int property_get_can_tty(
76                 sd_bus *bus,
77                 const char *path,
78                 const char *interface,
79                 const char *property,
80                 sd_bus_message *reply,
81                 void *userdata,
82                 sd_bus_error *error) {
83
84         Seat *s = userdata;
85
86         assert(bus);
87         assert(reply);
88         assert(s);
89
90         return sd_bus_message_append(reply, "b", seat_can_tty(s));
91 }
92
93 static int property_get_can_graphical(
94                 sd_bus *bus,
95                 const char *path,
96                 const char *interface,
97                 const char *property,
98                 sd_bus_message *reply,
99                 void *userdata,
100                 sd_bus_error *error) {
101
102         Seat *s = userdata;
103
104         assert(bus);
105         assert(reply);
106         assert(s);
107
108         return sd_bus_message_append(reply, "b", seat_can_graphical(s));
109 }
110
111 static int property_get_sessions(
112                 sd_bus *bus,
113                 const char *path,
114                 const char *interface,
115                 const char *property,
116                 sd_bus_message *reply,
117                 void *userdata,
118                 sd_bus_error *error) {
119
120         Seat *s = userdata;
121         Session *session;
122         int r;
123
124         assert(bus);
125         assert(reply);
126         assert(s);
127
128         r = sd_bus_message_open_container(reply, 'a', "(so)");
129         if (r < 0)
130                 return r;
131
132         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
133                 _cleanup_free_ char *p = NULL;
134
135                 p = session_bus_path(session);
136                 if (!p)
137                         return -ENOMEM;
138
139                 r = sd_bus_message_append(reply, "(so)", session->id, p);
140                 if (r < 0)
141                         return r;
142
143         }
144
145         r = sd_bus_message_close_container(reply);
146         if (r < 0)
147                 return r;
148
149         return 1;
150 }
151
152 static int property_get_idle_hint(
153                 sd_bus *bus,
154                 const char *path,
155                 const char *interface,
156                 const char *property,
157                 sd_bus_message *reply,
158                 void *userdata,
159                 sd_bus_error *error) {
160
161         Seat *s = userdata;
162
163         assert(bus);
164         assert(reply);
165         assert(s);
166
167         return sd_bus_message_append(reply, "b", seat_get_idle_hint(s, NULL) > 0);
168 }
169
170 static int property_get_idle_since_hint(
171                 sd_bus *bus,
172                 const char *path,
173                 const char *interface,
174                 const char *property,
175                 sd_bus_message *reply,
176                 void *userdata,
177                 sd_bus_error *error) {
178
179         Seat *s = userdata;
180         dual_timestamp t;
181         uint64_t u;
182         int r;
183
184         assert(bus);
185         assert(reply);
186         assert(s);
187
188         r = seat_get_idle_hint(s, &t);
189         if (r < 0)
190                 return r;
191
192         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
193
194         return sd_bus_message_append(reply, "t", u);
195 }
196
197 int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
198         Seat *s = userdata;
199         int r;
200
201         assert(message);
202         assert(s);
203
204         r = bus_verify_polkit_async(
205                         message,
206                         CAP_KILL,
207                         "org.freedesktop.login1.manage",
208                         NULL,
209                         false,
210                         UID_INVALID,
211                         &s->manager->polkit_registry,
212                         error);
213         if (r < 0)
214                 return r;
215         if (r == 0)
216                 return 1; /* Will call us back */
217
218         r = seat_stop_sessions(s, true);
219         if (r < 0)
220                 return r;
221
222         return sd_bus_reply_method_return(message, NULL);
223 }
224
225 static int method_activate_session(sd_bus_message *message, void *userdata, sd_bus_error *error) {
226         Seat *s = userdata;
227         const char *name;
228         Session *session;
229         int r;
230
231         assert(message);
232         assert(s);
233
234         r = sd_bus_message_read(message, "s", &name);
235         if (r < 0)
236                 return r;
237
238         session = hashmap_get(s->manager->sessions, name);
239         if (!session)
240                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name);
241
242         if (session->seat != s)
243                 return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id);
244
245         r = session_activate(session);
246         if (r < 0)
247                 return r;
248
249         return sd_bus_reply_method_return(message, NULL);
250 }
251
252 static int method_switch_to(sd_bus_message *message, void *userdata, sd_bus_error *error) {
253         Seat *s = userdata;
254         unsigned int to;
255         int r;
256
257         assert(message);
258         assert(s);
259
260         r = sd_bus_message_read(message, "u", &to);
261         if (r < 0)
262                 return r;
263
264         if (to <= 0)
265                 return -EINVAL;
266
267         r = seat_switch_to(s, to);
268         if (r < 0)
269                 return r;
270
271         return sd_bus_reply_method_return(message, NULL);
272 }
273
274 static int method_switch_to_next(sd_bus_message *message, void *userdata, sd_bus_error *error) {
275         Seat *s = userdata;
276         int r;
277
278         assert(message);
279         assert(s);
280
281         r = seat_switch_to_next(s);
282         if (r < 0)
283                 return r;
284
285         return sd_bus_reply_method_return(message, NULL);
286 }
287
288 static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd_bus_error *error) {
289         Seat *s = userdata;
290         int r;
291
292         assert(message);
293         assert(s);
294
295         r = seat_switch_to_previous(s);
296         if (r < 0)
297                 return r;
298
299         return sd_bus_reply_method_return(message, NULL);
300 }
301
302 const sd_bus_vtable seat_vtable[] = {
303         SD_BUS_VTABLE_START(0),
304
305         SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST),
306         SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
307         SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST),
308         SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST),
309         SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
310         SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
311         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
312         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
313         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
314
315         SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
316         SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
317         SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
318         SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
319         SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
320
321         SD_BUS_VTABLE_END
322 };
323
324 int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
325         Manager *m = userdata;
326         Seat *seat;
327         int r;
328
329         assert(bus);
330         assert(path);
331         assert(interface);
332         assert(found);
333         assert(m);
334
335         if (streq(path, "/org/freedesktop/login1/seat/self")) {
336                 sd_bus_message *message;
337
338                 message = sd_bus_get_current_message(bus);
339                 if (!message)
340                         return 0;
341
342                 r = manager_get_seat_from_creds(m, message, NULL, error, &seat);
343                 if (r < 0)
344                         return r;
345         } else {
346                 _cleanup_free_ char *e = NULL;
347                 const char *p;
348
349                 p = startswith(path, "/org/freedesktop/login1/seat/");
350                 if (!p)
351                         return 0;
352
353                 e = bus_label_unescape(p);
354                 if (!e)
355                         return -ENOMEM;
356
357                 seat = hashmap_get(m->seats, e);
358                 if (!seat)
359                         return 0;
360         }
361
362         *found = seat;
363         return 1;
364 }
365
366 char *seat_bus_path(Seat *s) {
367         _cleanup_free_ char *t = NULL;
368
369         assert(s);
370
371         t = bus_label_escape(s->id);
372         if (!t)
373                 return NULL;
374
375         return strappend("/org/freedesktop/login1/seat/", t);
376 }
377
378 int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
379         _cleanup_strv_free_ char **l = NULL;
380         sd_bus_message *message;
381         Manager *m = userdata;
382         Seat *seat;
383         Iterator i;
384         int r;
385
386         assert(bus);
387         assert(path);
388         assert(nodes);
389
390         HASHMAP_FOREACH(seat, m->seats, i) {
391                 char *p;
392
393                 p = seat_bus_path(seat);
394                 if (!p)
395                         return -ENOMEM;
396
397                 r = strv_consume(&l, p);
398                 if (r < 0)
399                         return r;
400         }
401
402         message = sd_bus_get_current_message(bus);
403         if (message) {
404                 _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
405                 const char *name;
406                 Session *session;
407
408                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
409                 if (r >= 0) {
410                         r = sd_bus_creds_get_session(creds, &name);
411                         if (r >= 0) {
412                                 session = hashmap_get(m->sessions, name);
413                                 if (session && session->seat) {
414                                         r = strv_extend(&l, "/org/freedesktop/login1/seat/self");
415                                         if (r < 0)
416                                                 return r;
417                                 }
418                         }
419                 }
420         }
421
422         *nodes = TAKE_PTR(l);
423
424         return 1;
425 }
426
427 int seat_send_signal(Seat *s, bool new_seat) {
428         _cleanup_free_ char *p = NULL;
429
430         assert(s);
431
432         p = seat_bus_path(s);
433         if (!p)
434                 return -ENOMEM;
435
436         return sd_bus_emit_signal(
437                         s->manager->bus,
438                         "/org/freedesktop/login1",
439                         "org.freedesktop.login1.Manager",
440                         new_seat ? "SeatNew" : "SeatRemoved",
441                         "so", s->id, p);
442 }
443
444 int seat_send_changed(Seat *s, const char *properties, ...) {
445         _cleanup_free_ char *p = NULL;
446         char **l;
447
448         assert(s);
449
450         if (!s->started)
451                 return 0;
452
453         p = seat_bus_path(s);
454         if (!p)
455                 return -ENOMEM;
456
457         l = strv_from_stdarg_alloca(properties);
458
459         return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l);
460 }