1/* main.c -- X application launcher 2 * Copyright (c) 2007 Jeremy Huddleston 3 * Copyright (c) 2007-2012 Apple Inc. All rights reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person 6 * obtaining a copy of this software and associated documentation files 7 * (the "Software"), to deal in the Software without restriction, 8 * including without limitation the rights to use, copy, modify, merge, 9 * publish, distribute, sublicense, and/or sell copies of the Software, 10 * and to permit persons to whom the Software is furnished to do so, 11 * subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be 14 * included in all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 * NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT 20 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Except as contained in this notice, the name(s) of the above 26 * copyright holders shall not be used in advertising or otherwise to 27 * promote the sale, use or other dealings in this Software without 28 * prior written authorization. 29 */ 30 31#include <CoreFoundation/CoreFoundation.h> 32 33#ifdef HAVE_DIX_CONFIG_H 34#include <dix-config.h> 35#endif 36 37#include <X11/Xlib.h> 38#include <assert.h> 39#include <unistd.h> 40#include <stdio.h> 41#include <string.h> 42#include <stdlib.h> 43#include <stdbool.h> 44#include <signal.h> 45 46#include <dispatch/dispatch.h> 47 48#include <sys/socket.h> 49#include <sys/un.h> 50 51#include <fcntl.h> 52 53#include <mach/mach.h> 54#include <mach/mach_error.h> 55#include <servers/bootstrap.h> 56#include "mach_startup.h" 57#include "mach_startupServer.h" 58 59#include <asl.h> 60 61/* From darwinEvents.c ... but don't want to pull in all the server cruft */ 62void 63DarwinListenOnOpenFD(int fd); 64 65extern aslclient aslc; 66 67/* Ditto, from os/log.c */ 68extern void 69ErrorF(const char *f, ...) _X_ATTRIBUTE_PRINTF(1, 2); 70extern void 71FatalError(const char *f, ...) _X_ATTRIBUTE_PRINTF(1, 2) _X_NORETURN; 72 73extern int noPanoramiXExtension; 74 75#ifdef COMPOSITE 76extern Bool noCompositeExtension; 77#endif 78 79#define DEFAULT_CLIENT X11BINDIR "/xterm" 80#define DEFAULT_STARTX X11BINDIR "/startx -- " X11BINDIR "/Xquartz" 81#define DEFAULT_SHELL "/bin/sh" 82 83#define _STRINGIZE(s) #s 84#define STRINGIZE(s) _STRINGIZE(s) 85 86#ifndef XSERVER_VERSION 87#define XSERVER_VERSION "?" 88#endif 89 90static char __crashreporter_info_buff__[4096] = { 0 }; 91static const char *__crashreporter_info__ __attribute__((__used__)) = 92 &__crashreporter_info_buff__[0]; 93// This line just tells the linker to never strip this symbol (such as for space optimization) 94asm (".desc ___crashreporter_info__, 0x10"); 95 96static const char *__crashreporter_info__base = 97 "X.Org X Server " XSERVER_VERSION; 98 99char *bundle_id_prefix = NULL; 100static char *server_bootstrap_name = NULL; 101 102#define DEBUG 1 103 104/* This is in quartzStartup.c */ 105int 106server_main(int argc, char **argv, char **envp); 107 108static int 109execute(const char *command); 110static char * 111command_from_prefs(const char *key, const char *default_value); 112 113static char *pref_app_to_run; 114static char *pref_login_shell; 115static char *pref_startx_script; 116 117 118/*** Mach-O IPC Stuffs ***/ 119 120union MaxMsgSize { 121 union __RequestUnion__mach_startup_subsystem req; 122 union __ReplyUnion__mach_startup_subsystem rep; 123}; 124 125static mach_port_t 126checkin_or_register(char *bname) 127{ 128 kern_return_t kr; 129 mach_port_t mp; 130 131 /* If we're started by launchd or the old mach_init */ 132 kr = bootstrap_check_in(bootstrap_port, bname, &mp); 133 if (kr == KERN_SUCCESS) 134 return mp; 135 136 /* We probably were not started by launchd or the old mach_init */ 137 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); 138 if (kr != KERN_SUCCESS) { 139 ErrorF("mach_port_allocate(): %s\n", mach_error_string(kr)); 140 exit(EXIT_FAILURE); 141 } 142 143 kr = mach_port_insert_right( 144 mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); 145 if (kr != KERN_SUCCESS) { 146 ErrorF("mach_port_insert_right(): %s\n", mach_error_string(kr)); 147 exit(EXIT_FAILURE); 148 } 149 150#ifdef __clang__ 151#pragma clang diagnostic push 152#pragma clang diagnostic ignored "-Wdeprecated-declarations" // bootstrap_register 153#endif 154 kr = bootstrap_register(bootstrap_port, bname, mp); 155#ifdef __clang__ 156#pragma clang diagnostic pop 157#endif 158 159 if (kr != KERN_SUCCESS) { 160 ErrorF("bootstrap_register(): %s\n", mach_error_string(kr)); 161 exit(EXIT_FAILURE); 162 } 163 164 return mp; 165} 166 167/*** $DISPLAY handoff ***/ 168static int 169accept_fd_handoff(int connected_fd) 170{ 171 int launchd_fd; 172 173 char databuf[] = "display"; 174 struct iovec iov[1]; 175 176 union { 177 struct cmsghdr hdr; 178 char bytes[CMSG_SPACE(sizeof(int))]; 179 } buf; 180 181 struct msghdr msg; 182 struct cmsghdr *cmsg; 183 184 iov[0].iov_base = databuf; 185 iov[0].iov_len = sizeof(databuf); 186 187 msg.msg_iov = iov; 188 msg.msg_iovlen = 1; 189 msg.msg_control = buf.bytes; 190 msg.msg_controllen = sizeof(buf); 191 msg.msg_name = 0; 192 msg.msg_namelen = 0; 193 msg.msg_flags = 0; 194 195 cmsg = CMSG_FIRSTHDR(&msg); 196 cmsg->cmsg_level = SOL_SOCKET; 197 cmsg->cmsg_type = SCM_RIGHTS; 198 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 199 200 msg.msg_controllen = cmsg->cmsg_len; 201 202 *((int *)CMSG_DATA(cmsg)) = -1; 203 204 if (recvmsg(connected_fd, &msg, 0) < 0) { 205 ErrorF( 206 "X11.app: Error receiving $DISPLAY file descriptor. recvmsg() error: %s\n", 207 strerror(errno)); 208 return -1; 209 } 210 211 launchd_fd = *((int *)CMSG_DATA(cmsg)); 212 213 return launchd_fd; 214} 215 216typedef struct { 217 int fd; 218 string_t filename; 219} socket_handoff_t; 220 221/* This thread accepts an incoming connection and hands off the file 222 * descriptor for the new connection to accept_fd_handoff() 223 */ 224static void 225socket_handoff(socket_handoff_t *handoff_data) 226{ 227 228 int launchd_fd = -1; 229 int connected_fd; 230 231 /* Now actually get the passed file descriptor from this connection 232 * If we encounter an error, keep listening. 233 */ 234 while (launchd_fd == -1) { 235 connected_fd = accept(handoff_data->fd, NULL, NULL); 236 if (connected_fd == -1) { 237 ErrorF( 238 "X11.app: Failed to accept incoming connection on socket (fd=%d): %s\n", 239 handoff_data->fd, strerror(errno)); 240 sleep(2); 241 continue; 242 } 243 244 launchd_fd = accept_fd_handoff(connected_fd); 245 if (launchd_fd == -1) 246 ErrorF( 247 "X11.app: Error receiving $DISPLAY file descriptor, no descriptor received? Waiting for another connection.\n"); 248 249 close(connected_fd); 250 } 251 252 close(handoff_data->fd); 253 unlink(handoff_data->filename); 254 free(handoff_data); 255 256 ErrorF( 257 "X11.app Handing off fd to server thread via DarwinListenOnOpenFD(%d)\n", 258 launchd_fd); 259 DarwinListenOnOpenFD(launchd_fd); 260 261} 262 263static int 264create_socket(char *filename_out) 265{ 266 struct sockaddr_un servaddr_un; 267 struct sockaddr *servaddr; 268 socklen_t servaddr_len; 269 int ret_fd; 270 size_t try, try_max; 271 272 for (try = 0, try_max = 5; try < try_max; try++) { 273 tmpnam(filename_out); 274 275 /* Setup servaddr_un */ 276 memset(&servaddr_un, 0, sizeof(struct sockaddr_un)); 277 servaddr_un.sun_family = AF_UNIX; 278 strlcpy(servaddr_un.sun_path, filename_out, 279 sizeof(servaddr_un.sun_path)); 280 281 servaddr = (struct sockaddr *)&servaddr_un; 282 servaddr_len = sizeof(struct sockaddr_un) - 283 sizeof(servaddr_un.sun_path) + strlen(filename_out); 284 285 ret_fd = socket(PF_UNIX, SOCK_STREAM, 0); 286 if (ret_fd == -1) { 287 ErrorF( 288 "X11.app: Failed to create socket (try %d / %d): %s - %s\n", 289 (int)try + 1, (int)try_max, filename_out, strerror(errno)); 290 continue; 291 } 292 293 if (bind(ret_fd, servaddr, servaddr_len) != 0) { 294 ErrorF("X11.app: Failed to bind socket: %d - %s\n", errno, 295 strerror( 296 errno)); 297 close(ret_fd); 298 return 0; 299 } 300 301 if (listen(ret_fd, 10) != 0) { 302 ErrorF("X11.app: Failed to listen to socket: %s - %d - %s\n", 303 filename_out, errno, strerror( 304 errno)); 305 close(ret_fd); 306 return 0; 307 } 308 309#ifdef DEBUG 310 ErrorF("X11.app: Listening on socket for fd handoff: (%d) %s\n", 311 ret_fd, 312 filename_out); 313#endif 314 315 return ret_fd; 316 } 317 318 return 0; 319} 320 321static int launchd_socket_handed_off = 0; 322 323kern_return_t 324do_request_fd_handoff_socket(mach_port_t port, string_t filename) 325{ 326 socket_handoff_t *handoff_data; 327 328 launchd_socket_handed_off = 1; 329 330 handoff_data = (socket_handoff_t *)calloc(1, sizeof(socket_handoff_t)); 331 if (!handoff_data) { 332 ErrorF("X11.app: Error allocating memory for handoff_data\n"); 333 return KERN_FAILURE; 334 } 335 336 handoff_data->fd = create_socket(handoff_data->filename); 337 if (!handoff_data->fd) { 338 free(handoff_data); 339 return KERN_FAILURE; 340 } 341 342 strlcpy(filename, handoff_data->filename, STRING_T_SIZE); 343 344 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 345 0), ^ { 346 socket_handoff(handoff_data); 347 }); 348 349#ifdef DEBUG 350 ErrorF( 351 "X11.app: Thread created for handoff. Returning success to tell caller to connect and push the fd.\n"); 352#endif 353 354 return KERN_SUCCESS; 355} 356 357kern_return_t 358do_request_pid(mach_port_t port, int *my_pid) 359{ 360 *my_pid = getpid(); 361 return KERN_SUCCESS; 362} 363 364/*** Server Startup ***/ 365kern_return_t 366do_start_x11_server(mach_port_t port, string_array_t argv, 367 mach_msg_type_number_t argvCnt, 368 string_array_t envp, 369 mach_msg_type_number_t envpCnt) 370{ 371 /* And now back to char ** */ 372 char **_argv = alloca((argvCnt + 1) * sizeof(char *)); 373 char **_envp = alloca((envpCnt + 1) * sizeof(char *)); 374 size_t i; 375 376 /* If we didn't get handed a launchd DISPLAY socket, we should 377 * unset DISPLAY or we can run into problems with pbproxy 378 */ 379 if (!launchd_socket_handed_off) { 380 ErrorF("X11.app: No launchd socket handed off, unsetting DISPLAY\n"); 381 unsetenv("DISPLAY"); 382 } 383 384 if (!_argv || !_envp) { 385 return KERN_FAILURE; 386 } 387 388 ErrorF("X11.app: do_start_x11_server(): argc=%d\n", argvCnt); 389 for (i = 0; i < argvCnt; i++) { 390 _argv[i] = argv[i]; 391 ErrorF("\targv[%u] = %s\n", (unsigned)i, argv[i]); 392 } 393 _argv[argvCnt] = NULL; 394 395 for (i = 0; i < envpCnt; i++) { 396 _envp[i] = envp[i]; 397 } 398 _envp[envpCnt] = NULL; 399 400 if (server_main(argvCnt, _argv, _envp) == 0) 401 return KERN_SUCCESS; 402 else 403 return KERN_FAILURE; 404} 405 406static int 407startup_trigger(int argc, char **argv, char **envp) 408{ 409 Display *display; 410 const char *s; 411 412 /* Take care of the case where we're called like a normal DDX */ 413 if (argc > 1 && argv[1][0] == ':') { 414 size_t i; 415 kern_return_t kr; 416 mach_port_t mp; 417 string_array_t newenvp; 418 string_array_t newargv; 419 420 /* We need to count envp */ 421 int envpc; 422 for (envpc = 0; envp[envpc]; envpc++) ; 423 424 /* We have fixed-size string lengths due to limitations in IPC, 425 * so we need to copy our argv and envp. 426 */ 427 newargv = (string_array_t)alloca(argc * sizeof(string_t)); 428 newenvp = (string_array_t)alloca(envpc * sizeof(string_t)); 429 430 if (!newargv || !newenvp) { 431 ErrorF("Memory allocation failure\n"); 432 exit(EXIT_FAILURE); 433 } 434 435 for (i = 0; i < argc; i++) { 436 strlcpy(newargv[i], argv[i], STRING_T_SIZE); 437 } 438 for (i = 0; i < envpc; i++) { 439 strlcpy(newenvp[i], envp[i], STRING_T_SIZE); 440 } 441 442 kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp); 443 if (kr != KERN_SUCCESS) { 444 ErrorF("bootstrap_look_up(%s): %s\n", server_bootstrap_name, 445 bootstrap_strerror( 446 kr)); 447 exit(EXIT_FAILURE); 448 } 449 450 kr = start_x11_server(mp, newargv, argc, newenvp, envpc); 451 if (kr != KERN_SUCCESS) { 452 ErrorF("start_x11_server: %s\n", mach_error_string(kr)); 453 exit(EXIT_FAILURE); 454 } 455 exit(EXIT_SUCCESS); 456 } 457 458 /* If we have a process serial number and it's our only arg, act as if 459 * the user double clicked the app bundle: launch app_to_run if possible 460 */ 461 if (argc == 1 || (argc == 2 && !strncmp(argv[1], "-psn_", 5))) { 462 /* Now, try to open a display, if so, run the launcher */ 463 display = XOpenDisplay(NULL); 464 if (display) { 465 /* Could open the display, start the launcher */ 466 XCloseDisplay(display); 467 468 return execute(pref_app_to_run); 469 } 470 } 471 472 /* Start the server */ 473 if ((s = getenv("DISPLAY"))) { 474 ErrorF( 475 "X11.app: Could not connect to server (DISPLAY=\"%s\", unsetting). Starting X server.\n", 476 s); 477 unsetenv("DISPLAY"); 478 } 479 else { 480 ErrorF( 481 "X11.app: Could not connect to server (DISPLAY is not set). Starting X server.\n"); 482 } 483 return execute(pref_startx_script); 484} 485 486/** Setup the environment we want our child processes to inherit */ 487static void 488ensure_path(const char *dir) 489{ 490 char buf[1024], *temp; 491 492 /* Make sure /usr/X11/bin is in the $PATH */ 493 temp = getenv("PATH"); 494 if (temp == NULL || temp[0] == 0) { 495 snprintf(buf, sizeof(buf), 496 "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:%s", 497 dir); 498 setenv("PATH", buf, TRUE); 499 } 500 else if (strnstr(temp, X11BINDIR, sizeof(temp)) == NULL) { 501 snprintf(buf, sizeof(buf), "%s:%s", temp, dir); 502 setenv("PATH", buf, TRUE); 503 } 504} 505 506static void 507setup_console_redirect(const char *bundle_id) 508{ 509 char *asl_sender; 510 char *asl_facility; 511 512 asprintf(&asl_sender, "%s.server", bundle_id); 513 assert(asl_sender); 514 515 asl_facility = strdup(bundle_id); 516 assert(asl_facility); 517 if (strcmp(asl_facility + strlen(asl_facility) - 4, ".X11") == 0) 518 asl_facility[strlen(asl_facility) - 4] = '\0'; 519 520 assert(aslc = asl_open(asl_sender, asl_facility, ASL_OPT_NO_DELAY)); 521 free(asl_sender); 522 free(asl_facility); 523 524 asl_set_filter(aslc, ASL_FILTER_MASK_UPTO(ASL_LEVEL_WARNING)); 525 526 asl_log_descriptor(aslc, NULL, ASL_LEVEL_INFO, STDOUT_FILENO, ASL_LOG_DESCRIPTOR_WRITE); 527 asl_log_descriptor(aslc, NULL, ASL_LEVEL_NOTICE, STDERR_FILENO, ASL_LOG_DESCRIPTOR_WRITE); 528} 529 530static void 531setup_env(void) 532{ 533 char *temp; 534 const char *pds = NULL; 535 const char *disp = getenv("DISPLAY"); 536 size_t len; 537 538 /* Pass on our prefs domain to startx and its inheritors (mainly for 539 * quartz-wm and the Xquartz stub's MachIPC) 540 */ 541 CFBundleRef bundle = CFBundleGetMainBundle(); 542 if (bundle) { 543 CFStringRef pd = CFBundleGetIdentifier(bundle); 544 if (pd) { 545 pds = CFStringGetCStringPtr(pd, 0); 546 } 547 } 548 549 /* fallback to hardcoded value if we can't discover it */ 550 if (!pds) { 551 pds = BUNDLE_ID_PREFIX ".X11"; 552 } 553 554 setup_console_redirect(pds); 555 556 server_bootstrap_name = strdup(pds); 557 if (!server_bootstrap_name) { 558 ErrorF("X11.app: Memory allocation error.\n"); 559 exit(1); 560 } 561 setenv("X11_PREFS_DOMAIN", server_bootstrap_name, 1); 562 563 len = strlen(server_bootstrap_name); 564 bundle_id_prefix = malloc(sizeof(char) * (len - 3)); 565 if (!bundle_id_prefix) { 566 ErrorF("X11.app: Memory allocation error.\n"); 567 exit(1); 568 } 569 strlcpy(bundle_id_prefix, server_bootstrap_name, len - 3); 570 571 /* We need to unset DISPLAY if it is not our socket */ 572 if (disp) { 573 /* s = basename(disp) */ 574 const char *d, *s; 575 for (s = NULL, d = disp; *d; d++) { 576 if (*d == '/') 577 s = d + 1; 578 } 579 580 if (s && *s) { 581 if (strcmp(bundle_id_prefix, 582 "org.x") == 0 && strcmp(s, ":0") == 0) { 583 ErrorF( 584 "X11.app: Detected old style launchd DISPLAY, please update xinit.\n"); 585 } 586 else { 587 temp = (char *)malloc(sizeof(char) * len); 588 if (!temp) { 589 ErrorF( 590 "X11.app: Memory allocation error creating space for socket name test.\n"); 591 exit(1); 592 } 593 strlcpy(temp, bundle_id_prefix, len); 594 strlcat(temp, ":0", len); 595 596 if (strcmp(temp, s) != 0) { 597 /* If we don't have a match, unset it. */ 598 ErrorF( 599 "X11.app: DISPLAY (\"%s\") does not match our id (\"%s\"), unsetting.\n", 600 disp, bundle_id_prefix); 601 unsetenv("DISPLAY"); 602 } 603 free(temp); 604 } 605 } 606 else { 607 /* The DISPLAY environment variable is not formatted like a launchd socket, so reset. */ 608 ErrorF( 609 "X11.app: DISPLAY does not look like a launchd set variable, unsetting.\n"); 610 unsetenv("DISPLAY"); 611 } 612 } 613 614 /* Make sure PATH is right */ 615 ensure_path(X11BINDIR); 616 617 /* cd $HOME */ 618 temp = getenv("HOME"); 619 if (temp != NULL && temp[0] != '\0') 620 chdir(temp); 621} 622 623/*** Main ***/ 624int 625main(int argc, char **argv, char **envp) 626{ 627 Bool listenOnly = FALSE; 628 int i; 629 mach_msg_size_t mxmsgsz = sizeof(union MaxMsgSize) + MAX_TRAILER_SIZE; 630 mach_port_t mp; 631 kern_return_t kr; 632 633 /* Ignore SIGPIPE */ 634 signal(SIGPIPE, SIG_IGN); 635 636 /* Setup our environment for our children */ 637 setup_env(); 638 639 /* The server must not run the PanoramiX operations. */ 640 noPanoramiXExtension = TRUE; 641 642#ifdef COMPOSITE 643 /* https://gitlab.freedesktop.org/xorg/xserver/-/issues/1409 */ 644 noCompositeExtension = TRUE; 645#endif 646 647 /* Setup the initial crasherporter info */ 648 strlcpy(__crashreporter_info_buff__, __crashreporter_info__base, 649 sizeof(__crashreporter_info_buff__)); 650 651 ErrorF("X11.app: main(): argc=%d\n", argc); 652 for (i = 0; i < argc; i++) { 653 ErrorF("\targv[%u] = %s\n", (unsigned)i, argv[i]); 654 if (!strcmp(argv[i], "--listenonly")) { 655 listenOnly = TRUE; 656 } 657 } 658 659 mp = checkin_or_register(server_bootstrap_name); 660 if (mp == MACH_PORT_NULL) { 661 ErrorF("NULL mach service: %s", server_bootstrap_name); 662 return EXIT_FAILURE; 663 } 664 665 /* Check if we need to do something other than listen, and make another 666 * thread handle it. 667 */ 668 if (!listenOnly) { 669 pid_t child1, child2; 670 int status; 671 672 pref_app_to_run = command_from_prefs("app_to_run", DEFAULT_CLIENT); 673 assert(pref_app_to_run); 674 675 pref_login_shell = command_from_prefs("login_shell", DEFAULT_SHELL); 676 assert(pref_login_shell); 677 678 pref_startx_script = command_from_prefs("startx_script", 679 DEFAULT_STARTX); 680 assert(pref_startx_script); 681 682 /* Do the fork-twice trick to avoid having to reap zombies */ 683 child1 = fork(); 684 switch (child1) { 685 case -1: /* error */ 686 FatalError("fork() failed: %s\n", strerror(errno)); 687 688 case 0: /* child1 */ 689 child2 = fork(); 690 691 switch (child2) { 692 int max_files; 693 694 case -1: /* error */ 695 FatalError("fork() failed: %s\n", strerror(errno)); 696 697 case 0: /* child2 */ 698 /* close all open files except for standard streams */ 699 max_files = sysconf(_SC_OPEN_MAX); 700 for (i = 3; i < max_files; i++) 701 close(i); 702 703 /* ensure stdin is on /dev/null */ 704 close(0); 705 open("/dev/null", O_RDONLY); 706 707 return startup_trigger(argc, argv, envp); 708 709 default: /* parent (child1) */ 710 _exit(0); 711 } 712 break; 713 714 default: /* parent */ 715 waitpid(child1, &status, 0); 716 } 717 718 free(pref_app_to_run); 719 free(pref_login_shell); 720 free(pref_startx_script); 721 } 722 723 /* Main event loop */ 724 ErrorF("Waiting for startup parameters via Mach IPC.\n"); 725 kr = mach_msg_server(mach_startup_server, mxmsgsz, mp, 0); 726 if (kr != KERN_SUCCESS) { 727 ErrorF("%s.X11(mp): %s\n", BUNDLE_ID_PREFIX, mach_error_string(kr)); 728 return EXIT_FAILURE; 729 } 730 731 return EXIT_SUCCESS; 732} 733 734static int 735execute(const char *command) 736{ 737 const char *newargv[4]; 738 const char **p; 739 740 newargv[0] = pref_login_shell; 741 newargv[1] = "-c"; 742 newargv[2] = command; 743 newargv[3] = NULL; 744 745 ErrorF("X11.app: Launching %s:\n", command); 746 for (p = newargv; *p; p++) { 747 ErrorF("\targv[%ld] = %s\n", (long int)(p - newargv), *p); 748 } 749 750 execvp(newargv[0], (char *const *)newargv); 751 perror("X11.app: Couldn't exec."); 752 return 1; 753} 754 755static char * 756command_from_prefs(const char *key, const char *default_value) 757{ 758 char *command = NULL; 759 760 CFStringRef cfKey; 761 CFPropertyListRef PlistRef; 762 763 if (!key) 764 return NULL; 765 766 cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingASCII); 767 768 if (!cfKey) 769 return NULL; 770 771 PlistRef = CFPreferencesCopyAppValue(cfKey, 772 kCFPreferencesCurrentApplication); 773 774 if ((PlistRef == NULL) || 775 (CFGetTypeID(PlistRef) != CFStringGetTypeID())) { 776 CFStringRef cfDefaultValue = CFStringCreateWithCString( 777 NULL, default_value, kCFStringEncodingASCII); 778 int len = strlen(default_value) + 1; 779 780 if (!cfDefaultValue) 781 goto command_from_prefs_out; 782 783 CFPreferencesSetAppValue(cfKey, cfDefaultValue, 784 kCFPreferencesCurrentApplication); 785 CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); 786 CFRelease(cfDefaultValue); 787 788 command = (char *)malloc(len * sizeof(char)); 789 if (!command) 790 goto command_from_prefs_out; 791 strcpy(command, default_value); 792 } 793 else { 794 int len = CFStringGetLength((CFStringRef)PlistRef) + 1; 795 command = (char *)malloc(len * sizeof(char)); 796 if (!command) 797 goto command_from_prefs_out; 798 CFStringGetCString((CFStringRef)PlistRef, command, len, 799 kCFStringEncodingASCII); 800 } 801 802command_from_prefs_out: 803 if (PlistRef) 804 CFRelease(PlistRef); 805 if (cfKey) 806 CFRelease(cfKey); 807 return command; 808} 809