launcher: Move rest of tty object into struct weston_launcher

The struct weston_launcher object will now either handle tty and vt switching
details in-process (when running weston directly as root) or talk to
the weston-launch process.
diff --git a/src/launcher-util.c b/src/launcher-util.c
index 75e7051..a2ad0ec 100644
--- a/src/launcher-util.c
+++ b/src/launcher-util.c
@@ -28,12 +28,17 @@
 #include <string.h>
 
 #include <errno.h>
+#include <signal.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/uio.h>
+#include <sys/ioctl.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include <linux/major.h>
 
 #include <xf86drm.h>
 
@@ -41,19 +46,28 @@
 #include "launcher-util.h"
 #include "weston-launch.h"
 
+#define DRM_MAJOR 226
+
+#ifndef KDSKBMUTE
+#define KDSKBMUTE	0x4B51
+#endif
+
 union cmsg_data { unsigned char b[4]; int fd; };
 
 struct weston_launcher {
 	struct weston_compositor *compositor;
 	int fd;
 	struct wl_event_source *source;
+
+	int kb_mode, tty, drm_fd;
+	struct wl_event_source *vt_source;
 };
 
 int
 weston_launcher_open(struct weston_launcher *launcher,
 		     const char *path, int flags)
 {
-	int n, ret = -1;
+	int n, fd, ret = -1;
 	struct msghdr msg;
 	struct cmsghdr *cmsg;
 	struct iovec iov;
@@ -61,9 +75,24 @@
 	char control[CMSG_SPACE(sizeof data->fd)];
 	ssize_t len;
 	struct weston_launcher_open *message;
+	struct stat s;
 
-	if (launcher == NULL)
-		return open(path, flags | O_CLOEXEC);
+	if (launcher->fd == -1) {
+		fd = open(path, flags | O_CLOEXEC);
+
+		if (fd == -1)
+			return -1;
+
+		if (fstat(fd, &s) == -1) {
+			close(fd);
+			return -1;
+		}
+
+		if (major(s.st_rdev) == DRM_MAJOR)
+			launcher->drm_fd = fd;
+
+		return fd;
+	}
 
 	n = sizeof(*message) + strlen(path) + 1;
 	message = malloc(n);
@@ -112,6 +141,23 @@
 	return data->fd;
 }
 
+void
+weston_launcher_restore(struct weston_launcher *launcher)
+{
+	struct vt_mode mode = { 0 };
+
+	if (ioctl(launcher->tty, KDSKBMUTE, 0) &&
+	    ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode))
+		weston_log("failed to restore kb mode: %m\n");
+
+	if (ioctl(launcher->tty, KDSETMODE, KD_TEXT))
+		weston_log("failed to set KD_TEXT mode on tty: %m\n");
+
+	mode.mode = VT_AUTO;
+	if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0)
+		weston_log("could not reset vt handling\n");
+}
+
 static int
 weston_launcher_data(int fd, uint32_t mask, void *data)
 {
@@ -120,6 +166,10 @@
 
 	if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
 		weston_log("launcher socket closed, exiting\n");
+		/* Normally the weston-launch will reset the tty, but
+		 * in this case it died or something, so do it here so
+		 * we don't end up with a stuck vt. */
+		weston_launcher_restore(launcher);
 		exit(-1);
 	}
 
@@ -146,30 +196,111 @@
 	return 1;
 }
 
+static int
+vt_handler(int signal_number, void *data)
+{
+	struct weston_launcher *launcher = data;
+	struct weston_compositor *compositor = launcher->compositor;
+
+	if (compositor->session_active) {
+		compositor->session_active = 0;
+		wl_signal_emit(&compositor->session_signal, compositor);
+		if (launcher->drm_fd != -1)
+			drmDropMaster(launcher->drm_fd);
+		ioctl(launcher->tty, VT_RELDISP, 1);
+	} else {
+		ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ);
+		if (launcher->drm_fd != -1)
+			drmSetMaster(launcher->drm_fd);
+		compositor->session_active = 1;
+		wl_signal_emit(&compositor->session_signal, compositor);
+	}
+
+	return 1;
+}
+
+static int
+setup_tty(struct weston_launcher *launcher)
+{
+	struct wl_event_loop *loop;
+	struct vt_mode mode = { 0 };
+	struct stat buf;
+	int ret;
+
+	if (fstat(STDIN_FILENO, &buf) == -1 ||
+	    major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) {
+		weston_log("stdin not a vt\n");
+		return -1;
+	}
+
+	launcher->tty = STDIN_FILENO;
+	if (ioctl(launcher->tty, KDGKBMODE, &launcher->kb_mode)) {
+		weston_log("failed to read keyboard mode: %m\n");
+		return -1;
+	}
+
+	if (ioctl(launcher->tty, KDSKBMUTE, 1) &&
+	    ioctl(launcher->tty, KDSKBMODE, K_OFF)) {
+		weston_log("failed to set K_OFF keyboard mode: %m\n");
+		return -1;
+	}
+
+	ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS);
+	if (ret) {
+		weston_log("failed to set KD_GRAPHICS mode on tty: %m\n");
+		return -1;
+	}
+
+	mode.mode = VT_PROCESS;
+	mode.relsig = SIGUSR1;
+	mode.acqsig = SIGUSR1;
+	if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) {
+		weston_log("failed to take control of vt handling\n");
+		return -1;
+	}
+
+	loop = wl_display_get_event_loop(launcher->compositor->wl_display);
+	launcher->vt_source =
+		wl_event_loop_add_signal(loop, SIGUSR1, vt_handler, launcher);
+	if (!launcher->vt_source)
+		return -1;
+
+	return 0;
+}
+
+int
+weston_launcher_activate_vt(struct weston_launcher *launcher, int vt)
+{
+	return ioctl(launcher->tty, VT_ACTIVATE, vt);
+}
+
 struct weston_launcher *
 weston_launcher_connect(struct weston_compositor *compositor)
 {
 	struct weston_launcher *launcher;
 	struct wl_event_loop *loop;
-	int fd;
-
-	fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
-	if (fd == -1)
-		return NULL;
 
 	launcher = malloc(sizeof *launcher);
 	if (launcher == NULL)
 		return NULL;
 
 	launcher->compositor = compositor;
-	launcher->fd = fd;
-
-	loop = wl_display_get_event_loop(compositor->wl_display);
-	launcher->source = wl_event_loop_add_fd(loop, launcher->fd,
-						WL_EVENT_READABLE,
-						weston_launcher_data,
-						launcher);
-	if (launcher->source == NULL) {
+	launcher->drm_fd = -1;
+	launcher->fd = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
+	if (launcher->fd != -1) {
+		launcher->tty = weston_environment_get_fd("WESTON_TTY_FD");
+		loop = wl_display_get_event_loop(compositor->wl_display);
+		launcher->source = wl_event_loop_add_fd(loop, launcher->fd,
+							WL_EVENT_READABLE,
+							weston_launcher_data,
+							launcher);
+		if (launcher->source == NULL) {
+			free(launcher);
+			return NULL;
+		}
+	} else if (geteuid() == 0) {
+		setup_tty(launcher);
+	} else {
 		free(launcher);
 		return NULL;
 	}
@@ -180,6 +311,13 @@
 void
 weston_launcher_destroy(struct weston_launcher *launcher)
 {
-	close(launcher->fd);
+	if (launcher->fd != -1) {
+		close(launcher->fd);
+		wl_event_source_remove(launcher->source);
+	} else {
+		weston_launcher_restore(launcher);
+		wl_event_source_remove(launcher->vt_source);
+	}
+
 	free(launcher);
 }