xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

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 }