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