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