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