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