chiark / gitweb /
e306eabb32c689e924d88709098c6b8dcda1a6ce
[elogind.git] / src / login / logind-session-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-session.h"
27 #include "dbus-common.h"
28 #include "util.h"
29
30 #define BUS_SESSION_INTERFACE \
31         " <interface name=\"org.freedesktop.login1.Session\">\n"        \
32         "  <method name=\"Terminate\"/>\n"                              \
33         "  <method name=\"Activate\"/>\n"                               \
34         "  <method name=\"Lock\"/>\n"                                   \
35         "  <method name=\"Unlock\"/>\n"                                 \
36         "  <method name=\"SetIdleHint\">\n"                             \
37         "   <arg name=\"b\" type=\"b\"/>\n"                             \
38         "  </method>\n"                                                 \
39         "  <method name=\"Kill\">\n"                                    \
40         "   <arg name=\"who\" type=\"s\"/>\n"                           \
41         "   <arg name=\"signal\" type=\"s\"/>\n"                        \
42         "  </method>\n"                                                 \
43         "  <signal name=\"Lock\"/>\n"                                   \
44         "  <signal name=\"Unlock\"/>\n"                                 \
45         "  <property name=\"Id\" type=\"s\" access=\"read\"/>\n"        \
46         "  <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n"   \
47         "  <property name=\"Name\" type=\"s\" access=\"read\"/>\n"      \
48         "  <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
49         "  <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
50         "  <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
51         "  <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n"      \
52         "  <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n"   \
53         "  <property name=\"TTY\" type=\"s\" access=\"read\"/>\n"       \
54         "  <property name=\"Display\" type=\"s\" access=\"read\"/>\n"   \
55         "  <property name=\"Remote\" type=\"b\" access=\"read\"/>\n"    \
56         "  <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
57         "  <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
58         "  <property name=\"Service\" type=\"s\" access=\"read\"/>\n"   \
59         "  <property name=\"Slice\" type=\"s\" access=\"read\"/>\n"     \
60         "  <property name=\"Leader\" type=\"u\" access=\"read\"/>\n"    \
61         "  <property name=\"Audit\" type=\"u\" access=\"read\"/>\n"     \
62         "  <property name=\"Type\" type=\"s\" access=\"read\"/>\n"      \
63         "  <property name=\"Class\" type=\"s\" access=\"read\"/>\n"     \
64         "  <property name=\"Active\" type=\"b\" access=\"read\"/>\n"    \
65         "  <property name=\"State\" type=\"s\" access=\"read\"/>\n"     \
66         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
67         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
68         "  <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
69         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
70         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
71         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
72         " </interface>\n"
73
74 #define INTROSPECTION                                                   \
75         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
76         "<node>\n"                                                      \
77         BUS_SESSION_INTERFACE                                           \
78         BUS_PROPERTIES_INTERFACE                                        \
79         BUS_PEER_INTERFACE                                              \
80         BUS_INTROSPECTABLE_INTERFACE                                    \
81         "</node>\n"
82
83 #define INTERFACES_LIST                              \
84         BUS_GENERIC_INTERFACES_LIST                  \
85         "org.freedesktop.login1.Session\0"
86
87 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
88         DBusMessageIter sub;
89         Session *s = data;
90         const char *id, *path;
91         char *p = NULL;
92
93         assert(i);
94         assert(property);
95         assert(s);
96
97         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
98                 return -ENOMEM;
99
100         if (s->seat) {
101                 id = s->seat->id;
102                 path = p = seat_bus_path(s->seat);
103
104                 if (!p)
105                         return -ENOMEM;
106         } else {
107                 id = "";
108                 path = "/";
109         }
110
111         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
112             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
113                 return -ENOMEM;
114
115         if (!dbus_message_iter_close_container(i, &sub))
116                 return -ENOMEM;
117
118         return 0;
119 }
120
121 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
122         DBusMessageIter sub;
123         User *u = data;
124         _cleanup_free_ char *p = NULL;
125
126         assert(i);
127         assert(property);
128         assert(u);
129
130         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
131                 return -ENOMEM;
132
133         p = user_bus_path(u);
134         if (!p)
135                 return -ENOMEM;
136
137         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
138             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
139                 return -ENOMEM;
140
141         if (!dbus_message_iter_close_container(i, &sub))
142                 return -ENOMEM;
143
144         return 0;
145 }
146
147 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
148         Session *s = data;
149         dbus_bool_t b;
150
151         assert(i);
152         assert(property);
153         assert(s);
154
155         b = session_is_active(s);
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_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
163         Session *s = data;
164         int b;
165
166         assert(i);
167         assert(property);
168         assert(s);
169
170         b = session_get_idle_hint(s, NULL) > 0;
171         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
172                 return -ENOMEM;
173
174         return 0;
175 }
176
177 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
178         Session *s = data;
179         dual_timestamp t;
180         uint64_t u;
181         int r;
182
183         assert(i);
184         assert(property);
185         assert(s);
186
187         r = session_get_idle_hint(s, &t);
188         if (r < 0)
189                 return r;
190
191         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
192
193         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
194                 return -ENOMEM;
195
196         return 0;
197 }
198
199 static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
200         Session *s = data;
201         _cleanup_free_ char *t = NULL;
202         int r;
203         bool success;
204
205         assert(i);
206         assert(property);
207         assert(s);
208
209         r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
210         if (r < 0)
211                 return r;
212
213         success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
214         return success ? 0 : -ENOMEM;
215 }
216
217 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
218 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
219
220 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
221         Session *s = data;
222         const char *state;
223
224         assert(i);
225         assert(property);
226         assert(s);
227
228         state = session_state_to_string(session_get_state(s));
229
230         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
231                 return -ENOMEM;
232
233         return 0;
234 }
235
236 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
237         _cleanup_free_ char *id = NULL;
238         Session *s;
239
240         assert(m);
241         assert(path);
242         assert(_s);
243
244         if (!startswith(path, "/org/freedesktop/login1/session/"))
245                 return -EINVAL;
246
247         id = bus_path_unescape(path + 32);
248         if (!id)
249                 return -ENOMEM;
250
251         s = hashmap_get(m->sessions, id);
252         if (!s)
253                 return -ENOENT;
254
255         *_s = s;
256         return 0;
257 }
258
259 static const BusProperty bus_login_session_properties[] = {
260         { "Id",                     bus_property_append_string,         "s", offsetof(Session, id),                 true },
261         { "Timestamp",              bus_property_append_usec,           "t", offsetof(Session, timestamp.realtime)  },
262         { "TimestampMonotonic",     bus_property_append_usec,           "t", offsetof(Session, timestamp.monotonic) },
263         { "DefaultControlGroup",    bus_session_append_default_cgroup,  "s", 0,                                     },
264         { "VTNr",                   bus_property_append_uint32,         "u", offsetof(Session, vtnr)                },
265         { "Seat",                   bus_session_append_seat,         "(so)", 0 },
266         { "TTY",                    bus_property_append_string,         "s", offsetof(Session, tty),                true },
267         { "Display",                bus_property_append_string,         "s", offsetof(Session, display),            true },
268         { "Remote",                 bus_property_append_bool,           "b", offsetof(Session, remote)              },
269         { "RemoteUser",             bus_property_append_string,         "s", offsetof(Session, remote_user),        true },
270         { "RemoteHost",             bus_property_append_string,         "s", offsetof(Session, remote_host),        true },
271         { "Service",                bus_property_append_string,         "s", offsetof(Session, service),            true },
272         { "Slice",                  bus_property_append_string,         "s", offsetof(Session, slice),              true },
273         { "Leader",                 bus_property_append_pid,            "u", offsetof(Session, leader)              },
274         { "Audit",                  bus_property_append_uint32,         "u", offsetof(Session, audit_id)            },
275         { "Type",                   bus_session_append_type,            "s", offsetof(Session, type)                },
276         { "Class",                  bus_session_append_class,           "s", offsetof(Session, class)               },
277         { "Active",                 bus_session_append_active,          "b", 0 },
278         { "State",                  bus_session_append_state,           "s", 0 },
279         { "Controllers",            bus_property_append_strv,          "as", offsetof(Session, controllers),        true },
280         { "ResetControllers",       bus_property_append_strv,          "as", offsetof(Session, reset_controllers),  true },
281         { "KillProcesses",          bus_property_append_bool,           "b", offsetof(Session, kill_processes)      },
282         { "IdleHint",               bus_session_append_idle_hint,       "b", 0 },
283         { "IdleSinceHint",          bus_session_append_idle_hint_since, "t", 0 },
284         { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
285         { NULL, }
286 };
287
288 static const BusProperty bus_login_session_user_properties[] = {
289         { "User",                   bus_session_append_user,         "(uo)", 0 },
290         { "Name",                   bus_property_append_string,         "s", offsetof(User, name),                  true },
291         { NULL, }
292 };
293
294 static DBusHandlerResult session_message_dispatch(
295                 Session *s,
296                 DBusConnection *connection,
297                 DBusMessage *message) {
298
299         DBusError error;
300         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
301         int r;
302
303         assert(s);
304         assert(connection);
305         assert(message);
306
307         dbus_error_init(&error);
308
309         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
310
311                 r = session_stop(s);
312                 if (r < 0)
313                         return bus_send_error_reply(connection, message, NULL, r);
314
315                 reply = dbus_message_new_method_return(message);
316                 if (!reply)
317                         goto oom;
318
319         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
320
321                 r = session_activate(s);
322                 if (r < 0)
323                         return bus_send_error_reply(connection, message, NULL, r);
324
325                 reply = dbus_message_new_method_return(message);
326                 if (!reply)
327                         goto oom;
328
329         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
330                    dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
331
332                 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
333                         goto oom;
334
335                 reply = dbus_message_new_method_return(message);
336                 if (!reply)
337                         goto oom;
338
339         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
340                 dbus_bool_t b;
341                 unsigned long ul;
342
343                 if (!dbus_message_get_args(
344                                     message,
345                                     &error,
346                                     DBUS_TYPE_BOOLEAN, &b,
347                                     DBUS_TYPE_INVALID))
348                         return bus_send_error_reply(connection, message, &error, -EINVAL);
349
350                 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
351                 if (ul == (unsigned long) -1)
352                         return bus_send_error_reply(connection, message, &error, -EIO);
353
354                 if (ul != 0 && ul != s->user->uid)
355                         return bus_send_error_reply(connection, message, NULL, -EPERM);
356
357                 session_set_idle_hint(s, b);
358
359                 reply = dbus_message_new_method_return(message);
360                 if (!reply)
361                         goto oom;
362
363         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
364                 const char *swho;
365                 int32_t signo;
366                 KillWho who;
367
368                 if (!dbus_message_get_args(
369                                     message,
370                                     &error,
371                                     DBUS_TYPE_STRING, &swho,
372                                     DBUS_TYPE_INT32, &signo,
373                                     DBUS_TYPE_INVALID))
374                         return bus_send_error_reply(connection, message, &error, -EINVAL);
375
376                 if (isempty(swho))
377                         who = KILL_ALL;
378                 else {
379                         who = kill_who_from_string(swho);
380                         if (who < 0)
381                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
382                 }
383
384                 if (signo <= 0 || signo >= _NSIG)
385                         return bus_send_error_reply(connection, message, &error, -EINVAL);
386
387                 r = session_kill(s, who, signo);
388                 if (r < 0)
389                         return bus_send_error_reply(connection, message, NULL, r);
390
391                 reply = dbus_message_new_method_return(message);
392                 if (!reply)
393                         goto oom;
394
395         } else {
396                 const BusBoundProperties bps[] = {
397                         { "org.freedesktop.login1.Session", bus_login_session_properties,      s       },
398                         { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
399                         { NULL, }
400                 };
401                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
402         }
403
404         if (reply) {
405                 if (!bus_maybe_send_reply(connection, message, reply))
406                         goto oom;
407         }
408
409         return DBUS_HANDLER_RESULT_HANDLED;
410
411 oom:
412         dbus_error_free(&error);
413
414         return DBUS_HANDLER_RESULT_NEED_MEMORY;
415 }
416
417 static DBusHandlerResult session_message_handler(
418                 DBusConnection *connection,
419                 DBusMessage *message,
420                 void *userdata) {
421
422         Manager *m = userdata;
423         Session *s;
424         int r;
425
426         r = get_session_for_path(m, dbus_message_get_path(message), &s);
427         if (r < 0) {
428
429                 if (r == -ENOMEM)
430                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
431
432                 if (r == -ENOENT) {
433                         DBusError e;
434
435                         dbus_error_init(&e);
436                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
437                         return bus_send_error_reply(connection, message, &e, r);
438                 }
439
440                 return bus_send_error_reply(connection, message, NULL, r);
441         }
442
443         return session_message_dispatch(s, connection, message);
444 }
445
446 const DBusObjectPathVTable bus_session_vtable = {
447         .message_function = session_message_handler
448 };
449
450 char *session_bus_path(Session *s) {
451         _cleanup_free_ char *t = NULL;
452
453         assert(s);
454
455         t = bus_path_escape(s->id);
456         if (!t)
457                 return NULL;
458
459         return strappend("/org/freedesktop/login1/session/", t);
460 }
461
462 int session_send_signal(Session *s, bool new_session) {
463         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
464         _cleanup_free_ char *p = NULL;
465
466         assert(s);
467
468         m = dbus_message_new_signal("/org/freedesktop/login1",
469                                     "org.freedesktop.login1.Manager",
470                                     new_session ? "SessionNew" : "SessionRemoved");
471
472         if (!m)
473                 return -ENOMEM;
474
475         p = session_bus_path(s);
476         if (!p)
477                 return -ENOMEM;
478
479         if (!dbus_message_append_args(
480                             m,
481                             DBUS_TYPE_STRING, &s->id,
482                             DBUS_TYPE_OBJECT_PATH, &p,
483                             DBUS_TYPE_INVALID))
484                 return -ENOMEM;
485
486         if (!dbus_connection_send(s->manager->bus, m, NULL))
487                 return -ENOMEM;
488
489         return 0;
490 }
491
492 int session_send_changed(Session *s, const char *properties) {
493         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
494         _cleanup_free_ char *p = NULL;
495
496         assert(s);
497
498         if (!s->started)
499                 return 0;
500
501         p = session_bus_path(s);
502         if (!p)
503                 return -ENOMEM;
504
505         m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
506         if (!m)
507                 return -ENOMEM;
508
509         if (!dbus_connection_send(s->manager->bus, m, NULL))
510                 return -ENOMEM;
511
512         return 0;
513 }
514
515 int session_send_lock(Session *s, bool lock) {
516         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
517         bool b;
518         _cleanup_free_ char *p = NULL;
519
520         assert(s);
521
522         p = session_bus_path(s);
523         if (!p)
524                 return -ENOMEM;
525
526         m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
527
528         if (!m)
529                 return -ENOMEM;
530
531         b = dbus_connection_send(s->manager->bus, m, NULL);
532         if (!b)
533                 return -ENOMEM;
534
535         return 0;
536 }
537
538 int session_send_lock_all(Manager *m, bool lock) {
539         Session *session;
540         Iterator i;
541         int r = 0;
542
543         assert(m);
544
545         HASHMAP_FOREACH(session, m->sessions, i) {
546                 int k;
547
548                 k = session_send_lock(session, lock);
549                 if (k < 0)
550                         r = k;
551         }
552
553         return r;
554 }