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