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