1706f2543Smrg/* Copyright (c) 2008 Apple Inc. 2706f2543Smrg * 3706f2543Smrg * Permission is hereby granted, free of charge, to any person 4706f2543Smrg * obtaining a copy of this software and associated documentation files 5706f2543Smrg * (the "Software"), to deal in the Software without restriction, 6706f2543Smrg * including without limitation the rights to use, copy, modify, merge, 7706f2543Smrg * publish, distribute, sublicense, and/or sell copies of the Software, 8706f2543Smrg * and to permit persons to whom the Software is furnished to do so, 9706f2543Smrg * subject to the following conditions: 10706f2543Smrg * 11706f2543Smrg * The above copyright notice and this permission notice shall be 12706f2543Smrg * included in all copies or substantial portions of the Software. 13706f2543Smrg * 14706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15706f2543Smrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16706f2543Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17706f2543Smrg * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 18706f2543Smrg * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19706f2543Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20706f2543Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21706f2543Smrg * DEALINGS IN THE SOFTWARE. 22706f2543Smrg * 23706f2543Smrg * Except as contained in this notice, the name(s) of the above 24706f2543Smrg * copyright holders shall not be used in advertising or otherwise to 25706f2543Smrg * promote the sale, use or other dealings in this Software without 26706f2543Smrg * prior written authorization. 27706f2543Smrg */ 28706f2543Smrg 29706f2543Smrg#include <CoreServices/CoreServices.h> 30706f2543Smrg 31706f2543Smrg#ifdef HAVE_DIX_CONFIG_H 32706f2543Smrg#include <dix-config.h> 33706f2543Smrg#endif 34706f2543Smrg 35706f2543Smrg#include <string.h> 36706f2543Smrg#include <stdio.h> 37706f2543Smrg#include <unistd.h> 38706f2543Smrg#include <errno.h> 39706f2543Smrg 40706f2543Smrg#include <sys/socket.h> 41706f2543Smrg#include <sys/un.h> 42706f2543Smrg 43706f2543Smrg#define kX11AppBundleId LAUNCHD_ID_PREFIX".X11" 44706f2543Smrg#define kX11AppBundlePath "/Contents/MacOS/X11" 45706f2543Smrg 46706f2543Smrgstatic char *server_bootstrap_name = kX11AppBundleId; 47706f2543Smrg 48706f2543Smrg#include <mach/mach.h> 49706f2543Smrg#include <mach/mach_error.h> 50706f2543Smrg#include <servers/bootstrap.h> 51706f2543Smrg#include "mach_startup.h" 52706f2543Smrg 53706f2543Smrg#include <signal.h> 54706f2543Smrg 55706f2543Smrg#include <AvailabilityMacros.h> 56706f2543Smrg 57706f2543Smrg#include "launchd_fd.h" 58706f2543Smrg 59706f2543Smrg#ifndef BUILD_DATE 60706f2543Smrg#define BUILD_DATE "?" 61706f2543Smrg#endif 62706f2543Smrg#ifndef XSERVER_VERSION 63706f2543Smrg#define XSERVER_VERSION "?" 64706f2543Smrg#endif 65706f2543Smrg 66706f2543Smrg#define DEBUG 1 67706f2543Smrg 68706f2543Smrgstatic char x11_path[PATH_MAX + 1]; 69706f2543Smrg 70706f2543Smrgstatic pid_t x11app_pid = 0; 71706f2543Smrg 72706f2543Smrgstatic void set_x11_path(void) { 73706f2543Smrg#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 74706f2543Smrg 75706f2543Smrg CFURLRef appURL = NULL; 76706f2543Smrg OSStatus osstatus = LSFindApplicationForInfo(kLSUnknownCreator, CFSTR(kX11AppBundleId), nil, nil, &appURL); 77706f2543Smrg 78706f2543Smrg switch (osstatus) { 79706f2543Smrg case noErr: 80706f2543Smrg if (appURL == NULL) { 81706f2543Smrg fprintf(stderr, "Xquartz: Invalid response from LSFindApplicationForInfo(%s)\n", 82706f2543Smrg kX11AppBundleId); 83706f2543Smrg exit(1); 84706f2543Smrg } 85706f2543Smrg 86706f2543Smrg if (!CFURLGetFileSystemRepresentation(appURL, true, (unsigned char *)x11_path, sizeof(x11_path))) { 87706f2543Smrg fprintf(stderr, "Xquartz: Error resolving URL for %s\n", kX11AppBundleId); 88706f2543Smrg exit(3); 89706f2543Smrg } 90706f2543Smrg 91706f2543Smrg strlcat(x11_path, kX11AppBundlePath, sizeof(x11_path)); 92706f2543Smrg#ifdef DEBUG 93706f2543Smrg fprintf(stderr, "Xquartz: X11.app = %s\n", x11_path); 94706f2543Smrg#endif 95706f2543Smrg break; 96706f2543Smrg case kLSApplicationNotFoundErr: 97706f2543Smrg fprintf(stderr, "Xquartz: Unable to find application for %s\n", kX11AppBundleId); 98706f2543Smrg exit(10); 99706f2543Smrg default: 100706f2543Smrg fprintf(stderr, "Xquartz: Unable to find application for %s, error code = %d\n", 101706f2543Smrg kX11AppBundleId, (int)osstatus); 102706f2543Smrg exit(11); 103706f2543Smrg } 104706f2543Smrg#else 105706f2543Smrg /* TODO: Make Tiger smarter... but TBH, this should never get called on Tiger... */ 106706f2543Smrg strlcpy(x11_path, "/Applications/Utilities/X11.app/Contents/MacOS/X11", sizeof(x11_path)); 107706f2543Smrg#endif 108706f2543Smrg} 109706f2543Smrg 110706f2543Smrgstatic int connect_to_socket(const char *filename) { 111706f2543Smrg struct sockaddr_un servaddr_un; 112706f2543Smrg struct sockaddr *servaddr; 113706f2543Smrg socklen_t servaddr_len; 114706f2543Smrg int ret_fd; 115706f2543Smrg 116706f2543Smrg /* Setup servaddr_un */ 117706f2543Smrg memset (&servaddr_un, 0, sizeof (struct sockaddr_un)); 118706f2543Smrg servaddr_un.sun_family = AF_UNIX; 119706f2543Smrg strlcpy(servaddr_un.sun_path, filename, sizeof(servaddr_un.sun_path)); 120706f2543Smrg 121706f2543Smrg servaddr = (struct sockaddr *) &servaddr_un; 122706f2543Smrg servaddr_len = sizeof(struct sockaddr_un) - sizeof(servaddr_un.sun_path) + strlen(filename); 123706f2543Smrg 124706f2543Smrg ret_fd = socket(PF_UNIX, SOCK_STREAM, 0); 125706f2543Smrg if(ret_fd == -1) { 126706f2543Smrg fprintf(stderr, "Xquartz: Failed to create socket: %s - %s\n", filename, strerror(errno)); 127706f2543Smrg return -1; 128706f2543Smrg } 129706f2543Smrg 130706f2543Smrg if(connect(ret_fd, servaddr, servaddr_len) < 0) { 131706f2543Smrg fprintf(stderr, "Xquartz: Failed to connect to socket: %s - %d - %s\n", filename, errno, strerror(errno)); 132706f2543Smrg close(ret_fd); 133706f2543Smrg return -1; 134706f2543Smrg } 135706f2543Smrg 136706f2543Smrg return ret_fd; 137706f2543Smrg} 138706f2543Smrg 139706f2543Smrgstatic void send_fd_handoff(int connected_fd, int launchd_fd) { 140706f2543Smrg char databuf[] = "display"; 141706f2543Smrg struct iovec iov[1]; 142706f2543Smrg 143706f2543Smrg union { 144706f2543Smrg struct cmsghdr hdr; 145706f2543Smrg char bytes[CMSG_SPACE(sizeof(int))]; 146706f2543Smrg } buf; 147706f2543Smrg 148706f2543Smrg struct msghdr msg; 149706f2543Smrg struct cmsghdr *cmsg; 150706f2543Smrg 151706f2543Smrg iov[0].iov_base = databuf; 152706f2543Smrg iov[0].iov_len = sizeof(databuf); 153706f2543Smrg 154706f2543Smrg msg.msg_iov = iov; 155706f2543Smrg msg.msg_iovlen = 1; 156706f2543Smrg msg.msg_control = buf.bytes; 157706f2543Smrg msg.msg_controllen = sizeof(buf); 158706f2543Smrg msg.msg_name = 0; 159706f2543Smrg msg.msg_namelen = 0; 160706f2543Smrg msg.msg_flags = 0; 161706f2543Smrg 162706f2543Smrg cmsg = CMSG_FIRSTHDR (&msg); 163706f2543Smrg cmsg->cmsg_level = SOL_SOCKET; 164706f2543Smrg cmsg->cmsg_type = SCM_RIGHTS; 165706f2543Smrg cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 166706f2543Smrg 167706f2543Smrg msg.msg_controllen = cmsg->cmsg_len; 168706f2543Smrg 169706f2543Smrg *((int*)CMSG_DATA(cmsg)) = launchd_fd; 170706f2543Smrg 171706f2543Smrg if(sendmsg(connected_fd, &msg, 0) < 0) { 172706f2543Smrg fprintf(stderr, "Xquartz: Error sending $DISPLAY file descriptor over fd %d: %d -- %s\n", connected_fd, errno, strerror(errno)); 173706f2543Smrg return; 174706f2543Smrg } 175706f2543Smrg 176706f2543Smrg#ifdef DEBUG 177706f2543Smrg fprintf(stderr, "Xquartz: Message sent. Closing handoff fd.\n"); 178706f2543Smrg#endif 179706f2543Smrg 180706f2543Smrg close(connected_fd); 181706f2543Smrg} 182706f2543Smrg 183706f2543Smrgstatic void signal_handler(int sig) { 184706f2543Smrg if(x11app_pid) 185706f2543Smrg kill(x11app_pid, sig); 186706f2543Smrg _exit(0); 187706f2543Smrg} 188706f2543Smrg 189706f2543Smrgint main(int argc, char **argv, char **envp) { 190706f2543Smrg int envpc; 191706f2543Smrg kern_return_t kr; 192706f2543Smrg mach_port_t mp; 193706f2543Smrg string_array_t newenvp; 194706f2543Smrg string_array_t newargv; 195706f2543Smrg size_t i; 196706f2543Smrg int launchd_fd; 197706f2543Smrg string_t handoff_socket_filename; 198706f2543Smrg sig_t handler; 199706f2543Smrg 200706f2543Smrg if(argc == 2 && !strcmp(argv[1], "-version")) { 201706f2543Smrg fprintf(stderr, "X.org Release 7.5\n"); 202706f2543Smrg fprintf(stderr, "X.Org X Server %s\n", XSERVER_VERSION); 203706f2543Smrg fprintf(stderr, "Build Date: %s\n", BUILD_DATE); 204706f2543Smrg return EXIT_SUCCESS; 205706f2543Smrg } 206706f2543Smrg 207706f2543Smrg if(getenv("X11_PREFS_DOMAIN")) 208706f2543Smrg server_bootstrap_name = getenv("X11_PREFS_DOMAIN"); 209706f2543Smrg 210706f2543Smrg /* We don't have a mechanism in place to handle this interrupt driven 211706f2543Smrg * server-start notification, so just send the signal now, so xinit doesn't 212706f2543Smrg * time out waiting for it and will just poll for the server. 213706f2543Smrg */ 214706f2543Smrg handler = signal(SIGUSR1, SIG_IGN); 215706f2543Smrg if(handler == SIG_IGN) 216706f2543Smrg kill(getppid(), SIGUSR1); 217706f2543Smrg signal(SIGUSR1, handler); 218706f2543Smrg 219706f2543Smrg /* Pass on SIGs to X11.app */ 220706f2543Smrg signal(SIGINT, signal_handler); 221706f2543Smrg signal(SIGTERM, signal_handler); 222706f2543Smrg 223706f2543Smrg /* Get the $DISPLAY FD */ 224706f2543Smrg launchd_fd = launchd_display_fd(); 225706f2543Smrg 226706f2543Smrg kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp); 227706f2543Smrg if(kr != KERN_SUCCESS) { 228706f2543Smrg pid_t child; 229706f2543Smrg 230706f2543Smrg fprintf(stderr, "Xquartz: Unable to locate waiting server: %s\n", server_bootstrap_name); 231706f2543Smrg set_x11_path(); 232706f2543Smrg 233706f2543Smrg /* This forking is ugly and will be cleaned up later */ 234706f2543Smrg child = fork(); 235706f2543Smrg if(child == -1) { 236706f2543Smrg fprintf(stderr, "Xquartz: Could not fork: %s\n", strerror(errno)); 237706f2543Smrg return EXIT_FAILURE; 238706f2543Smrg } 239706f2543Smrg 240706f2543Smrg if(child == 0) { 241706f2543Smrg char *_argv[3]; 242706f2543Smrg _argv[0] = x11_path; 243706f2543Smrg _argv[1] = "--listenonly"; 244706f2543Smrg _argv[2] = NULL; 245706f2543Smrg fprintf(stderr, "Xquartz: Starting X server: %s --listenonly\n", x11_path); 246706f2543Smrg return execvp(x11_path, _argv); 247706f2543Smrg } 248706f2543Smrg 249706f2543Smrg /* Try connecting for 10 seconds */ 250706f2543Smrg for(i=0; i < 80; i++) { 251706f2543Smrg usleep(250000); 252706f2543Smrg kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp); 253706f2543Smrg if(kr == KERN_SUCCESS) 254706f2543Smrg break; 255706f2543Smrg } 256706f2543Smrg 257706f2543Smrg if(kr != KERN_SUCCESS) { 258706f2543Smrg#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 259706f2543Smrg fprintf(stderr, "Xquartz: bootstrap_look_up(): %s\n", bootstrap_strerror(kr)); 260706f2543Smrg#else 261706f2543Smrg fprintf(stderr, "Xquartz: bootstrap_look_up(): %ul\n", (unsigned long)kr); 262706f2543Smrg#endif 263706f2543Smrg return EXIT_FAILURE; 264706f2543Smrg } 265706f2543Smrg } 266706f2543Smrg 267706f2543Smrg /* Get X11.app's pid */ 268706f2543Smrg request_pid(mp, &x11app_pid); 269706f2543Smrg 270706f2543Smrg /* Handoff the $DISPLAY FD */ 271706f2543Smrg if(launchd_fd != -1) { 272706f2543Smrg size_t try, try_max; 273706f2543Smrg int handoff_fd = -1; 274706f2543Smrg 275706f2543Smrg for(try=0, try_max=5; try < try_max; try++) { 276706f2543Smrg if(request_fd_handoff_socket(mp, handoff_socket_filename) != KERN_SUCCESS) { 277706f2543Smrg 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); 278706f2543Smrg continue; 279706f2543Smrg } 280706f2543Smrg 281706f2543Smrg handoff_fd = connect_to_socket(handoff_socket_filename); 282706f2543Smrg if(handoff_fd == -1) { 283706f2543Smrg fprintf(stderr, "Xquartz: Failed to connect to socket (try %d of %d)\n", (int)try+1, (int)try_max); 284706f2543Smrg continue; 285706f2543Smrg } 286706f2543Smrg 287706f2543Smrg#ifdef DEBUG 288706f2543Smrg 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); 289706f2543Smrg#endif 290706f2543Smrg 291706f2543Smrg send_fd_handoff(handoff_fd, launchd_fd); 292706f2543Smrg close(handoff_fd); 293706f2543Smrg break; 294706f2543Smrg } 295706f2543Smrg } 296706f2543Smrg 297706f2543Smrg /* Count envp */ 298706f2543Smrg for(envpc=0; envp[envpc]; envpc++); 299706f2543Smrg 300706f2543Smrg /* We have fixed-size string lengths due to limitations in IPC, 301706f2543Smrg * so we need to copy our argv and envp. 302706f2543Smrg */ 303706f2543Smrg newargv = (string_array_t)malloc(argc * sizeof(string_t)); 304706f2543Smrg newenvp = (string_array_t)malloc(envpc * sizeof(string_t)); 305706f2543Smrg 306706f2543Smrg if(!newargv || !newenvp) { 307706f2543Smrg fprintf(stderr, "Xquartz: Memory allocation failure\n"); 308706f2543Smrg return EXIT_FAILURE; 309706f2543Smrg } 310706f2543Smrg 311706f2543Smrg for(i=0; i < argc; i++) { 312706f2543Smrg strlcpy(newargv[i], argv[i], STRING_T_SIZE); 313706f2543Smrg } 314706f2543Smrg for(i=0; i < envpc; i++) { 315706f2543Smrg strlcpy(newenvp[i], envp[i], STRING_T_SIZE); 316706f2543Smrg } 317706f2543Smrg 318706f2543Smrg kr = start_x11_server(mp, newargv, argc, newenvp, envpc); 319706f2543Smrg 320706f2543Smrg free(newargv); 321706f2543Smrg free(newenvp); 322706f2543Smrg 323706f2543Smrg if (kr != KERN_SUCCESS) { 324706f2543Smrg fprintf(stderr, "Xquartz: start_x11_server: %s\n", mach_error_string(kr)); 325706f2543Smrg return EXIT_FAILURE; 326706f2543Smrg } 327706f2543Smrg return EXIT_SUCCESS; 328706f2543Smrg} 329