Remove .gitignore do nothing is ignored.
[synfig.git] / synfig-osx / trunk / launcher / bundle-main.c
1 /* bundle-main.c -- X server launcher
2    $Id: bundle-main.c,v 1.17 2003/09/11 00:17:10 jharper Exp $
3
4    Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
5
6    Permission is hereby granted, free of charge, to any person
7    obtaining a copy of this software and associated documentation files
8    (the "Software"), to deal in the Software without restriction,
9    including without limitation the rights to use, copy, modify, merge,
10    publish, distribute, sublicense, and/or sell copies of the Software,
11    and to permit persons to whom the Software is furnished to do so,
12    subject to the following conditions:
13
14    The above copyright notice and this permission notice shall be
15    included in all copies or substantial portions of the Software.
16
17    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20    NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
21    HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24    DEALINGS IN THE SOFTWARE.
25
26    Except as contained in this notice, the name(s) of the above
27    copyright holders shall not be used in advertising or otherwise to
28    promote the sale, use or other dealings in this Software without
29    prior written authorization.
30
31    Parts of this file are derived from xdm, which has this copyright:
32
33    Copyright 1988, 1998  The Open Group
34
35    Permission to use, copy, modify, distribute, and sell this software
36    and its documentation for any purpose is hereby granted without fee,
37    provided that the above copyright notice appear in all copies and
38    that both that copyright notice and this permission notice appear in
39    supporting documentation.
40
41    The above copyright notice and this permission notice shall be
42    included in all copies or substantial portions of the Software.
43
44    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
45    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
46    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
47    NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY
48    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
49    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
50    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
51
52    Except as contained in this notice, the name of The Open Group shall
53    not be used in advertising or otherwise to promote the sale, use or
54    other dealings in this Software without prior written authorization
55    from The Open Group. */
56
57 #include <stdio.h>
58 #include <unistd.h>
59 #include <stdlib.h>
60 #include <fcntl.h>
61 #include <errno.h>
62 #include <sys/socket.h>
63 #include <sys/utsname.h>
64 #include <ifaddrs.h>
65 #include <netdb.h>
66 #include <netinet/in.h>
67 #include <time.h>
68 #include <sys/wait.h>
69 #include <setjmp.h>
70 #include <sys/ioctl.h>
71
72 #include <X11/Xlib.h>
73 #include <X11/Xauth.h>
74
75 #include <CoreFoundation/CoreFoundation.h>
76 #include <SystemConfiguration/SystemConfiguration.h>
77
78 #define X_SERVER "/usr/X11R6/bin/Xquartz"
79 #define XTERM_PATH "/usr/X11R6/bin/xterm"
80 #define WM_PATH "/usr/X11R6/bin/quartz-wm"
81 #define DEFAULT_XINITRC "/usr/X11R6/lib/X11/xinit/xinitrc"
82 #include <mach-o/dyld.h>
83
84 /* what xinit does */
85 #ifndef SHELL
86 # define SHELL "sh"
87 #endif
88
89 #undef FALSE
90 #define FALSE 0
91 #undef TRUE
92 #define TRUE 1
93
94 #define MAX_DISPLAYS 64
95
96 static int server_pid = -1, client_pid = -1;
97 static int xinit_kills_server = FALSE;
98 static jmp_buf exit_continuation;
99 static const char *server_name = NULL;
100 static Display *server_dpy;
101
102 static char *auth_file;
103
104 typedef struct addr_list_struct addr_list;
105
106 struct addr_list_struct {
107     addr_list *next;
108     Xauth auth;
109 };
110
111 static addr_list *addresses;
112
113 \f
114 /* Utility functions. */
115
116 /* Return the current host name. Matches what Xlib does. */
117 static char *
118 host_name (void)
119 {
120 #ifdef NEED_UTSNAME
121     static struct utsname name;
122
123     uname(&name);
124
125     return name.nodename;
126 #else
127     static char buf[100];
128
129     gethostname(buf, sizeof(buf));
130
131     return buf;
132 #endif
133 }
134
135 static int
136 read_boolean_pref (CFStringRef name, int default_)
137 {
138     int value;
139     Boolean ok;
140
141     value = CFPreferencesGetAppBooleanValue (name,
142                                              CFSTR ("com.apple.x11"), &ok);
143     return ok ? value : default_;
144 }
145
146 static inline int
147 binary_equal (const void *a, const void *b, int length)
148 {
149     return memcmp (a, b, length) == 0;
150 }
151
152 static inline void *
153 binary_dup (const void *a, int length)
154 {
155     void *b = malloc (length);
156     if (b != NULL)
157         memcpy (b, a, length);
158     return b;
159 }
160
161 static inline void
162 binary_free (void *data, int length)
163 {
164     if (data != NULL)
165         free (data);
166 }
167
168 \f
169 /* Functions for managing the authentication entries. */
170
171 /* Returns true if something matching AUTH is in our list of auth items */
172 static int
173 check_auth_item (Xauth *auth)
174 {
175     addr_list *a;
176
177     for (a = addresses; a != NULL; a = a->next)
178     {
179         if (a->auth.family == auth->family
180             && a->auth.address_length == auth->address_length
181             && binary_equal (a->auth.address, auth->address, auth->address_length)
182             && a->auth.number_length == auth->number_length
183             && binary_equal (a->auth.number, auth->number, auth->number_length)
184             && a->auth.name_length == auth->name_length
185             && binary_equal (a->auth.name, auth->name, auth->name_length))
186         {
187             return TRUE;
188         }
189     }
190
191     return FALSE;
192 }
193
194 /* Add one item to our list of auth items. */
195 static void
196 add_auth_item (Xauth *auth)
197 {
198     addr_list *a = malloc (sizeof (addr_list));
199
200     a->auth.family = auth->family;
201     a->auth.address_length = auth->address_length;
202     a->auth.address = binary_dup (auth->address, auth->address_length);
203     a->auth.number_length = auth->number_length;
204     a->auth.number = binary_dup (auth->number, auth->number_length);
205     a->auth.name_length = auth->name_length;
206     a->auth.name = binary_dup (auth->name, auth->name_length);
207     a->auth.data_length = auth->data_length;
208     a->auth.data = binary_dup (auth->data, auth->data_length);
209
210     a->next = addresses;
211     addresses = a;
212 }
213
214 /* Free all allocated auth items. */
215 static void
216 free_auth_items (void)
217 {
218     addr_list *a;
219
220     while ((a = addresses) != NULL)
221     {
222         addresses = a->next;
223
224         binary_free (a->auth.address, a->auth.address_length);
225         binary_free (a->auth.number, a->auth.number_length);
226         binary_free (a->auth.name, a->auth.name_length);
227         binary_free (a->auth.data, a->auth.data_length);
228         free (a);
229     }
230 }
231
232 /* Add the unix domain auth item. */
233 static void
234 define_local (Xauth *auth)
235 {
236     char *host = host_name ();
237
238 #ifdef DEBUG
239     fprintf (stderr, "x11: hostname is %s\n", host);
240 #endif
241
242     auth->family = FamilyLocal;
243     auth->address_length = strlen (host);
244     auth->address = host;
245
246     add_auth_item (auth);
247 }
248
249 /* Add the tcp auth item. */
250 static void
251 define_named (Xauth *auth, const char *name)
252 {
253     struct ifaddrs *addrs, *ptr;
254
255     if (getifaddrs (&addrs) != 0)
256         return;
257
258     for (ptr = addrs; ptr != NULL; ptr = ptr->ifa_next)
259     {
260         if (ptr->ifa_addr->sa_family != AF_INET)
261             continue;
262
263         auth->family = FamilyInternet;
264         auth->address_length = sizeof (struct sockaddr_in);
265         auth->address = (char *) &(((struct sockaddr_in *) ptr->ifa_addr)->sin_addr);
266
267 #ifdef DEBUG
268         fprintf (stderr, "x11: ipaddr is %d.%d.%d.%d\n",
269                  (unsigned char) auth->address[0],
270                  (unsigned char) auth->address[1],
271                  (unsigned char) auth->address[2],
272                  (unsigned char) auth->address[3]);
273 #endif
274
275         add_auth_item (auth);
276     }
277
278     freeifaddrs (addrs);
279 }
280
281 /* Parse the display number from NAME and add it to AUTH. */
282 static void
283 set_auth_number (Xauth *auth, const char *name)
284 {
285     char *colon;
286     char *dot, *number;
287
288     colon = strrchr(name, ':');
289     if (colon != NULL)
290     {
291         colon++;
292         dot = strchr(colon, '.');
293
294         if (dot != NULL)
295             auth->number_length = dot - colon;
296         else
297             auth->number_length = strlen (colon);
298
299         number = malloc (auth->number_length + 1);
300         if (number != NULL)
301         {
302             strncpy (number, colon, auth->number_length);
303             number[auth->number_length] = '\0';
304         }
305         else
306         {
307             auth->number_length = 0;
308         }
309
310         auth->number = number;
311     }
312 }
313
314 /* Put 128 bits of random data into DATA. If possible, it will be "high
315    quality" */
316 static int
317 generate_mit_magic_cookie (char data[16])
318 {
319     int fd, ret, i;
320     long *ldata = (long *) data;
321
322     fd = open ("/dev/random", O_RDONLY);
323     if (fd > 0)
324     {
325         ret = read (fd, data, 16);
326         if (ret == 16)
327             return TRUE;
328
329         close (fd);
330     }
331
332     /* fall back to the usual crappy rng */
333
334     srand48 (getpid () ^ time (NULL));
335
336     for (i = 0; i < 4; i++)
337         ldata[i] = lrand48 ();
338
339     return TRUE;
340 }
341
342 /* Create the keys we'll be using for the display named NAME. */
343 static int
344 make_auth_keys (const char *name)
345 {
346     Xauth auth;
347     char key[16];
348
349     if (auth_file == NULL)
350         return FALSE;
351
352     auth.name = "MIT-MAGIC-COOKIE-1";
353     auth.name_length = strlen (auth.name);
354
355     if (!generate_mit_magic_cookie (key))
356     {
357         auth_file = NULL;
358         return FALSE;
359     }
360
361     auth.data = key;
362     auth.data_length = 16;
363
364     set_auth_number (&auth, name);
365
366     define_named (&auth, host_name ());
367     define_local (&auth);
368
369     free (auth.number);
370
371     return TRUE;
372 }
373
374 /* If ADD-ENTRIES is true, merge our auth entries into the existing
375    Xauthority file. If ADD-ENTRIES is false, remove our entries. */
376 static int
377 write_auth_file (int add_entries)
378 {
379     char *home, newname[1024];
380     int fd, ret;
381     FILE *new_fh, *old_fh;
382     addr_list *addr;
383     Xauth *auth;
384
385     if (auth_file == NULL)
386         return FALSE;
387
388     home = getenv ("HOME");
389     if (home == NULL)
390     {
391         auth_file = NULL;
392         return FALSE;
393     }
394
395     snprintf (newname, sizeof (newname), "%s/.XauthorityXXXXXX", home);
396     mktemp (newname);
397
398     if (XauLockAuth (auth_file, 1, 2, 10) != LOCK_SUCCESS)
399     {
400         /* FIXME: do something here? */
401
402         auth_file = NULL;
403         return FALSE;
404     }
405
406     fd = open (newname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
407     if (fd >= 0)
408     {
409         new_fh = fdopen (fd, "w");
410         if (new_fh != NULL)
411         {
412             if (add_entries)
413             {
414                 for (addr = addresses; addr != NULL; addr = addr->next)
415                 {
416                     XauWriteAuth (new_fh, &addr->auth);
417                 }
418             }
419
420             old_fh = fopen (auth_file, "r");
421             if (old_fh != NULL)
422             {
423                 while ((auth = XauReadAuth (old_fh)) != NULL)
424                 {
425                     if (!check_auth_item (auth))
426                         XauWriteAuth (new_fh, auth);
427                     XauDisposeAuth (auth);
428                 }
429                 fclose (old_fh);
430             }
431
432             fclose (new_fh);
433             unlink (auth_file);
434
435             ret = rename (newname, auth_file);
436
437             if (ret != 0)
438                 auth_file = NULL;
439
440             XauUnlockAuth (auth_file);
441             return ret == 0;
442         }
443
444         close (fd);
445     }
446
447     XauUnlockAuth (auth_file);
448     auth_file = NULL;
449     return FALSE;
450 }
451
452 \f
453 /* Subprocess management functions. */
454
455 static int
456 start_server (char **xargv)
457 {
458     int child;
459
460     child = fork ();
461
462     switch (child)
463     {
464     case -1:                            /* error */
465         perror ("fork");
466         return FALSE;
467
468     case 0:                             /* child */
469         execv (X_SERVER, xargv);
470         perror ("Couldn't exec " X_SERVER);
471         _exit (1);
472
473     default:                            /* parent */
474         server_pid = child;
475         return TRUE;
476     }
477 }
478
479 static int
480 wait_for_server (void)
481 {
482     int count = 100;
483
484     while (count-- > 0)
485     {
486         int status;
487
488         server_dpy = XOpenDisplay (server_name);
489         if (server_dpy != NULL)
490             return TRUE;
491
492         if (waitpid (server_pid, &status, WNOHANG) == server_pid)
493             return FALSE;
494
495         sleep (1);
496     }
497
498     return FALSE;
499 }
500
501 static int
502 start_client (void)
503 {
504     int child;
505
506     child = fork ();
507
508     switch (child)
509     {
510         char *tem, buf[1024];
511
512     case -1:                            /* error */
513         perror ("fork");
514         return FALSE;
515
516     case 0:                             /* child */
517         /* cd $HOME */
518         tem = getenv ("HOME");
519         if (tem != NULL)
520             chdir (tem);
521
522         /* Setup environment */
523
524         setenv ("DISPLAY", server_name, TRUE);
525         tem = getenv ("PATH");
526         if (tem != NULL && tem[0] != NULL)
527             snprintf (buf, sizeof (buf), "%s:/usr/X11R6/bin", tem);
528         else
529             snprintf (buf, sizeof (buf), "/bin:/usr/bin:/usr/X11R6/bin");
530         setenv ("PATH", buf, TRUE);
531
532 #if 1
533         setenv("GTK_USE_XFT","1",0);
534         system(WM_PATH " &");
535         execl("/usr/local/bin/synfigstudio","/usr/local/bin/synfigstudio",NULL);
536
537 #else
538         /* First look for .xinitrc in user's home directory. */
539
540         tem = getenv ("HOME");
541         if (tem != NULL)
542         {
543             snprintf (buf, sizeof (buf), "%s/.xinitrc", tem);
544             if (access (buf, R_OK) == 0)
545                 execlp (SHELL, SHELL, buf, NULL);
546         }
547
548         /* Then try the default xinitrc in the lib directory. */
549
550         if (access (DEFAULT_XINITRC, R_OK) == 0)
551             execlp (SHELL, SHELL, DEFAULT_XINITRC, NULL);
552
553         /* Then fallback to hardcoding an xterm and the window manager. */
554
555         system (XTERM_PATH " &");
556         execl (WM_PATH, WM_PATH, NULL);
557         
558 #endif
559         perror ("exec");
560         _exit (1);
561
562     default:                            /* parent */
563         client_pid = child;
564         return TRUE;
565     }
566 }
567
568 static void
569 sigchld_handler (int sig)
570 {
571     int pid, status;
572
573 again:
574     pid = waitpid (WAIT_ANY, &status, WNOHANG);
575
576     if (pid > 0)
577     {
578         if (pid == server_pid)
579         {
580             server_pid = -1;
581
582             if (client_pid >= 0)
583                 kill (client_pid, SIGTERM);
584         }
585         else if (pid == client_pid)
586         {
587             client_pid = -1;
588
589             if (server_pid >= 0 && xinit_kills_server)
590                 kill (server_pid, SIGTERM);
591         }
592         goto again;
593     }
594
595     if (server_pid == -1 && client_pid == -1)
596         longjmp (exit_continuation, 1);
597
598     signal (SIGCHLD, sigchld_handler);
599 }
600
601 \f
602 /* Server utilities. */
603
604 static Boolean
605 display_exists_p (int number)
606 {
607     char buf[64];
608     void *conn;
609     char *fullname = NULL;
610     int idisplay, iscreen;
611     char *conn_auth_name, *conn_auth_data;
612     int conn_auth_namelen, conn_auth_datalen;
613
614     extern void *_X11TransConnectDisplay ();
615     extern void _XDisconnectDisplay ();
616
617     /* Since connecting to the display waits for a few seconds if the
618        display doesn't exist, check for trivial non-existence - if the
619        socket in /tmp exists or not.. (note: if the socket exists, the
620        server may still not, so we need to try to connect in that case..) */
621
622     sprintf (buf, "/tmp/.X11-unix/X%d", number);
623     if (access (buf, F_OK) != 0)
624         return FALSE;
625
626     /* This is a private function that we shouldn't really be calling,
627        but it's the best way to see if the server exists (without
628        needing to hold the necessary authentication to use it) */
629
630     sprintf (buf, ":%d", number);
631     conn = _X11TransConnectDisplay (buf, &fullname, &idisplay, &iscreen,
632                                     &conn_auth_name, &conn_auth_namelen,
633                                     &conn_auth_data, &conn_auth_datalen);
634     if (conn == NULL)
635         return FALSE;
636
637     _XDisconnectDisplay (conn);
638     return TRUE;
639 }
640
641 \f
642 /* Monitoring when the system's ip addresses change. */
643
644 static Boolean pending_timer;
645
646 static void
647 timer_callback (CFRunLoopTimerRef timer, void *info)
648 {
649     pending_timer = FALSE;
650
651     /* Update authentication names. Need to write .Xauthority file first
652        without the existing entries, then again with the new entries.. */
653
654     write_auth_file (FALSE);
655
656     free_auth_items ();
657     make_auth_keys (server_name);
658
659     write_auth_file (TRUE);
660 }
661
662 /* This function is called when the system's ip addresses may have changed. */
663 static void
664 ipaddr_callback (SCDynamicStoreRef store, CFArrayRef changed_keys, void *info)
665 {
666     if (auth_file != NULL && !pending_timer)
667     {
668         CFRunLoopTimerRef timer;
669
670         timer = CFRunLoopTimerCreate (NULL, CFAbsoluteTimeGetCurrent () + 1.0,
671                                       0.0, 0, 0, timer_callback, NULL);
672         CFRunLoopAddTimer (CFRunLoopGetCurrent (), timer,
673                            kCFRunLoopDefaultMode);
674         CFRelease (timer);
675
676         pending_timer = TRUE;
677     }
678 }
679
680 /* This code adapted from "Living in a Dynamic TCP/IP Environment" technote. */
681 static Boolean
682 install_ipaddr_source (void)
683 {
684     CFRunLoopSourceRef source = NULL;
685
686     SCDynamicStoreContext context = {0};
687     SCDynamicStoreRef ref;
688
689     ref = SCDynamicStoreCreate (NULL,
690                                 CFSTR ("AddIPAddressListChangeCallbackSCF"),
691                                 ipaddr_callback, &context);
692
693     if (ref != NULL)
694     {
695         const void *keys[2];
696
697         /* This should tell us when any IPV4 address changes */
698         keys[0] = (SCDynamicStoreKeyCreateNetworkServiceEntity
699                    (NULL, kSCDynamicStoreDomainState,
700                     kSCCompAnyRegex, kSCEntNetIPv4));
701
702         /* This should tell us when the hostname(s) change */
703         keys[1] = SCDynamicStoreKeyCreateHostNames (NULL);
704
705         if (keys[0] != NULL && keys[1] != NULL)
706         {
707             CFArrayRef pattern_array;
708
709             pattern_array = CFArrayCreate (NULL, keys, 2,
710                                            &kCFTypeArrayCallBacks);
711
712             if (pattern_array != NULL)
713             {
714                 SCDynamicStoreSetNotificationKeys (ref, NULL, pattern_array);
715                 source = SCDynamicStoreCreateRunLoopSource (NULL, ref, 0);
716
717                 CFRelease (pattern_array);
718             }
719
720             if (keys[0] != NULL)
721                 CFRelease (keys[0]);
722             if (keys[1] != NULL)
723                 CFRelease (keys[1]);
724         }
725
726         CFRelease (ref); 
727    }
728
729     if (source != NULL)
730     {
731         CFRunLoopAddSource (CFRunLoopGetCurrent (),
732                             source, kCFRunLoopDefaultMode);
733         CFRelease (source);
734     }
735
736     return source != NULL;
737 }
738
739
740 \f
741 /* Entrypoint. */
742 int
743 main (int argc, char **argv)
744 {
745     char **xargv;
746     int i, j;
747     int fd;
748
749
750
751
752
753
754     xargv = alloca (sizeof (char *) * (argc + 32));
755
756     if (!read_boolean_pref (CFSTR ("no_auth"), FALSE))
757         auth_file = XauFileName ();
758
759     /* The standard X11 behaviour is for the server to quit when the first
760        client exits. But it can be useful for debugging (and to mimic our
761        behaviour in the beta releases) to not do that. */
762
763     xinit_kills_server = read_boolean_pref (CFSTR ("xinit_kills_server"), TRUE);
764
765     for (i = 1; i < argc; i++)
766     {
767         if (argv[i][0] == ':')
768             server_name = argv[i];
769     }
770
771     if (server_name == NULL)
772     {
773         static char name[8];
774
775         /* No display number specified, so search for the first unused.
776
777            There's a big old race condition here if two servers start at
778            the same time, but that's fairly unlikely. We could create
779            lockfiles or something, but that's seems more likely to cause
780            problems than the race condition itself.. */
781
782         for (i = 2; i < MAX_DISPLAYS; i++)
783         {
784             if (!display_exists_p (i))
785                 break;
786         }
787
788         if (i == MAX_DISPLAYS)
789         {
790             fprintf (stderr, "%s: couldn't allocate a display number", argv[0]);
791             exit (1);
792         }
793
794         sprintf (name, ":%d", i);
795         server_name = name;
796     }
797
798     if (auth_file != NULL)
799     {
800         /* Create new Xauth keys and add them to the .Xauthority file */
801
802         make_auth_keys (server_name);
803         write_auth_file (TRUE);
804     }
805
806     /* Construct our new argv */
807
808     i = j = 0;
809
810     xargv[i++] = argv[j++];
811
812     if (auth_file != NULL)
813     {
814         xargv[i++] = "-auth";
815         xargv[i++] = auth_file;
816     }
817
818     /* By default, don't listen on tcp sockets if Xauth is disabled. */
819
820     if (read_boolean_pref (CFSTR ("nolisten_tcp"), auth_file == NULL))
821     {
822         xargv[i++] = "-nolisten";
823         xargv[i++] = "tcp";
824     }
825
826     while (j < argc)
827     {
828         if (argv[j++][0] != ':')
829             xargv[i++] = argv[j-1];
830     }
831
832     xargv[i++] = (char *) server_name;
833     xargv[i++] = NULL;
834
835     /* Detach from any controlling terminal and connect stdin to /dev/null */
836
837 #ifdef TIOCNOTTY
838     fd = open ("/dev/tty", O_RDONLY);
839     if (fd != -1)
840     {
841         ioctl (fd, TIOCNOTTY, 0);
842         close (fd);
843     }
844 #endif
845
846     fd = open ("/dev/null", O_RDWR, 0);
847     if (fd >= 0)
848     {
849         dup2 (fd, 0);
850         if (fd > 0)
851             close (fd);
852     }
853
854     if (!start_server (xargv))
855         return 1;
856
857     if (!wait_for_server ())
858     {
859         kill (server_pid, SIGTERM);
860         return 1;
861     }
862
863     if (!start_client ())
864     {
865         kill (server_pid, SIGTERM);
866         return 1;
867     }
868
869     signal (SIGCHLD, sigchld_handler);
870
871         if (NSIsSymbolNameDefined("_ASKInitialize"))
872         {
873                 NSSymbol *symbol = NSLookupAndBindSymbol("_ASKInitialize");
874                 if (symbol)
875                 {
876                         void (*initializeASKFunc)(void) = NSAddressOfSymbol(symbol);
877                         if (initializeASKFunc)
878                         {
879                                 initializeASKFunc();
880                         }
881                         else
882                                 return 666;
883                 }
884                 else return 667;
885         }else
886         return 668;
887
888     if (setjmp (exit_continuation) == 0)
889     {
890         if (install_ipaddr_source ())
891             CFRunLoopRun ();
892         else
893             while (1) pause ();
894     }
895
896     signal (SIGCHLD, SIG_IGN);
897
898     if (auth_file != NULL)
899     {
900         /* Remove our Xauth keys */
901
902         write_auth_file (FALSE);
903     }
904
905     free_auth_items ();
906
907     return 0;
908 }