commit 61ab1cfbdaa073646593d3a67ba958c34d2132ba
parent caa0f9f3689e3bd67410deef82eb0bb29e55bbf1
Author: Michael Forney <mforney@mforney.org>
Date: Fri, 21 Jun 2013 00:45:48 -0700
Add swc-launch
Diffstat:
7 files changed, 432 insertions(+), 2 deletions(-)
diff --git a/Makefile.am b/Makefile.am
@@ -29,5 +29,5 @@ libswc_la_LIBADD = $(wayland_server_LIBS) $(udev_LIBS) $(xkbcommon_LIBS) \
$(drm_LIBS) $(drm_intel_LIBS) $(gbm_LIBS) $(egl_LIBS) $(pixman_LIBS) \
intel/libintel.la
-SUBDIRS = intel
+SUBDIRS = launch intel
diff --git a/configure.ac b/configure.ac
@@ -33,6 +33,6 @@ PKG_CHECK_MODULES([wayland_client], [wayland-client])
dnl }}}
#AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([Makefile intel/Makefile])
+AC_CONFIG_FILES([Makefile intel/Makefile launch/Makefile])
AC_OUTPUT
diff --git a/launch/Makefile.am b/launch/Makefile.am
@@ -0,0 +1,17 @@
+# launch/Makefile.am
+
+AM_CFLAGS = $(drm_CFLAGS)
+
+lib_LTLIBRARIES = libswc-launch.la
+noinst_LTLIBRARIES = liblaunch-protocol.la
+
+libswc_launch_la_SOURCES = \
+ launch.c swc-launch.h
+
+libswc_launch_la_LIBADD = $(drm_LIBS) liblaunch-protocol.la
+
+liblaunch_protocol_la_SOURCES = \
+ protocol.c protocol.h
+
+include_HEADERS = swc-launch.h
+
diff --git a/launch/launch.c b/launch/launch.c
@@ -0,0 +1,270 @@
+#include "swc-launch.h"
+#include "protocol.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <linux/major.h>
+#include <linux/vt.h>
+#include <xf86drm.h>
+
+static void print_usage(const char * name, const char * compositor_name)
+{
+ fprintf(stderr, "Usage: %s [-- [%s arguments]]\n",
+ name, compositor_name);
+}
+
+static void catch_chld(int signal)
+{
+ int status;
+
+ wait(&status);
+ exit(status);
+}
+
+static void handle_socket_data(int socket)
+{
+ char buffer[BUFSIZ];
+ struct swc_launch_request * request = (void *) &buffer;
+ struct swc_launch_response response = { false };
+ int fd;
+ ssize_t size;
+
+ size = receive_fd(socket, &fd, buffer, sizeof buffer);
+
+ if (size == -1 || size == 0)
+ return;
+
+ switch (request->type)
+ {
+ case SWC_LAUNCH_REQUEST_DRM_MASTER:
+ response.success = (request->set ? drmSetMaster(fd)
+ : drmDropMaster(fd)) == 0;
+ fd = -1;
+ break;
+ case SWC_LAUNCH_REQUEST_OPEN_INPUT_DEVICE:
+ {
+ struct stat st;
+
+ if (request->path[size - __builtin_offsetof(typeof(*request),
+ path)] != '\0')
+ {
+ fprintf(stderr, "Path is not NULL terminated\n");
+ goto fail;
+ }
+
+ stat(request->path, &st);
+
+ if (major(st.st_rdev) != INPUT_MAJOR)
+ {
+ fprintf(stderr, "Device is not an input device\n");
+ goto fail;
+ }
+
+ fd = open(request->path, request->flags);
+
+ if (fd == -1)
+ {
+ fprintf(stderr, "Could not open device %s\n", request->path);
+ goto fail;
+ }
+
+ break;
+ }
+ default:
+ fprintf(stderr, "Unknown request %u\n", request->type);
+ goto fail;
+ }
+
+ goto done;
+
+ fail:
+ response.success = false;
+ fd = -1;
+ done:
+ send_fd(socket, fd, &response, sizeof response);
+}
+
+static void monitor_socket(int epoll_fd)
+{
+ struct epoll_event event;
+ int count;
+
+ while (true)
+ {
+ printf("waiting\n");
+ count = epoll_wait(epoll_fd, &event, 1, -1);
+
+ if (count < 0)
+ break;
+
+ handle_socket_data(event.data.fd);
+ }
+}
+
+static int find_vt()
+{
+ char * vt_string;
+ int vt;
+ int tty0_fd;
+
+ vt_string = getenv("XDG_VTNR");
+
+ if (vt_string)
+ {
+ char * end;
+ vt = strtoul(vt_string, &end, 10);
+ printf("vt: %d\n", vt);
+ if (*end == '\0')
+ goto done;
+ }
+
+ tty0_fd = open("/dev/tty0", O_RDWR);
+
+ if (ioctl(tty0_fd, VT_OPENQRY, &vt) != 0)
+ {
+ printf("could not find open vt\n");
+ vt = 0;
+ }
+
+ close(tty0_fd);
+
+ done:
+ return vt;
+}
+
+static int open_tty(int vt)
+{
+ char * current_tty_name;
+ char tty_name[64];
+
+ snprintf(tty_name, sizeof tty_name, "/dev/tty%d", vt);
+
+ /* Check if we are running on the desired VT */
+ current_tty_name = ttyname(STDIN_FILENO);
+
+ if (strcmp(tty_name, current_tty_name) == 0)
+ return STDIN_FILENO;
+ else
+ {
+ int fd;
+
+ /* Open the new TTY. */
+ fd = open(tty_name, O_RDWR | O_NOCTTY);
+
+ if (fd < 0)
+ {
+ fprintf(stderr, "FATAL: Could not open %s\n", tty_name);
+ exit(EXIT_FAILURE);
+ }
+
+ return fd;
+ }
+}
+
+int swc_launch(int argc, const char * argv[], const char * path)
+{
+ int sockets[2];
+ int epoll_fd;
+ struct epoll_event event;
+
+ if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, sockets) == -1)
+ {
+ fprintf(stderr, "FATAL: Could not create socket pair\n");
+ return EXIT_FAILURE;
+ }
+
+ if (fcntl(sockets[0], F_SETFD, FD_CLOEXEC) == -1)
+ {
+ fprintf(stderr, "FATAL: Could not set CLOEXEC on socket\n");
+ return EXIT_FAILURE;
+ }
+
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+
+ if (epoll_fd == -1)
+ {
+ fprintf(stderr, "FATAL: Could not create epoll\n");
+ return EXIT_FAILURE;
+ }
+
+ event.events = EPOLLIN;
+ event.data.fd = sockets[0];
+ epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockets[0], &event);
+
+ /* Child */
+ if (fork() == 0)
+ {
+ char string[64];
+ int tty_fd;
+ int vt;
+ uid_t uid;
+ gid_t gid;
+
+ vt = find_vt();
+ tty_fd = open_tty(vt);
+
+ /* If the desired TTY is not the current TTY, start a new session, and
+ * set it's controlling TTY appropriately. */
+ if (tty_fd != STDIN_FILENO)
+ {
+ pid_t sid = setsid();
+
+ if (ioctl(tty_fd, TIOCSCTTY, vt) != 0)
+ {
+ fprintf(stderr, "FATAL: Couldn't set controlling TTY to "
+ "/dev/tty%u: %s\n", vt, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ //tcsetpgrp(tty_fd, sid);
+ }
+
+ sprintf(string, "%d", sockets[1]);
+ setenv(SWC_LAUNCH_SOCKET_ENV, string, 1);
+
+ sprintf(string, "%d", tty_fd);
+ setenv(SWC_LAUNCH_TTY_FD_ENV, string, 1);
+
+ printf("dropping privileges\n");
+ setuid(getuid());
+ setgid(getgid());
+ //execlp("valgrind", "valgrind", "-v", "--suppressions=drm.supp",
+ // "--track-origins=yes", path, NULL);
+ execlp("gdb", "gdb", path, NULL);
+ //execl(path, path, NULL);
+
+ printf("%s failed: %s\n", path, strerror(errno));
+
+ exit(EXIT_FAILURE);
+ }
+ /* Parent */
+ else
+ {
+ struct sigaction action;
+ sigset_t blocked_signals;
+
+ action.sa_handler = &catch_chld;
+ sigaction(SIGCHLD, &action, NULL);
+
+ sigemptyset(&blocked_signals);
+ sigaddset(&blocked_signals, SIGINT);
+ sigaddset(&blocked_signals, SIGTERM);
+ sigprocmask(SIG_BLOCK, &blocked_signals, NULL);
+
+ printf("monitoring socket\n");
+ monitor_socket(epoll_fd);
+ }
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/launch/protocol.c b/launch/protocol.c
@@ -0,0 +1,94 @@
+#include "protocol.h"
+
+#include <sys/socket.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+ssize_t send_fd(int socket, int fd, void * buffer, ssize_t buffer_size)
+{
+ char control[CMSG_SPACE(sizeof(int))];
+ struct iovec iov = {
+ .iov_base = buffer,
+ .iov_len = buffer_size
+ };
+ struct msghdr message = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ struct cmsghdr * cmsg;
+
+ if (fd != -1)
+ {
+ message.msg_control = control,
+ message.msg_controllen = sizeof control;
+
+ cmsg = CMSG_FIRSTHDR(&message);
+ cmsg->cmsg_len = CMSG_LEN(sizeof fd);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ *((int *) CMSG_DATA(cmsg)) = fd;
+ }
+ else
+ {
+ message.msg_control = NULL;
+ message.msg_controllen = 0;
+ }
+
+ return sendmsg(socket, &message, 0);
+}
+
+ssize_t receive_fd(int socket, int * fd, void * buffer,
+ ssize_t buffer_size)
+{
+ ssize_t size;
+
+ if (fd)
+ {
+ char control[CMSG_SPACE(sizeof(int))];
+ struct iovec iov = {
+ .iov_base = buffer,
+ .iov_len = buffer_size
+ };
+ struct msghdr message = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = &control,
+ .msg_controllen = sizeof control
+ };
+ struct cmsghdr * cmsg;
+
+ size = recvmsg(socket, &message, 0);
+
+ if (size < 0)
+ goto nofd;
+
+ cmsg = CMSG_FIRSTHDR(&message);
+
+ if (!cmsg || cmsg->cmsg_len != CMSG_LEN(sizeof(int))
+ || cmsg->cmsg_level != SOL_SOCKET
+ || cmsg->cmsg_type != SCM_RIGHTS)
+ {
+ goto nofd;
+ }
+
+ *fd = *((int *) CMSG_DATA(cmsg));
+ }
+ else
+ {
+ size = recv(socket, buffer, buffer_size, 0);
+ }
+
+ goto done;
+
+ nofd:
+ *fd = -1;
+ done:
+ return size;
+}
+
diff --git a/launch/protocol.h b/launch/protocol.h
@@ -0,0 +1,42 @@
+#ifndef SWC_LAUNCH_PROTOCOL_H
+#define SWC_LAUNCH_PROTOCOL_H 1
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#define SWC_LAUNCH_SOCKET_ENV "SWC_LAUNCH_SOCKET"
+#define SWC_LAUNCH_TTY_FD_ENV "SWC_LAUNCH_TTY_FD"
+
+struct swc_launch_request
+{
+ enum
+ {
+ SWC_LAUNCH_REQUEST_DRM_MASTER,
+ SWC_LAUNCH_REQUEST_OPEN_INPUT_DEVICE
+ } type;
+ union
+ {
+ struct /* DRM_MASTER */
+ {
+ bool set;
+ };
+ struct /* OPEN_INPUT_DEVICE */
+ {
+ int flags;
+ char path[];
+ };
+ };
+};
+
+struct swc_launch_response
+{
+ bool success;
+};
+
+ssize_t send_fd(int socket, int fd, void * buffer, ssize_t buffer_size);
+
+ssize_t receive_fd(int socket, int * fd, void * buffer,
+ ssize_t buffer_size);
+
+#endif
+
diff --git a/launch/swc-launch.h b/launch/swc-launch.h
@@ -0,0 +1,7 @@
+#ifndef SWC_LAUNCH_H
+#define SWC_LAUNCH_H 1
+
+int swc_launch(int argc, const char * argv[], const char * path);
+
+#endif
+