Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xquartz / mach-startup / stub.c
CommitLineData
a09e091a
JB
1/* Copyright (c) 2008-2012 Apple Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person
4 * obtaining a copy of this software and associated documentation files
5 * (the "Software"), to deal in the Software without restriction,
6 * including without limitation the rights to use, copy, modify, merge,
7 * publish, distribute, sublicense, and/or sell copies of the Software,
8 * and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be
12 * included in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
18 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Except as contained in this notice, the name(s) of the above
24 * copyright holders shall not be used in advertising or otherwise to
25 * promote the sale, use or other dealings in this Software without
26 * prior written authorization.
27 */
28
29#include <CoreServices/CoreServices.h>
30
31#ifdef HAVE_DIX_CONFIG_H
32#include <dix-config.h>
33#endif
34
35#include <string.h>
36#include <unistd.h>
37#include <errno.h>
38#include <asl.h>
39
40#include <sys/socket.h>
41#include <sys/un.h>
42
43#define kX11AppBundleId BUNDLE_ID_PREFIX ".X11"
44#define kX11AppBundlePath "/Contents/MacOS/X11"
45
46#include <mach/mach.h>
47#include <mach/mach_error.h>
48#include <servers/bootstrap.h>
49#include "mach_startup.h"
50
51#include <signal.h>
52
53#include <AvailabilityMacros.h>
54
55#include "launchd_fd.h"
56
57static char x11_path[PATH_MAX + 1];
58static pid_t x11app_pid = 0;
59aslclient aslc;
60
61static void
62set_x11_path(void)
63{
64#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
65
66 CFURLRef appURL = NULL;
67 OSStatus osstatus =
68 LSFindApplicationForInfo(kLSUnknownCreator, CFSTR(
69 kX11AppBundleId), nil, nil, &appURL);
70
71 switch (osstatus) {
72 case noErr:
73 if (appURL == NULL) {
74 asl_log(
75 aslc, NULL, ASL_LEVEL_ERR,
76 "Xquartz: Invalid response from LSFindApplicationForInfo(%s)",
77 kX11AppBundleId);
78 exit(1);
79 }
80
81 if (!CFURLGetFileSystemRepresentation(appURL, true,
82 (unsigned char *)x11_path,
83 sizeof(x11_path))) {
84 asl_log(aslc, NULL, ASL_LEVEL_ERR,
85 "Xquartz: Error resolving URL for %s",
86 kX11AppBundleId);
87 exit(3);
88 }
89
90 strlcat(x11_path, kX11AppBundlePath, sizeof(x11_path));
91 asl_log(aslc, NULL, ASL_LEVEL_INFO, "Xquartz: X11.app = %s", x11_path);
92 break;
93
94 case kLSApplicationNotFoundErr:
95 asl_log(aslc, NULL, ASL_LEVEL_ERR,
96 "Xquartz: Unable to find application for %s",
97 kX11AppBundleId);
98 exit(10);
99
100 default:
101 asl_log(aslc, NULL, ASL_LEVEL_ERR,
102 "Xquartz: Unable to find application for %s, error code = %d",
103 kX11AppBundleId,
104 (int)osstatus);
105 exit(11);
106 }
107#else
108 /* TODO: Make Tiger smarter... but TBH, this should never get called on Tiger... */
109 strlcpy(x11_path, "/Applications/Utilities/X11.app/Contents/MacOS/X11",
110 sizeof(x11_path));
111#endif
112}
113
114static int
115connect_to_socket(const char *filename)
116{
117 struct sockaddr_un servaddr_un;
118 struct sockaddr *servaddr;
119 socklen_t servaddr_len;
120 int ret_fd;
121
122 /* Setup servaddr_un */
123 memset(&servaddr_un, 0, sizeof(struct sockaddr_un));
124 servaddr_un.sun_family = AF_UNIX;
125 strlcpy(servaddr_un.sun_path, filename, sizeof(servaddr_un.sun_path));
126
127 servaddr = (struct sockaddr *)&servaddr_un;
128 servaddr_len = sizeof(struct sockaddr_un) -
129 sizeof(servaddr_un.sun_path) + strlen(filename);
130
131 ret_fd = socket(PF_UNIX, SOCK_STREAM, 0);
132 if (ret_fd == -1) {
133 asl_log(aslc, NULL, ASL_LEVEL_ERR,
134 "Xquartz: Failed to create socket: %s - %s", filename,
135 strerror(
136 errno));
137 return -1;
138 }
139
140 if (connect(ret_fd, servaddr, servaddr_len) < 0) {
141 asl_log(aslc, NULL, ASL_LEVEL_ERR,
142 "Xquartz: Failed to connect to socket: %s - %d - %s",
143 filename, errno,
144 strerror(
145 errno));
146 close(ret_fd);
147 return -1;
148 }
149
150 return ret_fd;
151}
152
153static void
154send_fd_handoff(int connected_fd, int launchd_fd)
155{
156 char databuf[] = "display";
157 struct iovec iov[1];
158
159 union {
160 struct cmsghdr hdr;
161 char bytes[CMSG_SPACE(sizeof(int))];
162 } buf;
163
164 struct msghdr msg;
165 struct cmsghdr *cmsg;
166
167 iov[0].iov_base = databuf;
168 iov[0].iov_len = sizeof(databuf);
169
170 msg.msg_iov = iov;
171 msg.msg_iovlen = 1;
172 msg.msg_control = buf.bytes;
173 msg.msg_controllen = sizeof(buf);
174 msg.msg_name = 0;
175 msg.msg_namelen = 0;
176 msg.msg_flags = 0;
177
178 cmsg = CMSG_FIRSTHDR(&msg);
179 cmsg->cmsg_level = SOL_SOCKET;
180 cmsg->cmsg_type = SCM_RIGHTS;
181 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
182
183 msg.msg_controllen = cmsg->cmsg_len;
184
185 *((int *)CMSG_DATA(cmsg)) = launchd_fd;
186
187 if (sendmsg(connected_fd, &msg, 0) < 0) {
188 asl_log(
189 aslc, NULL, ASL_LEVEL_ERR,
190 "Xquartz: Error sending $DISPLAY file descriptor over fd %d: %d -- %s",
191 connected_fd, errno, strerror(errno));
192 return;
193 }
194
195 asl_log(aslc, NULL, ASL_LEVEL_DEBUG,
196 "Xquartz: Message sent. Closing handoff fd.");
197 close(connected_fd);
198}
199
200__attribute__((__noreturn__))
201static void
202signal_handler(int sig)
203{
204 if (x11app_pid)
205 kill(x11app_pid, sig);
206 _exit(0);
207}
208
209int
210main(int argc, char **argv, char **envp)
211{
212 int envpc;
213 kern_return_t kr;
214 mach_port_t mp;
215 string_array_t newenvp;
216 string_array_t newargv;
217 size_t i;
218 int launchd_fd;
219 string_t handoff_socket_filename;
220 sig_t handler;
221 char *asl_sender;
222 char *asl_facility;
223 char *server_bootstrap_name = kX11AppBundleId;
224
225 if (getenv("X11_PREFS_DOMAIN"))
226 server_bootstrap_name = getenv("X11_PREFS_DOMAIN");
227
228 asprintf(&asl_sender, "%s.stub", server_bootstrap_name);
229 assert(asl_sender);
230
231 asl_facility = strdup(server_bootstrap_name);
232 assert(asl_facility);
233 if (strcmp(asl_facility + strlen(asl_facility) - 4, ".X11") == 0)
234 asl_facility[strlen(asl_facility) - 4] = '\0';
235
236 assert(aslc = asl_open(asl_sender, asl_facility, ASL_OPT_NO_DELAY));
237 free(asl_sender);
238 free(asl_facility);
239
240 /* We don't have a mechanism in place to handle this interrupt driven
241 * server-start notification, so just send the signal now, so xinit doesn't
242 * time out waiting for it and will just poll for the server.
243 */
244 handler = signal(SIGUSR1, SIG_IGN);
245 if (handler == SIG_IGN)
246 kill(getppid(), SIGUSR1);
247 signal(SIGUSR1, handler);
248
249 /* Pass on SIGs to X11.app */
250 signal(SIGINT, signal_handler);
251 signal(SIGTERM, signal_handler);
252
253 /* Get the $DISPLAY FD */
254 launchd_fd = launchd_display_fd();
255
256 kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp);
257 if (kr != KERN_SUCCESS) {
258 pid_t child;
259
260 asl_log(aslc, NULL, ASL_LEVEL_WARNING,
261 "Xquartz: Unable to locate waiting server: %s",
262 server_bootstrap_name);
263 set_x11_path();
264
265 /* This forking is ugly and will be cleaned up later */
266 child = fork();
267 if (child == -1) {
268 asl_log(aslc, NULL, ASL_LEVEL_ERR, "Xquartz: Could not fork: %s",
269 strerror(
270 errno));
271 return EXIT_FAILURE;
272 }
273
274 if (child == 0) {
275 char *_argv[3];
276 _argv[0] = x11_path;
277 _argv[1] = "--listenonly";
278 _argv[2] = NULL;
279 asl_log(aslc, NULL, ASL_LEVEL_NOTICE,
280 "Xquartz: Starting X server: %s --listenonly",
281 x11_path);
282 return execvp(x11_path, _argv);
283 }
284
285 /* Try connecting for 10 seconds */
286 for (i = 0; i < 80; i++) {
287 usleep(250000);
288 kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp);
289 if (kr == KERN_SUCCESS)
290 break;
291 }
292
293 if (kr != KERN_SUCCESS) {
294#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
295 asl_log(aslc, NULL, ASL_LEVEL_ERR,
296 "Xquartz: bootstrap_look_up(): %s", bootstrap_strerror(
297 kr));
298#else
299 asl_log(aslc, NULL, ASL_LEVEL_ERR,
300 "Xquartz: bootstrap_look_up(): %ul",
301 (unsigned long)kr);
302#endif
303 return EXIT_FAILURE;
304 }
305 }
306
307 /* Get X11.app's pid */
308 request_pid(mp, &x11app_pid);
309
310 /* Handoff the $DISPLAY FD */
311 if (launchd_fd != -1) {
312 size_t try, try_max;
313 int handoff_fd = -1;
314
315 for (try = 0, try_max = 5; try < try_max; try++) {
316 if (request_fd_handoff_socket(mp,
317 handoff_socket_filename) !=
318 KERN_SUCCESS) {
319 asl_log(
320 aslc, NULL, ASL_LEVEL_INFO,
321 "Xquartz: Failed to request a socket from the server to send the $DISPLAY fd over (try %d of %d)",
322 (int)try + 1, (int)try_max);
323 continue;
324 }
325
326 handoff_fd = connect_to_socket(handoff_socket_filename);
327 if (handoff_fd == -1) {
328 asl_log(aslc, NULL, ASL_LEVEL_ERR,
329 "Xquartz: Failed to connect to socket (try %d of %d)",
330 (int)try + 1,
331 (int)try_max);
332 continue;
333 }
334
335 asl_log(
336 aslc, NULL, ASL_LEVEL_INFO,
337 "Xquartz: Handoff connection established (try %d of %d) on fd %d, \"%s\". Sending message.",
338 (int)try + 1, (int)try_max, handoff_fd,
339 handoff_socket_filename);
340 send_fd_handoff(handoff_fd, launchd_fd);
341 close(handoff_fd);
342 break;
343 }
344 }
345
346 /* Count envp */
347 for (envpc = 0; envp[envpc]; envpc++) ;
348
349 /* We have fixed-size string lengths due to limitations in IPC,
350 * so we need to copy our argv and envp.
351 */
352 newargv = (string_array_t)calloc((1 + argc), sizeof(string_t));
353 newenvp = (string_array_t)calloc((1 + envpc), sizeof(string_t));
354
355 if (!newargv || !newenvp) {
356 /* Silence the clang static analyzer */
357 free(newargv);
358 free(newenvp);
359
360 asl_log(aslc, NULL, ASL_LEVEL_ERR,
361 "Xquartz: Memory allocation failure");
362 return EXIT_FAILURE;
363 }
364
365 for (i = 0; i < argc; i++) {
366 strlcpy(newargv[i], argv[i], STRING_T_SIZE);
367 }
368 for (i = 0; i < envpc; i++) {
369 strlcpy(newenvp[i], envp[i], STRING_T_SIZE);
370 }
371
372 kr = start_x11_server(mp, newargv, argc, newenvp, envpc);
373
374 free(newargv);
375 free(newenvp);
376
377 if (kr != KERN_SUCCESS) {
378 asl_log(aslc, NULL, ASL_LEVEL_ERR, "Xquartz: start_x11_server: %s",
379 mach_error_string(
380 kr));
381 return EXIT_FAILURE;
382 }
383 return EXIT_SUCCESS;
384}