xwayland: Add window-manager.c
[platform/upstream/weston.git] / src / xwayland / launcher.c
1 /*
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"
37
38
39 static int
40 weston_xserver_handle_event(int listen_fd, uint32_t mask, void *data)
41 {
42         struct weston_xserver *mxs = data;
43         char display[8], s[8];
44         int sv[2], client_fd;
45
46         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
47                 fprintf(stderr, "socketpair failed\n");
48                 return 1;
49         }
50
51         mxs->process.pid = fork();
52         switch (mxs->process.pid) {
53         case 0:
54                 /* SOCK_CLOEXEC closes both ends, so we need to unset
55                  * the flag on the client fd. */
56                 client_fd = dup(sv[1]);
57                 if (client_fd < 0)
58                         return 1;
59
60                 snprintf(s, sizeof s, "%d", client_fd);
61                 setenv("WAYLAND_SOCKET", s, 1);
62
63                 snprintf(display, sizeof display, ":%d", mxs->display);
64
65                 if (execl(XSERVER_PATH,
66                           XSERVER_PATH,
67                           display,
68                           "-wayland",
69                           "-rootless",
70                           "-retro",
71                           "-nolisten", "all",
72                           "-terminate",
73                           NULL) < 0)
74                         fprintf(stderr, "exec failed: %m\n");
75                 exit(-1);
76
77         default:
78                 fprintf(stderr, "forked X server, pid %d\n", mxs->process.pid);
79
80                 close(sv[1]);
81                 mxs->client = wl_client_create(mxs->wl_display, sv[0]);
82
83                 weston_watch_process(&mxs->process);
84
85                 wl_event_source_remove(mxs->abstract_source);
86                 wl_event_source_remove(mxs->unix_source);
87                 break;
88
89         case -1:
90                 fprintf(stderr, "failed to fork\n");
91                 break;
92         }
93
94         return 1;
95 }
96
97 static void
98 weston_xserver_shutdown(struct weston_xserver *wxs)
99 {
100         char path[256];
101
102         snprintf(path, sizeof path, "/tmp/.X%d-lock", wxs->display);
103         unlink(path);
104         snprintf(path, sizeof path, "/tmp/.X11-unix/X%d", wxs->display);
105         unlink(path);
106         if (wxs->process.pid == 0) {
107                 wl_event_source_remove(wxs->abstract_source);
108                 wl_event_source_remove(wxs->unix_source);
109         }
110         close(wxs->abstract_fd);
111         close(wxs->unix_fd);
112         if (wxs->wm)
113                 weston_wm_destroy(wxs->wm);
114         wxs->loop = NULL;
115 }
116
117 static void
118 weston_xserver_cleanup(struct weston_process *process, int status)
119 {
120         struct weston_xserver *mxs =
121                 container_of(process, struct weston_xserver, process);
122
123         mxs->process.pid = 0;
124         mxs->client = NULL;
125         mxs->resource = NULL;
126
127         mxs->abstract_source =
128                 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
129                                      WL_EVENT_READABLE,
130                                      weston_xserver_handle_event, mxs);
131
132         mxs->unix_source =
133                 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
134                                      WL_EVENT_READABLE,
135                                      weston_xserver_handle_event, mxs);
136
137         if (mxs->wm) {
138                 fprintf(stderr, "xserver exited, code %d\n", status);
139                 weston_wm_destroy(mxs->wm);
140                 mxs->wm = NULL;
141         } else {
142                 /* If the X server crashes before it binds to the
143                  * xserver interface, shut down and don't try
144                  * again. */
145                 fprintf(stderr, "xserver crashing too fast: %d\n", status);
146                 weston_xserver_shutdown(mxs);
147         }
148 }
149
150 static void
151 bind_xserver(struct wl_client *client,
152              void *data, uint32_t version, uint32_t id)
153 {
154         struct weston_xserver *wxs = data;
155
156         /* If it's a different client than the xserver we launched,
157          * don't start the wm. */
158         if (client != wxs->client)
159                 return;
160
161         wxs->resource = 
162                 wl_client_add_object(client, &xserver_interface,
163                                      &xserver_implementation, id, wxs);
164
165         wxs->wm = weston_wm_create(wxs);
166         if (wxs->wm == NULL) {
167                 fprintf(stderr, "failed to create wm\n");
168         }
169
170         xserver_send_listen_socket(wxs->resource, wxs->abstract_fd);
171         xserver_send_listen_socket(wxs->resource, wxs->unix_fd);
172 }
173
174 static int
175 bind_to_abstract_socket(int display)
176 {
177         struct sockaddr_un addr;
178         socklen_t size, name_size;
179         int fd;
180
181         fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
182         if (fd < 0)
183                 return -1;
184
185         addr.sun_family = AF_LOCAL;
186         name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
187                              "%c/tmp/.X11-unix/X%d", 0, display);
188         size = offsetof(struct sockaddr_un, sun_path) + name_size;
189         if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
190                 fprintf(stderr, "failed to bind to @%s: %s\n",
191                         addr.sun_path + 1, strerror(errno));
192                 close(fd);
193                 return -1;
194         }
195
196         if (listen(fd, 1) < 0) {
197                 close(fd);
198                 return -1;
199         }
200
201         return fd;
202 }
203
204 static int
205 bind_to_unix_socket(int display)
206 {
207         struct sockaddr_un addr;
208         socklen_t size, name_size;
209         int fd;
210
211         fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
212         if (fd < 0)
213                 return -1;
214
215         addr.sun_family = AF_LOCAL;
216         name_size = snprintf(addr.sun_path, sizeof addr.sun_path,
217                              "/tmp/.X11-unix/X%d", display) + 1;
218         size = offsetof(struct sockaddr_un, sun_path) + name_size;
219         unlink(addr.sun_path);
220         if (bind(fd, (struct sockaddr *) &addr, size) < 0) {
221                 fprintf(stderr, "failed to bind to %s (%s)\n",
222                         addr.sun_path, strerror(errno));
223                 close(fd);
224                 return -1;
225         }
226
227         if (listen(fd, 1) < 0) {
228                 unlink(addr.sun_path);
229                 close(fd);
230                 return -1;
231         }
232
233         return fd;
234 }
235
236 static int
237 create_lockfile(int display, char *lockfile, size_t lsize)
238 {
239         char pid[16], *end;
240         int fd, size;
241         pid_t other;
242
243         snprintf(lockfile, lsize, "/tmp/.X%d-lock", display);
244         fd = open(lockfile, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
245         if (fd < 0 && errno == EEXIST) {
246                 fd = open(lockfile, O_CLOEXEC, O_RDONLY);
247                 if (fd < 0 || read(fd, pid, 11) != 11) {
248                         fprintf(stderr, "can't read lock file %s: %s\n",
249                                 lockfile, strerror(errno));
250                         errno = EEXIST;
251                         return -1;
252                 }
253
254                 other = strtol(pid, &end, 0);
255                 if (end != pid + 10) {
256                         fprintf(stderr, "can't parse lock file %s\n",
257                                 lockfile);
258                         close(fd);
259                         errno = EEXIST;
260                         return -1;
261                 }
262
263                 if (kill(other, 0) < 0 && errno == ESRCH) {
264                         /* stale lock file; unlink and try again */
265                         fprintf(stderr,
266                                 "unlinking stale lock file %s\n", lockfile);
267                         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
277                 errno = EEXIST;
278                 return -1;
279         } else if (fd < 0) {
280                 fprintf(stderr, "failed to create lock file %s: %s\n",
281                         lockfile, strerror(errno));
282                 return -1;
283         }
284
285         /* Subtle detail: we use the pid of the wayland
286          * compositor, not the xserver in the lock file. */
287         size = snprintf(pid, sizeof pid, "%10d\n", getpid());
288         if (write(fd, pid, size) != size) {
289                 unlink(lockfile);
290                 close(fd);
291                 return -1;
292         }
293
294         close(fd);
295
296         return 0;
297 }
298
299 static void
300 weston_xserver_destroy(struct wl_listener *l, void *data)
301 {
302         struct weston_xserver *wxs =
303                 container_of(l, struct weston_xserver, destroy_listener);
304
305         if (!wxs)
306                 return;
307
308         if (wxs->loop)
309                 weston_xserver_shutdown(wxs);
310
311         free(wxs);
312 }
313
314 WL_EXPORT int
315 weston_xserver_init(struct weston_compositor *compositor)
316 {
317         struct wl_display *display = compositor->wl_display;
318         struct weston_xserver *mxs;
319         char lockfile[256], display_name[8];
320
321         mxs = malloc(sizeof *mxs);
322         memset(mxs, 0, sizeof *mxs);
323
324         mxs->process.cleanup = weston_xserver_cleanup;
325         mxs->wl_display = display;
326         mxs->compositor = compositor;
327
328         mxs->display = 0;
329
330  retry:
331         if (create_lockfile(mxs->display, lockfile, sizeof lockfile) < 0) {
332                 if (errno == EAGAIN) {
333                         goto retry;
334                 } else if (errno == EEXIST) {
335                         mxs->display++;
336                         goto retry;
337                 } else {
338                         free(mxs);
339                         return -1;
340                 }
341         }
342
343         mxs->abstract_fd = bind_to_abstract_socket(mxs->display);
344         if (mxs->abstract_fd < 0 && errno == EADDRINUSE) {
345                 mxs->display++;
346                 unlink(lockfile);
347                 goto retry;
348         }
349
350         mxs->unix_fd = bind_to_unix_socket(mxs->display);
351         if (mxs->unix_fd < 0) {
352                 unlink(lockfile);
353                 close(mxs->abstract_fd);
354                 free(mxs);
355                 return -1;
356         }
357
358         snprintf(display_name, sizeof display_name, ":%d", mxs->display);
359         fprintf(stderr, "xserver listening on display %s\n", display_name);
360         setenv("DISPLAY", display_name, 1);
361
362         mxs->loop = wl_display_get_event_loop(display);
363         mxs->abstract_source =
364                 wl_event_loop_add_fd(mxs->loop, mxs->abstract_fd,
365                                      WL_EVENT_READABLE,
366                                      weston_xserver_handle_event, mxs);
367         mxs->unix_source =
368                 wl_event_loop_add_fd(mxs->loop, mxs->unix_fd,
369                                      WL_EVENT_READABLE,
370                                      weston_xserver_handle_event, mxs);
371
372         wl_display_add_global(display, &xserver_interface, mxs, bind_xserver);
373
374         mxs->destroy_listener.notify = weston_xserver_destroy;
375         wl_signal_add(&compositor->destroy_signal, &mxs->destroy_listener);
376
377         return 0;
378 }