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