chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[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 "logind.h"
26 #include "logind-seat.h"
27 #include "dbus-common.h"
28 #include "util.h"
29
30 #define BUS_SEAT_INTERFACE \
31         " <interface name=\"org.freedesktop.login1.Seat\">\n"           \
32         "  <method name=\"Terminate\"/>\n"                              \
33         "  <method name=\"ActivateSession\">\n"                         \
34         "   <arg name=\"id\" type=\"s\"/>\n"                            \
35         "  </method>\n"                                                 \
36         "  <property name=\"Id\" type=\"s\" access=\"read\"/>\n"        \
37         "  <property name=\"ActiveSession\" type=\"so\" access=\"read\"/>\n" \
38         "  <property name=\"CanMultiSession\" type=\"b\" access=\"read\"/>\n" \
39         "  <property name=\"Sessions\" type=\"a(so)\" access=\"read\"/>\n" \
40         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
41         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
42         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
43         " </interface>\n"                                               \
44
45 #define INTROSPECTION                                                   \
46         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
47         "<node>\n"                                                      \
48         BUS_SEAT_INTERFACE                                              \
49         BUS_PROPERTIES_INTERFACE                                        \
50         BUS_PEER_INTERFACE                                              \
51         BUS_INTROSPECTABLE_INTERFACE                                    \
52         "</node>\n"
53
54 #define INTERFACES_LIST                              \
55         BUS_GENERIC_INTERFACES_LIST                  \
56         "org.freedesktop.login1.Seat\0"
57
58 static int bus_seat_append_active(DBusMessageIter *i, const char *property, void *data) {
59         DBusMessageIter sub;
60         Seat *s = data;
61         const char *id, *path;
62         char *p = NULL;
63
64         assert(i);
65         assert(property);
66         assert(s);
67
68         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
69                 return -ENOMEM;
70
71         if (s->active) {
72                 id = s->active->id;
73                 path = p = session_bus_path(s->active);
74
75                 if (!p)
76                         return -ENOMEM;
77         } else {
78                 id = "";
79                 path = "/";
80         }
81
82         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
83             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path)) {
84                 free(p);
85                 return -ENOMEM;
86         }
87
88         free(p);
89
90         if (!dbus_message_iter_close_container(i, &sub))
91                 return -ENOMEM;
92
93         return 0;
94 }
95
96 static int bus_seat_append_sessions(DBusMessageIter *i, const char *property, void *data) {
97         DBusMessageIter sub, sub2;
98         Seat *s = data;
99         Session *session;
100
101         assert(i);
102         assert(property);
103         assert(s);
104
105         if (!dbus_message_iter_open_container(i, DBUS_TYPE_ARRAY, "(so)", &sub))
106                 return -ENOMEM;
107
108         LIST_FOREACH(sessions_by_seat, session, s->sessions) {
109                 char *p;
110
111                 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
112                         return -ENOMEM;
113
114                 p = session_bus_path(session);
115                 if (!p)
116                         return -ENOMEM;
117
118                 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &session->id) ||
119                     !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
120                         free(p);
121                         return -ENOMEM;
122                 }
123
124                 free(p);
125
126                 if (!dbus_message_iter_close_container(&sub, &sub2))
127                         return -ENOMEM;
128         }
129
130         if (!dbus_message_iter_close_container(i, &sub))
131                 return -ENOMEM;
132
133         return 0;
134 }
135
136 static int bus_seat_append_multi_session(DBusMessageIter *i, const char *property, void *data) {
137         Seat *s = data;
138         dbus_bool_t b;
139
140         assert(i);
141         assert(property);
142         assert(s);
143
144         b = seat_can_multi_session(s);
145
146         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
147                 return -ENOMEM;
148
149         return 0;
150 }
151
152 static int bus_seat_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
153         Seat *s = data;
154         dbus_bool_t b;
155
156         assert(i);
157         assert(property);
158         assert(s);
159
160         b = seat_get_idle_hint(s, NULL) > 0;
161         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
162                 return -ENOMEM;
163
164         return 0;
165 }
166
167 static int bus_seat_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
168         Seat *s = data;
169         dual_timestamp t;
170         uint64_t k;
171
172         assert(i);
173         assert(property);
174         assert(s);
175
176         seat_get_idle_hint(s, &t);
177         k = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
178
179         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &k))
180                 return -ENOMEM;
181
182         return 0;
183 }
184
185 static int get_seat_for_path(Manager *m, const char *path, Seat **_s) {
186         Seat *s;
187         char *id;
188
189         assert(m);
190         assert(path);
191         assert(_s);
192
193         if (!startswith(path, "/org/freedesktop/login1/seat/"))
194                 return -EINVAL;
195
196         id = bus_path_unescape(path + 29);
197         if (!id)
198                 return -ENOMEM;
199
200         s = hashmap_get(m->seats, id);
201         free(id);
202
203         if (!s)
204                 return -ENOENT;
205
206         *_s = s;
207         return 0;
208 }
209
210 static const BusProperty bus_login_seat_properties[] = {
211         { "Id",                     bus_property_append_string,      "s", offsetof(Seat, id), true },
212         { "ActiveSession",          bus_seat_append_active,       "(so)", 0 },
213         { "CanMultiSession",        bus_seat_append_multi_session,   "b", 0 },
214         { "Sessions",               bus_seat_append_sessions,    "a(so)", 0 },
215         { "IdleHint",               bus_seat_append_idle_hint,       "b", 0 },
216         { "IdleSinceHint",          bus_seat_append_idle_hint_since, "t", 0 },
217         { "IdleSinceHintMonotonic", bus_seat_append_idle_hint_since, "t", 0 },
218         { NULL, }
219 };
220
221 static DBusHandlerResult seat_message_dispatch(
222                 Seat *s,
223                 DBusConnection *connection,
224                 DBusMessage *message) {
225
226         DBusError error;
227         DBusMessage *reply = NULL;
228         int r;
229
230         assert(s);
231         assert(connection);
232         assert(message);
233
234         dbus_error_init(&error);
235
236         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Seat", "Terminate")) {
237
238                 r = seat_stop_sessions(s);
239                 if (r < 0)
240                         return bus_send_error_reply(connection, message, NULL, r);
241
242                 reply = dbus_message_new_method_return(message);
243                 if (!reply)
244                         goto oom;
245
246         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Seat", "ActivateSession")) {
247                 const char *name;
248                 Session *session;
249
250                 if (!dbus_message_get_args(
251                                     message,
252                                     &error,
253                                     DBUS_TYPE_STRING, &name,
254                                     DBUS_TYPE_INVALID))
255                         return bus_send_error_reply(connection, message, &error, -EINVAL);
256
257                 session = hashmap_get(s->manager->sessions, name);
258                 if (!session || session->seat != s)
259                         return bus_send_error_reply(connection, message, &error, -ENOENT);
260
261                 r = session_activate(session);
262                 if (r < 0)
263                         return bus_send_error_reply(connection, message, NULL, r);
264
265                 reply = dbus_message_new_method_return(message);
266                 if (!reply)
267                         goto oom;
268         } else {
269                 const BusBoundProperties bps[] = {
270                         { "org.freedesktop.login1.Seat", bus_login_seat_properties, s },
271                         { NULL, }
272                 };
273                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
274         }
275
276         if (reply) {
277                 if (!dbus_connection_send(connection, reply, NULL))
278                         goto oom;
279
280                 dbus_message_unref(reply);
281         }
282
283         return DBUS_HANDLER_RESULT_HANDLED;
284
285 oom:
286         if (reply)
287                 dbus_message_unref(reply);
288
289         dbus_error_free(&error);
290
291         return DBUS_HANDLER_RESULT_NEED_MEMORY;
292 }
293
294 static DBusHandlerResult seat_message_handler(
295                 DBusConnection *connection,
296                 DBusMessage *message,
297                 void *userdata) {
298
299         Manager *m = userdata;
300         Seat *s;
301         int r;
302
303         r = get_seat_for_path(m, dbus_message_get_path(message), &s);
304         if (r < 0) {
305
306                 if (r == -ENOMEM)
307                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
308
309                 if (r == -ENOENT) {
310                         DBusError e;
311
312                         dbus_error_init(&e);
313                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown seat");
314                         return bus_send_error_reply(connection, message, &e, r);
315                 }
316
317                 return bus_send_error_reply(connection, message, NULL, r);
318         }
319
320         return seat_message_dispatch(s, connection, message);
321 }
322
323 const DBusObjectPathVTable bus_seat_vtable = {
324         .message_function = seat_message_handler
325 };
326
327 char *seat_bus_path(Seat *s) {
328         char *t, *r;
329
330         assert(s);
331
332         t = bus_path_escape(s->id);
333         if (!t)
334                 return NULL;
335
336         r = strappend("/org/freedesktop/login1/seat/", t);
337         free(t);
338
339         return r;
340 }
341
342 int seat_send_signal(Seat *s, bool new_seat) {
343         DBusMessage *m;
344         int r = -ENOMEM;
345         char *p = NULL;
346
347         assert(s);
348
349         m = dbus_message_new_signal("/org/freedesktop/login1",
350                                     "org.freedesktop.login1.Manager",
351                                     new_seat ? "SeatNew" : "SeatRemoved");
352
353         if (!m)
354                 return -ENOMEM;
355
356         p = seat_bus_path(s);
357         if (!p)
358                 goto finish;
359
360         if (!dbus_message_append_args(
361                             m,
362                             DBUS_TYPE_STRING, &s->id,
363                             DBUS_TYPE_OBJECT_PATH, &p,
364                             DBUS_TYPE_INVALID))
365                 goto finish;
366
367         if (!dbus_connection_send(s->manager->bus, m, NULL))
368                 goto finish;
369
370         r = 0;
371
372 finish:
373         dbus_message_unref(m);
374         free(p);
375
376         return r;
377 }
378
379 int seat_send_changed(Seat *s, const char *properties) {
380         DBusMessage *m;
381         int r = -ENOMEM;
382         char *p = NULL;
383
384         assert(s);
385
386         if (!s->started)
387                 return 0;
388
389         p = seat_bus_path(s);
390         if (!p)
391                 return -ENOMEM;
392
393         m = bus_properties_changed_new(p, "org.freedesktop.login1.Seat", properties);
394         if (!m)
395                 goto finish;
396
397         if (!dbus_connection_send(s->manager->bus, m, NULL))
398                 goto finish;
399
400         r = 0;
401
402 finish:
403         if (m)
404                 dbus_message_unref(m);
405         free(p);
406
407         return r;
408 }