1 /* Copyright (c) 2008-2012 Apple Inc.
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:
11 * The above copyright notice and this permission notice shall be
12 * included in all copies or substantial portions of the Software.
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.
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.
29 #include <CoreServices/CoreServices.h>
31 #ifdef HAVE_DIX_CONFIG_H
32 #include <dix-config.h>
40 #include <sys/socket.h>
43 #define kX11AppBundleId BUNDLE_ID_PREFIX ".X11"
44 #define kX11AppBundlePath "/Contents/MacOS/X11"
46 #include <mach/mach.h>
47 #include <mach/mach_error.h>
48 #include <servers/bootstrap.h>
49 #include "mach_startup.h"
53 #include <AvailabilityMacros.h>
55 #include "launchd_fd.h"
57 static char x11_path
[PATH_MAX
+ 1];
58 static pid_t x11app_pid
= 0;
64 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
66 CFURLRef appURL
= NULL
;
68 LSFindApplicationForInfo(kLSUnknownCreator
, CFSTR(
69 kX11AppBundleId
), nil
, nil
, &appURL
);
75 aslc
, NULL
, ASL_LEVEL_ERR
,
76 "Xquartz: Invalid response from LSFindApplicationForInfo(%s)",
81 if (!CFURLGetFileSystemRepresentation(appURL
, true,
82 (unsigned char *)x11_path
,
84 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
,
85 "Xquartz: Error resolving URL for %s",
90 strlcat(x11_path
, kX11AppBundlePath
, sizeof(x11_path
));
91 asl_log(aslc
, NULL
, ASL_LEVEL_INFO
, "Xquartz: X11.app = %s", x11_path
);
94 case kLSApplicationNotFoundErr
:
95 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
,
96 "Xquartz: Unable to find application for %s",
101 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
,
102 "Xquartz: Unable to find application for %s, error code = %d",
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",
115 connect_to_socket(const char *filename
)
117 struct sockaddr_un servaddr_un
;
118 struct sockaddr
*servaddr
;
119 socklen_t servaddr_len
;
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
));
127 servaddr
= (struct sockaddr
*)&servaddr_un
;
128 servaddr_len
= sizeof(struct sockaddr_un
) -
129 sizeof(servaddr_un
.sun_path
) + strlen(filename
);
131 ret_fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
133 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
,
134 "Xquartz: Failed to create socket: %s - %s", filename
,
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",
154 send_fd_handoff(int connected_fd
, int launchd_fd
)
156 char databuf
[] = "display";
161 char bytes
[CMSG_SPACE(sizeof(int))];
165 struct cmsghdr
*cmsg
;
167 iov
[0].iov_base
= databuf
;
168 iov
[0].iov_len
= sizeof(databuf
);
172 msg
.msg_control
= buf
.bytes
;
173 msg
.msg_controllen
= sizeof(buf
);
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));
183 msg
.msg_controllen
= cmsg
->cmsg_len
;
185 *((int *)CMSG_DATA(cmsg
)) = launchd_fd
;
187 if (sendmsg(connected_fd
, &msg
, 0) < 0) {
189 aslc
, NULL
, ASL_LEVEL_ERR
,
190 "Xquartz: Error sending $DISPLAY file descriptor over fd %d: %d -- %s",
191 connected_fd
, errno
, strerror(errno
));
195 asl_log(aslc
, NULL
, ASL_LEVEL_DEBUG
,
196 "Xquartz: Message sent. Closing handoff fd.");
200 __attribute__((__noreturn__
))
202 signal_handler(int sig
)
205 kill(x11app_pid
, sig
);
210 main(int argc
, char **argv
, char **envp
)
215 string_array_t newenvp
;
216 string_array_t newargv
;
219 string_t handoff_socket_filename
;
223 char *server_bootstrap_name
= kX11AppBundleId
;
225 if (getenv("X11_PREFS_DOMAIN"))
226 server_bootstrap_name
= getenv("X11_PREFS_DOMAIN");
228 asprintf(&asl_sender
, "%s.stub", server_bootstrap_name
);
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';
236 assert(aslc
= asl_open(asl_sender
, asl_facility
, ASL_OPT_NO_DELAY
));
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.
244 handler
= signal(SIGUSR1
, SIG_IGN
);
245 if (handler
== SIG_IGN
)
246 kill(getppid(), SIGUSR1
);
247 signal(SIGUSR1
, handler
);
249 /* Pass on SIGs to X11.app */
250 signal(SIGINT
, signal_handler
);
251 signal(SIGTERM
, signal_handler
);
253 /* Get the $DISPLAY FD */
254 launchd_fd
= launchd_display_fd();
256 kr
= bootstrap_look_up(bootstrap_port
, server_bootstrap_name
, &mp
);
257 if (kr
!= KERN_SUCCESS
) {
260 asl_log(aslc
, NULL
, ASL_LEVEL_WARNING
,
261 "Xquartz: Unable to locate waiting server: %s",
262 server_bootstrap_name
);
265 /* This forking is ugly and will be cleaned up later */
268 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
, "Xquartz: Could not fork: %s",
277 _argv
[1] = "--listenonly";
279 asl_log(aslc
, NULL
, ASL_LEVEL_NOTICE
,
280 "Xquartz: Starting X server: %s --listenonly",
282 return execvp(x11_path
, _argv
);
285 /* Try connecting for 10 seconds */
286 for (i
= 0; i
< 80; i
++) {
288 kr
= bootstrap_look_up(bootstrap_port
, server_bootstrap_name
, &mp
);
289 if (kr
== KERN_SUCCESS
)
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(
299 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
,
300 "Xquartz: bootstrap_look_up(): %ul",
307 /* Get X11.app's pid */
308 request_pid(mp
, &x11app_pid
);
310 /* Handoff the $DISPLAY FD */
311 if (launchd_fd
!= -1) {
315 for (try = 0, try_max
= 5; try < try_max
; try++) {
316 if (request_fd_handoff_socket(mp
,
317 handoff_socket_filename
) !=
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
);
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)",
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
);
347 for (envpc
= 0; envp
[envpc
]; envpc
++) ;
349 /* We have fixed-size string lengths due to limitations in IPC,
350 * so we need to copy our argv and envp.
352 newargv
= (string_array_t
)calloc((1 + argc
), sizeof(string_t
));
353 newenvp
= (string_array_t
)calloc((1 + envpc
), sizeof(string_t
));
355 if (!newargv
|| !newenvp
) {
356 /* Silence the clang static analyzer */
360 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
,
361 "Xquartz: Memory allocation failure");
365 for (i
= 0; i
< argc
; i
++) {
366 strlcpy(newargv
[i
], argv
[i
], STRING_T_SIZE
);
368 for (i
= 0; i
< envpc
; i
++) {
369 strlcpy(newenvp
[i
], envp
[i
], STRING_T_SIZE
);
372 kr
= start_x11_server(mp
, newargv
, argc
, newenvp
, envpc
);
377 if (kr
!= KERN_SUCCESS
) {
378 asl_log(aslc
, NULL
, ASL_LEVEL_ERR
, "Xquartz: start_x11_server: %s",