chiark / gitweb /
Do no isolate in case of emergency or severe problems
[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=\"Leader\" type=\"u\" access=\"read\"/>\n"    \
60         "  <property name=\"Audit\" type=\"u\" access=\"read\"/>\n"     \
61         "  <property name=\"Type\" type=\"s\" access=\"read\"/>\n"      \
62         "  <property name=\"Class\" type=\"s\" access=\"read\"/>\n"     \
63         "  <property name=\"Active\" type=\"b\" access=\"read\"/>\n"    \
64         "  <property name=\"State\" type=\"s\" access=\"read\"/>\n"     \
65         "  <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
66         "  <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
67         "  <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
68         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
69         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
70         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
71         " </interface>\n"
72
73 #define INTROSPECTION                                                   \
74         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
75         "<node>\n"                                                      \
76         BUS_SESSION_INTERFACE                                           \
77         BUS_PROPERTIES_INTERFACE                                        \
78         BUS_PEER_INTERFACE                                              \
79         BUS_INTROSPECTABLE_INTERFACE                                    \
80         "</node>\n"
81
82 #define INTERFACES_LIST                              \
83         BUS_GENERIC_INTERFACES_LIST                  \
84         "org.freedesktop.login1.Session\0"
85
86 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
87         DBusMessageIter sub;
88         Session *s = data;
89         const char *id, *path;
90         char *p = NULL;
91
92         assert(i);
93         assert(property);
94         assert(s);
95
96         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
97                 return -ENOMEM;
98
99         if (s->seat) {
100                 id = s->seat->id;
101                 path = p = seat_bus_path(s->seat);
102
103                 if (!p)
104                         return -ENOMEM;
105         } else {
106                 id = "";
107                 path = "/";
108         }
109
110         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
111             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
112                 return -ENOMEM;
113
114         if (!dbus_message_iter_close_container(i, &sub))
115                 return -ENOMEM;
116
117         return 0;
118 }
119
120 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
121         DBusMessageIter sub;
122         User *u = data;
123         char _cleanup_free_ *p = NULL;
124
125         assert(i);
126         assert(property);
127         assert(u);
128
129         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
130                 return -ENOMEM;
131
132         p = user_bus_path(u);
133         if (!p)
134                 return -ENOMEM;
135
136         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
137             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
138                 return -ENOMEM;
139
140         if (!dbus_message_iter_close_container(i, &sub))
141                 return -ENOMEM;
142
143         return 0;
144 }
145
146 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
147         Session *s = data;
148         dbus_bool_t b;
149
150         assert(i);
151         assert(property);
152         assert(s);
153
154         b = session_is_active(s);
155         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
156                 return -ENOMEM;
157
158         return 0;
159 }
160
161 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
162         Session *s = data;
163         int b;
164
165         assert(i);
166         assert(property);
167         assert(s);
168
169         b = session_get_idle_hint(s, NULL) > 0;
170         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
171                 return -ENOMEM;
172
173         return 0;
174 }
175
176 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
177         Session *s = data;
178         dual_timestamp t;
179         uint64_t u;
180         int r;
181
182         assert(i);
183         assert(property);
184         assert(s);
185
186         r = session_get_idle_hint(s, &t);
187         if (r < 0)
188                 return r;
189
190         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
191
192         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
193                 return -ENOMEM;
194
195         return 0;
196 }
197
198 static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
199         Session *s = data;
200         char _cleanup_free_ *t = NULL;
201         int r;
202         bool success;
203
204         assert(i);
205         assert(property);
206         assert(s);
207
208         r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
209         if (r < 0)
210                 return r;
211
212         success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
213         return success ? 0 : -ENOMEM;
214 }
215
216 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
217 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
218
219 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
220         Session *s = data;
221         const char *state;
222
223         assert(i);
224         assert(property);
225         assert(s);
226
227         state = session_state_to_string(session_get_state(s));
228
229         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
230                 return -ENOMEM;
231
232         return 0;
233 }
234
235 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
236         Session *s;
237         char *id;
238
239         assert(m);
240         assert(path);
241         assert(_s);
242
243         if (!startswith(path, "/org/freedesktop/login1/session/"))
244                 return -EINVAL;
245
246         id = bus_path_unescape(path + 32);
247         if (!id)
248                 return -ENOMEM;
249
250         s = hashmap_get(m->sessions, id);
251         free(id);
252
253         if (!s)
254                 return -ENOENT;
255
256         *_s = s;
257         return 0;
258 }
259
260 static const BusProperty bus_login_session_properties[] = {
261         { "Id",                     bus_property_append_string,         "s", offsetof(Session, id),                 true },
262         { "Timestamp",              bus_property_append_usec,           "t", offsetof(Session, timestamp.realtime)  },
263         { "TimestampMonotonic",     bus_property_append_usec,           "t", offsetof(Session, timestamp.monotonic) },
264         { "DefaultControlGroup",    bus_session_append_default_cgroup,  "s", 0,                                     },
265         { "VTNr",                   bus_property_append_uint32,         "u", offsetof(Session, vtnr)                },
266         { "Seat",                   bus_session_append_seat,         "(so)", 0 },
267         { "TTY",                    bus_property_append_string,         "s", offsetof(Session, tty),                true },
268         { "Display",                bus_property_append_string,         "s", offsetof(Session, display),            true },
269         { "Remote",                 bus_property_append_bool,           "b", offsetof(Session, remote)              },
270         { "RemoteUser",             bus_property_append_string,         "s", offsetof(Session, remote_user),        true },
271         { "RemoteHost",             bus_property_append_string,         "s", offsetof(Session, remote_host),        true },
272         { "Service",                bus_property_append_string,         "s", offsetof(Session, service),            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         char _cleanup_free_ *t;
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 }