chiark / gitweb /
update NEWS
[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 #include <sys/capability.h>
25
26 #include "util.h"
27 #include "bus-util.h"
28 #include "strv.h"
29 #include "bus-errors.h"
30 #include "bus-label.h"
31 #include "logind.h"
32 #include "logind-seat.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 static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
198         Seat *s = userdata;
199         int r;
200
201         assert(bus);
202         assert(message);
203         assert(s);
204
205         r = seat_stop_sessions(s, true);
206         if (r < 0)
207                 return r;
208
209         return sd_bus_reply_method_return(message, NULL);
210 }
211
212 static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
213         Seat *s = userdata;
214         const char *name;
215         Session *session;
216         int r;
217
218         assert(bus);
219         assert(message);
220         assert(s);
221
222         r = sd_bus_message_read(message, "s", &name);
223         if (r < 0)
224                 return r;
225
226         session = hashmap_get(s->manager->sessions, name);
227         if (!session)
228                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SESSION, "No session '%s' known", name);
229
230         if (session->seat != s)
231                 return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", name, s->id);
232
233         r = session_activate(session);
234         if (r < 0)
235                 return r;
236
237         return sd_bus_reply_method_return(message, NULL);
238 }
239
240 static int method_switch_to(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
241         Seat *s = userdata;
242         unsigned int to;
243         int r;
244
245         assert(bus);
246         assert(message);
247         assert(s);
248
249         r = sd_bus_message_read(message, "u", &to);
250         if (r < 0)
251                 return r;
252
253         if (to <= 0)
254                 return -EINVAL;
255
256         r = seat_switch_to(s, to);
257         if (r < 0)
258                 return r;
259
260         return sd_bus_reply_method_return(message, NULL);
261 }
262
263 static int method_switch_to_next(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
264         Seat *s = userdata;
265         int r;
266
267         assert(bus);
268         assert(message);
269         assert(s);
270
271         r = seat_switch_to_next(s);
272         if (r < 0)
273                 return r;
274
275         return sd_bus_reply_method_return(message, NULL);
276 }
277
278 static int method_switch_to_previous(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
279         Seat *s = userdata;
280         int r;
281
282         assert(bus);
283         assert(message);
284         assert(s);
285
286         r = seat_switch_to_previous(s);
287         if (r < 0)
288                 return r;
289
290         return sd_bus_reply_method_return(message, NULL);
291 }
292
293 const sd_bus_vtable seat_vtable[] = {
294         SD_BUS_VTABLE_START(0),
295
296         SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST),
297         SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
298         SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST),
299         SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST),
300         SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
301         SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
302         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
303         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
304         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
305
306         SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
307         SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
308         SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
309         SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
310         SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
311
312         SD_BUS_VTABLE_END
313 };
314
315 int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
316         Manager *m = userdata;
317         Seat *seat;
318         int r;
319
320         assert(bus);
321         assert(path);
322         assert(interface);
323         assert(found);
324         assert(m);
325
326         if (streq(path, "/org/freedesktop/login1/seat/self")) {
327                 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
328                 sd_bus_message *message;
329                 Session *session;
330                 pid_t pid;
331
332                 message = sd_bus_get_current_message(bus);
333                 if (!message)
334                         return 0;
335
336                 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
337                 if (r < 0)
338                         return r;
339
340                 r = sd_bus_creds_get_pid(creds, &pid);
341                 if (r < 0)
342                         return r;
343
344                 r = manager_get_session_by_pid(m, pid, &session);
345                 if (r <= 0)
346                         return 0;
347
348                 if (!session->seat)
349                         return 0;
350
351                 seat = session->seat;
352         } else {
353                 _cleanup_free_ char *e = NULL;
354                 const char *p;
355
356                 p = startswith(path, "/org/freedesktop/login1/seat/");
357                 if (!p)
358                         return 0;
359
360                 e = bus_label_unescape(p);
361                 if (!e)
362                         return -ENOMEM;
363
364                 seat = hashmap_get(m->seats, e);
365                 if (!seat)
366                         return 0;
367         }
368
369         *found = seat;
370         return 1;
371 }
372
373 char *seat_bus_path(Seat *s) {
374         _cleanup_free_ char *t = NULL;
375
376         assert(s);
377
378         t = bus_label_escape(s->id);
379         if (!t)
380                 return NULL;
381
382         return strappend("/org/freedesktop/login1/seat/", t);
383 }
384
385 int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
386         _cleanup_strv_free_ char **l = NULL;
387         Manager *m = userdata;
388         Seat *seat;
389         Iterator i;
390         int r;
391
392         assert(bus);
393         assert(path);
394         assert(nodes);
395
396         HASHMAP_FOREACH(seat, m->seats, i) {
397                 char *p;
398
399                 p = seat_bus_path(seat);
400                 if (!p)
401                         return -ENOMEM;
402
403                 r = strv_consume(&l, p);
404                 if (r < 0)
405                         return r;
406         }
407
408         *nodes = l;
409         l = NULL;
410
411         return 1;
412 }
413
414 int seat_send_signal(Seat *s, bool new_seat) {
415         _cleanup_free_ char *p = NULL;
416
417         assert(s);
418
419         p = seat_bus_path(s);
420         if (!p)
421                 return -ENOMEM;
422
423         return sd_bus_emit_signal(
424                         s->manager->bus,
425                         "/org/freedesktop/login1",
426                         "org.freedesktop.login1.Manager",
427                         new_seat ? "SeatNew" : "SeatRemoved",
428                         "so", s->id, p);
429 }
430
431 int seat_send_changed(Seat *s, const char *properties, ...) {
432         _cleanup_free_ char *p = NULL;
433         char **l;
434
435         assert(s);
436
437         if (!s->started)
438                 return 0;
439
440         p = seat_bus_path(s);
441         if (!p)
442                 return -ENOMEM;
443
444         l = strv_from_stdarg_alloca(properties);
445
446         return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l);
447 }