commit 61ab1cf

Michael Forney  ·  2013-06-21 07:45:48 +0000 UTC
parent caa0f9f
Add swc-launch
7 files changed,  +432, -2
+1, -1
1@@ -29,5 +29,5 @@ libswc_la_LIBADD = $(wayland_server_LIBS) $(udev_LIBS) $(xkbcommon_LIBS) \
2 	$(drm_LIBS) $(drm_intel_LIBS) $(gbm_LIBS) $(egl_LIBS) $(pixman_LIBS) \
3 	intel/libintel.la
4 
5-SUBDIRS = intel
6+SUBDIRS = launch intel
7 
+1, -1
1@@ -33,6 +33,6 @@ PKG_CHECK_MODULES([wayland_client], [wayland-client])
2 dnl }}}
3 
4 #AC_CONFIG_HEADERS([config.h])
5-AC_CONFIG_FILES([Makefile intel/Makefile])
6+AC_CONFIG_FILES([Makefile intel/Makefile launch/Makefile])
7 AC_OUTPUT
8 
+17, -0
 1@@ -0,0 +1,17 @@
 2+# launch/Makefile.am
 3+
 4+AM_CFLAGS = $(drm_CFLAGS)
 5+
 6+lib_LTLIBRARIES = libswc-launch.la
 7+noinst_LTLIBRARIES = liblaunch-protocol.la
 8+
 9+libswc_launch_la_SOURCES = \
10+	launch.c swc-launch.h
11+
12+libswc_launch_la_LIBADD = $(drm_LIBS) liblaunch-protocol.la
13+
14+liblaunch_protocol_la_SOURCES = \
15+	protocol.c protocol.h
16+
17+include_HEADERS = swc-launch.h
18+
+270, -0
  1@@ -0,0 +1,270 @@
  2+#include "swc-launch.h"
  3+#include "protocol.h"
  4+
  5+#include <stdlib.h>
  6+#include <stdio.h>
  7+#include <stdbool.h>
  8+#include <string.h>
  9+#include <unistd.h>
 10+#include <errno.h>
 11+#include <fcntl.h>
 12+#include <sys/epoll.h>
 13+#include <sys/socket.h>
 14+#include <sys/stat.h>
 15+#include <sys/wait.h>
 16+#include <sys/ioctl.h>
 17+#include <linux/major.h>
 18+#include <linux/vt.h>
 19+#include <xf86drm.h>
 20+
 21+static void print_usage(const char * name, const char * compositor_name)
 22+{
 23+    fprintf(stderr, "Usage: %s [-- [%s arguments]]\n",
 24+            name, compositor_name);
 25+}
 26+
 27+static void catch_chld(int signal)
 28+{
 29+    int status;
 30+
 31+    wait(&status);
 32+    exit(status);
 33+}
 34+
 35+static void handle_socket_data(int socket)
 36+{
 37+    char buffer[BUFSIZ];
 38+    struct swc_launch_request * request = (void *) &buffer;
 39+    struct swc_launch_response response = { false };
 40+    int fd;
 41+    ssize_t size;
 42+
 43+    size = receive_fd(socket, &fd, buffer, sizeof buffer);
 44+
 45+    if (size == -1 || size == 0)
 46+        return;
 47+
 48+    switch (request->type)
 49+    {
 50+        case SWC_LAUNCH_REQUEST_DRM_MASTER:
 51+            response.success = (request->set ? drmSetMaster(fd)
 52+                                             : drmDropMaster(fd)) == 0;
 53+            fd = -1;
 54+            break;
 55+        case SWC_LAUNCH_REQUEST_OPEN_INPUT_DEVICE:
 56+        {
 57+            struct stat st;
 58+
 59+            if (request->path[size - __builtin_offsetof(typeof(*request),
 60+                                                        path)] != '\0')
 61+            {
 62+                fprintf(stderr, "Path is not NULL terminated\n");
 63+                goto fail;
 64+            }
 65+
 66+            stat(request->path, &st);
 67+
 68+            if (major(st.st_rdev) != INPUT_MAJOR)
 69+            {
 70+                fprintf(stderr, "Device is not an input device\n");
 71+                goto fail;
 72+            }
 73+
 74+            fd = open(request->path, request->flags);
 75+
 76+            if (fd == -1)
 77+            {
 78+                fprintf(stderr, "Could not open device %s\n", request->path);
 79+                goto fail;
 80+            }
 81+
 82+            break;
 83+        }
 84+        default:
 85+            fprintf(stderr, "Unknown request %u\n", request->type);
 86+            goto fail;
 87+    }
 88+
 89+    goto done;
 90+
 91+  fail:
 92+    response.success = false;
 93+    fd = -1;
 94+  done:
 95+    send_fd(socket, fd, &response, sizeof response);
 96+}
 97+
 98+static void monitor_socket(int epoll_fd)
 99+{
100+    struct epoll_event event;
101+    int count;
102+
103+    while (true)
104+    {
105+        printf("waiting\n");
106+        count = epoll_wait(epoll_fd, &event, 1, -1);
107+
108+        if (count < 0)
109+            break;
110+
111+        handle_socket_data(event.data.fd);
112+    }
113+}
114+
115+static int find_vt()
116+{
117+    char * vt_string;
118+    int vt;
119+    int tty0_fd;
120+
121+    vt_string = getenv("XDG_VTNR");
122+
123+    if (vt_string)
124+    {
125+        char * end;
126+        vt = strtoul(vt_string, &end, 10);
127+        printf("vt: %d\n", vt);
128+        if (*end == '\0')
129+            goto done;
130+    }
131+
132+    tty0_fd = open("/dev/tty0", O_RDWR);
133+
134+    if (ioctl(tty0_fd, VT_OPENQRY, &vt) != 0)
135+    {
136+        printf("could not find open vt\n");
137+        vt = 0;
138+    }
139+
140+    close(tty0_fd);
141+
142+  done:
143+    return vt;
144+}
145+
146+static int open_tty(int vt)
147+{
148+    char * current_tty_name;
149+    char tty_name[64];
150+
151+    snprintf(tty_name, sizeof tty_name, "/dev/tty%d", vt);
152+
153+    /* Check if we are running on the desired VT */
154+    current_tty_name = ttyname(STDIN_FILENO);
155+
156+    if (strcmp(tty_name, current_tty_name) == 0)
157+        return STDIN_FILENO;
158+    else
159+    {
160+        int fd;
161+
162+        /* Open the new TTY. */
163+        fd = open(tty_name, O_RDWR | O_NOCTTY);
164+
165+        if (fd < 0)
166+        {
167+            fprintf(stderr, "FATAL: Could not open %s\n", tty_name);
168+            exit(EXIT_FAILURE);
169+        }
170+
171+        return fd;
172+    }
173+}
174+
175+int swc_launch(int argc, const char * argv[], const char * path)
176+{
177+    int sockets[2];
178+    int epoll_fd;
179+    struct epoll_event event;
180+
181+    if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, sockets) == -1)
182+    {
183+        fprintf(stderr, "FATAL: Could not create socket pair\n");
184+        return EXIT_FAILURE;
185+    }
186+
187+    if (fcntl(sockets[0], F_SETFD, FD_CLOEXEC) == -1)
188+    {
189+        fprintf(stderr, "FATAL: Could not set CLOEXEC on socket\n");
190+        return EXIT_FAILURE;
191+    }
192+
193+    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
194+
195+    if (epoll_fd == -1)
196+    {
197+        fprintf(stderr, "FATAL: Could not create epoll\n");
198+        return EXIT_FAILURE;
199+    }
200+
201+    event.events = EPOLLIN;
202+    event.data.fd = sockets[0];
203+    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockets[0], &event);
204+
205+    /* Child */
206+    if (fork() == 0)
207+    {
208+        char string[64];
209+        int tty_fd;
210+        int vt;
211+        uid_t uid;
212+        gid_t gid;
213+
214+        vt = find_vt();
215+        tty_fd = open_tty(vt);
216+
217+        /* If the desired TTY is not the current TTY, start a new session, and
218+         * set it's controlling TTY appropriately. */
219+        if (tty_fd != STDIN_FILENO)
220+        {
221+            pid_t sid = setsid();
222+
223+            if (ioctl(tty_fd, TIOCSCTTY, vt) != 0)
224+            {
225+                fprintf(stderr, "FATAL: Couldn't set controlling TTY to "
226+                                "/dev/tty%u: %s\n", vt, strerror(errno));
227+                exit(EXIT_FAILURE);
228+            }
229+
230+            //tcsetpgrp(tty_fd, sid);
231+        }
232+
233+        sprintf(string, "%d", sockets[1]);
234+        setenv(SWC_LAUNCH_SOCKET_ENV, string, 1);
235+
236+        sprintf(string, "%d", tty_fd);
237+        setenv(SWC_LAUNCH_TTY_FD_ENV, string, 1);
238+
239+        printf("dropping privileges\n");
240+        setuid(getuid());
241+        setgid(getgid());
242+        //execlp("valgrind", "valgrind", "-v", "--suppressions=drm.supp",
243+        //       "--track-origins=yes", path, NULL);
244+        execlp("gdb", "gdb", path, NULL);
245+        //execl(path, path, NULL);
246+
247+        printf("%s failed: %s\n", path, strerror(errno));
248+
249+        exit(EXIT_FAILURE);
250+    }
251+    /* Parent */
252+    else
253+    {
254+        struct sigaction action;
255+        sigset_t blocked_signals;
256+
257+        action.sa_handler = &catch_chld;
258+        sigaction(SIGCHLD, &action, NULL);
259+
260+        sigemptyset(&blocked_signals);
261+        sigaddset(&blocked_signals, SIGINT);
262+        sigaddset(&blocked_signals, SIGTERM);
263+        sigprocmask(SIG_BLOCK, &blocked_signals, NULL);
264+
265+        printf("monitoring socket\n");
266+        monitor_socket(epoll_fd);
267+    }
268+
269+    return EXIT_SUCCESS;
270+}
271+
+94, -0
 1@@ -0,0 +1,94 @@
 2+#include "protocol.h"
 3+
 4+#include <sys/socket.h>
 5+#include <stdio.h>
 6+#include <string.h>
 7+#include <errno.h>
 8+
 9+ssize_t send_fd(int socket, int fd, void * buffer, ssize_t buffer_size)
10+{
11+    char control[CMSG_SPACE(sizeof(int))];
12+    struct iovec iov = {
13+        .iov_base = buffer,
14+        .iov_len = buffer_size
15+    };
16+    struct msghdr message = {
17+        .msg_name = NULL,
18+        .msg_namelen = 0,
19+        .msg_iov = &iov,
20+        .msg_iovlen = 1,
21+    };
22+    struct cmsghdr * cmsg;
23+
24+    if (fd != -1)
25+    {
26+        message.msg_control = control,
27+        message.msg_controllen = sizeof control;
28+
29+        cmsg = CMSG_FIRSTHDR(&message);
30+        cmsg->cmsg_len = CMSG_LEN(sizeof fd);
31+        cmsg->cmsg_level = SOL_SOCKET;
32+        cmsg->cmsg_type = SCM_RIGHTS;
33+
34+        *((int *) CMSG_DATA(cmsg)) = fd;
35+    }
36+    else
37+    {
38+        message.msg_control = NULL;
39+        message.msg_controllen = 0;
40+    }
41+
42+    return sendmsg(socket, &message, 0);
43+}
44+
45+ssize_t receive_fd(int socket, int * fd, void * buffer,
46+                   ssize_t buffer_size)
47+{
48+    ssize_t size;
49+
50+    if (fd)
51+    {
52+        char control[CMSG_SPACE(sizeof(int))];
53+        struct iovec iov = {
54+            .iov_base = buffer,
55+            .iov_len = buffer_size
56+        };
57+        struct msghdr message = {
58+            .msg_name = NULL,
59+            .msg_namelen = 0,
60+            .msg_iov = &iov,
61+            .msg_iovlen = 1,
62+            .msg_control = &control,
63+            .msg_controllen = sizeof control
64+        };
65+        struct cmsghdr * cmsg;
66+
67+        size = recvmsg(socket, &message, 0);
68+
69+        if (size < 0)
70+            goto nofd;
71+
72+        cmsg = CMSG_FIRSTHDR(&message);
73+
74+        if (!cmsg || cmsg->cmsg_len != CMSG_LEN(sizeof(int))
75+            || cmsg->cmsg_level != SOL_SOCKET
76+            || cmsg->cmsg_type != SCM_RIGHTS)
77+        {
78+            goto nofd;
79+        }
80+
81+        *fd = *((int *) CMSG_DATA(cmsg));
82+    }
83+    else
84+    {
85+        size = recv(socket, buffer, buffer_size, 0);
86+    }
87+
88+    goto done;
89+
90+  nofd:
91+    *fd = -1;
92+  done:
93+    return size;
94+}
95+
+42, -0
 1@@ -0,0 +1,42 @@
 2+#ifndef SWC_LAUNCH_PROTOCOL_H
 3+#define SWC_LAUNCH_PROTOCOL_H 1
 4+
 5+#include <stdbool.h>
 6+#include <sys/types.h>
 7+
 8+#define SWC_LAUNCH_SOCKET_ENV "SWC_LAUNCH_SOCKET"
 9+#define SWC_LAUNCH_TTY_FD_ENV "SWC_LAUNCH_TTY_FD"
10+
11+struct swc_launch_request
12+{
13+    enum
14+    {
15+        SWC_LAUNCH_REQUEST_DRM_MASTER,
16+        SWC_LAUNCH_REQUEST_OPEN_INPUT_DEVICE
17+    } type;
18+    union
19+    {
20+        struct /* DRM_MASTER */
21+        {
22+            bool set;
23+        };
24+        struct /* OPEN_INPUT_DEVICE */
25+        {
26+            int flags;
27+            char path[];
28+        };
29+    };
30+};
31+
32+struct swc_launch_response
33+{
34+    bool success;
35+};
36+
37+ssize_t send_fd(int socket, int fd, void * buffer, ssize_t buffer_size);
38+
39+ssize_t receive_fd(int socket, int * fd, void * buffer,
40+                   ssize_t buffer_size);
41+
42+#endif
43+
+7, -0
1@@ -0,0 +1,7 @@
2+#ifndef SWC_LAUNCH_H
3+#define SWC_LAUNCH_H 1
4+
5+int swc_launch(int argc, const char * argv[], const char * path);
6+
7+#endif
8+