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 "launchd_fd.h" 54 55static CFURLRef x11appURL; 56static FSRef x11_appRef; 57static pid_t x11app_pid = 0; 58aslclient aslc; 59 60static void 61set_x11_path(void) 62{ 63 OSStatus osstatus = LSFindApplicationForInfo(kLSUnknownCreator, CFSTR(kX11AppBundleId), 64 nil, &x11_appRef, &x11appURL); 65 66 switch (osstatus) { 67 case noErr: 68 if (x11appURL == NULL) { 69 asl_log(aslc, NULL, ASL_LEVEL_ERR, 70 "Xquartz: Invalid response from LSFindApplicationForInfo(%s)", 71 kX11AppBundleId); 72 exit(1); 73 } 74 break; 75 76 case kLSApplicationNotFoundErr: 77 asl_log(aslc, NULL, ASL_LEVEL_ERR, 78 "Xquartz: Unable to find application for %s", 79 kX11AppBundleId); 80 exit(10); 81 82 default: 83 asl_log(aslc, NULL, ASL_LEVEL_ERR, 84 "Xquartz: Unable to find application for %s, error code = %d", 85 kX11AppBundleId, (int)osstatus); 86 exit(11); 87 } 88} 89 90static int 91connect_to_socket(const char *filename) 92{ 93 struct sockaddr_un servaddr_un; 94 struct sockaddr *servaddr; 95 socklen_t servaddr_len; 96 int ret_fd; 97 98 /* Setup servaddr_un */ 99 memset(&servaddr_un, 0, sizeof(struct sockaddr_un)); 100 servaddr_un.sun_family = AF_UNIX; 101 strlcpy(servaddr_un.sun_path, filename, sizeof(servaddr_un.sun_path)); 102 103 servaddr = (struct sockaddr *)&servaddr_un; 104 servaddr_len = sizeof(struct sockaddr_un) - 105 sizeof(servaddr_un.sun_path) + strlen(filename); 106 107 ret_fd = socket(PF_UNIX, SOCK_STREAM, 0); 108 if (ret_fd == -1) { 109 asl_log(aslc, NULL, ASL_LEVEL_ERR, 110 "Xquartz: Failed to create socket: %s - %d - %s", 111 filename, errno, strerror(errno)); 112 return -1; 113 } 114 115 if (connect(ret_fd, servaddr, servaddr_len) < 0) { 116 asl_log(aslc, NULL, ASL_LEVEL_ERR, 117 "Xquartz: Failed to connect to socket: %s - %d - %s", 118 filename, errno, strerror(errno)); 119 close(ret_fd); 120 return -1; 121 } 122 123 return ret_fd; 124} 125 126static void 127send_fd_handoff(int connected_fd, int launchd_fd) 128{ 129 char databuf[] = "display"; 130 struct iovec iov[1]; 131 132 union { 133 struct cmsghdr hdr; 134 char bytes[CMSG_SPACE(sizeof(int))]; 135 } buf; 136 137 struct msghdr msg; 138 struct cmsghdr *cmsg; 139 140 iov[0].iov_base = databuf; 141 iov[0].iov_len = sizeof(databuf); 142 143 msg.msg_iov = iov; 144 msg.msg_iovlen = 1; 145 msg.msg_control = buf.bytes; 146 msg.msg_controllen = sizeof(buf); 147 msg.msg_name = 0; 148 msg.msg_namelen = 0; 149 msg.msg_flags = 0; 150 151 cmsg = CMSG_FIRSTHDR(&msg); 152 cmsg->cmsg_level = SOL_SOCKET; 153 cmsg->cmsg_type = SCM_RIGHTS; 154 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 155 156 msg.msg_controllen = cmsg->cmsg_len; 157 158 *((int *)CMSG_DATA(cmsg)) = launchd_fd; 159 160 if (sendmsg(connected_fd, &msg, 0) < 0) { 161 asl_log(aslc, NULL, ASL_LEVEL_ERR, 162 "Xquartz: Error sending $DISPLAY file descriptor over fd %d: %d -- %s", 163 connected_fd, errno, strerror(errno)); 164 return; 165 } 166 167 asl_log(aslc, NULL, ASL_LEVEL_DEBUG, 168 "Xquartz: Message sent. Closing handoff fd."); 169 close(connected_fd); 170} 171 172__attribute__((__noreturn__)) 173static void 174signal_handler(int sig) 175{ 176 if (x11app_pid) 177 kill(x11app_pid, sig); 178 _exit(0); 179} 180 181int 182main(int argc, char **argv, char **envp) 183{ 184 int envpc; 185 kern_return_t kr; 186 mach_port_t mp; 187 string_array_t newenvp; 188 string_array_t newargv; 189 size_t i; 190 int launchd_fd; 191 string_t handoff_socket_filename; 192 sig_t handler; 193 char *asl_sender; 194 char *asl_facility; 195 char *server_bootstrap_name = kX11AppBundleId; 196 197 if (getenv("X11_PREFS_DOMAIN")) 198 server_bootstrap_name = getenv("X11_PREFS_DOMAIN"); 199 200 asprintf(&asl_sender, "%s.stub", server_bootstrap_name); 201 assert(asl_sender); 202 203 asl_facility = strdup(server_bootstrap_name); 204 assert(asl_facility); 205 if (strcmp(asl_facility + strlen(asl_facility) - 4, ".X11") == 0) 206 asl_facility[strlen(asl_facility) - 4] = '\0'; 207 208 assert(aslc = asl_open(asl_sender, asl_facility, ASL_OPT_NO_DELAY)); 209 free(asl_sender); 210 free(asl_facility); 211 212 /* We don't have a mechanism in place to handle this interrupt driven 213 * server-start notification, so just send the signal now, so xinit doesn't 214 * time out waiting for it and will just poll for the server. 215 */ 216 handler = signal(SIGUSR1, SIG_IGN); 217 if (handler == SIG_IGN) 218 kill(getppid(), SIGUSR1); 219 signal(SIGUSR1, handler); 220 221 /* Pass on SIGs to X11.app */ 222 signal(SIGINT, signal_handler); 223 signal(SIGTERM, signal_handler); 224 225 /* Get the $DISPLAY FD */ 226 launchd_fd = launchd_display_fd(); 227 228 kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp); 229 if (kr != KERN_SUCCESS) { 230 pid_t child; 231 232 asl_log(aslc, NULL, ASL_LEVEL_WARNING, 233 "Xquartz: Unable to locate waiting server: %s", 234 server_bootstrap_name); 235 set_x11_path(); 236 237 char *listenOnlyArg = "--listenonly"; 238 CFStringRef silentLaunchArg = CFStringCreateWithCString(NULL, listenOnlyArg, kCFStringEncodingUTF8); 239 CFStringRef args[] = { silentLaunchArg }; 240 CFArrayRef passArgv = CFArrayCreate(NULL, (const void**) args, 1, NULL); 241 LSApplicationParameters params = { 0, /* CFIndex version == 0 */ 242 kLSLaunchDefaults, /* LSLaunchFlags flags */ 243 &x11_appRef, /* FSRef application */ 244 NULL, /* void* asyncLaunchRefCon*/ 245 NULL, /* CFDictionaryRef environment */ 246 passArgv, /* CFArrayRef arguments */ 247 NULL /* AppleEvent* initialEvent */ 248 }; 249 250 OSStatus status = LSOpenApplication(¶ms, NULL); 251 if (status != noErr) { 252 asl_log(aslc, NULL, ASL_LEVEL_ERR, "Xquartz: Unable to launch: %d", (int)status); 253 return EXIT_FAILURE; 254 } 255 256 /* Try connecting for 10 seconds */ 257 for (i = 0; i < 80; i++) { 258 usleep(250000); 259 kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp); 260 if (kr == KERN_SUCCESS) 261 break; 262 } 263 264 if (kr != KERN_SUCCESS) { 265 asl_log(aslc, NULL, ASL_LEVEL_ERR, 266 "Xquartz: bootstrap_look_up(): %s", bootstrap_strerror(kr)); 267 return EXIT_FAILURE; 268 } 269 } 270 271 /* Get X11.app's pid */ 272 request_pid(mp, &x11app_pid); 273 274 /* Handoff the $DISPLAY FD */ 275 if (launchd_fd != -1) { 276 size_t try, try_max; 277 int handoff_fd = -1; 278 279 for (try = 0, try_max = 5; try < try_max; try++) { 280 if (request_fd_handoff_socket(mp, handoff_socket_filename) != KERN_SUCCESS) { 281 asl_log(aslc, NULL, ASL_LEVEL_INFO, 282 "Xquartz: Failed to request a socket from the server to send the $DISPLAY fd over (try %d of %d)", 283 (int)try + 1, (int)try_max); 284 continue; 285 } 286 287 handoff_fd = connect_to_socket(handoff_socket_filename); 288 if (handoff_fd == -1) { 289 asl_log(aslc, NULL, ASL_LEVEL_ERR, 290 "Xquartz: Failed to connect to socket (try %d of %d)", 291 (int)try + 1, (int)try_max); 292 continue; 293 } 294 295 asl_log(aslc, NULL, ASL_LEVEL_INFO, 296 "Xquartz: Handoff connection established (try %d of %d) on fd %d, \"%s\". Sending message.", 297 (int)try + 1, (int)try_max, handoff_fd, handoff_socket_filename); 298 send_fd_handoff(handoff_fd, launchd_fd); 299 close(handoff_fd); 300 break; 301 } 302 } 303 304 /* Count envp */ 305 for (envpc = 0; envp[envpc]; envpc++) ; 306 307 /* We have fixed-size string lengths due to limitations in IPC, 308 * so we need to copy our argv and envp. 309 */ 310 newargv = (string_array_t)calloc((1 + argc), sizeof(string_t)); 311 newenvp = (string_array_t)calloc((1 + envpc), sizeof(string_t)); 312 313 if (!newargv || !newenvp) { 314 /* Silence the clang static analyzer */ 315 free(newargv); 316 free(newenvp); 317 318 asl_log(aslc, NULL, ASL_LEVEL_ERR, "Xquartz: Memory allocation failure"); 319 return EXIT_FAILURE; 320 } 321 322 for (i = 0; i < argc; i++) { 323 strlcpy(newargv[i], argv[i], STRING_T_SIZE); 324 } 325 for (i = 0; i < envpc; i++) { 326 strlcpy(newenvp[i], envp[i], STRING_T_SIZE); 327 } 328 329 kr = start_x11_server(mp, newargv, argc, newenvp, envpc); 330 331 free(newargv); 332 free(newenvp); 333 334 if (kr != KERN_SUCCESS) { 335 asl_log(aslc, NULL, ASL_LEVEL_ERR, "Xquartz: start_x11_server: %s", 336 mach_error_string(kr)); 337 return EXIT_FAILURE; 338 } 339 return EXIT_SUCCESS; 340} 341