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