14642e01fSmrg/* main.c -- X application launcher
235c4bbdfSmrg * Copyright (c) 2007 Jeremy Huddleston
335c4bbdfSmrg * Copyright (c) 2007-2012 Apple Inc. All rights reserved.
435c4bbdfSmrg *
535c4bbdfSmrg * Permission is hereby granted, free of charge, to any person
635c4bbdfSmrg * obtaining a copy of this software and associated documentation files
735c4bbdfSmrg * (the "Software"), to deal in the Software without restriction,
835c4bbdfSmrg * including without limitation the rights to use, copy, modify, merge,
935c4bbdfSmrg * publish, distribute, sublicense, and/or sell copies of the Software,
1035c4bbdfSmrg * and to permit persons to whom the Software is furnished to do so,
1135c4bbdfSmrg * subject to the following conditions:
1235c4bbdfSmrg *
1335c4bbdfSmrg * The above copyright notice and this permission notice shall be
1435c4bbdfSmrg * included in all copies or substantial portions of the Software.
1535c4bbdfSmrg *
1635c4bbdfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1735c4bbdfSmrg * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1835c4bbdfSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1935c4bbdfSmrg * NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
2035c4bbdfSmrg * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
2135c4bbdfSmrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2235c4bbdfSmrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2335c4bbdfSmrg * DEALINGS IN THE SOFTWARE.
2435c4bbdfSmrg *
2535c4bbdfSmrg * Except as contained in this notice, the name(s) of the above
2635c4bbdfSmrg * copyright holders shall not be used in advertising or otherwise to
2735c4bbdfSmrg * promote the sale, use or other dealings in this Software without
2835c4bbdfSmrg * prior written authorization.
2935c4bbdfSmrg */
304642e01fSmrg
314642e01fSmrg#include <CoreFoundation/CoreFoundation.h>
324642e01fSmrg
336747b715Smrg#ifdef HAVE_DIX_CONFIG_H
346747b715Smrg#include <dix-config.h>
356747b715Smrg#endif
366747b715Smrg
374642e01fSmrg#include <X11/Xlib.h>
389ace9065Smrg#include <assert.h>
394642e01fSmrg#include <unistd.h>
404642e01fSmrg#include <stdio.h>
414642e01fSmrg#include <string.h>
424642e01fSmrg#include <stdlib.h>
434642e01fSmrg#include <stdbool.h>
444642e01fSmrg#include <signal.h>
454642e01fSmrg
4635c4bbdfSmrg#include <dispatch/dispatch.h>
4735c4bbdfSmrg
484642e01fSmrg#include <sys/socket.h>
494642e01fSmrg#include <sys/un.h>
504642e01fSmrg
516747b715Smrg#include <fcntl.h>
524642e01fSmrg
534642e01fSmrg#include <mach/mach.h>
544642e01fSmrg#include <mach/mach_error.h>
554642e01fSmrg#include <servers/bootstrap.h>
564642e01fSmrg#include "mach_startup.h"
574642e01fSmrg#include "mach_startupServer.h"
584642e01fSmrg
59c8548ba8Smrg#include <asl.h>
6035c4bbdfSmrg
614642e01fSmrg/* From darwinEvents.c ... but don't want to pull in all the server cruft */
6235c4bbdfSmrgvoid
6335c4bbdfSmrgDarwinListenOnOpenFD(int fd);
6435c4bbdfSmrg
6535c4bbdfSmrgextern aslclient aslc;
6635c4bbdfSmrg
6735c4bbdfSmrg/* Ditto, from os/log.c */
6835c4bbdfSmrgextern void
6935c4bbdfSmrgErrorF(const char *f, ...) _X_ATTRIBUTE_PRINTF(1, 2);
7035c4bbdfSmrgextern void
7135c4bbdfSmrgFatalError(const char *f, ...) _X_ATTRIBUTE_PRINTF(1, 2) _X_NORETURN;
724642e01fSmrg
734642e01fSmrgextern int noPanoramiXExtension;
744642e01fSmrg
75a1e1cf94Smrg#ifdef COMPOSITE
76a1e1cf94Smrgextern Bool noCompositeExtension;
77a1e1cf94Smrg#endif
78a1e1cf94Smrg
794642e01fSmrg#define DEFAULT_CLIENT X11BINDIR "/xterm"
8035c4bbdfSmrg#define DEFAULT_STARTX X11BINDIR "/startx -- " X11BINDIR "/Xquartz"
814642e01fSmrg#define DEFAULT_SHELL  "/bin/sh"
824642e01fSmrg
83ed6184dfSmrg#define _STRINGIZE(s) #s
84ed6184dfSmrg#define STRINGIZE(s) _STRINGIZE(s)
85ed6184dfSmrg
864642e01fSmrg#ifndef XSERVER_VERSION
874642e01fSmrg#define XSERVER_VERSION "?"
884642e01fSmrg#endif
894642e01fSmrg
9035c4bbdfSmrgstatic char __crashreporter_info_buff__[4096] = { 0 };
9135c4bbdfSmrgstatic const char *__crashreporter_info__ __attribute__((__used__)) =
9235c4bbdfSmrg    &__crashreporter_info_buff__[0];
93c8548ba8Smrg// This line just tells the linker to never strip this symbol (such as for space optimization)
946747b715Smrgasm (".desc ___crashreporter_info__, 0x10");
956747b715Smrg
9635c4bbdfSmrgstatic const char *__crashreporter_info__base =
97ed6184dfSmrg    "X.Org X Server " XSERVER_VERSION;
984642e01fSmrg
9935c4bbdfSmrgchar *bundle_id_prefix = NULL;
1006747b715Smrgstatic char *server_bootstrap_name = NULL;
1014642e01fSmrg
1024642e01fSmrg#define DEBUG 1
1034642e01fSmrg
1044642e01fSmrg/* This is in quartzStartup.c */
10535c4bbdfSmrgint
10635c4bbdfSmrgserver_main(int argc, char **argv, char **envp);
1074642e01fSmrg
10835c4bbdfSmrgstatic int
10935c4bbdfSmrgexecute(const char *command);
11035c4bbdfSmrgstatic char *
11135c4bbdfSmrgcommand_from_prefs(const char *key, const char *default_value);
1124642e01fSmrg
1139ace9065Smrgstatic char *pref_app_to_run;
1149ace9065Smrgstatic char *pref_login_shell;
1159ace9065Smrgstatic char *pref_startx_script;
1169ace9065Smrg
1174642e01fSmrg
1184642e01fSmrg/*** Mach-O IPC Stuffs ***/
1194642e01fSmrg
1204642e01fSmrgunion MaxMsgSize {
12135c4bbdfSmrg    union __RequestUnion__mach_startup_subsystem req;
12235c4bbdfSmrg    union __ReplyUnion__mach_startup_subsystem rep;
1234642e01fSmrg};
1244642e01fSmrg
12535c4bbdfSmrgstatic mach_port_t
12635c4bbdfSmrgcheckin_or_register(char *bname)
12735c4bbdfSmrg{
1284642e01fSmrg    kern_return_t kr;
1294642e01fSmrg    mach_port_t mp;
1304642e01fSmrg
1314642e01fSmrg    /* If we're started by launchd or the old mach_init */
1324642e01fSmrg    kr = bootstrap_check_in(bootstrap_port, bname, &mp);
1334642e01fSmrg    if (kr == KERN_SUCCESS)
1344642e01fSmrg        return mp;
1354642e01fSmrg
1364642e01fSmrg    /* We probably were not started by launchd or the old mach_init */
1374642e01fSmrg    kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
1384642e01fSmrg    if (kr != KERN_SUCCESS) {
13935c4bbdfSmrg        ErrorF("mach_port_allocate(): %s\n", mach_error_string(kr));
1404642e01fSmrg        exit(EXIT_FAILURE);
1414642e01fSmrg    }
1424642e01fSmrg
14335c4bbdfSmrg    kr = mach_port_insert_right(
14435c4bbdfSmrg        mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND);
1454642e01fSmrg    if (kr != KERN_SUCCESS) {
14635c4bbdfSmrg        ErrorF("mach_port_insert_right(): %s\n", mach_error_string(kr));
1474642e01fSmrg        exit(EXIT_FAILURE);
1484642e01fSmrg    }
1494642e01fSmrg
15035c4bbdfSmrg#ifdef __clang__
15135c4bbdfSmrg#pragma clang diagnostic push
15235c4bbdfSmrg#pragma clang diagnostic ignored "-Wdeprecated-declarations" // bootstrap_register
15335c4bbdfSmrg#endif
1544642e01fSmrg    kr = bootstrap_register(bootstrap_port, bname, mp);
15535c4bbdfSmrg#ifdef __clang__
15635c4bbdfSmrg#pragma clang diagnostic pop
15735c4bbdfSmrg#endif
15835c4bbdfSmrg
1594642e01fSmrg    if (kr != KERN_SUCCESS) {
16035c4bbdfSmrg        ErrorF("bootstrap_register(): %s\n", mach_error_string(kr));
1614642e01fSmrg        exit(EXIT_FAILURE);
1624642e01fSmrg    }
1634642e01fSmrg
1644642e01fSmrg    return mp;
1654642e01fSmrg}
1664642e01fSmrg
1674642e01fSmrg/*** $DISPLAY handoff ***/
16835c4bbdfSmrgstatic int
16935c4bbdfSmrgaccept_fd_handoff(int connected_fd)
17035c4bbdfSmrg{
1714642e01fSmrg    int launchd_fd;
17235c4bbdfSmrg
1734642e01fSmrg    char databuf[] = "display";
1744642e01fSmrg    struct iovec iov[1];
17535c4bbdfSmrg
1764642e01fSmrg    union {
1774642e01fSmrg        struct cmsghdr hdr;
1784642e01fSmrg        char bytes[CMSG_SPACE(sizeof(int))];
1794642e01fSmrg    } buf;
18035c4bbdfSmrg
1814642e01fSmrg    struct msghdr msg;
1826747b715Smrg    struct cmsghdr *cmsg;
1836747b715Smrg
1846747b715Smrg    iov[0].iov_base = databuf;
18535c4bbdfSmrg    iov[0].iov_len = sizeof(databuf);
18635c4bbdfSmrg
1874642e01fSmrg    msg.msg_iov = iov;
1884642e01fSmrg    msg.msg_iovlen = 1;
1894642e01fSmrg    msg.msg_control = buf.bytes;
1904642e01fSmrg    msg.msg_controllen = sizeof(buf);
1914642e01fSmrg    msg.msg_name = 0;
1924642e01fSmrg    msg.msg_namelen = 0;
1934642e01fSmrg    msg.msg_flags = 0;
19435c4bbdfSmrg
19535c4bbdfSmrg    cmsg = CMSG_FIRSTHDR(&msg);
1964642e01fSmrg    cmsg->cmsg_level = SOL_SOCKET;
1974642e01fSmrg    cmsg->cmsg_type = SCM_RIGHTS;
1984642e01fSmrg    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
19935c4bbdfSmrg
2004642e01fSmrg    msg.msg_controllen = cmsg->cmsg_len;
20135c4bbdfSmrg
20235c4bbdfSmrg    *((int *)CMSG_DATA(cmsg)) = -1;
20335c4bbdfSmrg
20435c4bbdfSmrg    if (recvmsg(connected_fd, &msg, 0) < 0) {
20535c4bbdfSmrg        ErrorF(
20635c4bbdfSmrg            "X11.app: Error receiving $DISPLAY file descriptor.  recvmsg() error: %s\n",
20735c4bbdfSmrg            strerror(errno));
2084642e01fSmrg        return -1;
2094642e01fSmrg    }
21035c4bbdfSmrg
21135c4bbdfSmrg    launchd_fd = *((int *)CMSG_DATA(cmsg));
21235c4bbdfSmrg
2134642e01fSmrg    return launchd_fd;
2144642e01fSmrg}
2154642e01fSmrg
2164642e01fSmrgtypedef struct {
2174642e01fSmrg    int fd;
2184642e01fSmrg    string_t filename;
2194642e01fSmrg} socket_handoff_t;
2204642e01fSmrg
2214642e01fSmrg/* This thread accepts an incoming connection and hands off the file
2224642e01fSmrg * descriptor for the new connection to accept_fd_handoff()
2234642e01fSmrg */
22435c4bbdfSmrgstatic void
22535c4bbdfSmrgsocket_handoff(socket_handoff_t *handoff_data)
22635c4bbdfSmrg{
22735c4bbdfSmrg
2284642e01fSmrg    int launchd_fd = -1;
2294642e01fSmrg    int connected_fd;
2304642e01fSmrg
2314642e01fSmrg    /* Now actually get the passed file descriptor from this connection
2324642e01fSmrg     * If we encounter an error, keep listening.
2334642e01fSmrg     */
23435c4bbdfSmrg    while (launchd_fd == -1) {
2354642e01fSmrg        connected_fd = accept(handoff_data->fd, NULL, NULL);
23635c4bbdfSmrg        if (connected_fd == -1) {
23735c4bbdfSmrg            ErrorF(
23835c4bbdfSmrg                "X11.app: Failed to accept incoming connection on socket (fd=%d): %s\n",
23935c4bbdfSmrg                handoff_data->fd, strerror(errno));
2404642e01fSmrg            sleep(2);
2414642e01fSmrg            continue;
2424642e01fSmrg        }
2434642e01fSmrg
2444642e01fSmrg        launchd_fd = accept_fd_handoff(connected_fd);
24535c4bbdfSmrg        if (launchd_fd == -1)
24635c4bbdfSmrg            ErrorF(
24735c4bbdfSmrg                "X11.app: Error receiving $DISPLAY file descriptor, no descriptor received?  Waiting for another connection.\n");
2484642e01fSmrg
2494642e01fSmrg        close(connected_fd);
2504642e01fSmrg    }
2514642e01fSmrg
2524642e01fSmrg    close(handoff_data->fd);
2534642e01fSmrg    unlink(handoff_data->filename);
2544642e01fSmrg    free(handoff_data);
25535c4bbdfSmrg
25635c4bbdfSmrg    ErrorF(
25735c4bbdfSmrg        "X11.app Handing off fd to server thread via DarwinListenOnOpenFD(%d)\n",
25835c4bbdfSmrg        launchd_fd);
2594642e01fSmrg    DarwinListenOnOpenFD(launchd_fd);
2609ace9065Smrg
2614642e01fSmrg}
2624642e01fSmrg
26335c4bbdfSmrgstatic int
26435c4bbdfSmrgcreate_socket(char *filename_out)
26535c4bbdfSmrg{
2664642e01fSmrg    struct sockaddr_un servaddr_un;
2674642e01fSmrg    struct sockaddr *servaddr;
2684642e01fSmrg    socklen_t servaddr_len;
2694642e01fSmrg    int ret_fd;
2704642e01fSmrg    size_t try, try_max;
27135c4bbdfSmrg
27235c4bbdfSmrg    for (try = 0, try_max = 5; try < try_max; try++) {
2734642e01fSmrg        tmpnam(filename_out);
27435c4bbdfSmrg
2754642e01fSmrg        /* Setup servaddr_un */
27635c4bbdfSmrg        memset(&servaddr_un, 0, sizeof(struct sockaddr_un));
2774642e01fSmrg        servaddr_un.sun_family = AF_UNIX;
27835c4bbdfSmrg        strlcpy(servaddr_un.sun_path, filename_out,
27935c4bbdfSmrg                sizeof(servaddr_un.sun_path));
28035c4bbdfSmrg
28135c4bbdfSmrg        servaddr = (struct sockaddr *)&servaddr_un;
28235c4bbdfSmrg        servaddr_len = sizeof(struct sockaddr_un) -
28335c4bbdfSmrg                       sizeof(servaddr_un.sun_path) + strlen(filename_out);
28435c4bbdfSmrg
2854642e01fSmrg        ret_fd = socket(PF_UNIX, SOCK_STREAM, 0);
28635c4bbdfSmrg        if (ret_fd == -1) {
28735c4bbdfSmrg            ErrorF(
28835c4bbdfSmrg                "X11.app: Failed to create socket (try %d / %d): %s - %s\n",
28935c4bbdfSmrg                (int)try + 1, (int)try_max, filename_out, strerror(errno));
2904642e01fSmrg            continue;
2914642e01fSmrg        }
29235c4bbdfSmrg
29335c4bbdfSmrg        if (bind(ret_fd, servaddr, servaddr_len) != 0) {
29435c4bbdfSmrg            ErrorF("X11.app: Failed to bind socket: %d - %s\n", errno,
29535c4bbdfSmrg                   strerror(
29635c4bbdfSmrg                       errno));
2974642e01fSmrg            close(ret_fd);
2984642e01fSmrg            return 0;
2994642e01fSmrg        }
30035c4bbdfSmrg
30135c4bbdfSmrg        if (listen(ret_fd, 10) != 0) {
30235c4bbdfSmrg            ErrorF("X11.app: Failed to listen to socket: %s - %d - %s\n",
30335c4bbdfSmrg                   filename_out, errno, strerror(
30435c4bbdfSmrg                       errno));
3054642e01fSmrg            close(ret_fd);
3064642e01fSmrg            return 0;
3074642e01fSmrg        }
30835c4bbdfSmrg
3094642e01fSmrg#ifdef DEBUG
31035c4bbdfSmrg        ErrorF("X11.app: Listening on socket for fd handoff:  (%d) %s\n",
31135c4bbdfSmrg               ret_fd,
31235c4bbdfSmrg               filename_out);
3134642e01fSmrg#endif
31435c4bbdfSmrg
3154642e01fSmrg        return ret_fd;
3164642e01fSmrg    }
31735c4bbdfSmrg
3184642e01fSmrg    return 0;
3194642e01fSmrg}
3204642e01fSmrg
3214642e01fSmrgstatic int launchd_socket_handed_off = 0;
3224642e01fSmrg
32335c4bbdfSmrgkern_return_t
32435c4bbdfSmrgdo_request_fd_handoff_socket(mach_port_t port, string_t filename)
32535c4bbdfSmrg{
3264642e01fSmrg    socket_handoff_t *handoff_data;
32735c4bbdfSmrg
3284642e01fSmrg    launchd_socket_handed_off = 1;
3294642e01fSmrg
33035c4bbdfSmrg    handoff_data = (socket_handoff_t *)calloc(1, sizeof(socket_handoff_t));
33135c4bbdfSmrg    if (!handoff_data) {
33235c4bbdfSmrg        ErrorF("X11.app: Error allocating memory for handoff_data\n");
3334642e01fSmrg        return KERN_FAILURE;
3344642e01fSmrg    }
3354642e01fSmrg
3364642e01fSmrg    handoff_data->fd = create_socket(handoff_data->filename);
33735c4bbdfSmrg    if (!handoff_data->fd) {
3386747b715Smrg        free(handoff_data);
3394642e01fSmrg        return KERN_FAILURE;
3404642e01fSmrg    }
3414642e01fSmrg
3424642e01fSmrg    strlcpy(filename, handoff_data->filename, STRING_T_SIZE);
34335c4bbdfSmrg
34435c4bbdfSmrg    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
34535c4bbdfSmrg                                             0), ^ {
34635c4bbdfSmrg                       socket_handoff(handoff_data);
34735c4bbdfSmrg                   });
34835c4bbdfSmrg
3494642e01fSmrg#ifdef DEBUG
35035c4bbdfSmrg    ErrorF(
35135c4bbdfSmrg        "X11.app: Thread created for handoff.  Returning success to tell caller to connect and push the fd.\n");
3524642e01fSmrg#endif
3534642e01fSmrg
3544642e01fSmrg    return KERN_SUCCESS;
3554642e01fSmrg}
3564642e01fSmrg
35735c4bbdfSmrgkern_return_t
35835c4bbdfSmrgdo_request_pid(mach_port_t port, int *my_pid)
35935c4bbdfSmrg{
3604642e01fSmrg    *my_pid = getpid();
3614642e01fSmrg    return KERN_SUCCESS;
3624642e01fSmrg}
3634642e01fSmrg
3644642e01fSmrg/*** Server Startup ***/
36535c4bbdfSmrgkern_return_t
36635c4bbdfSmrgdo_start_x11_server(mach_port_t port, string_array_t argv,
36735c4bbdfSmrg                    mach_msg_type_number_t argvCnt,
36835c4bbdfSmrg                    string_array_t envp,
36935c4bbdfSmrg                    mach_msg_type_number_t envpCnt)
37035c4bbdfSmrg{
3714642e01fSmrg    /* And now back to char ** */
3724642e01fSmrg    char **_argv = alloca((argvCnt + 1) * sizeof(char *));
3734642e01fSmrg    char **_envp = alloca((envpCnt + 1) * sizeof(char *));
3744642e01fSmrg    size_t i;
37535c4bbdfSmrg
3766747b715Smrg    /* If we didn't get handed a launchd DISPLAY socket, we should
3774642e01fSmrg     * unset DISPLAY or we can run into problems with pbproxy
3784642e01fSmrg     */
37935c4bbdfSmrg    if (!launchd_socket_handed_off) {
38035c4bbdfSmrg        ErrorF("X11.app: No launchd socket handed off, unsetting DISPLAY\n");
3814642e01fSmrg        unsetenv("DISPLAY");
3826747b715Smrg    }
38335c4bbdfSmrg
38435c4bbdfSmrg    if (!_argv || !_envp) {
3854642e01fSmrg        return KERN_FAILURE;
3864642e01fSmrg    }
3874642e01fSmrg
38835c4bbdfSmrg    ErrorF("X11.app: do_start_x11_server(): argc=%d\n", argvCnt);
38935c4bbdfSmrg    for (i = 0; i < argvCnt; i++) {
3904642e01fSmrg        _argv[i] = argv[i];
39135c4bbdfSmrg        ErrorF("\targv[%u] = %s\n", (unsigned)i, argv[i]);
3924642e01fSmrg    }
3934642e01fSmrg    _argv[argvCnt] = NULL;
39435c4bbdfSmrg
39535c4bbdfSmrg    for (i = 0; i < envpCnt; i++) {
3964642e01fSmrg        _envp[i] = envp[i];
3974642e01fSmrg    }
3984642e01fSmrg    _envp[envpCnt] = NULL;
39935c4bbdfSmrg
40035c4bbdfSmrg    if (server_main(argvCnt, _argv, _envp) == 0)
4014642e01fSmrg        return KERN_SUCCESS;
4024642e01fSmrg    else
4034642e01fSmrg        return KERN_FAILURE;
4044642e01fSmrg}
4054642e01fSmrg
40635c4bbdfSmrgstatic int
40735c4bbdfSmrgstartup_trigger(int argc, char **argv, char **envp)
40835c4bbdfSmrg{
4094642e01fSmrg    Display *display;
4104642e01fSmrg    const char *s;
41135c4bbdfSmrg
4124642e01fSmrg    /* Take care of the case where we're called like a normal DDX */
41335c4bbdfSmrg    if (argc > 1 && argv[1][0] == ':') {
4144642e01fSmrg        size_t i;
4154642e01fSmrg        kern_return_t kr;
4164642e01fSmrg        mach_port_t mp;
4174642e01fSmrg        string_array_t newenvp;
4184642e01fSmrg        string_array_t newargv;
4194642e01fSmrg
4204642e01fSmrg        /* We need to count envp */
4214642e01fSmrg        int envpc;
42235c4bbdfSmrg        for (envpc = 0; envp[envpc]; envpc++) ;
4234642e01fSmrg
4244642e01fSmrg        /* We have fixed-size string lengths due to limitations in IPC,
4254642e01fSmrg         * so we need to copy our argv and envp.
4264642e01fSmrg         */
4274642e01fSmrg        newargv = (string_array_t)alloca(argc * sizeof(string_t));
4284642e01fSmrg        newenvp = (string_array_t)alloca(envpc * sizeof(string_t));
42935c4bbdfSmrg
43035c4bbdfSmrg        if (!newargv || !newenvp) {
43135c4bbdfSmrg            ErrorF("Memory allocation failure\n");
4324642e01fSmrg            exit(EXIT_FAILURE);
4334642e01fSmrg        }
43435c4bbdfSmrg
43535c4bbdfSmrg        for (i = 0; i < argc; i++) {
4364642e01fSmrg            strlcpy(newargv[i], argv[i], STRING_T_SIZE);
4374642e01fSmrg        }
43835c4bbdfSmrg        for (i = 0; i < envpc; i++) {
4394642e01fSmrg            strlcpy(newenvp[i], envp[i], STRING_T_SIZE);
4404642e01fSmrg        }
4414642e01fSmrg
4424642e01fSmrg        kr = bootstrap_look_up(bootstrap_port, server_bootstrap_name, &mp);
4434642e01fSmrg        if (kr != KERN_SUCCESS) {
44435c4bbdfSmrg            ErrorF("bootstrap_look_up(%s): %s\n", server_bootstrap_name,
44535c4bbdfSmrg                   bootstrap_strerror(
44635c4bbdfSmrg                       kr));
4474642e01fSmrg            exit(EXIT_FAILURE);
4484642e01fSmrg        }
4494642e01fSmrg
4504642e01fSmrg        kr = start_x11_server(mp, newargv, argc, newenvp, envpc);
4514642e01fSmrg        if (kr != KERN_SUCCESS) {
45235c4bbdfSmrg            ErrorF("start_x11_server: %s\n", mach_error_string(kr));
4534642e01fSmrg            exit(EXIT_FAILURE);
4544642e01fSmrg        }
4554642e01fSmrg        exit(EXIT_SUCCESS);
4564642e01fSmrg    }
4574642e01fSmrg
4584642e01fSmrg    /* If we have a process serial number and it's our only arg, act as if
4594642e01fSmrg     * the user double clicked the app bundle: launch app_to_run if possible
4604642e01fSmrg     */
46135c4bbdfSmrg    if (argc == 1 || (argc == 2 && !strncmp(argv[1], "-psn_", 5))) {
4624642e01fSmrg        /* Now, try to open a display, if so, run the launcher */
4634642e01fSmrg        display = XOpenDisplay(NULL);
46435c4bbdfSmrg        if (display) {
4654642e01fSmrg            /* Could open the display, start the launcher */
4664642e01fSmrg            XCloseDisplay(display);
4674642e01fSmrg
4689ace9065Smrg            return execute(pref_app_to_run);
4694642e01fSmrg        }
4704642e01fSmrg    }
4714642e01fSmrg
4724642e01fSmrg    /* Start the server */
47335c4bbdfSmrg    if ((s = getenv("DISPLAY"))) {
47435c4bbdfSmrg        ErrorF(
47535c4bbdfSmrg            "X11.app: Could not connect to server (DISPLAY=\"%s\", unsetting).  Starting X server.\n",
47635c4bbdfSmrg            s);
4774642e01fSmrg        unsetenv("DISPLAY");
47835c4bbdfSmrg    }
47935c4bbdfSmrg    else {
48035c4bbdfSmrg        ErrorF(
48135c4bbdfSmrg            "X11.app: Could not connect to server (DISPLAY is not set).  Starting X server.\n");
4824642e01fSmrg    }
4839ace9065Smrg    return execute(pref_startx_script);
4844642e01fSmrg}
4854642e01fSmrg
4864642e01fSmrg/** Setup the environment we want our child processes to inherit */
48735c4bbdfSmrgstatic void
48835c4bbdfSmrgensure_path(const char *dir)
48935c4bbdfSmrg{
4904642e01fSmrg    char buf[1024], *temp;
49135c4bbdfSmrg
4924642e01fSmrg    /* Make sure /usr/X11/bin is in the $PATH */
4934642e01fSmrg    temp = getenv("PATH");
49435c4bbdfSmrg    if (temp == NULL || temp[0] == 0) {
49535c4bbdfSmrg        snprintf(buf, sizeof(buf),
49635c4bbdfSmrg                 "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:%s",
49735c4bbdfSmrg                 dir);
4984642e01fSmrg        setenv("PATH", buf, TRUE);
49935c4bbdfSmrg    }
50035c4bbdfSmrg    else if (strnstr(temp, X11BINDIR, sizeof(temp)) == NULL) {
5014642e01fSmrg        snprintf(buf, sizeof(buf), "%s:%s", temp, dir);
5024642e01fSmrg        setenv("PATH", buf, TRUE);
5034642e01fSmrg    }
5044642e01fSmrg}
5054642e01fSmrg
50635c4bbdfSmrgstatic void
50735c4bbdfSmrgsetup_console_redirect(const char *bundle_id)
50835c4bbdfSmrg{
50935c4bbdfSmrg    char *asl_sender;
51035c4bbdfSmrg    char *asl_facility;
51135c4bbdfSmrg
51235c4bbdfSmrg    asprintf(&asl_sender, "%s.server", bundle_id);
51335c4bbdfSmrg    assert(asl_sender);
51435c4bbdfSmrg
51535c4bbdfSmrg    asl_facility = strdup(bundle_id);
51635c4bbdfSmrg    assert(asl_facility);
51735c4bbdfSmrg    if (strcmp(asl_facility + strlen(asl_facility) - 4, ".X11") == 0)
51835c4bbdfSmrg        asl_facility[strlen(asl_facility) - 4] = '\0';
51935c4bbdfSmrg
52035c4bbdfSmrg    assert(aslc = asl_open(asl_sender, asl_facility, ASL_OPT_NO_DELAY));
52135c4bbdfSmrg    free(asl_sender);
52235c4bbdfSmrg    free(asl_facility);
52335c4bbdfSmrg
52435c4bbdfSmrg    asl_set_filter(aslc, ASL_FILTER_MASK_UPTO(ASL_LEVEL_WARNING));
52535c4bbdfSmrg
526c8548ba8Smrg    asl_log_descriptor(aslc, NULL, ASL_LEVEL_INFO, STDOUT_FILENO, ASL_LOG_DESCRIPTOR_WRITE);
527c8548ba8Smrg    asl_log_descriptor(aslc, NULL, ASL_LEVEL_NOTICE, STDERR_FILENO, ASL_LOG_DESCRIPTOR_WRITE);
52835c4bbdfSmrg}
52935c4bbdfSmrg
53035c4bbdfSmrgstatic void
53135c4bbdfSmrgsetup_env(void)
53235c4bbdfSmrg{
5334642e01fSmrg    char *temp;
5344642e01fSmrg    const char *pds = NULL;
5356747b715Smrg    const char *disp = getenv("DISPLAY");
5366747b715Smrg    size_t len;
5374642e01fSmrg
5384642e01fSmrg    /* Pass on our prefs domain to startx and its inheritors (mainly for
5394642e01fSmrg     * quartz-wm and the Xquartz stub's MachIPC)
5404642e01fSmrg     */
5414642e01fSmrg    CFBundleRef bundle = CFBundleGetMainBundle();
54235c4bbdfSmrg    if (bundle) {
5434642e01fSmrg        CFStringRef pd = CFBundleGetIdentifier(bundle);
54435c4bbdfSmrg        if (pd) {
5454642e01fSmrg            pds = CFStringGetCStringPtr(pd, 0);
5464642e01fSmrg        }
5474642e01fSmrg    }
5484642e01fSmrg
5496747b715Smrg    /* fallback to hardcoded value if we can't discover it */
55035c4bbdfSmrg    if (!pds) {
55135c4bbdfSmrg        pds = BUNDLE_ID_PREFIX ".X11";
5526747b715Smrg    }
5536747b715Smrg
55435c4bbdfSmrg    setup_console_redirect(pds);
55535c4bbdfSmrg
5569ace9065Smrg    server_bootstrap_name = strdup(pds);
55735c4bbdfSmrg    if (!server_bootstrap_name) {
55835c4bbdfSmrg        ErrorF("X11.app: Memory allocation error.\n");
5596747b715Smrg        exit(1);
5606747b715Smrg    }
5616747b715Smrg    setenv("X11_PREFS_DOMAIN", server_bootstrap_name, 1);
56235c4bbdfSmrg
5636747b715Smrg    len = strlen(server_bootstrap_name);
56435c4bbdfSmrg    bundle_id_prefix = malloc(sizeof(char) * (len - 3));
56535c4bbdfSmrg    if (!bundle_id_prefix) {
56635c4bbdfSmrg        ErrorF("X11.app: Memory allocation error.\n");
5676747b715Smrg        exit(1);
5686747b715Smrg    }
56935c4bbdfSmrg    strlcpy(bundle_id_prefix, server_bootstrap_name, len - 3);
57035c4bbdfSmrg
5716747b715Smrg    /* We need to unset DISPLAY if it is not our socket */
57235c4bbdfSmrg    if (disp) {
5736747b715Smrg        /* s = basename(disp) */
5746747b715Smrg        const char *d, *s;
57535c4bbdfSmrg        for (s = NULL, d = disp; *d; d++) {
57635c4bbdfSmrg            if (*d == '/')
5776747b715Smrg                s = d + 1;
5786747b715Smrg        }
5796747b715Smrg
58035c4bbdfSmrg        if (s && *s) {
58135c4bbdfSmrg            if (strcmp(bundle_id_prefix,
58235c4bbdfSmrg                       "org.x") == 0 && strcmp(s, ":0") == 0) {
58335c4bbdfSmrg                ErrorF(
58435c4bbdfSmrg                    "X11.app: Detected old style launchd DISPLAY, please update xinit.\n");
58535c4bbdfSmrg            }
58635c4bbdfSmrg            else {
5876747b715Smrg                temp = (char *)malloc(sizeof(char) * len);
58835c4bbdfSmrg                if (!temp) {
58935c4bbdfSmrg                    ErrorF(
59035c4bbdfSmrg                        "X11.app: Memory allocation error creating space for socket name test.\n");
5916747b715Smrg                    exit(1);
5926747b715Smrg                }
59335c4bbdfSmrg                strlcpy(temp, bundle_id_prefix, len);
5946747b715Smrg                strlcat(temp, ":0", len);
59535c4bbdfSmrg
59635c4bbdfSmrg                if (strcmp(temp, s) != 0) {
5976747b715Smrg                    /* If we don't have a match, unset it. */
59835c4bbdfSmrg                    ErrorF(
59935c4bbdfSmrg                        "X11.app: DISPLAY (\"%s\") does not match our id (\"%s\"), unsetting.\n",
60035c4bbdfSmrg                        disp, bundle_id_prefix);
6016747b715Smrg                    unsetenv("DISPLAY");
6026747b715Smrg                }
6036747b715Smrg                free(temp);
6046747b715Smrg            }
60535c4bbdfSmrg        }
60635c4bbdfSmrg        else {
6076747b715Smrg            /* The DISPLAY environment variable is not formatted like a launchd socket, so reset. */
60835c4bbdfSmrg            ErrorF(
60935c4bbdfSmrg                "X11.app: DISPLAY does not look like a launchd set variable, unsetting.\n");
6106747b715Smrg            unsetenv("DISPLAY");
6116747b715Smrg        }
6126747b715Smrg    }
6134642e01fSmrg
6144642e01fSmrg    /* Make sure PATH is right */
6154642e01fSmrg    ensure_path(X11BINDIR);
61635c4bbdfSmrg
6174642e01fSmrg    /* cd $HOME */
6184642e01fSmrg    temp = getenv("HOME");
61935c4bbdfSmrg    if (temp != NULL && temp[0] != '\0')
6204642e01fSmrg        chdir(temp);
6214642e01fSmrg}
6224642e01fSmrg
6234642e01fSmrg/*** Main ***/
62435c4bbdfSmrgint
62535c4bbdfSmrgmain(int argc, char **argv, char **envp)
62635c4bbdfSmrg{
6274642e01fSmrg    Bool listenOnly = FALSE;
6284642e01fSmrg    int i;
6294642e01fSmrg    mach_msg_size_t mxmsgsz = sizeof(union MaxMsgSize) + MAX_TRAILER_SIZE;
6304642e01fSmrg    mach_port_t mp;
6314642e01fSmrg    kern_return_t kr;
6324642e01fSmrg
633a1e1cf94Smrg    /* Ignore SIGPIPE */
634a1e1cf94Smrg    signal(SIGPIPE, SIG_IGN);
635a1e1cf94Smrg
6364642e01fSmrg    /* Setup our environment for our children */
6374642e01fSmrg    setup_env();
63835c4bbdfSmrg
6394642e01fSmrg    /* The server must not run the PanoramiX operations. */
6404642e01fSmrg    noPanoramiXExtension = TRUE;
6414642e01fSmrg
642a1e1cf94Smrg#ifdef COMPOSITE
643a1e1cf94Smrg    /* https://gitlab.freedesktop.org/xorg/xserver/-/issues/1409 */
644a1e1cf94Smrg    noCompositeExtension = TRUE;
645a1e1cf94Smrg#endif
646a1e1cf94Smrg
6474642e01fSmrg    /* Setup the initial crasherporter info */
64835c4bbdfSmrg    strlcpy(__crashreporter_info_buff__, __crashreporter_info__base,
64935c4bbdfSmrg            sizeof(__crashreporter_info_buff__));
65035c4bbdfSmrg
65135c4bbdfSmrg    ErrorF("X11.app: main(): argc=%d\n", argc);
65235c4bbdfSmrg    for (i = 0; i < argc; i++) {
65335c4bbdfSmrg        ErrorF("\targv[%u] = %s\n", (unsigned)i, argv[i]);
65435c4bbdfSmrg        if (!strcmp(argv[i], "--listenonly")) {
6554642e01fSmrg            listenOnly = TRUE;
6564642e01fSmrg        }
6574642e01fSmrg    }
6584642e01fSmrg
6594642e01fSmrg    mp = checkin_or_register(server_bootstrap_name);
66035c4bbdfSmrg    if (mp == MACH_PORT_NULL) {
66135c4bbdfSmrg        ErrorF("NULL mach service: %s", server_bootstrap_name);
6624642e01fSmrg        return EXIT_FAILURE;
6634642e01fSmrg    }
66435c4bbdfSmrg
6654642e01fSmrg    /* Check if we need to do something other than listen, and make another
6664642e01fSmrg     * thread handle it.
6674642e01fSmrg     */
66835c4bbdfSmrg    if (!listenOnly) {
6696747b715Smrg        pid_t child1, child2;
6706747b715Smrg        int status;
6716747b715Smrg
6729ace9065Smrg        pref_app_to_run = command_from_prefs("app_to_run", DEFAULT_CLIENT);
6739ace9065Smrg        assert(pref_app_to_run);
6749ace9065Smrg
6759ace9065Smrg        pref_login_shell = command_from_prefs("login_shell", DEFAULT_SHELL);
6769ace9065Smrg        assert(pref_login_shell);
6779ace9065Smrg
67835c4bbdfSmrg        pref_startx_script = command_from_prefs("startx_script",
67935c4bbdfSmrg                                                DEFAULT_STARTX);
6809ace9065Smrg        assert(pref_startx_script);
6819ace9065Smrg
6826747b715Smrg        /* Do the fork-twice trick to avoid having to reap zombies */
6836747b715Smrg        child1 = fork();
6846747b715Smrg        switch (child1) {
68535c4bbdfSmrg        case -1:                                    /* error */
68635c4bbdfSmrg            FatalError("fork() failed: %s\n", strerror(errno));
6876747b715Smrg
68835c4bbdfSmrg        case 0:                                     /* child1 */
68935c4bbdfSmrg            child2 = fork();
6906747b715Smrg
69135c4bbdfSmrg            switch (child2) {
69235c4bbdfSmrg                int max_files;
6936747b715Smrg
69435c4bbdfSmrg            case -1:                                    /* error */
69535c4bbdfSmrg                FatalError("fork() failed: %s\n", strerror(errno));
6966747b715Smrg
69735c4bbdfSmrg            case 0:                                     /* child2 */
69835c4bbdfSmrg                /* close all open files except for standard streams */
69935c4bbdfSmrg                max_files = sysconf(_SC_OPEN_MAX);
70035c4bbdfSmrg                for (i = 3; i < max_files; i++)
70135c4bbdfSmrg                    close(i);
7026747b715Smrg
70335c4bbdfSmrg                /* ensure stdin is on /dev/null */
70435c4bbdfSmrg                close(0);
70535c4bbdfSmrg                open("/dev/null", O_RDONLY);
7066747b715Smrg
70735c4bbdfSmrg                return startup_trigger(argc, argv, envp);
7086747b715Smrg
70935c4bbdfSmrg            default:                                    /* parent (child1) */
71035c4bbdfSmrg                _exit(0);
71135c4bbdfSmrg            }
71235c4bbdfSmrg            break;
7136747b715Smrg
71435c4bbdfSmrg        default:                                    /* parent */
71535c4bbdfSmrg            waitpid(child1, &status, 0);
7164642e01fSmrg        }
7179ace9065Smrg
7189ace9065Smrg        free(pref_app_to_run);
7199ace9065Smrg        free(pref_login_shell);
7209ace9065Smrg        free(pref_startx_script);
7214642e01fSmrg    }
72235c4bbdfSmrg
7234642e01fSmrg    /* Main event loop */
72435c4bbdfSmrg    ErrorF("Waiting for startup parameters via Mach IPC.\n");
7254642e01fSmrg    kr = mach_msg_server(mach_startup_server, mxmsgsz, mp, 0);
7264642e01fSmrg    if (kr != KERN_SUCCESS) {
72735c4bbdfSmrg        ErrorF("%s.X11(mp): %s\n", BUNDLE_ID_PREFIX, mach_error_string(kr));
7284642e01fSmrg        return EXIT_FAILURE;
7294642e01fSmrg    }
73035c4bbdfSmrg
7314642e01fSmrg    return EXIT_SUCCESS;
7324642e01fSmrg}
7334642e01fSmrg
73435c4bbdfSmrgstatic int
73535c4bbdfSmrgexecute(const char *command)
73635c4bbdfSmrg{
7374642e01fSmrg    const char *newargv[4];
7384642e01fSmrg    const char **p;
73935c4bbdfSmrg
7409ace9065Smrg    newargv[0] = pref_login_shell;
7414642e01fSmrg    newargv[1] = "-c";
7424642e01fSmrg    newargv[2] = command;
7434642e01fSmrg    newargv[3] = NULL;
74435c4bbdfSmrg
74535c4bbdfSmrg    ErrorF("X11.app: Launching %s:\n", command);
74635c4bbdfSmrg    for (p = newargv; *p; p++) {
74735c4bbdfSmrg        ErrorF("\targv[%ld] = %s\n", (long int)(p - newargv), *p);
7484642e01fSmrg    }
7494642e01fSmrg
75035c4bbdfSmrg    execvp(newargv[0], (char *const *)newargv);
75135c4bbdfSmrg    perror("X11.app: Couldn't exec.");
7526747b715Smrg    return 1;
7534642e01fSmrg}
7544642e01fSmrg
75535c4bbdfSmrgstatic char *
75635c4bbdfSmrgcommand_from_prefs(const char *key, const char *default_value)
75735c4bbdfSmrg{
7584642e01fSmrg    char *command = NULL;
75935c4bbdfSmrg
7606747b715Smrg    CFStringRef cfKey;
7616747b715Smrg    CFPropertyListRef PlistRef;
7626747b715Smrg
76335c4bbdfSmrg    if (!key)
7646747b715Smrg        return NULL;
7656747b715Smrg
7666747b715Smrg    cfKey = CFStringCreateWithCString(NULL, key, kCFStringEncodingASCII);
7676747b715Smrg
76835c4bbdfSmrg    if (!cfKey)
7696747b715Smrg        return NULL;
7706747b715Smrg
77135c4bbdfSmrg    PlistRef = CFPreferencesCopyAppValue(cfKey,
77235c4bbdfSmrg                                         kCFPreferencesCurrentApplication);
77335c4bbdfSmrg
77435c4bbdfSmrg    if ((PlistRef == NULL) ||
77535c4bbdfSmrg        (CFGetTypeID(PlistRef) != CFStringGetTypeID())) {
77635c4bbdfSmrg        CFStringRef cfDefaultValue = CFStringCreateWithCString(
77735c4bbdfSmrg            NULL, default_value, kCFStringEncodingASCII);
7786747b715Smrg        int len = strlen(default_value) + 1;
7796747b715Smrg
78035c4bbdfSmrg        if (!cfDefaultValue)
7816747b715Smrg            goto command_from_prefs_out;
7824642e01fSmrg
78335c4bbdfSmrg        CFPreferencesSetAppValue(cfKey, cfDefaultValue,
78435c4bbdfSmrg                                 kCFPreferencesCurrentApplication);
7854642e01fSmrg        CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
7866747b715Smrg        CFRelease(cfDefaultValue);
78735c4bbdfSmrg
7884642e01fSmrg        command = (char *)malloc(len * sizeof(char));
78935c4bbdfSmrg        if (!command)
7906747b715Smrg            goto command_from_prefs_out;
7914642e01fSmrg        strcpy(command, default_value);
79235c4bbdfSmrg    }
79335c4bbdfSmrg    else {
7944642e01fSmrg        int len = CFStringGetLength((CFStringRef)PlistRef) + 1;
7954642e01fSmrg        command = (char *)malloc(len * sizeof(char));
79635c4bbdfSmrg        if (!command)
7976747b715Smrg            goto command_from_prefs_out;
79835c4bbdfSmrg        CFStringGetCString((CFStringRef)PlistRef, command, len,
79935c4bbdfSmrg                           kCFStringEncodingASCII);
8006747b715Smrg    }
8016747b715Smrg
8026747b715Smrgcommand_from_prefs_out:
8034642e01fSmrg    if (PlistRef)
8044642e01fSmrg        CFRelease(PlistRef);
80535c4bbdfSmrg    if (cfKey)
8066747b715Smrg        CFRelease(cfKey);
8074642e01fSmrg    return command;
8084642e01fSmrg}
809