xwayland: Don't leak fd while reading lock file in src/xwayland/launcher.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 #include "../log.h"
38
39
40 static int
41 weston_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) {
48                 weston_log("socketpair failed\n");
49                 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)
75                         weston_log("exec failed: %m\n");
76                 exit(-1);
77
78         default:
79                 weston_log("forked X server, pid %d\n", mxs->process.pid);
80
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:
91                 weston_log( "failed to fork\n");
92                 break;
93         }
94
95         return 1;
96 }
97
98 static void
99 weston_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
118 static void
119 weston_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) {
139                 weston_log("xserver exited, code %d\n", status);
140                 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. */
146                 weston_log("xserver crashing too fast: %d\n", status);
147                 weston_xserver_shutdown(mxs);
148         }
149 }
150
151 static void
152 bind_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) {
168                 weston_log("failed to create wm\n");
169         }
170
171         xserver_send_listen_socket(wxs->resource, wxs->abstract_fd);
172         xserver_send_listen_socket(wxs->resource, wxs->unix_fd);
173 }
174
175 static int
176 bind_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) {
191                 weston_log("failed to bind to @%s: %s\n",
192                         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
205 static int
206 bind_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) {
222                 weston_log("failed to bind to %s (%s)\n",
223                         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
237 static int
238 create_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) {
249                         weston_log("can't read lock file %s: %s\n",
250                                 lockfile, strerror(errno));
251                         errno = EEXIST;
252                         return -1;
253                 }
254
255                 other = strtol(pid, &end, 0);
256                 if (end != pid + 10) {
257                         weston_log("can't parse lock file %s\n",
258                                 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 */
266                         weston_log("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                 close(fd);
278                 errno = EEXIST;
279                 return -1;
280         } else if (fd < 0) {
281                 weston_log("failed to create lock file %s: %s\n",
282                         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
300 static void
301 weston_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
315 WL_EXPORT int
316 weston_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);
360         weston_log("xserver listening on display %s\n", display_name);
361         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 }