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