1706f2543Smrg/* main.c -- X application launcher 2706f2543Smrg 3706f2543Smrg Copyright (c) 2007 Jeremy Huddleston 4706f2543Smrg Copyright (c) 2007 Apple Inc 5706f2543Smrg 6706f2543Smrg Permission is hereby granted, free of charge, to any person 7706f2543Smrg obtaining a copy of this software and associated documentation files 8706f2543Smrg (the "Software"), to deal in the Software without restriction, 9706f2543Smrg including without limitation the rights to use, copy, modify, merge, 10706f2543Smrg publish, distribute, sublicense, and/or sell copies of the Software, 11706f2543Smrg and to permit persons to whom the Software is furnished to do so, 12706f2543Smrg subject to the following conditions: 13706f2543Smrg 14706f2543Smrg The above copyright notice and this permission notice shall be 15706f2543Smrg included in all copies or substantial portions of the Software. 16706f2543Smrg 17706f2543Smrg THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18706f2543Smrg EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19706f2543Smrg MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20706f2543Smrg NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 21706f2543Smrg HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22706f2543Smrg WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23706f2543Smrg OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24706f2543Smrg DEALINGS IN THE SOFTWARE. 25706f2543Smrg 26706f2543Smrg Except as contained in this notice, the name(s) of the above 27706f2543Smrg copyright holders shall not be used in advertising or otherwise to 28706f2543Smrg promote the sale, use or other dealings in this Software without 29706f2543Smrg prior written authorization. */ 30706f2543Smrg 31706f2543Smrg#include <CoreFoundation/CoreFoundation.h> 32706f2543Smrg#include <AvailabilityMacros.h> 33706f2543Smrg 34706f2543Smrg#ifdef HAVE_DIX_CONFIG_H 35706f2543Smrg#include <dix-config.h> 36706f2543Smrg#endif 37706f2543Smrg 38706f2543Smrg#include <X11/Xlib.h> 39706f2543Smrg#include <assert.h> 40706f2543Smrg#include <unistd.h> 41706f2543Smrg#include <stdio.h> 42706f2543Smrg#include <string.h> 43706f2543Smrg#include <stdlib.h> 44706f2543Smrg#include <pthread.h> 45706f2543Smrg#include <stdbool.h> 46706f2543Smrg#include <signal.h> 47706f2543Smrg 48706f2543Smrg#include <sys/socket.h> 49706f2543Smrg#include <sys/un.h> 50706f2543Smrg 51706f2543Smrg#include <fcntl.h> 52706f2543Smrg 53706f2543Smrg#include <mach/mach.h> 54706f2543Smrg#include <mach/mach_error.h> 55706f2543Smrg#include <servers/bootstrap.h> 56706f2543Smrg#include "mach_startup.h" 57706f2543Smrg#include "mach_startupServer.h" 58706f2543Smrg 59706f2543Smrg#include "launchd_fd.h" 60706f2543Smrg/* From darwinEvents.c ... but don't want to pull in all the server cruft */ 61706f2543Smrgvoid DarwinListenOnOpenFD(int fd); 62706f2543Smrg 63706f2543Smrgextern int noPanoramiXExtension; 64706f2543Smrg 65706f2543Smrg#define DEFAULT_CLIENT X11BINDIR "/xterm" 66706f2543Smrg#define DEFAULT_STARTX X11BINDIR "/startx" 67706f2543Smrg#define DEFAULT_SHELL "/bin/sh" 68706f2543Smrg 69706f2543Smrg#ifndef BUILD_DATE 70706f2543Smrg#define BUILD_DATE "" 71706f2543Smrg#endif 72706f2543Smrg#ifndef XSERVER_VERSION 73706f2543Smrg#define XSERVER_VERSION "?" 74706f2543Smrg#endif 75706f2543Smrg 76706f2543Smrgstatic char __crashreporter_info_buff__[4096] = {0}; 77706f2543Smrgstatic const char *__crashreporter_info__ __attribute__((__used__)) = &__crashreporter_info_buff__[0]; 78706f2543Smrg#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 79706f2543Smrg// This is actually a toolchain requirement, but I'm not sure the correct check, 80706f2543Smrg// but it should be fine to just only include it for Leopard and later. This line 81706f2543Smrg// just tells the linker to never strip this symbol (such as for space optimization) 82706f2543Smrgasm (".desc ___crashreporter_info__, 0x10"); 83706f2543Smrg#endif 84706f2543Smrg 85706f2543Smrgstatic const char *__crashreporter_info__base = "X.Org X Server " XSERVER_VERSION " Build Date: " BUILD_DATE; 86706f2543Smrg 87706f2543Smrgstatic char *launchd_id_prefix = NULL; 88706f2543Smrgstatic char *server_bootstrap_name = NULL; 89706f2543Smrg 90706f2543Smrg#define DEBUG 1 91706f2543Smrg 92706f2543Smrg/* This is in quartzStartup.c */ 93706f2543Smrgint server_main(int argc, char **argv, char **envp); 94706f2543Smrg 95706f2543Smrgstatic int execute(const char *command); 96706f2543Smrgstatic char *command_from_prefs(const char *key, const char *default_value); 97706f2543Smrg 98706f2543Smrgstatic char *pref_app_to_run; 99706f2543Smrgstatic char *pref_login_shell; 100706f2543Smrgstatic char *pref_startx_script; 101706f2543Smrg 102706f2543Smrg/*** Pthread Magics ***/ 103706f2543Smrgstatic pthread_t create_thread(void *(*func)(void *), void *arg) { 104706f2543Smrg pthread_attr_t attr; 105706f2543Smrg pthread_t tid; 106706f2543Smrg 107706f2543Smrg pthread_attr_init (&attr); 108706f2543Smrg pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); 109706f2543Smrg pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 110706f2543Smrg pthread_create (&tid, &attr, func, arg); 111706f2543Smrg pthread_attr_destroy (&attr); 112706f2543Smrg 113706f2543Smrg return tid; 114706f2543Smrg} 115706f2543Smrg 116706f2543Smrg/*** Mach-O IPC Stuffs ***/ 117706f2543Smrg 118706f2543Smrgunion MaxMsgSize { 119706f2543Smrg union __RequestUnion__mach_startup_subsystem req; 120706f2543Smrg union __ReplyUnion__mach_startup_subsystem rep; 121706f2543Smrg}; 122706f2543Smrg 123706f2543Smrgstatic mach_port_t checkin_or_register(char *bname) { 124706f2543Smrg kern_return_t kr; 125706f2543Smrg mach_port_t mp; 126706f2543Smrg 127706f2543Smrg /* If we're started by launchd or the old mach_init */ 128706f2543Smrg kr = bootstrap_check_in(bootstrap_port, bname, &mp); 129706f2543Smrg if (kr == KERN_SUCCESS) 130706f2543Smrg return mp; 131706f2543Smrg 132706f2543Smrg /* We probably were not started by launchd or the old mach_init */ 133706f2543Smrg kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); 134706f2543Smrg if (kr != KERN_SUCCESS) { 135706f2543Smrg fprintf(stderr, "mach_port_allocate(): %s\n", mach_error_string(kr)); 136706f2543Smrg exit(EXIT_FAILURE); 137706f2543Smrg } 138706f2543Smrg 139706f2543Smrg kr = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); 140706f2543Smrg if (kr != KERN_SUCCESS) { 141706f2543Smrg fprintf(stderr, "mach_port_insert_right(): %s\n", mach_error_string(kr)); 142706f2543Smrg exit(EXIT_FAILURE); 143706f2543Smrg } 144706f2543Smrg 145706f2543Smrg kr = bootstrap_register(bootstrap_port, bname, mp); 146706f2543Smrg if (kr != KERN_SUCCESS) { 147706f2543Smrg fprintf(stderr, "bootstrap_register(): %s\n", mach_error_string(kr)); 148706f2543Smrg exit(EXIT_FAILURE); 149706f2543Smrg } 150706f2543Smrg 151706f2543Smrg return mp; 152706f2543Smrg} 153706f2543Smrg 154706f2543Smrg/*** $DISPLAY handoff ***/ 155706f2543Smrgstatic int accept_fd_handoff(int connected_fd) { 156706f2543Smrg int launchd_fd; 157706f2543Smrg 158706f2543Smrg char databuf[] = "display"; 159706f2543Smrg struct iovec iov[1]; 160706f2543Smrg 161706f2543Smrg union { 162706f2543Smrg struct cmsghdr hdr; 163706f2543Smrg char bytes[CMSG_SPACE(sizeof(int))]; 164706f2543Smrg } buf; 165706f2543Smrg 166706f2543Smrg struct msghdr msg; 167706f2543Smrg struct cmsghdr *cmsg; 168706f2543Smrg 169706f2543Smrg iov[0].iov_base = databuf; 170706f2543Smrg iov[0].iov_len = sizeof(databuf); 171706f2543Smrg 172706f2543Smrg msg.msg_iov = iov; 173706f2543Smrg msg.msg_iovlen = 1; 174706f2543Smrg msg.msg_control = buf.bytes; 175706f2543Smrg msg.msg_controllen = sizeof(buf); 176706f2543Smrg msg.msg_name = 0; 177706f2543Smrg msg.msg_namelen = 0; 178706f2543Smrg msg.msg_flags = 0; 179706f2543Smrg 180706f2543Smrg cmsg = CMSG_FIRSTHDR (&msg); 181706f2543Smrg cmsg->cmsg_level = SOL_SOCKET; 182706f2543Smrg cmsg->cmsg_type = SCM_RIGHTS; 183706f2543Smrg cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 184706f2543Smrg 185706f2543Smrg msg.msg_controllen = cmsg->cmsg_len; 186706f2543Smrg 187706f2543Smrg *((int*)CMSG_DATA(cmsg)) = -1; 188706f2543Smrg 189706f2543Smrg if(recvmsg(connected_fd, &msg, 0) < 0) { 190706f2543Smrg fprintf(stderr, "X11.app: Error receiving $DISPLAY file descriptor. recvmsg() error: %s\n", strerror(errno)); 191706f2543Smrg return -1; 192706f2543Smrg } 193706f2543Smrg 194706f2543Smrg launchd_fd = *((int*)CMSG_DATA(cmsg)); 195706f2543Smrg 196706f2543Smrg return launchd_fd; 197706f2543Smrg} 198706f2543Smrg 199706f2543Smrgtypedef struct { 200706f2543Smrg int fd; 201706f2543Smrg string_t filename; 202706f2543Smrg} socket_handoff_t; 203706f2543Smrg 204706f2543Smrg/* This thread accepts an incoming connection and hands off the file 205706f2543Smrg * descriptor for the new connection to accept_fd_handoff() 206706f2543Smrg */ 207706f2543Smrgstatic void *socket_handoff_thread(void *arg) { 208706f2543Smrg socket_handoff_t *handoff_data = (socket_handoff_t *)arg; 209706f2543Smrg int launchd_fd = -1; 210706f2543Smrg int connected_fd; 211706f2543Smrg 212706f2543Smrg /* Now actually get the passed file descriptor from this connection 213706f2543Smrg * If we encounter an error, keep listening. 214706f2543Smrg */ 215706f2543Smrg while(launchd_fd == -1) { 216706f2543Smrg connected_fd = accept(handoff_data->fd, NULL, NULL); 217706f2543Smrg if(connected_fd == -1) { 218706f2543Smrg fprintf(stderr, "X11.app: Failed to accept incoming connection on socket (fd=%d): %s\n", handoff_data->fd, strerror(errno)); 219706f2543Smrg sleep(2); 220706f2543Smrg continue; 221706f2543Smrg } 222706f2543Smrg 223706f2543Smrg launchd_fd = accept_fd_handoff(connected_fd); 224706f2543Smrg if(launchd_fd == -1) 225706f2543Smrg fprintf(stderr, "X11.app: Error receiving $DISPLAY file descriptor, no descriptor received? Waiting for another connection.\n"); 226706f2543Smrg 227706f2543Smrg close(connected_fd); 228706f2543Smrg } 229706f2543Smrg 230706f2543Smrg close(handoff_data->fd); 231706f2543Smrg unlink(handoff_data->filename); 232706f2543Smrg free(handoff_data); 233706f2543Smrg 234706f2543Smrg fprintf(stderr, "X11.app Handing off fd to server thread via DarwinListenOnOpenFD(%d)\n", launchd_fd); 235706f2543Smrg DarwinListenOnOpenFD(launchd_fd); 236706f2543Smrg 237706f2543Smrg return NULL; 238706f2543Smrg} 239706f2543Smrg 240706f2543Smrgstatic int create_socket(char *filename_out) { 241706f2543Smrg struct sockaddr_un servaddr_un; 242706f2543Smrg struct sockaddr *servaddr; 243706f2543Smrg socklen_t servaddr_len; 244706f2543Smrg int ret_fd; 245706f2543Smrg size_t try, try_max; 246706f2543Smrg 247706f2543Smrg for(try=0, try_max=5; try < try_max; try++) { 248706f2543Smrg tmpnam(filename_out); 249706f2543Smrg 250706f2543Smrg /* Setup servaddr_un */ 251706f2543Smrg memset (&servaddr_un, 0, sizeof (struct sockaddr_un)); 252706f2543Smrg servaddr_un.sun_family = AF_UNIX; 253706f2543Smrg strlcpy(servaddr_un.sun_path, filename_out, sizeof(servaddr_un.sun_path)); 254706f2543Smrg 255706f2543Smrg servaddr = (struct sockaddr *) &servaddr_un; 256706f2543Smrg servaddr_len = sizeof(struct sockaddr_un) - sizeof(servaddr_un.sun_path) + strlen(filename_out); 257706f2543Smrg 258706f2543Smrg ret_fd = socket(PF_UNIX, SOCK_STREAM, 0); 259706f2543Smrg if(ret_fd == -1) { 260706f2543Smrg fprintf(stderr, "X11.app: Failed to create socket (try %d / %d): %s - %s\n", (int)try+1, (int)try_max, filename_out, strerror(errno)); 261706f2543Smrg continue; 262706f2543Smrg } 263706f2543Smrg 264706f2543Smrg if(bind(ret_fd, servaddr, servaddr_len) != 0) { 265706f2543Smrg fprintf(stderr, "X11.app: Failed to bind socket: %d - %s\n", errno, strerror(errno)); 266706f2543Smrg close(ret_fd); 267706f2543Smrg return 0; 268706f2543Smrg } 269706f2543Smrg 270706f2543Smrg if(listen(ret_fd, 10) != 0) { 271706f2543Smrg fprintf(stderr, "X11.app: Failed to listen to socket: %s - %d - %s\n", filename_out, errno, strerror(errno)); 272706f2543Smrg close(ret_fd); 273706f2543Smrg return 0; 274706f2543Smrg } 275706f2543Smrg 276706f2543Smrg#ifdef DEBUG 277706f2543Smrg fprintf(stderr, "X11.app: Listening on socket for fd handoff: (%d) %s\n", ret_fd, filename_out); 278706f2543Smrg#endif 279706f2543Smrg 280706f2543Smrg return ret_fd; 281706f2543Smrg } 282706f2543Smrg 283706f2543Smrg return 0; 284706f2543Smrg} 285706f2543Smrg 286706f2543Smrgstatic int launchd_socket_handed_off = 0; 287706f2543Smrg 288706f2543Smrgkern_return_t do_request_fd_handoff_socket(mach_port_t port, string_t filename) { 289706f2543Smrg socket_handoff_t *handoff_data; 290706f2543Smrg 291706f2543Smrg launchd_socket_handed_off = 1; 292706f2543Smrg 293706f2543Smrg handoff_data = (socket_handoff_t *)calloc(1,sizeof(socket_handoff_t)); 294706f2543Smrg if(!handoff_data) { 295706f2543Smrg fprintf(stderr, "X11.app: Error allocating memory for handoff_data\n"); 296706f2543Smrg return KERN_FAILURE; 297706f2543Smrg } 298706f2543Smrg 299706f2543Smrg handoff_data->fd = create_socket(handoff_data->filename); 300706f2543Smrg if(!handoff_data->fd) { 301706f2543Smrg free(handoff_data); 302706f2543Smrg return KERN_FAILURE; 303706f2543Smrg } 304706f2543Smrg 305706f2543Smrg strlcpy(filename, handoff_data->filename, STRING_T_SIZE); 306706f2543Smrg 307706f2543Smrg create_thread(socket_handoff_thread, handoff_data); 308706f2543Smrg 309706f2543Smrg#ifdef DEBUG 310706f2543Smrg fprintf(stderr, "X11.app: Thread created for handoff. Returning success to tell caller to connect and push the fd.\n"); 311706f2543Smrg#endif 312706f2543Smrg 313706f2543Smrg return KERN_SUCCESS; 314706f2543Smrg} 315706f2543Smrg 316706f2543Smrgkern_return_t do_request_pid(mach_port_t port, int *my_pid) { 317706f2543Smrg *my_pid = getpid(); 318706f2543Smrg return KERN_SUCCESS; 319706f2543Smrg} 320706f2543Smrg 321706f2543Smrg/*** Server Startup ***/ 322706f2543Smrgkern_return_t do_start_x11_server(mach_port_t port, string_array_t argv, 323706f2543Smrg mach_msg_type_number_t argvCnt, 324706f2543Smrg string_array_t envp, 325706f2543Smrg mach_msg_type_number_t envpCnt) { 326706f2543Smrg /* And now back to char ** */ 327706f2543Smrg char **_argv = alloca((argvCnt + 1) * sizeof(char *)); 328706f2543Smrg char **_envp = alloca((envpCnt + 1) * sizeof(char *)); 329706f2543Smrg size_t i; 330706f2543Smrg 331706f2543Smrg /* If we didn't get handed a launchd DISPLAY socket, we should 332706f2543Smrg * unset DISPLAY or we can run into problems with pbproxy 333706f2543Smrg */ 334706f2543Smrg if(!launchd_socket_handed_off) { 335706f2543Smrg fprintf(stderr, "X11.app: No launchd socket handed off, unsetting DISPLAY\n"); 336706f2543Smrg unsetenv("DISPLAY"); 337706f2543Smrg } 338706f2543Smrg 339706f2543Smrg if(!_argv || !_envp) { 340706f2543Smrg return KERN_FAILURE; 341706f2543Smrg } 342706f2543Smrg 343706f2543Smrg fprintf(stderr, "X11.app: do_start_x11_server(): argc=%d\n", argvCnt); 344706f2543Smrg for(i=0; i < argvCnt; i++) { 345706f2543Smrg _argv[i] = argv[i]; 346706f2543Smrg fprintf(stderr, "\targv[%u] = %s\n", (unsigned)i, argv[i]); 347706f2543Smrg } 348706f2543Smrg _argv[argvCnt] = NULL; 349706f2543Smrg 350706f2543Smrg for(i=0; i < envpCnt; i++) { 351706f2543Smrg _envp[i] = envp[i]; 352706f2543Smrg } 353706f2543Smrg _envp[envpCnt] = NULL; 354706f2543Smrg 355706f2543Smrg if(server_main(argvCnt, _argv, _envp) == 0) 356706f2543Smrg return KERN_SUCCESS; 357706f2543Smrg else 358706f2543Smrg return KERN_FAILURE; 359706f2543Smrg} 360706f2543Smrg 361706f2543Smrgstatic int startup_trigger(int argc, char **argv, char **envp) { 362706f2543Smrg Display *display; 363706f2543Smrg const char *s; 364706f2543Smrg 365706f2543Smrg /* Take care of the case where we're called like a normal DDX */ 366706f2543Smrg if(argc > 1 && argv[1][0] == ':') { 367706f2543Smrg size_t i; 368706f2543Smrg kern_return_t kr; 369706f2543Smrg mach_port_t mp; 370706f2543Smrg string_array_t newenvp; 371706f2543Smrg string_array_t newargv; 372706f2543Smrg 373706f2543Smrg /* We need to count envp */ 374706f2543Smrg int envpc; 375706f2543Smrg for(envpc=0; envp[envpc]; envpc++); 376706f2543Smrg 377706f2543Smrg /* We have fixed-size string lengths due to limitations in IPC, 378706f2543Smrg * so we need to copy our argv and envp. 379706f2543Smrg */ 380706f2543Smrg newargv = (string_array_t)alloca(argc * sizeof(string_t)); 381706f2543Smrg newenvp = (string_array_t)alloca(envpc * sizeof(string_t)); 382706f2543Smrg 383706f2543Smrg if(!newargv || !newenvp) { 384706f2543Smrg fprintf(stderr, "Memory allocation failure\n"); 385706f2543Smrg exit(EXIT_FAILURE); 386706f2543Smrg } 387706f2543Smrg 388706f2543Smrg for(i=0; i < argc; i++) { 389706f2543Smrg strlcpy(newargv[i], argv[i], STRING_T_SIZE); 390706f2543Smrg } 391706f2543Smrg for(i=0; i < envpc; i++) { 392706f2543Smrg strlcpy(newenvp[i], envp[i], STRING_T_SIZE); 393706f2543Smrg } 394706f2543Smrg 395706f2543Smrg kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp); 396706f2543Smrg if (kr != KERN_SUCCESS) { 397706f2543Smrg#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 398706f2543Smrg fprintf(stderr, "bootstrap_look_up(%s): %s\n", server_bootstrap_name, bootstrap_strerror(kr)); 399706f2543Smrg#else 400706f2543Smrg fprintf(stderr, "bootstrap_look_up(%s): %ul\n", server_bootstrap_name, (unsigned long)kr); 401706f2543Smrg#endif 402706f2543Smrg exit(EXIT_FAILURE); 403706f2543Smrg } 404706f2543Smrg 405706f2543Smrg kr = start_x11_server(mp, newargv, argc, newenvp, envpc); 406706f2543Smrg if (kr != KERN_SUCCESS) { 407706f2543Smrg fprintf(stderr, "start_x11_server: %s\n", mach_error_string(kr)); 408706f2543Smrg exit(EXIT_FAILURE); 409706f2543Smrg } 410706f2543Smrg exit(EXIT_SUCCESS); 411706f2543Smrg } 412706f2543Smrg 413706f2543Smrg /* If we have a process serial number and it's our only arg, act as if 414706f2543Smrg * the user double clicked the app bundle: launch app_to_run if possible 415706f2543Smrg */ 416706f2543Smrg if(argc == 1 || (argc == 2 && !strncmp(argv[1], "-psn_", 5))) { 417706f2543Smrg /* Now, try to open a display, if so, run the launcher */ 418706f2543Smrg display = XOpenDisplay(NULL); 419706f2543Smrg if(display) { 420706f2543Smrg /* Could open the display, start the launcher */ 421706f2543Smrg XCloseDisplay(display); 422706f2543Smrg 423706f2543Smrg return execute(pref_app_to_run); 424706f2543Smrg } 425706f2543Smrg } 426706f2543Smrg 427706f2543Smrg /* Start the server */ 428706f2543Smrg if((s = getenv("DISPLAY"))) { 429706f2543Smrg fprintf(stderr, "X11.app: Could not connect to server (DISPLAY=\"%s\", unsetting). Starting X server.\n", s); 430706f2543Smrg unsetenv("DISPLAY"); 431706f2543Smrg } else { 432706f2543Smrg fprintf(stderr, "X11.app: Could not connect to server (DISPLAY is not set). Starting X server.\n"); 433706f2543Smrg } 434706f2543Smrg return execute(pref_startx_script); 435706f2543Smrg} 436706f2543Smrg 437706f2543Smrg/** Setup the environment we want our child processes to inherit */ 438706f2543Smrgstatic void ensure_path(const char *dir) { 439706f2543Smrg char buf[1024], *temp; 440706f2543Smrg 441706f2543Smrg /* Make sure /usr/X11/bin is in the $PATH */ 442706f2543Smrg temp = getenv("PATH"); 443706f2543Smrg if(temp == NULL || temp[0] == 0) { 444706f2543Smrg snprintf(buf, sizeof(buf), "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:%s", dir); 445706f2543Smrg setenv("PATH", buf, TRUE); 446706f2543Smrg } else if(strnstr(temp, X11BINDIR, sizeof(temp)) == NULL) { 447706f2543Smrg snprintf(buf, sizeof(buf), "%s:%s", temp, dir); 448706f2543Smrg setenv("PATH", buf, TRUE); 449706f2543Smrg } 450706f2543Smrg} 451706f2543Smrg 452706f2543Smrgstatic void setup_env(void) { 453706f2543Smrg char *temp; 454706f2543Smrg const char *pds = NULL; 455706f2543Smrg const char *disp = getenv("DISPLAY"); 456706f2543Smrg size_t len; 457706f2543Smrg 458706f2543Smrg /* Pass on our prefs domain to startx and its inheritors (mainly for 459706f2543Smrg * quartz-wm and the Xquartz stub's MachIPC) 460706f2543Smrg */ 461706f2543Smrg CFBundleRef bundle = CFBundleGetMainBundle(); 462706f2543Smrg if(bundle) { 463706f2543Smrg CFStringRef pd = CFBundleGetIdentifier(bundle); 464706f2543Smrg if(pd) { 465706f2543Smrg pds = CFStringGetCStringPtr(pd, 0); 466706f2543Smrg } 467706f2543Smrg } 468706f2543Smrg 469706f2543Smrg /* fallback to hardcoded value if we can't discover it */ 470706f2543Smrg if(!pds) { 471706f2543Smrg pds = LAUNCHD_ID_PREFIX".X11"; 472706f2543Smrg } 473706f2543Smrg 474706f2543Smrg server_bootstrap_name = strdup(pds); 475706f2543Smrg if(!server_bootstrap_name) { 476706f2543Smrg fprintf(stderr, "X11.app: Memory allocation error.\n"); 477706f2543Smrg exit(1); 478706f2543Smrg } 479706f2543Smrg setenv("X11_PREFS_DOMAIN", server_bootstrap_name, 1); 480706f2543Smrg 481706f2543Smrg len = strlen(server_bootstrap_name); 482706f2543Smrg launchd_id_prefix = malloc(sizeof(char) * (len - 3)); 483706f2543Smrg if(!launchd_id_prefix) { 484706f2543Smrg fprintf(stderr, "X11.app: Memory allocation error.\n"); 485706f2543Smrg exit(1); 486706f2543Smrg } 487706f2543Smrg strlcpy(launchd_id_prefix, server_bootstrap_name, len - 3); 488706f2543Smrg 489706f2543Smrg /* We need to unset DISPLAY if it is not our socket */ 490706f2543Smrg if(disp) { 491706f2543Smrg /* s = basename(disp) */ 492706f2543Smrg const char *d, *s; 493706f2543Smrg for(s = NULL, d = disp; *d; d++) { 494706f2543Smrg if(*d == '/') 495706f2543Smrg s = d + 1; 496706f2543Smrg } 497706f2543Smrg 498706f2543Smrg if(s && *s) { 499706f2543Smrg if(strcmp(launchd_id_prefix, "org.x") == 0 && strcmp(s, ":0") == 0) { 500706f2543Smrg fprintf(stderr, "X11.app: Detected old style launchd DISPLAY, please update xinit.\n"); 501706f2543Smrg } else { 502706f2543Smrg temp = (char *)malloc(sizeof(char) * len); 503706f2543Smrg if(!temp) { 504706f2543Smrg fprintf(stderr, "X11.app: Memory allocation error creating space for socket name test.\n"); 505706f2543Smrg exit(1); 506706f2543Smrg } 507706f2543Smrg strlcpy(temp, launchd_id_prefix, len); 508706f2543Smrg strlcat(temp, ":0", len); 509706f2543Smrg 510706f2543Smrg if(strcmp(temp, s) != 0) { 511706f2543Smrg /* If we don't have a match, unset it. */ 512706f2543Smrg fprintf(stderr, "X11.app: DISPLAY (\"%s\") does not match our id (\"%s\"), unsetting.\n", disp, launchd_id_prefix); 513706f2543Smrg unsetenv("DISPLAY"); 514706f2543Smrg } 515706f2543Smrg free(temp); 516706f2543Smrg } 517706f2543Smrg } else { 518706f2543Smrg /* The DISPLAY environment variable is not formatted like a launchd socket, so reset. */ 519706f2543Smrg fprintf(stderr, "X11.app: DISPLAY does not look like a launchd set variable, unsetting.\n"); 520706f2543Smrg unsetenv("DISPLAY"); 521706f2543Smrg } 522706f2543Smrg } 523706f2543Smrg 524706f2543Smrg /* Make sure PATH is right */ 525706f2543Smrg ensure_path(X11BINDIR); 526706f2543Smrg 527706f2543Smrg /* cd $HOME */ 528706f2543Smrg temp = getenv("HOME"); 529706f2543Smrg if(temp != NULL && temp[0] != '\0') 530706f2543Smrg chdir(temp); 531706f2543Smrg} 532706f2543Smrg 533706f2543Smrg/*** Main ***/ 534706f2543Smrgint main(int argc, char **argv, char **envp) { 535706f2543Smrg Bool listenOnly = FALSE; 536706f2543Smrg int i; 537706f2543Smrg mach_msg_size_t mxmsgsz = sizeof(union MaxMsgSize) + MAX_TRAILER_SIZE; 538706f2543Smrg mach_port_t mp; 539706f2543Smrg kern_return_t kr; 540706f2543Smrg 541706f2543Smrg /* Setup our environment for our children */ 542706f2543Smrg setup_env(); 543706f2543Smrg 544706f2543Smrg /* The server must not run the PanoramiX operations. */ 545706f2543Smrg noPanoramiXExtension = TRUE; 546706f2543Smrg 547706f2543Smrg /* Setup the initial crasherporter info */ 548706f2543Smrg strlcpy(__crashreporter_info_buff__, __crashreporter_info__base, sizeof(__crashreporter_info_buff__)); 549706f2543Smrg 550706f2543Smrg fprintf(stderr, "X11.app: main(): argc=%d\n", argc); 551706f2543Smrg for(i=0; i < argc; i++) { 552706f2543Smrg fprintf(stderr, "\targv[%u] = %s\n", (unsigned)i, argv[i]); 553706f2543Smrg if(!strcmp(argv[i], "--listenonly")) { 554706f2543Smrg listenOnly = TRUE; 555706f2543Smrg } 556706f2543Smrg } 557706f2543Smrg 558706f2543Smrg mp = checkin_or_register(server_bootstrap_name); 559706f2543Smrg if(mp == MACH_PORT_NULL) { 560706f2543Smrg fprintf(stderr, "NULL mach service: %s", server_bootstrap_name); 561706f2543Smrg return EXIT_FAILURE; 562706f2543Smrg } 563706f2543Smrg 564706f2543Smrg /* Check if we need to do something other than listen, and make another 565706f2543Smrg * thread handle it. 566706f2543Smrg */ 567706f2543Smrg if(!listenOnly) { 568706f2543Smrg pid_t child1, child2; 569706f2543Smrg int status; 570706f2543Smrg 571706f2543Smrg pref_app_to_run = command_from_prefs("app_to_run", DEFAULT_CLIENT); 572706f2543Smrg assert(pref_app_to_run); 573706f2543Smrg 574706f2543Smrg pref_login_shell = command_from_prefs("login_shell", DEFAULT_SHELL); 575706f2543Smrg assert(pref_login_shell); 576706f2543Smrg 577706f2543Smrg pref_startx_script = command_from_prefs("startx_script", DEFAULT_STARTX); 578706f2543Smrg assert(pref_startx_script); 579706f2543Smrg 580706f2543Smrg /* Do the fork-twice trick to avoid having to reap zombies */ 581706f2543Smrg child1 = fork(); 582706f2543Smrg switch (child1) { 583706f2543Smrg case -1: /* error */ 584706f2543Smrg break; 585706f2543Smrg 586706f2543Smrg case 0: /* child1 */ 587706f2543Smrg child2 = fork(); 588706f2543Smrg 589706f2543Smrg switch (child2) { 590706f2543Smrg int max_files, i; 591706f2543Smrg 592706f2543Smrg case -1: /* error */ 593706f2543Smrg break; 594706f2543Smrg 595706f2543Smrg case 0: /* child2 */ 596706f2543Smrg /* close all open files except for standard streams */ 597706f2543Smrg max_files = sysconf(_SC_OPEN_MAX); 598706f2543Smrg for(i = 3; i < max_files; i++) 599706f2543Smrg close(i); 600706f2543Smrg 601706f2543Smrg /* ensure stdin is on /dev/null */ 602706f2543Smrg close(0); 603706f2543Smrg open("/dev/null", O_RDONLY); 604706f2543Smrg 605706f2543Smrg return startup_trigger(argc, argv, envp); 606706f2543Smrg 607706f2543Smrg default: /* parent (child1) */ 608706f2543Smrg _exit(0); 609706f2543Smrg } 610706f2543Smrg break; 611706f2543Smrg 612706f2543Smrg default: /* parent */ 613706f2543Smrg waitpid(child1, &status, 0); 614706f2543Smrg } 615706f2543Smrg 616706f2543Smrg free(pref_app_to_run); 617706f2543Smrg free(pref_login_shell); 618706f2543Smrg free(pref_startx_script); 619706f2543Smrg } 620706f2543Smrg 621706f2543Smrg /* Main event loop */ 622706f2543Smrg fprintf(stderr, "Waiting for startup parameters via Mach IPC.\n"); 623706f2543Smrg kr = mach_msg_server(mach_startup_server, mxmsgsz, mp, 0); 624706f2543Smrg if (kr != KERN_SUCCESS) { 625706f2543Smrg fprintf(stderr, "%s.X11(mp): %s\n", LAUNCHD_ID_PREFIX, mach_error_string(kr)); 626706f2543Smrg return EXIT_FAILURE; 627706f2543Smrg } 628706f2543Smrg 629706f2543Smrg return EXIT_SUCCESS; 630706f2543Smrg} 631706f2543Smrg 632706f2543Smrgstatic int execute(const char *command) { 633706f2543Smrg const char *newargv[4]; 634706f2543Smrg const char **p; 635706f2543Smrg 636706f2543Smrg newargv[0] = pref_login_shell; 637706f2543Smrg newargv[1] = "-c"; 638706f2543Smrg newargv[2] = command; 639706f2543Smrg newargv[3] = NULL; 640706f2543Smrg 641706f2543Smrg fprintf(stderr, "X11.app: Launching %s:\n", command); 642706f2543Smrg for(p=newargv; *p; p++) { 643706f2543Smrg fprintf(stderr, "\targv[%ld] = %s\n", (long int)(p - newargv), *p); 644706f2543Smrg } 645706f2543Smrg 646706f2543Smrg execvp (newargv[0], (char * const *) newargv); 647706f2543Smrg perror ("X11.app: Couldn't exec."); 648706f2543Smrg return 1; 649706f2543Smrg} 650706f2543Smrg 651706f2543Smrgstatic char *command_from_prefs(const char *key, const char *default_value) { 652706f2543Smrg char *command = NULL; 653706f2543Smrg 654706f2543Smrg CFStringRef cfKey; 655706f2543Smrg CFPropertyListRef PlistRef; 656706f2543Smrg 657706f2543Smrg if(!key) 658706f2543Smrg return NULL; 659706f2543Smrg 660706f2543Smrg cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingASCII); 661706f2543Smrg 662706f2543Smrg if(!cfKey) 663706f2543Smrg return NULL; 664706f2543Smrg 665706f2543Smrg PlistRef = CFPreferencesCopyAppValue(cfKey, kCFPreferencesCurrentApplication); 666706f2543Smrg 667706f2543Smrg if ((PlistRef == NULL) || (CFGetTypeID(PlistRef) != CFStringGetTypeID())) { 668706f2543Smrg CFStringRef cfDefaultValue = CFStringCreateWithCString(NULL, default_value, kCFStringEncodingASCII); 669706f2543Smrg int len = strlen(default_value) + 1; 670706f2543Smrg 671706f2543Smrg if(!cfDefaultValue) 672706f2543Smrg goto command_from_prefs_out; 673706f2543Smrg 674706f2543Smrg CFPreferencesSetAppValue(cfKey, cfDefaultValue, kCFPreferencesCurrentApplication); 675706f2543Smrg CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); 676706f2543Smrg CFRelease(cfDefaultValue); 677706f2543Smrg 678706f2543Smrg command = (char *)malloc(len * sizeof(char)); 679706f2543Smrg if(!command) 680706f2543Smrg goto command_from_prefs_out; 681706f2543Smrg strcpy(command, default_value); 682706f2543Smrg } else { 683706f2543Smrg int len = CFStringGetLength((CFStringRef)PlistRef) + 1; 684706f2543Smrg command = (char *)malloc(len * sizeof(char)); 685706f2543Smrg if(!command) 686706f2543Smrg goto command_from_prefs_out; 687706f2543Smrg CFStringGetCString((CFStringRef)PlistRef, command, len, kCFStringEncodingASCII); 688706f2543Smrg } 689706f2543Smrg 690706f2543Smrgcommand_from_prefs_out: 691706f2543Smrg if (PlistRef) 692706f2543Smrg CFRelease(PlistRef); 693706f2543Smrg if(cfKey) 694706f2543Smrg CFRelease(cfKey); 695706f2543Smrg return command; 696706f2543Smrg} 697