chiark / gitweb /
random-seed: fix error message typo
[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         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 (!dbus_connection_send(connection, reply, NULL))
416                         goto oom;
417
418                 dbus_message_unref(reply);
419         }
420
421         return DBUS_HANDLER_RESULT_HANDLED;
422
423 oom:
424         if (reply)
425                 dbus_message_unref(reply);
426
427         dbus_error_free(&error);
428
429         return DBUS_HANDLER_RESULT_NEED_MEMORY;
430 }
431
432 static DBusHandlerResult session_message_handler(
433                 DBusConnection *connection,
434                 DBusMessage *message,
435                 void *userdata) {
436
437         Manager *m = userdata;
438         Session *s;
439         int r;
440
441         r = get_session_for_path(m, dbus_message_get_path(message), &s);
442         if (r < 0) {
443
444                 if (r == -ENOMEM)
445                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
446
447                 if (r == -ENOENT) {
448                         DBusError e;
449
450                         dbus_error_init(&e);
451                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
452                         return bus_send_error_reply(connection, message, &e, r);
453                 }
454
455                 return bus_send_error_reply(connection, message, NULL, r);
456         }
457
458         return session_message_dispatch(s, connection, message);
459 }
460
461 const DBusObjectPathVTable bus_session_vtable = {
462         .message_function = session_message_handler
463 };
464
465 char *session_bus_path(Session *s) {
466         char *t, *r;
467
468         assert(s);
469
470         t = bus_path_escape(s->id);
471         if (!t)
472                 return NULL;
473
474         r = strappend("/org/freedesktop/login1/session/", t);
475         free(t);
476
477         return r;
478 }
479
480 int session_send_signal(Session *s, bool new_session) {
481         DBusMessage *m;
482         int r = -ENOMEM;
483         char *p = NULL;
484
485         assert(s);
486
487         m = dbus_message_new_signal("/org/freedesktop/login1",
488                                     "org.freedesktop.login1.Manager",
489                                     new_session ? "SessionNew" : "SessionRemoved");
490
491         if (!m)
492                 return -ENOMEM;
493
494         p = session_bus_path(s);
495         if (!p)
496                 goto finish;
497
498         if (!dbus_message_append_args(
499                             m,
500                             DBUS_TYPE_STRING, &s->id,
501                             DBUS_TYPE_OBJECT_PATH, &p,
502                             DBUS_TYPE_INVALID))
503                 goto finish;
504
505         if (!dbus_connection_send(s->manager->bus, m, NULL))
506                 goto finish;
507
508         r = 0;
509
510 finish:
511         dbus_message_unref(m);
512         free(p);
513
514         return r;
515 }
516
517 int session_send_changed(Session *s, const char *properties) {
518         DBusMessage *m;
519         int r = -ENOMEM;
520         char *p = NULL;
521
522         assert(s);
523
524         if (!s->started)
525                 return 0;
526
527         p = session_bus_path(s);
528         if (!p)
529                 return -ENOMEM;
530
531         m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
532         if (!m)
533                 goto finish;
534
535         if (!dbus_connection_send(s->manager->bus, m, NULL))
536                 goto finish;
537
538         r = 0;
539
540 finish:
541         if (m)
542                 dbus_message_unref(m);
543         free(p);
544
545         return r;
546 }
547
548 int session_send_lock(Session *s, bool lock) {
549         DBusMessage *m;
550         bool b;
551         char *p;
552
553         assert(s);
554
555         p = session_bus_path(s);
556         if (!p)
557                 return -ENOMEM;
558
559         m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
560         free(p);
561
562         if (!m)
563                 return -ENOMEM;
564
565         b = dbus_connection_send(s->manager->bus, m, NULL);
566         dbus_message_unref(m);
567
568         if (!b)
569                 return -ENOMEM;
570
571         return 0;
572 }