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