xref: /OK3568_Linux_fs/external/xserver/hw/xquartz/mach-startup/bundle-main.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 #ifndef BUILD_DATE
80 #define BUILD_DATE ""
81 #endif
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 " Build Date: " BUILD_DATE;
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
checkin_or_register(char * bname)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
accept_fd_handoff(int connected_fd)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
socket_handoff(socket_handoff_t * handoff_data)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
create_socket(char * filename_out)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
do_request_fd_handoff_socket(mach_port_t port,string_t filename)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
do_request_pid(mach_port_t port,int * my_pid)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
do_start_x11_server(mach_port_t port,string_array_t argv,mach_msg_type_number_t argvCnt,string_array_t envp,mach_msg_type_number_t envpCnt)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
startup_trigger(int argc,char ** argv,char ** envp)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
ensure_path(const char * dir)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
setup_console_redirect(const char * bundle_id)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
setup_env(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
main(int argc,char ** argv,char ** envp)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
execute(const char * command)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 *
command_from_prefs(const char * key,const char * default_value)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 }
797