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