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