chiark / gitweb /
95ef5ffdb52dd6cefc4053fd369b06bc109c312a
[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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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 }