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