chiark / gitweb /
logind: fix build for ARM with sizeof(dev_t) > sizeof(void*)
[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                 sd = hashmap_get(s->devices, &dev);
456                 if (sd) {
457                         /* We don't allow retrieving a device multiple times.
458                          * The related ReleaseDevice call is not ref-counted.
459                          * The caller should use dup() if it requires more than
460                          * one fd (it would be functionally equivalent). */
461                         return bus_send_error_reply(connection, message, &error, -EBUSY);
462                 }
463
464                 r = session_device_new(s, dev, &sd);
465                 if (r < 0)
466                         return bus_send_error_reply(connection, message, NULL, r);
467
468                 reply = dbus_message_new_method_return(message);
469                 if (!reply) {
470                         session_device_free(sd);
471                         goto oom;
472                 }
473
474                 paused = !sd->active;
475                 b = dbus_message_append_args(
476                                 reply,
477                                 DBUS_TYPE_UNIX_FD, &sd->fd,
478                                 DBUS_TYPE_BOOLEAN, &paused,
479                                 DBUS_TYPE_INVALID);
480                 if (!b) {
481                         session_device_free(sd);
482                         return bus_send_error_reply(connection, message, NULL, -ENOMEM);
483                 }
484
485         } else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Session", "ReleaseDevice")) {
486                 SessionDevice *sd;
487                 uint32_t major, minor;
488                 dev_t dev;
489
490                 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
491                         return bus_send_error_reply(connection, message, NULL, -EPERM);
492
493                 if (!dbus_message_get_args(
494                                     message,
495                                     &error,
496                                     DBUS_TYPE_UINT32, &major,
497                                     DBUS_TYPE_UINT32, &minor,
498                                     DBUS_TYPE_INVALID))
499                         return bus_send_error_reply(connection, message, &error, -EINVAL);
500
501                 dev = makedev(major, minor);
502                 sd = hashmap_get(s->devices, &dev);
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                 dev_t dev;
516
517                 if (!session_is_controller(s, bus_message_get_sender_with_fallback(message)))
518                         return bus_send_error_reply(connection, message, NULL, -EPERM);
519
520                 if (!dbus_message_get_args(
521                                     message,
522                                     &error,
523                                     DBUS_TYPE_UINT32, &major,
524                                     DBUS_TYPE_UINT32, &minor,
525                                     DBUS_TYPE_INVALID))
526                         return bus_send_error_reply(connection, message, &error, -EINVAL);
527
528                 dev = makedev(major, minor);
529                 sd = hashmap_get(s->devices, &dev);
530                 if (!sd)
531                         return bus_send_error_reply(connection, message, NULL, -ENODEV);
532
533                 session_device_complete_pause(sd);
534
535                 reply = dbus_message_new_method_return(message);
536                 if (!reply)
537                         goto oom;
538
539         } else {
540                 const BusBoundProperties bps[] = {
541                         { "org.freedesktop.login1.Session", bus_login_session_properties,      s       },
542                         { "org.freedesktop.login1.Session", bus_login_session_user_properties, s->user },
543                         { NULL, }
544                 };
545                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, bps);
546         }
547
548         if (reply) {
549                 if (!bus_maybe_send_reply(connection, message, reply))
550                         goto oom;
551         }
552
553         return DBUS_HANDLER_RESULT_HANDLED;
554
555 oom:
556         dbus_error_free(&error);
557
558         return DBUS_HANDLER_RESULT_NEED_MEMORY;
559 }
560
561 static DBusHandlerResult session_message_handler(
562                 DBusConnection *connection,
563                 DBusMessage *message,
564                 void *userdata) {
565
566         Manager *m = userdata;
567         Session *s;
568         int r;
569
570         r = get_session_for_path(m, dbus_message_get_path(message), &s);
571         if (r < 0) {
572
573                 if (r == -ENOMEM)
574                         return DBUS_HANDLER_RESULT_NEED_MEMORY;
575
576                 if (r == -ENOENT) {
577                         DBusError e;
578
579                         dbus_error_init(&e);
580                         dbus_set_error_const(&e, DBUS_ERROR_UNKNOWN_OBJECT, "Unknown session");
581                         return bus_send_error_reply(connection, message, &e, r);
582                 }
583
584                 return bus_send_error_reply(connection, message, NULL, r);
585         }
586
587         return session_message_dispatch(s, connection, message);
588 }
589
590 const DBusObjectPathVTable bus_session_vtable = {
591         .message_function = session_message_handler
592 };
593
594 char *session_bus_path(Session *s) {
595         _cleanup_free_ char *t = NULL;
596
597         assert(s);
598
599         t = bus_path_escape(s->id);
600         if (!t)
601                 return NULL;
602
603         return strappend("/org/freedesktop/login1/session/", t);
604 }
605
606 int session_send_signal(Session *s, bool new_session) {
607         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
608         _cleanup_free_ char *p = NULL;
609
610         assert(s);
611
612         m = dbus_message_new_signal("/org/freedesktop/login1",
613                                     "org.freedesktop.login1.Manager",
614                                     new_session ? "SessionNew" : "SessionRemoved");
615
616         if (!m)
617                 return -ENOMEM;
618
619         p = session_bus_path(s);
620         if (!p)
621                 return -ENOMEM;
622
623         if (!dbus_message_append_args(
624                             m,
625                             DBUS_TYPE_STRING, &s->id,
626                             DBUS_TYPE_OBJECT_PATH, &p,
627                             DBUS_TYPE_INVALID))
628                 return -ENOMEM;
629
630         if (!dbus_connection_send(s->manager->bus, m, NULL))
631                 return -ENOMEM;
632
633         return 0;
634 }
635
636 int session_send_changed(Session *s, const char *properties) {
637         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
638         _cleanup_free_ char *p = NULL;
639
640         assert(s);
641
642         if (!s->started)
643                 return 0;
644
645         p = session_bus_path(s);
646         if (!p)
647                 return -ENOMEM;
648
649         m = bus_properties_changed_new(p, "org.freedesktop.login1.Session", properties);
650         if (!m)
651                 return -ENOMEM;
652
653         if (!dbus_connection_send(s->manager->bus, m, NULL))
654                 return -ENOMEM;
655
656         return 0;
657 }
658
659 int session_send_lock(Session *s, bool lock) {
660         _cleanup_dbus_message_unref_ DBusMessage *m = NULL;
661         bool b;
662         _cleanup_free_ char *p = NULL;
663
664         assert(s);
665
666         p = session_bus_path(s);
667         if (!p)
668                 return -ENOMEM;
669
670         m = dbus_message_new_signal(p, "org.freedesktop.login1.Session", lock ? "Lock" : "Unlock");
671
672         if (!m)
673                 return -ENOMEM;
674
675         b = dbus_connection_send(s->manager->bus, m, NULL);
676         if (!b)
677                 return -ENOMEM;
678
679         return 0;
680 }
681
682 int session_send_lock_all(Manager *m, bool lock) {
683         Session *session;
684         Iterator i;
685         int r = 0;
686
687         assert(m);
688
689         HASHMAP_FOREACH(session, m->sessions, i) {
690                 int k;
691
692                 k = session_send_lock(session, lock);
693                 if (k < 0)
694                         r = k;
695         }
696
697         return r;
698 }
699
700 int session_send_create_reply(Session *s, DBusError *error) {
701         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
702
703         assert(s);
704
705         if (!s->create_message)
706                 return 0;
707
708         /* This is called after the session scope was successfully
709          * created, and finishes where bus_manager_create_session()
710          * left off. */
711
712         if (error) {
713                 DBusError buffer;
714
715                 dbus_error_init(&buffer);
716
717                 if (!dbus_error_is_set(error)) {
718                         dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
719                         error = &buffer;
720                 }
721
722                 reply = dbus_message_new_error(s->create_message, error->name, error->message);
723                 dbus_error_free(&buffer);
724
725                 if (!reply)
726                         return log_oom();
727         } else {
728                 _cleanup_close_ int fifo_fd = -1;
729                 _cleanup_free_ char *path = NULL;
730                 const char *cseat;
731                 uint32_t vtnr;
732                 dbus_bool_t exists;
733
734                 fifo_fd = session_create_fifo(s);
735                 if (fifo_fd < 0) {
736                         log_error("Failed to create fifo: %s", strerror(-fifo_fd));
737                         return fifo_fd;
738                 }
739
740                 path = session_bus_path(s);
741                 if (!path)
742                         return log_oom();
743
744                 reply = dbus_message_new_method_return(s->create_message);
745                 if (!reply)
746                         return log_oom();
747
748                 cseat = s->seat ? s->seat->id : "";
749                 vtnr = s->vtnr;
750                 exists = false;
751
752                 if (!dbus_message_append_args(
753                                     reply,
754                                     DBUS_TYPE_STRING, &s->id,
755                                     DBUS_TYPE_OBJECT_PATH, &path,
756                                     DBUS_TYPE_STRING, &s->user->runtime_path,
757                                     DBUS_TYPE_UNIX_FD, &fifo_fd,
758                                     DBUS_TYPE_STRING, &cseat,
759                                     DBUS_TYPE_UINT32, &vtnr,
760                                     DBUS_TYPE_BOOLEAN, &exists,
761                                     DBUS_TYPE_INVALID))
762                         return log_oom();
763         }
764
765         /* Update the state file before we notify the client about the
766          * result */
767         session_save(s);
768
769         if (!dbus_connection_send(s->manager->bus, reply, NULL))
770                 return log_oom();
771
772         dbus_message_unref(s->create_message);
773         s->create_message = NULL;
774
775         return 0;
776 }