blob: 035c8e3076b1f94e74ec9b73da0901b02447a4d7 [file] [log] [blame]
Kristian Høgsberge10b1242012-05-21 16:48:05 -04001/*
2 * Copyright © 2011 Intel Corporation
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#define _GNU_SOURCE
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28#include <sys/socket.h>
29#include <sys/un.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <unistd.h>
33#include <signal.h>
34
35#include "xwayland.h"
36#include "xserver-server-protocol.h"
Martin Minarik6d118362012-06-07 18:01:59 +020037#include "../log.h"
Kristian Høgsberge10b1242012-05-21 16:48:05 -040038
39
40static int
41weston_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
42{
43 struct weston_xserver *mxs = data;
44 char display[8], s[8];
45 int sv[2], client_fd;
46
47 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +020048 weston_log("socketpair failed\n");
Kristian Høgsberge10b1242012-05-21 16:48:05 -040049 return 1;
50 }
51
52 mxs->process.pid = fork();
53 switch (mxs->process.pid) {
54 case 0:
55 /* SOCK_CLOEXEC closes both ends, so we need to unset
56 * the flag on the client fd. */
57 client_fd = dup(sv[1]);
58 if (client_fd < 0)
59 return 1;
60
61 snprintf(s, sizeof s, "%d", client_fd);
62 setenv("WAYLAND_SOCKET", s, 1);
63
64 snprintf(display, sizeof display, ":%d", mxs->display);
65
66 if (execl(XSERVER_PATH,
67 XSERVER_PATH,
68 display,
69 "-wayland",
70 "-rootless",
71 "-retro",
72 "-nolisten", "all",
73 "-terminate",
74 NULL) < 0)
Martin Minarik6d118362012-06-07 18:01:59 +020075 weston_log("exec failed: %m\n");
Kristian Høgsberge10b1242012-05-21 16:48:05 -040076 exit(-1);
77
78 default:
Martin Minarik6d118362012-06-07 18:01:59 +020079 weston_log("forked X server, pid %d\n", mxs->process.pid);
Kristian Høgsberge10b1242012-05-21 16:48:05 -040080
81 close(sv[1]);
82 mxs->client = wl_client_create(mxs->wl_display, sv[0]);
83
84 weston_watch_process(&mxs->process);
85
86 wl_event_source_remove(mxs->abstract_source);
87 wl_event_source_remove(mxs->unix_source);
88 break;
89
90 case -1:
Martin Minarik6d118362012-06-07 18:01:59 +020091 weston_log( "failed to fork\n");
Kristian Høgsberge10b1242012-05-21 16:48:05 -040092 break;
93 }
94
95 return 1;
96}
97
98static void
99weston_xserver_shutdown(struct weston_xserver *wxs)
100{
101 char path[256];
102
103 snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
104 unlink(path);
105 snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
106 unlink(path);
107 if (wxs->process.pid == 0) {
108 wl_event_source_remove(wxs->abstract_source);
109 wl_event_source_remove(wxs->unix_source);
110 }
111 close(wxs->abstract_fd);
112 close(wxs->unix_fd);
113 if (wxs->wm)
114 weston_wm_destroy(wxs->wm);
115 wxs->loop = NULL;
116}
117
118static void
119weston_xserver_cleanup(struct weston_process *process, int status)
120{
121 struct weston_xserver *mxs =
122 container_of(process, struct weston_xserver, process);
123
124 mxs->process.pid = 0;
125 mxs->client = NULL;
126 mxs->resource = NULL;
127
128 mxs->abstract_source =
129 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
130 WL_EVENT_READABLE,
131 weston_xserver_handle_event, mxs);
132
133 mxs->unix_source =
134 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
135 WL_EVENT_READABLE,
136 weston_xserver_handle_event, mxs);
137
138 if (mxs->wm) {
Martin Minarik6d118362012-06-07 18:01:59 +0200139 weston_log("xserver exited, code %d\n", status);
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400140 weston_wm_destroy(mxs->wm);
141 mxs->wm = NULL;
142 } else {
143 /* If the X server crashes before it binds to the
144 * xserver interface, shut down and don't try
145 * again. */
Martin Minarik6d118362012-06-07 18:01:59 +0200146 weston_log("xserver crashing too fast: %d\n", status);
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400147 weston_xserver_shutdown(mxs);
148 }
149}
150
151static void
152bind_xserver(struct wl_client *client,
153 void *data, uint32_t version, uint32_t id)
154{
155 struct weston_xserver *wxs = data;
156
157 /* If it's a different client than the xserver we launched,
158 * don't start the wm. */
159 if (client != wxs->client)
160 return;
161
162 wxs->resource =
163 wl_client_add_object(client, &xserver_interface,
164 &xserver_implementation, id, wxs);
165
166 wxs->wm = weston_wm_create(wxs);
167 if (wxs->wm == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200168 weston_log("failed to create wm\n");
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400169 }
170
171 xserver_send_listen_socket(wxs->resource, wxs->abstract_fd);
172 xserver_send_listen_socket(wxs->resource, wxs->unix_fd);
173}
174
175static int
176bind_to_abstract_socket(int display)
177{
178 struct sockaddr_un addr;
179 socklen_t size, name_size;
180 int fd;
181
182 fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
183 if (fd < 0)
184 return -1;
185
186 addr.sun_family = AF_LOCAL;
187 name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
188 "%c/tmp/.X11-unix/X%d", 0, display);
189 size = offsetof(struct sockaddr_un, sun_path) + name_size;
190 if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200191 weston_log("failed to bind to @%s: %s\n",
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400192 addr.sun_path + 1, strerror(errno));
193 close(fd);
194 return -1;
195 }
196
197 if (listen(fd, 1) < 0) {
198 close(fd);
199 return -1;
200 }
201
202 return fd;
203}
204
205static int
206bind_to_unix_socket(int display)
207{
208 struct sockaddr_un addr;
209 socklen_t size, name_size;
210 int fd;
211
212 fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
213 if (fd < 0)
214 return -1;
215
216 addr.sun_family = AF_LOCAL;
217 name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
218 "/tmp/.X11-unix/X%d", display) + 1;
219 size = offsetof(struct sockaddr_un, sun_path) + name_size;
220 unlink(addr.sun_path);
221 if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200222 weston_log("failed to bind to %s (%s)\n",
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400223 addr.sun_path, strerror(errno));
224 close(fd);
225 return -1;
226 }
227
228 if (listen(fd, 1) < 0) {
229 unlink(addr.sun_path);
230 close(fd);
231 return -1;
232 }
233
234 return fd;
235}
236
237static int
238create_lockfile(int display, char *lockfile, size_t lsize)
239{
240 char pid[16], *end;
241 int fd, size;
242 pid_t other;
243
244 snprintf(lockfile, lsize, "/tmp/.X%d-lock", display);
245 fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
246 if (fd < 0 && errno == EEXIST) {
247 fd = open(lockfile, O_CLOEXEC, O_RDONLY);
248 if (fd < 0 || read(fd, pid, 11) != 11) {
Martin Minarik6d118362012-06-07 18:01:59 +0200249 weston_log("can't read lock file %s: %s\n",
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400250 lockfile, strerror(errno));
251 errno = EEXIST;
252 return -1;
253 }
254
255 other = strtol(pid, &end, 0);
256 if (end != pid + 10) {
Martin Minarik6d118362012-06-07 18:01:59 +0200257 weston_log("can't parse lock file %s\n",
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400258 lockfile);
259 close(fd);
260 errno = EEXIST;
261 return -1;
262 }
263
264 if (kill(other, 0) < 0 && errno == ESRCH) {
265 /* stale lock file; unlink and try again */
Martin Minarik6d118362012-06-07 18:01:59 +0200266 weston_log("unlinking stale lock file %s\n", lockfile);
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400267 close(fd);
268 if (unlink(lockfile))
269 /* If we fail to unlink, return EEXIST
270 so we try the next display number.*/
271 errno = EEXIST;
272 else
273 errno = EAGAIN;
274 return -1;
275 }
276
Martin Olsson19721412012-07-08 03:03:45 +0200277 close(fd);
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400278 errno = EEXIST;
279 return -1;
280 } else if (fd < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200281 weston_log("failed to create lock file %s: %s\n",
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400282 lockfile, strerror(errno));
283 return -1;
284 }
285
286 /* Subtle detail: we use the pid of the wayland
287 * compositor, not the xserver in the lock file. */
288 size = snprintf(pid, sizeof pid, "%10d\n", getpid());
289 if (write(fd, pid, size) != size) {
290 unlink(lockfile);
291 close(fd);
292 return -1;
293 }
294
295 close(fd);
296
297 return 0;
298}
299
300static void
301weston_xserver_destroy(struct wl_listener *l, void *data)
302{
303 struct weston_xserver *wxs =
304 container_of(l, struct weston_xserver, destroy_listener);
305
306 if (!wxs)
307 return;
308
309 if (wxs->loop)
310 weston_xserver_shutdown(wxs);
311
312 free(wxs);
313}
314
315WL_EXPORT int
316weston_xserver_init(struct weston_compositor *compositor)
317{
318 struct wl_display *display = compositor->wl_display;
319 struct weston_xserver *mxs;
320 char lockfile[256], display_name[8];
321
322 mxs = malloc(sizeof *mxs);
323 memset(mxs, 0, sizeof *mxs);
324
325 mxs->process.cleanup = weston_xserver_cleanup;
326 mxs->wl_display = display;
327 mxs->compositor = compositor;
328
329 mxs->display = 0;
330
331 retry:
332 if (create_lockfile(mxs->display, lockfile, sizeof lockfile) < 0) {
333 if (errno == EAGAIN) {
334 goto retry;
335 } else if (errno == EEXIST) {
336 mxs->display++;
337 goto retry;
338 } else {
339 free(mxs);
340 return -1;
341 }
342 }
343
344 mxs->abstract_fd = bind_to_abstract_socket(mxs->display);
345 if (mxs->abstract_fd < 0 && errno == EADDRINUSE) {
346 mxs->display++;
347 unlink(lockfile);
348 goto retry;
349 }
350
351 mxs->unix_fd = bind_to_unix_socket(mxs->display);
352 if (mxs->unix_fd < 0) {
353 unlink(lockfile);
354 close(mxs->abstract_fd);
355 free(mxs);
356 return -1;
357 }
358
359 snprintf(display_name, sizeof display_name, ":%d", mxs->display);
Martin Minarik6d118362012-06-07 18:01:59 +0200360 weston_log("xserver listening on display %s\n", display_name);
Kristian Høgsberge10b1242012-05-21 16:48:05 -0400361 setenv("DISPLAY", display_name, 1);
362
363 mxs->loop = wl_display_get_event_loop(display);
364 mxs->abstract_source =
365 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
366 WL_EVENT_READABLE,
367 weston_xserver_handle_event, mxs);
368 mxs->unix_source =
369 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
370 WL_EVENT_READABLE,
371 weston_xserver_handle_event, mxs);
372
373 wl_display_add_global(display, &xserver_interface, mxs, bind_xserver);
374
375 mxs->destroy_listener.notify = weston_xserver_destroy;
376 wl_signal_add(&compositor->destroy_signal, &mxs->destroy_listener);
377
378 return 0;
379}