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