compositor-drm: Work around page flip not setting tiling mode on BYT
[platform/upstream/weston.git] / src / logind-util.c
1 /*
2  * Copyright © 2013 David Herrmann
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of the copyright holders not be used in
9  * advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission.  The copyright holders make
11  * no representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19  * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include "config.h"
24
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <linux/vt.h>
28 #include <linux/kd.h>
29 #include <linux/major.h>
30 #include <signal.h>
31 #include <stdarg.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/ioctl.h>
37 #include <sys/signalfd.h>
38 #include <sys/stat.h>
39 #include <systemd/sd-login.h>
40 #include <unistd.h>
41
42 #include "compositor.h"
43 #include "dbus.h"
44 #include "logind-util.h"
45
46 #define DRM_MAJOR 226
47
48 #ifndef KDSKBMUTE
49 #define KDSKBMUTE       0x4B51
50 #endif
51
52 struct weston_logind {
53         struct weston_compositor *compositor;
54         char *seat;
55         char *sid;
56         unsigned int vtnr;
57         int vt;
58         int kb_mode;
59         int sfd;
60         struct wl_event_source *sfd_source;
61
62         DBusConnection *dbus;
63         struct wl_event_source *dbus_ctx;
64         char *spath;
65         DBusPendingCall *pending_active;
66 };
67
68 static int
69 weston_logind_take_device(struct weston_logind *wl, uint32_t major,
70                           uint32_t minor, bool *paused_out)
71 {
72         DBusMessage *m, *reply;
73         bool b;
74         int r, fd;
75         dbus_bool_t paused;
76
77         m = dbus_message_new_method_call("org.freedesktop.login1",
78                                          wl->spath,
79                                          "org.freedesktop.login1.Session",
80                                          "TakeDevice");
81         if (!m)
82                 return -ENOMEM;
83
84         b = dbus_message_append_args(m,
85                                      DBUS_TYPE_UINT32, &major,
86                                      DBUS_TYPE_UINT32, &minor,
87                                      DBUS_TYPE_INVALID);
88         if (!b) {
89                 r = -ENOMEM;
90                 goto err_unref;
91         }
92
93         reply = dbus_connection_send_with_reply_and_block(wl->dbus, m,
94                                                           -1, NULL);
95         if (!reply) {
96                 r = -ENODEV;
97                 goto err_unref;
98         }
99
100         b = dbus_message_get_args(reply, NULL,
101                                   DBUS_TYPE_UNIX_FD, &fd,
102                                   DBUS_TYPE_BOOLEAN, &paused,
103                                   DBUS_TYPE_INVALID);
104         if (!b) {
105                 r = -ENODEV;
106                 goto err_reply;
107         }
108
109         r = fd;
110         if (paused_out)
111                 *paused_out = paused;
112
113 err_reply:
114         dbus_message_unref(reply);
115 err_unref:
116         dbus_message_unref(m);
117         return r;
118 }
119
120 static void
121 weston_logind_release_device(struct weston_logind *wl, uint32_t major,
122                              uint32_t minor)
123 {
124         DBusMessage *m;
125         bool b;
126
127         m = dbus_message_new_method_call("org.freedesktop.login1",
128                                          wl->spath,
129                                          "org.freedesktop.login1.Session",
130                                          "ReleaseDevice");
131         if (m) {
132                 b = dbus_message_append_args(m,
133                                              DBUS_TYPE_UINT32, &major,
134                                              DBUS_TYPE_UINT32, &minor,
135                                              DBUS_TYPE_INVALID);
136                 if (b)
137                         dbus_connection_send(wl->dbus, m, NULL);
138                 dbus_message_unref(m);
139         }
140 }
141
142 static void
143 weston_logind_pause_device_complete(struct weston_logind *wl, uint32_t major,
144                                     uint32_t minor)
145 {
146         DBusMessage *m;
147         bool b;
148
149         m = dbus_message_new_method_call("org.freedesktop.login1",
150                                          wl->spath,
151                                          "org.freedesktop.login1.Session",
152                                          "PauseDeviceComplete");
153         if (m) {
154                 b = dbus_message_append_args(m,
155                                              DBUS_TYPE_UINT32, &major,
156                                              DBUS_TYPE_UINT32, &minor,
157                                              DBUS_TYPE_INVALID);
158                 if (b)
159                         dbus_connection_send(wl->dbus, m, NULL);
160                 dbus_message_unref(m);
161         }
162 }
163
164 WL_EXPORT int
165 weston_logind_open(struct weston_logind *wl, const char *path,
166                    int flags)
167 {
168         struct stat st;
169         int fl, r, fd;
170
171         r = stat(path, &st);
172         if (r < 0)
173                 return -1;
174         if (!S_ISCHR(st.st_mode)) {
175                 errno = ENODEV;
176                 return -1;
177         }
178
179         fd = weston_logind_take_device(wl, major(st.st_rdev),
180                                        minor(st.st_rdev), NULL);
181         if (fd < 0)
182                 return fd;
183
184         /* Compared to weston_launcher_open() we cannot specify the open-mode
185          * directly. Instead, logind passes us an fd with sane default modes.
186          * For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want
187          * something else, we need to change it afterwards. We currently
188          * only support dropping FD_CLOEXEC and setting O_NONBLOCK. Changing
189          * access-modes is not possible so accept whatever logind passes us. */
190
191         fl = fcntl(fd, F_GETFL);
192         if (fl < 0) {
193                 r = -errno;
194                 goto err_close;
195         }
196
197         if (flags & O_NONBLOCK)
198                 fl |= O_NONBLOCK;
199
200         r = fcntl(fd, F_SETFL, fl);
201         if (r < 0) {
202                 r = -errno;
203                 goto err_close;
204         }
205
206         fl = fcntl(fd, F_GETFD);
207         if (fl < 0) {
208                 r = -errno;
209                 goto err_close;
210         }
211
212         if (!(flags & O_CLOEXEC))
213                 fl &= ~FD_CLOEXEC;
214
215         r = fcntl(fd, F_SETFD, fl);
216         if (r < 0) {
217                 r = -errno;
218                 goto err_close;
219         }
220
221         return fd;
222
223 err_close:
224         close(fd);
225         weston_logind_release_device(wl, major(st.st_rdev),
226                                      minor(st.st_rdev));
227         errno = -r;
228         return -1;
229 }
230
231 WL_EXPORT void
232 weston_logind_close(struct weston_logind *wl, int fd)
233 {
234         struct stat st;
235         int r;
236
237         r = fstat(fd, &st);
238         if (r < 0) {
239                 weston_log("logind: cannot fstat fd: %m\n");
240                 return;
241         }
242
243         if (!S_ISCHR(st.st_mode)) {
244                 weston_log("logind: invalid device passed\n");
245                 return;
246         }
247
248         weston_logind_release_device(wl, major(st.st_rdev),
249                                      minor(st.st_rdev));
250 }
251
252 WL_EXPORT void
253 weston_logind_restore(struct weston_logind *wl)
254 {
255         struct vt_mode mode = { 0 };
256
257         ioctl(wl->vt, KDSETMODE, KD_TEXT);
258         ioctl(wl->vt, KDSKBMUTE, 0);
259         ioctl(wl->vt, KDSKBMODE, wl->kb_mode);
260         mode.mode = VT_AUTO;
261         ioctl(wl->vt, VT_SETMODE, &mode);
262 }
263
264 WL_EXPORT int
265 weston_logind_activate_vt(struct weston_logind *wl, int vt)
266 {
267         int r;
268
269         r = ioctl(wl->vt, VT_ACTIVATE, vt);
270         if (r < 0)
271                 return -1;
272
273         return 0;
274 }
275
276 static void
277 weston_logind_set_active(struct weston_logind *wl, bool active)
278 {
279         if (!wl->compositor->session_active == !active)
280                 return;
281
282         wl->compositor->session_active = active;
283
284         wl_signal_emit(&wl->compositor->session_signal,
285                        wl->compositor);
286 }
287
288 static void
289 get_active_cb(DBusPendingCall *pending, void *data)
290 {
291         struct weston_logind *wl = data;
292         DBusMessage *m;
293         DBusMessageIter iter, sub;
294         int type;
295         dbus_bool_t b;
296
297         dbus_pending_call_unref(wl->pending_active);
298         wl->pending_active = NULL;
299
300         m = dbus_pending_call_steal_reply(pending);
301         if (!m)
302                 return;
303
304         type = dbus_message_get_type(m);
305         if (type != DBUS_MESSAGE_TYPE_METHOD_RETURN)
306                 goto err_unref;
307
308         if (!dbus_message_iter_init(m, &iter) ||
309             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
310                 goto err_unref;
311
312         dbus_message_iter_recurse(&iter, &sub);
313
314         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
315                 goto err_unref;
316
317         dbus_message_iter_get_basic(&sub, &b);
318         if (!b)
319                 weston_logind_set_active(wl, false);
320
321 err_unref:
322         dbus_message_unref(m);
323 }
324
325 static void
326 weston_logind_get_active(struct weston_logind *wl)
327 {
328         DBusPendingCall *pending;
329         DBusMessage *m;
330         bool b;
331         const char *iface, *name;
332
333         m = dbus_message_new_method_call("org.freedesktop.login1",
334                                          wl->spath,
335                                          "org.freedesktop.DBus.Properties",
336                                          "Get");
337         if (!m)
338                 return;
339
340         iface = "org.freedesktop.login1.Session";
341         name = "Active";
342         b = dbus_message_append_args(m,
343                                      DBUS_TYPE_STRING, &iface,
344                                      DBUS_TYPE_STRING, &name,
345                                      DBUS_TYPE_INVALID);
346         if (!b)
347                 goto err_unref;
348
349         b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1);
350         if (!b)
351                 goto err_unref;
352
353         b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL);
354         if (!b) {
355                 dbus_pending_call_cancel(pending);
356                 dbus_pending_call_unref(pending);
357                 goto err_unref;
358         }
359
360         if (wl->pending_active) {
361                 dbus_pending_call_cancel(wl->pending_active);
362                 dbus_pending_call_unref(wl->pending_active);
363         }
364         wl->pending_active = pending;
365         return;
366
367 err_unref:
368         dbus_message_unref(m);
369 }
370
371 static void
372 disconnected_dbus(struct weston_logind *wl)
373 {
374         weston_log("logind: dbus connection lost, exiting..\n");
375         weston_logind_restore(wl);
376         exit(-1);
377 }
378
379 static void
380 session_removed(struct weston_logind *wl, DBusMessage *m)
381 {
382         const char *name, *obj;
383         bool r;
384
385         r = dbus_message_get_args(m, NULL,
386                                   DBUS_TYPE_STRING, &name,
387                                   DBUS_TYPE_OBJECT_PATH, &obj,
388                                   DBUS_TYPE_INVALID);
389         if (!r) {
390                 weston_log("logind: cannot parse SessionRemoved dbus signal\n");
391                 return;
392         }
393
394         if (!strcmp(name, wl->sid)) {
395                 weston_log("logind: our session got closed, exiting..\n");
396                 weston_logind_restore(wl);
397                 exit(-1);
398         }
399 }
400
401 static void
402 property_changed(struct weston_logind *wl, DBusMessage *m)
403 {
404         DBusMessageIter iter, sub, entry;
405         const char *interface, *name;
406         dbus_bool_t b;
407
408         if (!dbus_message_iter_init(m, &iter) ||
409             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
410                 goto error;
411
412         dbus_message_iter_get_basic(&iter, &interface);
413
414         if (!dbus_message_iter_next(&iter) ||
415             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
416                 goto error;
417
418         dbus_message_iter_recurse(&iter, &sub);
419
420         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) {
421                 dbus_message_iter_recurse(&sub, &entry);
422
423                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
424                         goto error;
425
426                 dbus_message_iter_get_basic(&entry, &name);
427                 if (!dbus_message_iter_next(&entry))
428                         goto error;
429
430                 if (!strcmp(name, "Active")) {
431                         if (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_BOOLEAN) {
432                                 dbus_message_iter_get_basic(&entry, &b);
433                                 if (!b)
434                                         weston_logind_set_active(wl, false);
435                                 return;
436                         }
437                 }
438
439                 dbus_message_iter_next(&sub);
440         }
441
442         if (!dbus_message_iter_next(&iter) ||
443             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
444                 goto error;
445
446         dbus_message_iter_recurse(&iter, &sub);
447
448         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
449                 dbus_message_iter_get_basic(&sub, &name);
450
451                 if (!strcmp(name, "Active")) {
452                         weston_logind_get_active(wl);
453                         return;
454                 }
455
456                 dbus_message_iter_next(&sub);
457         }
458
459         return;
460
461 error:
462         weston_log("logind: cannot parse PropertiesChanged dbus signal\n");
463 }
464
465 static void
466 device_paused(struct weston_logind *wl, DBusMessage *m)
467 {
468         bool r;
469         const char *type;
470         uint32_t major, minor;
471
472         r = dbus_message_get_args(m, NULL,
473                                   DBUS_TYPE_UINT32, &major,
474                                   DBUS_TYPE_UINT32, &minor,
475                                   DBUS_TYPE_STRING, &type,
476                                   DBUS_TYPE_INVALID);
477         if (!r) {
478                 weston_log("logind: cannot parse PauseDevice dbus signal\n");
479                 return;
480         }
481
482         /* "pause" means synchronous pausing. Acknowledge it unconditionally
483          * as we support asynchronous device shutdowns, anyway.
484          * "force" means asynchronous pausing.
485          * "gone" means the device is gone. We handle it the same as "force" as
486          * a following udev event will be caught, too.
487          *
488          * If it's our main DRM device, tell the compositor to go asleep. */
489
490         if (!strcmp(type, "pause"))
491                 weston_logind_pause_device_complete(wl, major, minor);
492
493         if (major == DRM_MAJOR)
494                 weston_logind_set_active(wl, false);
495 }
496
497 static void
498 device_resumed(struct weston_logind *wl, DBusMessage *m)
499 {
500         bool r;
501         uint32_t major;
502
503         r = dbus_message_get_args(m, NULL,
504                                   DBUS_TYPE_UINT32, &major,
505                                   /*DBUS_TYPE_UINT32, &minor,
506                                   DBUS_TYPE_UNIX_FD, &fd,*/
507                                   DBUS_TYPE_INVALID);
508         if (!r) {
509                 weston_log("logind: cannot parse ResumeDevice dbus signal\n");
510                 return;
511         }
512
513         /* DeviceResumed messages provide us a new file-descriptor for
514          * resumed devices. For DRM devices it's the same as before, for evdev
515          * devices it's a new open-file. As we reopen evdev devices, anyway,
516          * there is no need for us to handle this event for evdev. For DRM, we
517          * notify the compositor to wake up. */
518
519         if (major == DRM_MAJOR)
520                 weston_logind_set_active(wl, true);
521 }
522
523 static DBusHandlerResult
524 filter_dbus(DBusConnection *c, DBusMessage *m, void *data)
525 {
526         struct weston_logind *wl = data;
527
528         if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
529                 disconnected_dbus(wl);
530         } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager",
531                                           "SessionRemoved")) {
532                 session_removed(wl, m);
533         } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties",
534                                           "PropertiesChanged")) {
535                 property_changed(wl, m);
536         } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
537                                           "PauseDevice")) {
538                 device_paused(wl, m);
539         } else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
540                                           "ResumeDevice")) {
541                 device_resumed(wl, m);
542         }
543
544         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
545 }
546
547 static int
548 weston_logind_setup_dbus(struct weston_logind *wl)
549 {
550         bool b;
551         int r;
552
553         r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s",
554                      wl->sid);
555         if (r < 0)
556                 return -ENOMEM;
557
558         b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL);
559         if (!b) {
560                 weston_log("logind: cannot add dbus filter\n");
561                 r = -ENOMEM;
562                 goto err_spath;
563         }
564
565         r = weston_dbus_add_match_signal(wl->dbus,
566                                          "org.freedesktop.login1",
567                                          "org.freedesktop.login1.Manager",
568                                          "SessionRemoved",
569                                          "/org/freedesktop/login1");
570         if (r < 0) {
571                 weston_log("logind: cannot add dbus match\n");
572                 goto err_spath;
573         }
574
575         r = weston_dbus_add_match_signal(wl->dbus,
576                                         "org.freedesktop.login1",
577                                         "org.freedesktop.login1.Session",
578                                         "PauseDevice",
579                                         wl->spath);
580         if (r < 0) {
581                 weston_log("logind: cannot add dbus match\n");
582                 goto err_spath;
583         }
584
585         r = weston_dbus_add_match_signal(wl->dbus,
586                                         "org.freedesktop.login1",
587                                         "org.freedesktop.login1.Session",
588                                         "ResumeDevice",
589                                         wl->spath);
590         if (r < 0) {
591                 weston_log("logind: cannot add dbus match\n");
592                 goto err_spath;
593         }
594
595         r = weston_dbus_add_match_signal(wl->dbus,
596                                         "org.freedesktop.login1",
597                                         "org.freedesktop.DBus.Properties",
598                                         "PropertiesChanged",
599                                         wl->spath);
600         if (r < 0) {
601                 weston_log("logind: cannot add dbus match\n");
602                 goto err_spath;
603         }
604
605         return 0;
606
607 err_spath:
608         /* don't remove any dbus-match as the connection is closed, anyway */
609         free(wl->spath);
610         return r;
611 }
612
613 static void
614 weston_logind_destroy_dbus(struct weston_logind *wl)
615 {
616         /* don't remove any dbus-match as the connection is closed, anyway */
617         free(wl->spath);
618 }
619
620 static int
621 weston_logind_take_control(struct weston_logind *wl)
622 {
623         DBusError err;
624         DBusMessage *m, *reply;
625         dbus_bool_t force;
626         bool b;
627         int r;
628
629         dbus_error_init(&err);
630
631         m = dbus_message_new_method_call("org.freedesktop.login1",
632                                          wl->spath,
633                                          "org.freedesktop.login1.Session",
634                                          "TakeControl");
635         if (!m)
636                 return -ENOMEM;
637
638         force = false;
639         b = dbus_message_append_args(m,
640                                      DBUS_TYPE_BOOLEAN, &force,
641                                      DBUS_TYPE_INVALID);
642         if (!b) {
643                 r = -ENOMEM;
644                 goto err_unref;
645         }
646
647         reply = dbus_connection_send_with_reply_and_block(wl->dbus,
648                                                           m, -1, &err);
649         if (!reply) {
650                 if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD))
651                         weston_log("logind: old systemd version detected\n");
652                 else
653                         weston_log("logind: cannot take control over session %s\n", wl->sid);
654
655                 dbus_error_free(&err);
656                 r = -EIO;
657                 goto err_unref;
658         }
659
660         dbus_message_unref(reply);
661         dbus_message_unref(m);
662         return 0;
663
664 err_unref:
665         dbus_message_unref(m);
666         return r;
667 }
668
669 static void
670 weston_logind_release_control(struct weston_logind *wl)
671 {
672         DBusMessage *m;
673
674         m = dbus_message_new_method_call("org.freedesktop.login1",
675                                          wl->spath,
676                                          "org.freedesktop.login1.Session",
677                                          "ReleaseControl");
678         if (m) {
679                 dbus_connection_send(wl->dbus, m, NULL);
680                 dbus_message_unref(m);
681         }
682 }
683
684 static int
685 signal_event(int fd, uint32_t mask, void *data)
686 {
687         struct weston_logind *wl = data;
688         struct signalfd_siginfo sig;
689
690         if (read(fd, &sig, sizeof sig) != sizeof sig) {
691                 weston_log("logind: cannot read signalfd: %m\n");
692                 return 0;
693         }
694
695         switch (sig.ssi_signo) {
696         case SIGUSR1:
697                 ioctl(wl->vt, VT_RELDISP, 1);
698                 break;
699         case SIGUSR2:
700                 ioctl(wl->vt, VT_RELDISP, VT_ACKACQ);
701                 break;
702         }
703
704         return 0;
705 }
706
707 static int
708 weston_logind_setup_vt(struct weston_logind *wl)
709 {
710         struct stat st;
711         char buf[64];
712         struct vt_mode mode = { 0 };
713         int r;
714         sigset_t mask;
715         struct wl_event_loop *loop;
716
717         snprintf(buf, sizeof(buf), "/dev/tty%d", wl->vtnr);
718         buf[sizeof(buf) - 1] = 0;
719
720         wl->vt = open(buf, O_RDWR|O_CLOEXEC|O_NONBLOCK);
721
722         if (wl->vt < 0) {
723                 r = -errno;
724                 weston_log("logind: cannot open VT %s: %m\n", buf);
725                 return r;
726         }
727
728         if (fstat(wl->vt, &st) == -1 ||
729             major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) <= 0 ||
730             minor(st.st_rdev) >= 64) {
731                 r = -EINVAL;
732                 weston_log("logind: TTY %s is no virtual terminal\n", buf);
733                 goto err_close;
734         }
735
736         /*r = setsid();
737         if (r < 0 && errno != EPERM) {
738                 r = -errno;
739                 weston_log("logind: setsid() failed: %m\n");
740                 goto err_close;
741         }
742
743         r = ioctl(wl->vt, TIOCSCTTY, 0);
744         if (r < 0)
745                 weston_log("logind: VT %s already in use\n", buf);*/
746
747         if (ioctl(wl->vt, KDGKBMODE, &wl->kb_mode) < 0) {
748                 weston_log("logind: cannot read keyboard mode on %s: %m\n",
749                            buf);
750                 wl->kb_mode = K_UNICODE;
751         } else if (wl->kb_mode == K_OFF) {
752                 wl->kb_mode = K_UNICODE;
753         }
754
755         if (ioctl(wl->vt, KDSKBMUTE, 1) < 0 &&
756             ioctl(wl->vt, KDSKBMODE, K_OFF) < 0) {
757                 r = -errno;
758                 weston_log("logind: cannot set K_OFF KB-mode on %s: %m\n",
759                            buf);
760                 goto err_close;
761         }
762
763         if (ioctl(wl->vt, KDSETMODE, KD_GRAPHICS) < 0) {
764                 r = -errno;
765                 weston_log("logind: cannot set KD_GRAPHICS mode on %s: %m\n",
766                            buf);
767                 goto err_kbmode;
768         }
769
770         sigemptyset(&mask);
771         sigaddset(&mask, SIGUSR1);
772         sigaddset(&mask, SIGUSR2);
773         sigprocmask(SIG_BLOCK, &mask, NULL);
774
775         wl->sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
776         if (wl->sfd < 0) {
777                 r = -errno;
778                 weston_log("logind: cannot create signalfd: %m\n");
779                 goto err_mode;
780         }
781
782         loop = wl_display_get_event_loop(wl->compositor->wl_display);
783         wl->sfd_source = wl_event_loop_add_fd(loop, wl->sfd,
784                                               WL_EVENT_READABLE,
785                                               signal_event, wl);
786         if (!wl->sfd_source) {
787                 r = -errno;
788                 weston_log("logind: cannot create signalfd source: %m\n");
789                 goto err_sfd;
790         }
791
792         mode.mode = VT_PROCESS;
793         mode.relsig = SIGUSR1;
794         mode.acqsig = SIGUSR2;
795         if (ioctl(wl->vt, VT_SETMODE, &mode) < 0) {
796                 r = -errno;
797                 weston_log("logind: cannot take over VT: %m\n");
798                 goto err_sfd_source;
799         }
800
801         weston_log("logind: using VT %s\n", buf);
802         return 0;
803
804 err_sfd_source:
805         wl_event_source_remove(wl->sfd_source);
806 err_sfd:
807         close(wl->sfd);
808 err_mode:
809         ioctl(wl->vt, KDSETMODE, KD_TEXT);
810 err_kbmode:
811         ioctl(wl->vt, KDSKBMUTE, 0);
812         ioctl(wl->vt, KDSKBMODE, wl->kb_mode);
813 err_close:
814         close(wl->vt);
815         return r;
816 }
817
818 static void
819 weston_logind_destroy_vt(struct weston_logind *wl)
820 {
821         weston_logind_restore(wl);
822         wl_event_source_remove(wl->sfd_source);
823         close(wl->sfd);
824         close(wl->vt);
825 }
826
827 WL_EXPORT int
828 weston_logind_connect(struct weston_logind **out,
829                       struct weston_compositor *compositor,
830                       const char *seat_id, int tty)
831 {
832         struct weston_logind *wl;
833         struct wl_event_loop *loop;
834         char *t;
835         int r;
836
837         wl = calloc(1, sizeof(*wl));
838         if (!wl) {
839                 r = -ENOMEM;
840                 goto err_out;
841         }
842
843         wl->compositor = compositor;
844
845         wl->seat = strdup(seat_id);
846         if (!wl->seat) {
847                 r = -ENOMEM;
848                 goto err_wl;
849         }
850
851         r = sd_pid_get_session(getpid(), &wl->sid);
852         if (r < 0) {
853                 weston_log("logind: not running in a systemd session\n");
854                 goto err_seat;
855         }
856
857         t = NULL;
858         r = sd_session_get_seat(wl->sid, &t);
859         if (r < 0) {
860                 weston_log("logind: failed to get session seat\n");
861                 free(t);
862                 goto err_session;
863         } else if (strcmp(seat_id, t)) {
864                 weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n",
865                            seat_id, t);
866                 r = -EINVAL;
867                 free(t);
868                 goto err_session;
869         }
870         free(t);
871
872         r = weston_sd_session_get_vt(wl->sid, &wl->vtnr);
873         if (r < 0) {
874                 weston_log("logind: session not running on a VT\n");
875                 goto err_session;
876         } else if (tty > 0 && wl->vtnr != (unsigned int )tty) {
877                 weston_log("logind: requested VT --tty=%d differs from real session VT %u\n",
878                            tty, wl->vtnr);
879                 r = -EINVAL;
880                 goto err_session;
881         }
882
883         loop = wl_display_get_event_loop(compositor->wl_display);
884         r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx);
885         if (r < 0) {
886                 weston_log("logind: cannot connect to system dbus\n");
887                 goto err_session;
888         }
889
890         r = weston_logind_setup_dbus(wl);
891         if (r < 0)
892                 goto err_dbus;
893
894         r = weston_logind_take_control(wl);
895         if (r < 0)
896                 goto err_dbus_cleanup;
897
898         r = weston_logind_setup_vt(wl);
899         if (r < 0)
900                 goto err_control;
901
902         weston_log("logind: session control granted\n");
903         *out = wl;
904         return 0;
905
906 err_control:
907         weston_logind_release_control(wl);
908 err_dbus_cleanup:
909         weston_logind_destroy_dbus(wl);
910 err_dbus:
911         weston_dbus_close(wl->dbus, wl->dbus_ctx);
912 err_session:
913         free(wl->sid);
914 err_seat:
915         free(wl->seat);
916 err_wl:
917         free(wl);
918 err_out:
919         weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r);
920         errno = -r;
921         return -1;
922 }
923
924 WL_EXPORT void
925 weston_logind_destroy(struct weston_logind *wl)
926 {
927         if (wl->pending_active) {
928                 dbus_pending_call_cancel(wl->pending_active);
929                 dbus_pending_call_unref(wl->pending_active);
930         }
931
932         weston_logind_destroy_vt(wl);
933         weston_logind_release_control(wl);
934         weston_logind_destroy_dbus(wl);
935         weston_dbus_close(wl->dbus, wl->dbus_ctx);
936         free(wl->sid);
937         free(wl->seat);
938         free(wl);
939 }