chiark / gitweb /
unit-name: style fix in unit_name_is_template()
[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
187         assert(i);
188         assert(property);
189         assert(s);
190
191         session_get_idle_hint(s, &t);
192         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
193
194         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
195                 return -ENOMEM;
196
197         return 0;
198 }
199
200 static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
201         Session *s = data;
202         char *t;
203         int r;
204         bool success;
205
206         assert(i);
207         assert(property);
208         assert(s);
209
210         r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
211         if (r < 0)
212                 return r;
213
214         success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
215         free(t);
216
217         return success ? 0 : -ENOMEM;
218 }
219
220 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
221 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
222
223 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
224         Session *s = data;
225         const char *state;
226
227         assert(i);
228         assert(property);
229         assert(s);
230
231         state = session_state_to_string(session_get_state(s));
232
233         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
234                 return -ENOMEM;
235
236         return 0;
237 }
238
239 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
240         Session *s;
241         char *id;
242
243         assert(m);
244         assert(path);
245         assert(_s);
246
247         if (!startswith(path, "/org/freedesktop/login1/session/"))
248                 return -EINVAL;
249
250         id = bus_path_unescape(path + 32);
251         if (!id)
252                 return -ENOMEM;
253
254         s = hashmap_get(m->sessions, id);
255         free(id);
256
257         if (!s)
258                 return -ENOENT;
259
260         *_s = s;
261         return 0;
262 }
263
264 static const BusProperty bus_login_session_properties[] = {
265         { "Id",                     bus_property_append_string,         "s", offsetof(Session, id),                 true },
266         { "Timestamp",              bus_property_append_usec,           "t", offsetof(Session, timestamp.realtime)  },
267         { "TimestampMonotonic",     bus_property_append_usec,           "t", offsetof(Session, timestamp.monotonic) },
268         { "DefaultControlGroup",    bus_session_append_default_cgroup,  "s", 0,                                     },
269         { "VTNr",                   bus_property_append_uint32,         "u", offsetof(Session, vtnr)                },
270         { "Seat",                   bus_session_append_seat,         "(so)", 0 },
271         { "TTY",                    bus_property_append_string,         "s", offsetof(Session, tty),                true },
272         { "Display",                bus_property_append_string,         "s", offsetof(Session, display),            true },
273         { "Remote",                 bus_property_append_bool,           "b", offsetof(Session, remote)              },
274         { "RemoteUser",             bus_property_append_string,         "s", offsetof(Session, remote_user),        true },
275         { "RemoteHost",             bus_property_append_string,         "s", offsetof(Session, remote_host),        true },
276         { "Service",                bus_property_append_string,         "s", offsetof(Session, service),            true },
277         { "Leader",                 bus_property_append_pid,            "u", offsetof(Session, leader)              },
278         { "Audit",                  bus_property_append_uint32,         "u", offsetof(Session, audit_id)            },
279         { "Type",                   bus_session_append_type,            "s", offsetof(Session, type)                },
280         { "Class",                  bus_session_append_class,           "s", offsetof(Session, class)               },
281         { "Active",                 bus_session_append_active,          "b", 0 },
282         { "State",                  bus_session_append_state,           "s", 0 },
283         { "Controllers",            bus_property_append_strv,          "as", offsetof(Session, controllers),        true },
284         { "ResetControllers",       bus_property_append_strv,          "as", offsetof(Session, reset_controllers),  true },
285         { "KillProcesses",          bus_property_append_bool,           "b", offsetof(Session, kill_processes)      },
286         { "IdleHint",               bus_session_append_idle_hint,       "b", 0 },
287         { "IdleSinceHint",          bus_session_append_idle_hint_since, "t", 0 },
288         { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
289         { NULL, }
290 };
291
292 static const BusProperty bus_login_session_user_properties[] = {
293         { "User",                   bus_session_append_user,         "(uo)", 0 },
294         { "Name",                   bus_property_append_string,         "s", offsetof(User, name),                  true },
295         { NULL, }
296 };
297
298 static DBusHandlerResult session_message_dispatch(
299                 Session *s,
300                 DBusConnection *connection,
301                 DBusMessage *message) {
302
303         DBusError error;
304         DBusMessage *reply = NULL;
305         int r;
306
307         assert(s);
308         assert(connection);
309         assert(message);
310
311         dbus_error_init(&error);
312
313         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
314
315                 r = session_stop(s);
316                 if (r < 0)
317                         return bus_send_error_reply(connection, message, NULL, r);
318
319                 reply = dbus_message_new_method_return(message);
320                 if (!reply)
321                         goto oom;
322
323         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
324
325                 r = session_activate(s);
326                 if (r < 0)
327                         return bus_send_error_reply(connection, message, NULL, r);
328
329                 reply = dbus_message_new_method_return(message);
330                 if (!reply)
331                         goto oom;
332
333         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
334                    dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
335
336                 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
337                         goto oom;
338
339                 reply = dbus_message_new_method_return(message);
340                 if (!reply)
341                         goto oom;
342
343         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
344                 dbus_bool_t b;
345                 unsigned long ul;
346
347                 if (!dbus_message_get_args(
348                                     message,
349                                     &error,
350                                     DBUS_TYPE_BOOLEAN, &b,
351                                     DBUS_TYPE_INVALID))
352                         return bus_send_error_reply(connection, message, &error, -EINVAL);
353
354                 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
355                 if (ul == (unsigned long) -1)
356                         return bus_send_error_reply(connection, message, &error, -EIO);
357
358                 if (ul != 0 && ul != s->user->uid)
359                         return bus_send_error_reply(connection, message, NULL, -EPERM);
360
361                 session_set_idle_hint(s, b);
362
363                 reply = dbus_message_new_method_return(message);
364                 if (!reply)
365                         goto oom;
366
367         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
368                 const char *swho;
369                 int32_t signo;
370                 KillWho who;
371
372                 if (!dbus_message_get_args(
373                                     message,
374                                     &error,
375                                     DBUS_TYPE_STRING, &swho,
376                                     DBUS_TYPE_INT32, &signo,
377                                     DBUS_TYPE_INVALID))
378                         return bus_send_error_reply(connection, message, &error, -EINVAL);
379
380                 if (isempty(swho))
381                         who = KILL_ALL;
382                 else {
383                         who = kill_who_from_string(swho);
384                         if (who < 0)
385                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
386                 }
387
388                 if (signo <= 0 || signo >= _NSIG)
389                         return bus_send_error_reply(connection, message, &error, -EINVAL);
390
391                 r = session_kill(s, who, signo);
392                 if (r < 0)
393                         return bus_send_error_reply(connection, message, NULL, r);
394
395                 reply = dbus_message_new_method_return(message);
396                 if (!reply)
397                         goto oom;
398
399         } else {
400                 const BusBoundProperties bps[] = {
401                         { "org.freedesktop.login1.Session", bus_login_session_properties,      s       },
402                         { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
403                         { NULL, }
404                 };
405                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
406         }
407
408         if (reply) {
409                 if (!dbus_connection_send(connection, reply, NULL))
410                         goto oom;
411
412                 dbus_message_unref(reply);
413         }
414
415         return DBUS_HANDLER_RESULT_HANDLED;
416
417 oom:
418         if (reply)
419                 dbus_message_unref(reply);
420
421         dbus_error_free(&error);
422
423         return DBUS_HANDLER_RESULT_NEED_MEMORY;
424 }
425
426 static DBusHandlerResult session_message_handler(
427                 DBusConnection *connection,
428                 DBusMessage *message,
429                 void *userdata) {
430
431         Manager *m = userdata;
432         Session *s;
433         int r;
434
435         r = get_session_for_path(m, dbus_message_get_path(message), &s);
436         if (r < 0) {
437
438                 if (r == -ENOMEM)
439                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
440
441                 if (r == -ENOENT) {
442                         DBusError e;
443
444                         dbus_error_init(&e);
445                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
446                         return bus_send_error_reply(connection, message, &e, r);
447                 }
448
449                 return bus_send_error_reply(connection, message, NULL, r);
450         }
451
452         return session_message_dispatch(s, connection, message);
453 }
454
455 const DBusObjectPathVTable bus_session_vtable = {
456         .message_function = session_message_handler
457 };
458
459 char *session_bus_path(Session *s) {
460         char *t, *r;
461
462         assert(s);
463
464         t = bus_path_escape(s->id);
465         if (!t)
466                 return NULL;
467
468         r = strappend("/org/freedesktop/login1/session/", t);
469         free(t);
470
471         return r;
472 }
473
474 int session_send_signal(Session *s, bool new_session) {
475         DBusMessage *m;
476         int r = -ENOMEM;
477         char *p = NULL;
478
479         assert(s);
480
481         m = dbus_message_new_signal("/org/freedesktop/login1",
482                                     "org.freedesktop.login1.Manager",
483                                     new_session ? "SessionNew" : "SessionRemoved");
484
485         if (!m)
486                 return -ENOMEM;
487
488         p = session_bus_path(s);
489         if (!p)
490                 goto finish;
491
492         if (!dbus_message_append_args(
493                             m,
494                             DBUS_TYPE_STRING, &s->id,
495                             DBUS_TYPE_OBJECT_PATH, &p,
496                             DBUS_TYPE_INVALID))
497                 goto finish;
498
499         if (!dbus_connection_send(s->manager->bus, m, NULL))
500                 goto finish;
501
502         r = 0;
503
504 finish:
505         dbus_message_unref(m);
506         free(p);
507
508         return r;
509 }
510
511 int session_send_changed(Session *s, const char *properties) {
512         DBusMessage *m;
513         int r = -ENOMEM;
514         char *p = NULL;
515
516         assert(s);
517
518         if (!s->started)
519                 return 0;
520
521         p = session_bus_path(s);
522         if (!p)
523                 return -ENOMEM;
524
525         m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
526         if (!m)
527                 goto finish;
528
529         if (!dbus_connection_send(s->manager->bus, m, NULL))
530                 goto finish;
531
532         r = 0;
533
534 finish:
535         if (m)
536                 dbus_message_unref(m);
537         free(p);
538
539         return r;
540 }
541
542 int session_send_lock(Session *s, bool lock) {
543         DBusMessage *m;
544         bool b;
545         char *p;
546
547         assert(s);
548
549         p = session_bus_path(s);
550         if (!p)
551                 return -ENOMEM;
552
553         m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
554         free(p);
555
556         if (!m)
557                 return -ENOMEM;
558
559         b = dbus_connection_send(s->manager->bus, m, NULL);
560         dbus_message_unref(m);
561
562         if (!b)
563                 return -ENOMEM;
564
565         return 0;
566 }