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