1 /* bundle-main.c -- X server launcher
2 $Id: bundle-main.c,v 1.17 2003/09/11 00:17:10 jharper Exp $
4 Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
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:
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
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.
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.
31 Parts of this file are derived from xdm, which has this copyright:
33 Copyright 1988, 1998 The Open Group
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.
41 The above copyright notice and this permission notice shall be
42 included in all copies or substantial portions of the Software.
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.
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. */
62 #include <sys/socket.h>
63 #include <sys/utsname.h>
66 #include <netinet/in.h>
70 #include <sys/ioctl.h>
73 #include <X11/Xauth.h>
75 #include <CoreFoundation/CoreFoundation.h>
76 #include <SystemConfiguration/SystemConfiguration.h>
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>
94 #define MAX_DISPLAYS 64
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;
102 static char *auth_file;
104 typedef struct addr_list_struct addr_list;
106 struct addr_list_struct {
111 static addr_list *addresses;
114 /* Utility functions. */
116 /* Return the current host name. Matches what Xlib does. */
121 static struct utsname name;
125 return name.nodename;
127 static char buf[100];
129 gethostname(buf, sizeof(buf));
136 read_boolean_pref (CFStringRef name, int default_)
141 value = CFPreferencesGetAppBooleanValue (name,
142 CFSTR ("com.apple.x11"), &ok);
143 return ok ? value : default_;
147 binary_equal (const void *a, const void *b, int length)
149 return memcmp (a, b, length) == 0;
153 binary_dup (const void *a, int length)
155 void *b = malloc (length);
157 memcpy (b, a, length);
162 binary_free (void *data, int length)
169 /* Functions for managing the authentication entries. */
171 /* Returns true if something matching AUTH is in our list of auth items */
173 check_auth_item (Xauth *auth)
177 for (a = addresses; a != NULL; a = a->next)
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))
194 /* Add one item to our list of auth items. */
196 add_auth_item (Xauth *auth)
198 addr_list *a = malloc (sizeof (addr_list));
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);
214 /* Free all allocated auth items. */
216 free_auth_items (void)
220 while ((a = addresses) != NULL)
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);
232 /* Add the unix domain auth item. */
234 define_local (Xauth *auth)
236 char *host = host_name ();
239 fprintf (stderr, "x11: hostname is %s\n", host);
242 auth->family = FamilyLocal;
243 auth->address_length = strlen (host);
244 auth->address = host;
246 add_auth_item (auth);
249 /* Add the tcp auth item. */
251 define_named (Xauth *auth, const char *name)
253 struct ifaddrs *addrs, *ptr;
255 if (getifaddrs (&addrs) != 0)
258 for (ptr = addrs; ptr != NULL; ptr = ptr->ifa_next)
260 if (ptr->ifa_addr->sa_family != AF_INET)
263 auth->family = FamilyInternet;
264 auth->address_length = sizeof (struct sockaddr_in);
265 auth->address = (char *) &(((struct sockaddr_in *) ptr->ifa_addr)->sin_addr);
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]);
275 add_auth_item (auth);
281 /* Parse the display number from NAME and add it to AUTH. */
283 set_auth_number (Xauth *auth, const char *name)
288 colon = strrchr(name, ':');
292 dot = strchr(colon, '.');
295 auth->number_length = dot - colon;
297 auth->number_length = strlen (colon);
299 number = malloc (auth->number_length + 1);
302 strncpy (number, colon, auth->number_length);
303 number[auth->number_length] = '\0';
307 auth->number_length = 0;
310 auth->number = number;
314 /* Put 128 bits of random data into DATA. If possible, it will be "high
317 generate_mit_magic_cookie (char data[16])
320 long *ldata = (long *) data;
322 fd = open ("/dev/random", O_RDONLY);
325 ret = read (fd, data, 16);
332 /* fall back to the usual crappy rng */
334 srand48 (getpid () ^ time (NULL));
336 for (i = 0; i < 4; i++)
337 ldata[i] = lrand48 ();
342 /* Create the keys we'll be using for the display named NAME. */
344 make_auth_keys (const char *name)
349 if (auth_file == NULL)
352 auth.name = "MIT-MAGIC-COOKIE-1";
353 auth.name_length = strlen (auth.name);
355 if (!generate_mit_magic_cookie (key))
362 auth.data_length = 16;
364 set_auth_number (&auth, name);
366 define_named (&auth, host_name ());
367 define_local (&auth);
374 /* If ADD-ENTRIES is true, merge our auth entries into the existing
375 Xauthority file. If ADD-ENTRIES is false, remove our entries. */
377 write_auth_file (int add_entries)
379 char *home, newname[1024];
381 FILE *new_fh, *old_fh;
385 if (auth_file == NULL)
388 home = getenv ("HOME");
395 snprintf (newname, sizeof (newname), "%s/.XauthorityXXXXXX", home);
398 if (XauLockAuth (auth_file, 1, 2, 10) != LOCK_SUCCESS)
400 /* FIXME: do something here? */
406 fd = open (newname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
409 new_fh = fdopen (fd, "w");
414 for (addr = addresses; addr != NULL; addr = addr->next)
416 XauWriteAuth (new_fh, &addr->auth);
420 old_fh = fopen (auth_file, "r");
423 while ((auth = XauReadAuth (old_fh)) != NULL)
425 if (!check_auth_item (auth))
426 XauWriteAuth (new_fh, auth);
427 XauDisposeAuth (auth);
435 ret = rename (newname, auth_file);
440 XauUnlockAuth (auth_file);
447 XauUnlockAuth (auth_file);
453 /* Subprocess management functions. */
456 start_server (char **xargv)
469 execv (X_SERVER, xargv);
470 perror ("Couldn't exec " X_SERVER);
473 default: /* parent */
480 wait_for_server (void)
488 server_dpy = XOpenDisplay (server_name);
489 if (server_dpy != NULL)
492 if (waitpid (server_pid, &status, WNOHANG) == server_pid)
510 char *tem, buf[1024];
518 tem = getenv ("HOME");
522 /* Setup environment */
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);
529 snprintf (buf, sizeof (buf), "/bin:/usr/bin:/usr/X11R6/bin");
530 setenv ("PATH", buf, TRUE);
533 setenv("GTK_USE_XFT","1",0);
534 system(WM_PATH " &");
535 execl("/usr/local/bin/synfigstudio","/usr/local/bin/synfigstudio",NULL);
538 /* First look for .xinitrc in user's home directory. */
540 tem = getenv ("HOME");
543 snprintf (buf, sizeof (buf), "%s/.xinitrc", tem);
544 if (access (buf, R_OK) == 0)
545 execlp (SHELL, SHELL, buf, NULL);
548 /* Then try the default xinitrc in the lib directory. */
550 if (access (DEFAULT_XINITRC, R_OK) == 0)
551 execlp (SHELL, SHELL, DEFAULT_XINITRC, NULL);
553 /* Then fallback to hardcoding an xterm and the window manager. */
555 system (XTERM_PATH " &");
556 execl (WM_PATH, WM_PATH, NULL);
562 default: /* parent */
569 sigchld_handler (int sig)
574 pid = waitpid (WAIT_ANY, &status, WNOHANG);
578 if (pid == server_pid)
583 kill (client_pid, SIGTERM);
585 else if (pid == client_pid)
589 if (server_pid >= 0 && xinit_kills_server)
590 kill (server_pid, SIGTERM);
595 if (server_pid == -1 && client_pid == -1)
596 longjmp (exit_continuation, 1);
598 signal (SIGCHLD, sigchld_handler);
602 /* Server utilities. */
605 display_exists_p (int number)
609 char *fullname = NULL;
610 int idisplay, iscreen;
611 char *conn_auth_name, *conn_auth_data;
612 int conn_auth_namelen, conn_auth_datalen;
614 extern void *_X11TransConnectDisplay ();
615 extern void _XDisconnectDisplay ();
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..) */
622 sprintf (buf, "/tmp/.X11-unix/X%d", number);
623 if (access (buf, F_OK) != 0)
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) */
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);
637 _XDisconnectDisplay (conn);
642 /* Monitoring when the system's ip addresses change. */
644 static Boolean pending_timer;
647 timer_callback (CFRunLoopTimerRef timer, void *info)
649 pending_timer = FALSE;
651 /* Update authentication names. Need to write .Xauthority file first
652 without the existing entries, then again with the new entries.. */
654 write_auth_file (FALSE);
657 make_auth_keys (server_name);
659 write_auth_file (TRUE);
662 /* This function is called when the system's ip addresses may have changed. */
664 ipaddr_callback (SCDynamicStoreRef store, CFArrayRef changed_keys, void *info)
666 if (auth_file != NULL && !pending_timer)
668 CFRunLoopTimerRef timer;
670 timer = CFRunLoopTimerCreate (NULL, CFAbsoluteTimeGetCurrent () + 1.0,
671 0.0, 0, 0, timer_callback, NULL);
672 CFRunLoopAddTimer (CFRunLoopGetCurrent (), timer,
673 kCFRunLoopDefaultMode);
676 pending_timer = TRUE;
680 /* This code adapted from "Living in a Dynamic TCP/IP Environment" technote. */
682 install_ipaddr_source (void)
684 CFRunLoopSourceRef source = NULL;
686 SCDynamicStoreContext context = {0};
687 SCDynamicStoreRef ref;
689 ref = SCDynamicStoreCreate (NULL,
690 CFSTR ("AddIPAddressListChangeCallbackSCF"),
691 ipaddr_callback, &context);
697 /* This should tell us when any IPV4 address changes */
698 keys[0] = (SCDynamicStoreKeyCreateNetworkServiceEntity
699 (NULL, kSCDynamicStoreDomainState,
700 kSCCompAnyRegex, kSCEntNetIPv4));
702 /* This should tell us when the hostname(s) change */
703 keys[1] = SCDynamicStoreKeyCreateHostNames (NULL);
705 if (keys[0] != NULL && keys[1] != NULL)
707 CFArrayRef pattern_array;
709 pattern_array = CFArrayCreate (NULL, keys, 2,
710 &kCFTypeArrayCallBacks);
712 if (pattern_array != NULL)
714 SCDynamicStoreSetNotificationKeys (ref, NULL, pattern_array);
715 source = SCDynamicStoreCreateRunLoopSource (NULL, ref, 0);
717 CFRelease (pattern_array);
731 CFRunLoopAddSource (CFRunLoopGetCurrent (),
732 source, kCFRunLoopDefaultMode);
736 return source != NULL;
743 main (int argc, char **argv)
754 xargv = alloca (sizeof (char *) * (argc + 32));
756 if (!read_boolean_pref (CFSTR ("no_auth"), FALSE))
757 auth_file = XauFileName ();
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. */
763 xinit_kills_server = read_boolean_pref (CFSTR ("xinit_kills_server"), TRUE);
765 for (i = 1; i < argc; i++)
767 if (argv[i][0] == ':')
768 server_name = argv[i];
771 if (server_name == NULL)
775 /* No display number specified, so search for the first unused.
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.. */
782 for (i = 2; i < MAX_DISPLAYS; i++)
784 if (!display_exists_p (i))
788 if (i == MAX_DISPLAYS)
790 fprintf (stderr, "%s: couldn't allocate a display number", argv[0]);
794 sprintf (name, ":%d", i);
798 if (auth_file != NULL)
800 /* Create new Xauth keys and add them to the .Xauthority file */
802 make_auth_keys (server_name);
803 write_auth_file (TRUE);
806 /* Construct our new argv */
810 xargv[i++] = argv[j++];
812 if (auth_file != NULL)
814 xargv[i++] = "-auth";
815 xargv[i++] = auth_file;
818 /* By default, don't listen on tcp sockets if Xauth is disabled. */
820 if (read_boolean_pref (CFSTR ("nolisten_tcp"), auth_file == NULL))
822 xargv[i++] = "-nolisten";
828 if (argv[j++][0] != ':')
829 xargv[i++] = argv[j-1];
832 xargv[i++] = (char *) server_name;
835 /* Detach from any controlling terminal and connect stdin to /dev/null */
838 fd = open ("/dev/tty", O_RDONLY);
841 ioctl (fd, TIOCNOTTY, 0);
846 fd = open ("/dev/null", O_RDWR, 0);
854 if (!start_server (xargv))
857 if (!wait_for_server ())
859 kill (server_pid, SIGTERM);
863 if (!start_client ())
865 kill (server_pid, SIGTERM);
869 signal (SIGCHLD, sigchld_handler);
871 if (NSIsSymbolNameDefined("_ASKInitialize"))
873 NSSymbol *symbol = NSLookupAndBindSymbol("_ASKInitialize");
876 void (*initializeASKFunc)(void) = NSAddressOfSymbol(symbol);
877 if (initializeASKFunc)
888 if (setjmp (exit_continuation) == 0)
890 if (install_ipaddr_source ())
896 signal (SIGCHLD, SIG_IGN);
898 if (auth_file != NULL)
900 /* Remove our Xauth keys */
902 write_auth_file (FALSE);