chiark / gitweb /
logind: introduce session-devices
[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 "logind-session-device.h"
28 #include "dbus-common.h"
29 #include "util.h"
30
31 #define BUS_SESSION_INTERFACE \
32         " <interface name=\"org.freedesktop.login1.Session\">\n"        \
33         "  <method name=\"Terminate\"/>\n"                              \
34         "  <method name=\"Activate\"/>\n"                               \
35         "  <method name=\"Lock\"/>\n"                                   \
36         "  <method name=\"Unlock\"/>\n"                                 \
37         "  <method name=\"SetIdleHint\">\n"                             \
38         "   <arg name=\"b\" type=\"b\"/>\n"                             \
39         "  </method>\n"                                                 \
40         "  <method name=\"Kill\">\n"                                    \
41         "   <arg name=\"who\" type=\"s\"/>\n"                           \
42         "   <arg name=\"signal\" type=\"s\"/>\n"                        \
43         "  </method>\n"                                                 \
44         "  <method name=\"TakeControl\"/>\n"                            \
45         "   <arg name=\"force\" type=\"b\"/>\n"                         \
46         "  </method>\n"                                                 \
47         "  <method name=\"ReleaseControl\"/>\n"                         \
48         "  <method name=\"TakeDevice\">\n"                              \
49         "   <arg name=\"major\" type=\"u\" direction=\"in\"/>\n"        \
50         "   <arg name=\"minor\" type=\"u\" direction=\"in\"/>\n"        \
51         "   <arg name=\"fd\" type=\"h\" direction=\"out\"/>\n"          \
52         "   <arg name=\"paused\" type=\"b\" direction=\"out\"/>\n"      \
53         "  </method>\n"                                                 \
54         "  <method name=\"ReleaseDevice\">\n"                           \
55         "   <arg name=\"major\" type=\"u\"/>\n"                         \
56         "   <arg name=\"minor\" type=\"u\"/>\n"                         \
57         "  </method>\n"                                                 \
58         "  <method name=\"PauseDeviceComplete\">\n"                     \
59         "   <arg name=\"major\" type=\"u\"/>\n"                         \
60         "   <arg name=\"minor\" type=\"u\"/>\n"                         \
61         "  </method>\n"                                                 \
62         "  <signal name=\"PauseDevice\">\n"                             \
63         "   <arg name=\"major\" type=\"u\"/>\n"                         \
64         "   <arg name=\"minor\" type=\"u\"/>\n"                         \
65         "   <arg name=\"type\" type=\"s\"/>\n"                          \
66         "  </signal>\n"                                                 \
67         "  <signal name=\"ResumeDevice\">\n"                            \
68         "   <arg name=\"major\" type=\"u\"/>\n"                         \
69         "   <arg name=\"minor\" type=\"u\"/>\n"                         \
70         "   <arg name=\"fd\" type=\"h\"/>\n"                            \
71         "  </signal>\n"                                                 \
72         "  <signal name=\"Lock\"/>\n"                                   \
73         "  <signal name=\"Unlock\"/>\n"                                 \
74         "  <property name=\"Id\" type=\"s\" access=\"read\"/>\n"        \
75         "  <property name=\"User\" type=\"(uo)\" access=\"read\"/>\n"   \
76         "  <property name=\"Name\" type=\"s\" access=\"read\"/>\n"      \
77         "  <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
78         "  <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
79         "  <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n"      \
80         "  <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n"   \
81         "  <property name=\"TTY\" type=\"s\" access=\"read\"/>\n"       \
82         "  <property name=\"Display\" type=\"s\" access=\"read\"/>\n"   \
83         "  <property name=\"Remote\" type=\"b\" access=\"read\"/>\n"    \
84         "  <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
85         "  <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
86         "  <property name=\"Service\" type=\"s\" access=\"read\"/>\n"   \
87         "  <property name=\"Scope\" type=\"s\" access=\"read\"/>\n"     \
88         "  <property name=\"Leader\" type=\"u\" access=\"read\"/>\n"    \
89         "  <property name=\"Audit\" type=\"u\" access=\"read\"/>\n"     \
90         "  <property name=\"Type\" type=\"s\" access=\"read\"/>\n"      \
91         "  <property name=\"Class\" type=\"s\" access=\"read\"/>\n"     \
92         "  <property name=\"Active\" type=\"b\" access=\"read\"/>\n"    \
93         "  <property name=\"State\" type=\"s\" access=\"read\"/>\n"     \
94         "  <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n"  \
95         "  <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
96         "  <property name=\"IdleSinceHintMonotonic\" type=\"t\" access=\"read\"/>\n" \
97         " </interface>\n"
98
99 #define INTROSPECTION                                                   \
100         DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                       \
101         "<node>\n"                                                      \
102         BUS_SESSION_INTERFACE                                           \
103         BUS_PROPERTIES_INTERFACE                                        \
104         BUS_PEER_INTERFACE                                              \
105         BUS_INTROSPECTABLE_INTERFACE                                    \
106         "</node>\n"
107
108 #define INTERFACES_LIST                              \
109         BUS_GENERIC_INTERFACES_LIST                  \
110         "org.freedesktop.login1.Session\0"
111
112 static int bus_session_append_seat(DBusMessageIter *i, const char *property, void *data) {
113         DBusMessageIter sub;
114         Session *s = data;
115         const char *id, *path;
116         char *p = NULL;
117
118         assert(i);
119         assert(property);
120         assert(s);
121
122         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
123                 return -ENOMEM;
124
125         if (s->seat) {
126                 id = s->seat->id;
127                 path = p = seat_bus_path(s->seat);
128
129                 if (!p)
130                         return -ENOMEM;
131         } else {
132                 id = "";
133                 path = "/";
134         }
135
136         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &id) ||
137             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &path))
138                 return -ENOMEM;
139
140         if (!dbus_message_iter_close_container(i, &sub))
141                 return -ENOMEM;
142
143         return 0;
144 }
145
146 static int bus_session_append_user(DBusMessageIter *i, const char *property, void *data) {
147         DBusMessageIter sub;
148         User *u = data;
149         _cleanup_free_ char *p = NULL;
150
151         assert(i);
152         assert(property);
153         assert(u);
154
155         if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub))
156                 return -ENOMEM;
157
158         p = user_bus_path(u);
159         if (!p)
160                 return -ENOMEM;
161
162         if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT32, &u->uid) ||
163             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_OBJECT_PATH, &p))
164                 return -ENOMEM;
165
166         if (!dbus_message_iter_close_container(i, &sub))
167                 return -ENOMEM;
168
169         return 0;
170 }
171
172 static int bus_session_append_active(DBusMessageIter *i, const char *property, void *data) {
173         Session *s = data;
174         dbus_bool_t b;
175
176         assert(i);
177         assert(property);
178         assert(s);
179
180         b = session_is_active(s);
181         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
182                 return -ENOMEM;
183
184         return 0;
185 }
186
187 static int bus_session_append_idle_hint(DBusMessageIter *i, const char *property, void *data) {
188         Session *s = data;
189         int b;
190
191         assert(i);
192         assert(property);
193         assert(s);
194
195         b = session_get_idle_hint(s, NULL) > 0;
196         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b))
197                 return -ENOMEM;
198
199         return 0;
200 }
201
202 static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *property, void *data) {
203         Session *s = data;
204         dual_timestamp t;
205         uint64_t u;
206         int r;
207
208         assert(i);
209         assert(property);
210         assert(s);
211
212         r = session_get_idle_hint(s, &t);
213         if (r < 0)
214                 return r;
215
216         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
217
218         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_UINT64, &u))
219                 return -ENOMEM;
220
221         return 0;
222 }
223
224 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
225 static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
226
227 static int bus_session_append_state(DBusMessageIter *i, const char *property, void *data) {
228         Session *s = data;
229         const char *state;
230
231         assert(i);
232         assert(property);
233         assert(s);
234
235         state = session_state_to_string(session_get_state(s));
236
237         if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
238                 return -ENOMEM;
239
240         return 0;
241 }
242
243 static int get_session_for_path(Manager *m, const char *path, Session **_s) {
244         _cleanup_free_ char *id = NULL;
245         Session *s;
246
247         assert(m);
248         assert(path);
249         assert(_s);
250
251         if (!startswith(path, "/org/freedesktop/login1/session/"))
252                 return -EINVAL;
253
254         id = bus_path_unescape(path + 32);
255         if (!id)
256                 return -ENOMEM;
257
258         s = hashmap_get(m->sessions, id);
259         if (!s)
260                 return -ENOENT;
261
262         *_s = s;
263         return 0;
264 }
265
266 static const BusProperty bus_login_session_properties[] = {
267         { "Id",                     bus_property_append_string,         "s", offsetof(Session, id),                 true },
268         { "Timestamp",              bus_property_append_usec,           "t", offsetof(Session, timestamp.realtime)  },
269         { "TimestampMonotonic",     bus_property_append_usec,           "t", offsetof(Session, timestamp.monotonic) },
270         { "VTNr",                   bus_property_append_uint32,         "u", offsetof(Session, vtnr)                },
271         { "Seat",                   bus_session_append_seat,         "(so)", 0 },
272         { "TTY",                    bus_property_append_string,         "s", offsetof(Session, tty),                true },
273         { "Display",                bus_property_append_string,         "s", offsetof(Session, display),            true },
274         { "Remote",                 bus_property_append_bool,           "b", offsetof(Session, remote)              },
275         { "RemoteUser",             bus_property_append_string,         "s", offsetof(Session, remote_user),        true },
276         { "RemoteHost",             bus_property_append_string,         "s", offsetof(Session, remote_host),        true },
277         { "Service",                bus_property_append_string,         "s", offsetof(Session, service),            true },
278         { "Scope",                  bus_property_append_string,         "s", offsetof(Session, scope),              true },
279         { "Leader",                 bus_property_append_pid,            "u", offsetof(Session, leader)              },
280         { "Audit",                  bus_property_append_uint32,         "u", offsetof(Session, audit_id)            },
281         { "Type",                   bus_session_append_type,            "s", offsetof(Session, type)                },
282         { "Class",                  bus_session_append_class,           "s", offsetof(Session, class)               },
283         { "Active",                 bus_session_append_active,          "b", 0 },
284         { "State",                  bus_session_append_state,           "s", 0 },
285         { "IdleHint",               bus_session_append_idle_hint,       "b", 0 },
286         { "IdleSinceHint",          bus_session_append_idle_hint_since, "t", 0 },
287         { "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
288         { NULL, }
289 };
290
291 static const BusProperty bus_login_session_user_properties[] = {
292         { "User",                   bus_session_append_user,         "(uo)", 0 },
293         { "Name",                   bus_property_append_string,         "s", offsetof(User, name),                  true },
294         { NULL, }
295 };
296
297 static DBusHandlerResult session_message_dispatch(
298                 Session *s,
299                 DBusConnection *connection,
300                 DBusMessage *message) {
301
302         DBusError error;
303         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
304         int r;
305
306         assert(s);
307         assert(connection);
308         assert(message);
309
310         dbus_error_init(&error);
311
312         if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Terminate")) {
313
314                 r = session_stop(s);
315                 if (r < 0)
316                         return bus_send_error_reply(connection, message, NULL, r);
317
318                 reply = dbus_message_new_method_return(message);
319                 if (!reply)
320                         goto oom;
321
322         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Activate")) {
323
324                 r = session_activate(s);
325                 if (r < 0)
326                         return bus_send_error_reply(connection, message, NULL, r);
327
328                 reply = dbus_message_new_method_return(message);
329                 if (!reply)
330                         goto oom;
331
332         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Lock") ||
333                    dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Unlock")) {
334
335                 if (session_send_lock(s, streq(dbus_message_get_member(message), "Lock")) < 0)
336                         goto oom;
337
338                 reply = dbus_message_new_method_return(message);
339                 if (!reply)
340                         goto oom;
341
342         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "SetIdleHint")) {
343                 dbus_bool_t b;
344                 unsigned long ul;
345
346                 if (!dbus_message_get_args(
347                                     message,
348                                     &error,
349                                     DBUS_TYPE_BOOLEAN, &b,
350                                     DBUS_TYPE_INVALID))
351                         return bus_send_error_reply(connection, message, &error, -EINVAL);
352
353                 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
354                 if (ul == (unsigned long) -1)
355                         return bus_send_error_reply(connection, message, &error, -EIO);
356
357                 if (ul != 0 && ul != s->user->uid)
358                         return bus_send_error_reply(connection, message, NULL, -EPERM);
359
360                 session_set_idle_hint(s, b);
361
362                 reply = dbus_message_new_method_return(message);
363                 if (!reply)
364                         goto oom;
365
366         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "Kill")) {
367                 const char *swho;
368                 int32_t signo;
369                 KillWho who;
370
371                 if (!dbus_message_get_args(
372                                     message,
373                                     &error,
374                                     DBUS_TYPE_STRING, &swho,
375                                     DBUS_TYPE_INT32, &signo,
376                                     DBUS_TYPE_INVALID))
377                         return bus_send_error_reply(connection, message, &error, -EINVAL);
378
379                 if (isempty(swho))
380                         who = KILL_ALL;
381                 else {
382                         who = kill_who_from_string(swho);
383                         if (who < 0)
384                                 return bus_send_error_reply(connection, message, &error, -EINVAL);
385                 }
386
387                 if (signo <= 0 || signo >= _NSIG)
388                         return bus_send_error_reply(connection, message, &error, -EINVAL);
389
390                 r = session_kill(s, who, signo);
391                 if (r < 0)
392                         return bus_send_error_reply(connection, message, NULL, r);
393
394                 reply = dbus_message_new_method_return(message);
395                 if (!reply)
396                         goto oom;
397
398         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "TakeControl")) {
399                 dbus_bool_t force;
400                 unsigned long ul;
401
402                 if (!dbus_message_get_args(
403                                     message,
404                                     &error,
405                                     DBUS_TYPE_BOOLEAN, &force,
406                                     DBUS_TYPE_INVALID))
407                         return bus_send_error_reply(connection, message, &error, -EINVAL);
408
409                 ul = dbus_bus_get_unix_user(connection, dbus_message_get_sender(message), &error);
410                 if (ul == (unsigned long) -1)
411                         return bus_send_error_reply(connection, message, &error, -EIO);
412
413                 if (ul != 0 && (force || ul != s->user->uid))
414                         return bus_send_error_reply(connection, message, NULL, -EPERM);
415
416                 r = session_set_controller(s, bus_message_get_sender_with_fallback(message), force);
417                 if (r < 0)
418                         return bus_send_error_reply(connection, message, NULL, r);
419
420                 reply = dbus_message_new_method_return(message);
421                 if (!reply)
422                         goto oom;
423
424         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseControl")) {
425                 const char *sender = bus_message_get_sender_with_fallback(message);
426
427                 if (!session_is_controller(s, sender))
428                         return bus_send_error_reply(connection, message, NULL, -EPERM);
429
430                 session_drop_controller(s);
431
432                 reply = dbus_message_new_method_return(message);
433                 if (!reply)
434                         goto oom;
435
436         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "TakeDevice")) {
437                 SessionDevice *sd;
438                 bool b;
439                 dbus_bool_t paused;
440                 uint32_t major, minor;
441                 dev_t dev;
442
443                 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
444                         return bus_send_error_reply(connection, message, NULL, -EPERM);
445
446                 if (!dbus_message_get_args(
447                                     message,
448                                     &error,
449                                     DBUS_TYPE_UINT32, &major,
450                                     DBUS_TYPE_UINT32, &minor,
451                                     DBUS_TYPE_INVALID))
452                         return bus_send_error_reply(connection, message, &error, -EINVAL);
453
454                 dev = makedev(major, minor);
455                 assert_cc(sizeof(unsigned long) >= sizeof(dev_t));
456
457                 sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)dev));
458                 if (sd) {
459                         /* We don't allow retrieving a device multiple times.
460                          * The related ReleaseDevice call is not ref-counted.
461                          * The caller should use dup() if it requires more than
462                          * one fd (it would be functionally equivalent). */
463                         return bus_send_error_reply(connection, message, &error, -EBUSY);
464                 }
465
466                 r = session_device_new(s, dev, &sd);
467                 if (r < 0)
468                         return bus_send_error_reply(connection, message, NULL, r);
469
470                 reply = dbus_message_new_method_return(message);
471                 if (!reply) {
472                         session_device_free(sd);
473                         goto oom;
474                 }
475
476                 paused = !sd->active;
477                 b = dbus_message_append_args(
478                                 reply,
479                                 DBUS_TYPE_UNIX_FD, &sd->fd,
480                                 DBUS_TYPE_BOOLEAN, &paused,
481                                 DBUS_TYPE_INVALID);
482                 if (!b) {
483                         session_device_free(sd);
484                         return bus_send_error_reply(connection, message, NULL, -ENOMEM);
485                 }
486
487         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseDevice")) {
488                 SessionDevice *sd;
489                 uint32_t major, minor;
490
491                 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
492                         return bus_send_error_reply(connection, message, NULL, -EPERM);
493
494                 if (!dbus_message_get_args(
495                                     message,
496                                     &error,
497                                     DBUS_TYPE_UINT32, &major,
498                                     DBUS_TYPE_UINT32, &minor,
499                                     DBUS_TYPE_INVALID))
500                         return bus_send_error_reply(connection, message, &error, -EINVAL);
501
502                 sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)makedev(major, minor)));
503                 if (!sd)
504                         return bus_send_error_reply(connection, message, NULL, -ENODEV);
505
506                 session_device_free(sd);
507
508                 reply = dbus_message_new_method_return(message);
509                 if (!reply)
510                         goto oom;
511
512         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "PauseDeviceComplete")) {
513                 SessionDevice *sd;
514                 uint32_t major, minor;
515
516                 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
517                         return bus_send_error_reply(connection, message, NULL, -EPERM);
518
519                 if (!dbus_message_get_args(
520                                     message,
521                                     &error,
522                                     DBUS_TYPE_UINT32, &major,
523                                     DBUS_TYPE_UINT32, &minor,
524                                     DBUS_TYPE_INVALID))
525                         return bus_send_error_reply(connection, message, &error, -EINVAL);
526
527                 sd = hashmap_get(s->devices, ULONG_TO_PTR((unsigned long)makedev(major, minor)));
528                 if (!sd)
529                         return bus_send_error_reply(connection, message, NULL, -ENODEV);
530
531                 session_device_complete_pause(sd);
532
533                 reply = dbus_message_new_method_return(message);
534                 if (!reply)
535                         goto oom;
536
537         } else {
538                 const BusBoundProperties bps[] = {
539                         { "org.freedesktop.login1.Session", bus_login_session_properties,      s       },
540                         { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
541                         { NULL, }
542                 };
543                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
544         }
545
546         if (reply) {
547                 if (!bus_maybe_send_reply(connection, message, reply))
548                         goto oom;
549         }
550
551         return DBUS_HANDLER_RESULT_HANDLED;
552
553 oom:
554         dbus_error_free(&error);
555
556         return DBUS_HANDLER_RESULT_NEED_MEMORY;
557 }
558
559 static DBusHandlerResult session_message_handler(
560                 DBusConnection *connection,
561                 DBusMessage *message,
562                 void *userdata) {
563
564         Manager *m = userdata;
565         Session *s;
566         int r;
567
568         r = get_session_for_path(m, dbus_message_get_path(message), &s);
569         if (r < 0) {
570
571                 if (r == -ENOMEM)
572                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
573
574                 if (r == -ENOENT) {
575                         DBusError e;
576
577                         dbus_error_init(&e);
578                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
579                         return bus_send_error_reply(connection, message, &e, r);
580                 }
581
582                 return bus_send_error_reply(connection, message, NULL, r);
583         }
584
585         return session_message_dispatch(s, connection, message);
586 }
587
588 const DBusObjectPathVTable bus_session_vtable = {
589         .message_function = session_message_handler
590 };
591
592 char *session_bus_path(Session *s) {
593         _cleanup_free_ char *t = NULL;
594
595         assert(s);
596
597         t = bus_path_escape(s->id);
598         if (!t)
599                 return NULL;
600
601         return strappend("/org/freedesktop/login1/session/", t);
602 }
603
604 int session_send_signal(Session *s, bool new_session) {
605         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
606         _cleanup_free_ char *p = NULL;
607
608         assert(s);
609
610         m = dbus_message_new_signal("/org/freedesktop/login1",
611                                     "org.freedesktop.login1.Manager",
612                                     new_session ? "SessionNew" : "SessionRemoved");
613
614         if (!m)
615                 return -ENOMEM;
616
617         p = session_bus_path(s);
618         if (!p)
619                 return -ENOMEM;
620
621         if (!dbus_message_append_args(
622                             m,
623                             DBUS_TYPE_STRING, &s->id,
624                             DBUS_TYPE_OBJECT_PATH, &p,
625                             DBUS_TYPE_INVALID))
626                 return -ENOMEM;
627
628         if (!dbus_connection_send(s->manager->bus, m, NULL))
629                 return -ENOMEM;
630
631         return 0;
632 }
633
634 int session_send_changed(Session *s, const char *properties) {
635         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
636         _cleanup_free_ char *p = NULL;
637
638         assert(s);
639
640         if (!s->started)
641                 return 0;
642
643         p = session_bus_path(s);
644         if (!p)
645                 return -ENOMEM;
646
647         m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
648         if (!m)
649                 return -ENOMEM;
650
651         if (!dbus_connection_send(s->manager->bus, m, NULL))
652                 return -ENOMEM;
653
654         return 0;
655 }
656
657 int session_send_lock(Session *s, bool lock) {
658         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
659         bool b;
660         _cleanup_free_ char *p = NULL;
661
662         assert(s);
663
664         p = session_bus_path(s);
665         if (!p)
666                 return -ENOMEM;
667
668         m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
669
670         if (!m)
671                 return -ENOMEM;
672
673         b = dbus_connection_send(s->manager->bus, m, NULL);
674         if (!b)
675                 return -ENOMEM;
676
677         return 0;
678 }
679
680 int session_send_lock_all(Manager *m, bool lock) {
681         Session *session;
682         Iterator i;
683         int r = 0;
684
685         assert(m);
686
687         HASHMAP_FOREACH(session, m->sessions, i) {
688                 int k;
689
690                 k = session_send_lock(session, lock);
691                 if (k < 0)
692                         r = k;
693         }
694
695         return r;
696 }
697
698 int session_send_create_reply(Session *s, DBusError *error) {
699         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
700
701         assert(s);
702
703         if (!s->create_message)
704                 return 0;
705
706         /* This is called after the session scope was successfully
707          * created, and finishes where bus_manager_create_session()
708          * left off. */
709
710         if (error) {
711                 DBusError buffer;
712
713                 dbus_error_init(&buffer);
714
715                 if (!dbus_error_is_set(error)) {
716                         dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
717                         error = &buffer;
718                 }
719
720                 reply = dbus_message_new_error(s->create_message, error->name, error->message);
721                 dbus_error_free(&buffer);
722
723                 if (!reply)
724                         return log_oom();
725         } else {
726                 _cleanup_close_ int fifo_fd = -1;
727                 _cleanup_free_ char *path = NULL;
728                 const char *cseat;
729                 uint32_t vtnr;
730                 dbus_bool_t exists;
731
732                 fifo_fd = session_create_fifo(s);
733                 if (fifo_fd < 0) {
734                         log_error("Failed to create fifo: %s", strerror(-fifo_fd));
735                         return fifo_fd;
736                 }
737
738                 path = session_bus_path(s);
739                 if (!path)
740                         return log_oom();
741
742                 reply = dbus_message_new_method_return(s->create_message);
743                 if (!reply)
744                         return log_oom();
745
746                 cseat = s->seat ? s->seat->id : "";
747                 vtnr = s->vtnr;
748                 exists = false;
749
750                 if (!dbus_message_append_args(
751                                     reply,
752                                     DBUS_TYPE_STRING, &s->id,
753                                     DBUS_TYPE_OBJECT_PATH, &path,
754                                     DBUS_TYPE_STRING, &s->user->runtime_path,
755                                     DBUS_TYPE_UNIX_FD, &fifo_fd,
756                                     DBUS_TYPE_STRING, &cseat,
757                                     DBUS_TYPE_UINT32, &vtnr,
758                                     DBUS_TYPE_BOOLEAN, &exists,
759                                     DBUS_TYPE_INVALID))
760                         return log_oom();
761         }
762
763         /* Update the state file before we notify the client about the
764          * result */
765         session_save(s);
766
767         if (!dbus_connection_send(s->manager->bus, reply, NULL))
768                 return log_oom();
769
770         dbus_message_unref(s->create_message);
771         s->create_message = NULL;
772
773         return 0;
774 }