From f8d7729df388c142624def36ba6d8c3b15922018 Mon Sep 17 00:00:00 2001 From: Jeremy Huddleston Date: Mon, 3 Dec 2007 20:20:05 -0800 Subject: [PATCH] Darwin: Combine launcher and server X11.app This should hopefully eliminate confusion some people have over which X11.app is which. Now BOTH are in /A/U/X11.app and we intelligently determine whether to execute our app_to_run or launch the server. If arguments are given, we launch the server. Otherwise if we can connect to an X DISPLAY, we execute app_to_run. Otherwise, we launch the server. (cherry picked from commit e7026216ccaa8e4fb073800ba947c9909d4faada) --- configure.ac | 1 - hw/darwin/Makefile.am | 2 +- hw/darwin/apple/Makefile.am | 7 +- hw/darwin/apple/X11.xcodeproj/project.pbxproj | 9 +- hw/darwin/apple/bundle-main.c | 922 +----------------- .../bundle-main.c => apple/launcher-main.c} | 2 +- hw/darwin/apple/org.x.X11.plist | 25 + hw/darwin/apple/server-main.c | 904 +++++++++++++++++ hw/darwin/launcher/Info.plist | 30 - hw/darwin/launcher/Makefile.am | 18 - hw/darwin/launcher/X11.icns | Bin 65908 -> 0 bytes .../launcher/X11.xcodeproj/project.pbxproj | 290 ------ 12 files changed, 995 insertions(+), 1215 deletions(-) rename hw/darwin/{launcher/bundle-main.c => apple/launcher-main.c} (98%) create mode 100644 hw/darwin/apple/org.x.X11.plist create mode 100644 hw/darwin/apple/server-main.c delete mode 100644 hw/darwin/launcher/Info.plist delete mode 100644 hw/darwin/launcher/Makefile.am delete mode 100644 hw/darwin/launcher/X11.icns delete mode 100644 hw/darwin/launcher/X11.xcodeproj/project.pbxproj diff --git a/configure.ac b/configure.ac index 618a9c4ed..5b21e69da 100644 --- a/configure.ac +++ b/configure.ac @@ -2172,7 +2172,6 @@ hw/xnest/Makefile hw/xwin/Makefile hw/darwin/Makefile hw/darwin/apple/Makefile -hw/darwin/launcher/Makefile hw/darwin/quartz/Makefile hw/darwin/quartz/xpr/Makefile hw/kdrive/Makefile diff --git a/hw/darwin/Makefile.am b/hw/darwin/Makefile.am index 1faedcbef..136c41a0a 100644 --- a/hw/darwin/Makefile.am +++ b/hw/darwin/Makefile.am @@ -6,7 +6,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/miext/rootless if X11APP -X11APP_SUBDIRS = apple launcher +X11APP_SUBDIRS = apple endif SUBDIRS = quartz utils $(X11APP_SUBDIRS) diff --git a/hw/darwin/apple/Makefile.am b/hw/darwin/apple/Makefile.am index 02a2c25b1..a6e2dfbf9 100644 --- a/hw/darwin/apple/Makefile.am +++ b/hw/darwin/apple/Makefile.am @@ -6,15 +6,20 @@ x11app: xcodebuild CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" ARCHS="$(X11APP_ARCHS)" install-data-hook: - xcodebuild install DSTROOT=$(DESTDIR) INSTALL_PATH=$(prefix) DEPLOYMENT_LOCATION=YES SKIP_INSTALL=NO ARCHS="$(X11APP_ARCHS)" + xcodebuild install DSTROOT="/$(DESTDIR)" INSTALL_PATH="$(APPLE_APPLICATIONS_DIR)" DEPLOYMENT_LOCATION=YES SKIP_INSTALL=NO ARCHS="$(X11APP_ARCHS)" + $(MKDIR_P) "$(DESTDIR)/System/Library/LaunchAgents/" + $(INSTALL) org.x.X11.plist "$(DESTDIR)/System/Library/LaunchAgents/" clean-local: rm -rf build EXTRA_DIST = \ + org.x.X11.plist \ Info.plist \ X11.icns \ bundle-main.c \ + launcher-main.c \ + server-main.c \ English.lproj/InfoPlist.strings \ English.lproj/Localizable.strings \ English.lproj/main.nib/classes.nib \ diff --git a/hw/darwin/apple/X11.xcodeproj/project.pbxproj b/hw/darwin/apple/X11.xcodeproj/project.pbxproj index 217f07e52..225f371c5 100644 --- a/hw/darwin/apple/X11.xcodeproj/project.pbxproj +++ b/hw/darwin/apple/X11.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 3F5E1BE00D04BF110020CA24 /* launcher-main.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F5E1BDE0D04BF110020CA24 /* launcher-main.c */; }; + 3F5E1BE10D04BF110020CA24 /* server-main.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F5E1BDF0D04BF110020CA24 /* server-main.c */; }; 527F24190B5D938C007840A7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */; }; 527F241A0B5D938C007840A7 /* main.nib in Resources */ = {isa = PBXBuildFile; fileRef = 02345980000FD03B11CA0E72 /* main.nib */; }; 527F241B0B5D938C007840A7 /* X11.icns in Resources */ = {isa = PBXBuildFile; fileRef = 50459C5F038587C60ECA21EC /* X11.icns */; }; @@ -20,6 +22,8 @@ /* Begin PBXFileReference section */ 0867D6ABFE840B52C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1870340FFE93FCAF11CA0CD7 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/main.nib; sourceTree = ""; }; + 3F5E1BDE0D04BF110020CA24 /* launcher-main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "launcher-main.c"; sourceTree = ""; }; + 3F5E1BDF0D04BF110020CA24 /* server-main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "server-main.c"; sourceTree = ""; }; 50459C5F038587C60ECA21EC /* X11.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = X11.icns; sourceTree = ""; }; 50EE2AB703849F0B0ECA21EC /* bundle-main.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = "bundle-main.c"; sourceTree = ""; }; 50F4F0A7039D6ACA0E82C0CB /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; @@ -65,6 +69,8 @@ 20286C2AFDCF999611CA2CEA /* Sources */ = { isa = PBXGroup; children = ( + 3F5E1BDE0D04BF110020CA24 /* launcher-main.c */, + 3F5E1BDF0D04BF110020CA24 /* server-main.c */, 50EE2AB703849F0B0ECA21EC /* bundle-main.c */, ); name = Sources; @@ -133,7 +139,6 @@ mainGroup = 20286C29FDCF999611CA2CEA /* X11 */; projectDirPath = ""; projectRoot = ""; - shouldCheckCompatibility = 1; targets = ( 527F24160B5D938C007840A7 /* X11 */, ); @@ -171,6 +176,8 @@ buildActionMask = 2147483647; files = ( 527F241D0B5D938C007840A7 /* bundle-main.c in Sources */, + 3F5E1BE00D04BF110020CA24 /* launcher-main.c in Sources */, + 3F5E1BE10D04BF110020CA24 /* server-main.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/hw/darwin/apple/bundle-main.c b/hw/darwin/apple/bundle-main.c index 10d2f2041..c436d51bb 100644 --- a/hw/darwin/apple/bundle-main.c +++ b/hw/darwin/apple/bundle-main.c @@ -1,6 +1,7 @@ -/* bundle-main.c -- X server launcher +/* main.c -- X application launcher - Copyright (c) 2002-2007 Apple Inc. All rights reserved. + Copyright (c) 2007 Jeremy Huddleston + Copyright (c) 2007 Apple Inc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -25,880 +26,57 @@ Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without - prior written authorization. - - Parts of this file are derived from xdm, which has this copyright: - - Copyright 1988, 1998 The Open Group - - Permission to use, copy, modify, distribute, and sell this software - and its documentation for any purpose is hereby granted without fee, - provided that the above copyright notice appear in all copies and - that both that copyright notice and this permission notice appear in - supporting documentation. - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of The Open Group shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from The Open Group. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + prior written authorization. */ #include -#include -#include +#include +#include +#include -#include -#include +int launcher_main(int argc, char **argv); +int server_main(int argc, char **argv); -#define X_SERVER "/usr/X11/bin/Xquartz" -#define XTERM_PATH "/usr/X11/bin/xterm" -#define WM_PATH "/usr/bin/quartz-wm" -#define DEFAULT_XINITRC "/usr/X11/lib/X11/xinit/xinitrc" -#define DEFAULT_PATH "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/X11/bin" - -/* what xinit does */ -#ifndef SHELL -# define SHELL "sh" -#endif - -#undef FALSE -#define FALSE 0 -#undef TRUE -#define TRUE 1 - -#define MAX_DISPLAYS 64 - -static int server_pid = -1, client_pid = -1; -static int xinit_kills_server = FALSE; -static jmp_buf exit_continuation; -static const char *server_name = NULL; -static Display *server_dpy; - -static char *auth_file; - -typedef struct addr_list_struct addr_list; - -struct addr_list_struct { - addr_list *next; - Xauth auth; -}; - -static addr_list *addresses; - - -/* Utility functions. */ - -/* Return the current host name. Matches what Xlib does. */ -static char * -host_name (void) -{ -#ifdef NEED_UTSNAME - static struct utsname name; - - uname(&name); - - return name.nodename; -#else - static char buf[100]; - - gethostname(buf, sizeof(buf)); - - return buf; -#endif -} - -static int -read_boolean_pref (CFStringRef name, int default_) -{ - int value; - Boolean ok; - - value = CFPreferencesGetAppBooleanValue (name, - CFSTR ("com.apple.x11"), &ok); - return ok ? value : default_; -} - -static inline int -binary_equal (const void *a, const void *b, int length) -{ - return memcmp (a, b, length) == 0; -} - -static inline void * -binary_dup (const void *a, int length) -{ - void *b = malloc (length); - if (b != NULL) - memcpy (b, a, length); - return b; -} - -static inline void -binary_free (void *data, int length) -{ - if (data != NULL) - free (data); -} - - -/* Functions for managing the authentication entries. */ - -/* Returns true if something matching AUTH is in our list of auth items */ -static int -check_auth_item (Xauth *auth) -{ - addr_list *a; - - for (a = addresses; a != NULL; a = a->next) - { - if (a->auth.family == auth->family - && a->auth.address_length == auth->address_length - && binary_equal (a->auth.address, auth->address, auth->address_length) - && a->auth.number_length == auth->number_length - && binary_equal (a->auth.number, auth->number, auth->number_length) - && a->auth.name_length == auth->name_length - && binary_equal (a->auth.name, auth->name, auth->name_length)) - { - return TRUE; - } +int main(int argc, char **argv) { + Display *display; + + fprintf(stderr, "X11.app: main(): argc=%d\n", argc); + int i; + for(i=0; i < argc; i++) { + fprintf(stderr, "\targv[%d] = %s\n", i, argv[i]); } - - return FALSE; -} - -/* Add one item to our list of auth items. */ -static void -add_auth_item (Xauth *auth) -{ - addr_list *a = malloc (sizeof (addr_list)); - - a->auth.family = auth->family; - a->auth.address_length = auth->address_length; - a->auth.address = binary_dup (auth->address, auth->address_length); - a->auth.number_length = auth->number_length; - a->auth.number = binary_dup (auth->number, auth->number_length); - a->auth.name_length = auth->name_length; - a->auth.name = binary_dup (auth->name, auth->name_length); - a->auth.data_length = auth->data_length; - a->auth.data = binary_dup (auth->data, auth->data_length); - - a->next = addresses; - addresses = a; -} - -/* Free all allocated auth items. */ -static void -free_auth_items (void) -{ - addr_list *a; - - while ((a = addresses) != NULL) - { - addresses = a->next; - - binary_free (a->auth.address, a->auth.address_length); - binary_free (a->auth.number, a->auth.number_length); - binary_free (a->auth.name, a->auth.name_length); - binary_free (a->auth.data, a->auth.data_length); - free (a); - } -} - -/* Add the unix domain auth item. */ -static void -define_local (Xauth *auth) -{ - char *host = host_name (); - -#ifdef DEBUG - fprintf (stderr, "x11: hostname is %s\n", host); -#endif - - auth->family = FamilyLocal; - auth->address_length = strlen (host); - auth->address = host; - - add_auth_item (auth); -} - -/* Add the tcp auth item. */ -static void -define_named (Xauth *auth, const char *name) -{ - struct ifaddrs *addrs, *ptr; - - if (getifaddrs (&addrs) != 0) - return; - - for (ptr = addrs; ptr != NULL; ptr = ptr->ifa_next) - { - if (ptr->ifa_addr->sa_family != AF_INET) - continue; - - auth->family = FamilyInternet; - auth->address_length = sizeof (struct in_addr); - auth->address = (char *) &(((struct sockaddr_in *) ptr->ifa_addr)->sin_addr); - -#ifdef DEBUG - fprintf (stderr, "x11: ipaddr is %d.%d.%d.%d\n", - (unsigned char) auth->address[0], - (unsigned char) auth->address[1], - (unsigned char) auth->address[2], - (unsigned char) auth->address[3]); -#endif - - add_auth_item (auth); - } - - freeifaddrs (addrs); -} - -/* Parse the display number from NAME and add it to AUTH. */ -static void -set_auth_number (Xauth *auth, const char *name) -{ - char *colon; - char *dot, *number; - - colon = strrchr(name, ':'); - if (colon != NULL) - { - colon++; - dot = strchr(colon, '.'); - - if (dot != NULL) - auth->number_length = dot - colon; - else - auth->number_length = strlen (colon); - - number = malloc (auth->number_length + 1); - if (number != NULL) - { - strncpy (number, colon, auth->number_length); - number[auth->number_length] = '\0'; - } - else - { - auth->number_length = 0; - } - - auth->number = number; - } -} - -/* Put 128 bits of random data into DATA. If possible, it will be "high - quality" */ -static int -generate_mit_magic_cookie (char data[16]) -{ - int fd, ret, i; - long *ldata = (long *) data; - - fd = open ("/dev/random", O_RDONLY); - if (fd > 0) { - ret = read (fd, data, 16); - close (fd); - if (ret == 16) return TRUE; - } - - /* fall back to the usual crappy rng */ - - srand48 (getpid () ^ time (NULL)); - - for (i = 0; i < 4; i++) - ldata[i] = lrand48 (); - - return TRUE; -} - -/* Create the keys we'll be using for the display named NAME. */ -static int -make_auth_keys (const char *name) -{ - Xauth auth; - char key[16]; - - if (auth_file == NULL) - return FALSE; - - auth.name = "MIT-MAGIC-COOKIE-1"; - auth.name_length = strlen (auth.name); - - if (!generate_mit_magic_cookie (key)) - { - auth_file = NULL; - return FALSE; - } - - auth.data = key; - auth.data_length = 16; - - set_auth_number (&auth, name); - - define_named (&auth, host_name ()); - define_local (&auth); - - free (auth.number); - - return TRUE; -} - -/* If ADD-ENTRIES is true, merge our auth entries into the existing - Xauthority file. If ADD-ENTRIES is false, remove our entries. */ -static int -write_auth_file (int add_entries) -{ - char *home, newname[1024]; - int fd, ret; - FILE *new_fh, *old_fh; - addr_list *addr; - Xauth *auth; - - if (auth_file == NULL) - return FALSE; - - home = getenv ("HOME"); - if (home == NULL) - { - auth_file = NULL; - return FALSE; - } - - snprintf (newname, sizeof (newname), "%s/.XauthorityXXXXXX", home); - mktemp (newname); - - if (XauLockAuth (auth_file, 1, 2, 10) != LOCK_SUCCESS) - { - /* FIXME: do something here? */ - - auth_file = NULL; - return FALSE; - } - - fd = open (newname, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd >= 0) - { - new_fh = fdopen (fd, "w"); - if (new_fh != NULL) - { - if (add_entries) - { - for (addr = addresses; addr != NULL; addr = addr->next) - { - XauWriteAuth (new_fh, &addr->auth); - } - } - - old_fh = fopen (auth_file, "r"); - if (old_fh != NULL) - { - while ((auth = XauReadAuth (old_fh)) != NULL) - { - if (!check_auth_item (auth)) - XauWriteAuth (new_fh, auth); - XauDisposeAuth (auth); - } - fclose (old_fh); - } - - fclose (new_fh); - unlink (auth_file); - - ret = rename (newname, auth_file); - - if (ret != 0) - auth_file = NULL; - - XauUnlockAuth (auth_file); - return ret == 0; - } - - close (fd); - } - - XauUnlockAuth (auth_file); - auth_file = NULL; - return FALSE; -} - - -/* Subprocess management functions. */ - -static int -start_server (char **xargv) -{ - int child; - - child = fork (); - - switch (child) - { - case -1: /* error */ - perror ("fork"); - return FALSE; - - case 0: /* child */ - execv (X_SERVER, xargv); - perror ("Couldn't exec " X_SERVER); - _exit (1); - - default: /* parent */ - server_pid = child; - return TRUE; - } -} - -static int -wait_for_server (void) -{ - int count = 100; - - while (count-- > 0) - { - int status; - - server_dpy = XOpenDisplay (server_name); - if (server_dpy != NULL) - return TRUE; - - if (waitpid (server_pid, &status, WNOHANG) == server_pid) - return FALSE; - - sleep (1); - } - - return FALSE; -} - -static int -start_client (void) -{ - int child; - - child = fork(); - - switch (child) { - char *temp, buf[1024]; - - case -1: /* error */ - perror("fork"); - return FALSE; - - case 0: /* child */ - /* Setup environment */ - temp = getenv("DISPLAY"); - if (temp != NULL && temp[0] != 0) - setenv("DISPLAY", server_name, TRUE); - - temp = getenv("PATH"); - if (temp == NULL || temp[0] == 0) - setenv ("PATH", DEFAULT_PATH, TRUE); - else if (strnstr(temp, "/usr/X11/bin", sizeof(temp)) == NULL) { - snprintf(buf, sizeof(buf), "%s:/usr/X11/bin", temp); - setenv("PATH", buf, TRUE); - } - - /* First try value of $XINITRC, if set. */ - temp = getenv("XINITRC"); - if (temp != NULL && temp[0] != 0 && access(temp, R_OK) == 0) - execlp (SHELL, SHELL, temp, NULL); - - /* Then look for .xinitrc in user's home directory. */ - temp = getenv("HOME"); - if (temp != NULL && temp[0] != 0) { - chdir(temp); - snprintf (buf, sizeof (buf), "%s/.xinitrc", temp); - if (access(buf, R_OK) == 0) - execlp(SHELL, SHELL, buf, NULL); - } - - /* Then try the default xinitrc in the lib directory. */ - - if (access(DEFAULT_XINITRC, R_OK) == 0) - execlp(SHELL, SHELL, DEFAULT_XINITRC, NULL); - - /* Then fallback to hardcoding an xterm and the window manager. */ - - // system(XTERM_PATH " &"); - execl(WM_PATH, WM_PATH, NULL); - - perror("exec"); - _exit(1); - - default: /* parent */ - client_pid = child; - return TRUE; - } -} - -static void -sigchld_handler (int sig) -{ - int pid, status; - - again: - pid = waitpid (WAIT_ANY, &status, WNOHANG); - - if (pid > 0) - { - if (pid == server_pid) - { - server_pid = -1; - - if (client_pid >= 0) - kill (client_pid, SIGTERM); - } - else if (pid == client_pid) - { - client_pid = -1; - - if (server_pid >= 0 && xinit_kills_server) - kill (server_pid, SIGTERM); - } - goto again; - } - - if (server_pid == -1 && client_pid == -1) - longjmp (exit_continuation, 1); - - signal (SIGCHLD, sigchld_handler); -} - - -/* Server utilities. */ - -static Boolean -display_exists_p (int number) -{ - char buf[64]; - xcb_connection_t *conn; - char *fullname = NULL; - int idisplay, iscreen; - char *conn_auth_name, *conn_auth_data; - int conn_auth_namelen, conn_auth_datalen; - // extern void *_X11TransConnectDisplay (); - // extern void _XDisconnectDisplay (); - - /* Since connecting to the display waits for a few seconds if the - display doesn't exist, check for trivial non-existence - if the - socket in /tmp exists or not.. (note: if the socket exists, the - server may still not, so we need to try to connect in that case..) */ - - sprintf (buf, "/tmp/.X11-unix/X%d", number); - if (access (buf, F_OK) != 0) - return FALSE; - - sprintf (buf, ":%d", number); - conn = xcb_connect(buf, NULL); - if (xcb_connection_has_error(conn)) return FALSE; - - xcb_disconnect(conn); - return TRUE; -} - - -/* Monitoring when the system's ip addresses change. */ - -static Boolean pending_timer; - -static void -timer_callback (CFRunLoopTimerRef timer, void *info) -{ - pending_timer = FALSE; - - /* Update authentication names. Need to write .Xauthority file first - without the existing entries, then again with the new entries.. */ - - write_auth_file (FALSE); - - free_auth_items (); - make_auth_keys (server_name); - - write_auth_file (TRUE); -} - -/* This function is called when the system's ip addresses may have changed. */ -static void -ipaddr_callback (SCDynamicStoreRef store, CFArrayRef changed_keys, void *info) -{ -#if DEBUG - if (changed_keys != NULL) { - fprintf (stderr, "x11: changed sc keys: "); - CFShow (changed_keys); - } -#endif - - if (auth_file != NULL && !pending_timer) - { - CFRunLoopTimerRef timer; - - timer = CFRunLoopTimerCreate (NULL, CFAbsoluteTimeGetCurrent () + 1.0, - 0.0, 0, 0, timer_callback, NULL); - CFRunLoopAddTimer (CFRunLoopGetCurrent (), timer, - kCFRunLoopDefaultMode); - CFRelease (timer); - - pending_timer = TRUE; - } -} - -/* This code adapted from "Living in a Dynamic TCP/IP Environment" technote. */ -static Boolean -install_ipaddr_source (void) -{ - CFRunLoopSourceRef source = NULL; - - SCDynamicStoreContext context = {0}; - SCDynamicStoreRef ref; - - ref = SCDynamicStoreCreate (NULL, - CFSTR ("AddIPAddressListChangeCallbackSCF"), - ipaddr_callback, &context); - - if (ref != NULL) - { - const void *keys[4], *patterns[2]; - int i; - - keys[0] = SCDynamicStoreKeyCreateNetworkGlobalEntity (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); - keys[1] = SCDynamicStoreKeyCreateNetworkGlobalEntity (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); - keys[2] = SCDynamicStoreKeyCreateComputerName (NULL); - keys[3] = SCDynamicStoreKeyCreateHostNames (NULL); - - patterns[0] = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); - patterns[1] = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); - - if (keys[0] != NULL && keys[1] != NULL && keys[2] != NULL - && keys[3] != NULL && patterns[0] != NULL && patterns[1] != NULL) - { - CFArrayRef key_array, pattern_array; - - key_array = CFArrayCreate (NULL, keys, 4, &kCFTypeArrayCallBacks); - pattern_array = CFArrayCreate (NULL, patterns, 2, &kCFTypeArrayCallBacks); - - if (key_array != NULL || pattern_array != NULL) - { - SCDynamicStoreSetNotificationKeys (ref, key_array, pattern_array); - source = SCDynamicStoreCreateRunLoopSource (NULL, ref, 0); - } - - if (key_array != NULL) - CFRelease (key_array); - if (pattern_array != NULL) - CFRelease (pattern_array); - } - - - for (i = 0; i < 4; i++) - if (keys[i] != NULL) - CFRelease (keys[i]); - for (i = 0; i < 2; i++) - if (patterns[i] != NULL) - CFRelease (patterns[i]); - - CFRelease (ref); - } - - if (source != NULL) - { - CFRunLoopAddSource (CFRunLoopGetCurrent (), - source, kCFRunLoopDefaultMode); - CFRelease (source); - } - - return source != NULL; -} - - -/* Entrypoint. */ - -void -termination_signal_handler (int unused_sig) -{ - signal (SIGTERM, SIG_DFL); - signal (SIGHUP, SIG_DFL); - signal (SIGINT, SIG_DFL); - signal (SIGQUIT, SIG_DFL); - - longjmp (exit_continuation, 1); -} - -int -main (int argc, char **argv) -{ - char **xargv; - int i, j; - int fd; - - xargv = alloca (sizeof (char *) * (argc + 32)); - - if (!read_boolean_pref (CFSTR ("no_auth"), FALSE)) - auth_file = XauFileName (); - - /* The standard X11 behaviour is for the server to quit when the first - client exits. But it can be useful for debugging (and to mimic our - behaviour in the beta releases) to not do that. */ - - xinit_kills_server = read_boolean_pref (CFSTR ("xinit_kills_server"), TRUE); - - for (i = 1; i < argc; i++) - { - if (argv[i][0] == ':') - server_name = argv[i]; - } - - if (server_name == NULL) - { - static char name[8]; - - /* No display number specified, so search for the first unused. - - There's a big old race condition here if two servers start at - the same time, but that's fairly unlikely. We could create - lockfiles or something, but that's seems more likely to cause - problems than the race condition itself.. */ - - for (i = 0; i < MAX_DISPLAYS; i++) - { - if (!display_exists_p (i)) - break; - } - - if (i == MAX_DISPLAYS) - { - fprintf (stderr, "%s: couldn't allocate a display number", argv[0]); - exit (1); - } - - sprintf (name, ":%d", i); - server_name = name; - } - - if (auth_file != NULL) - { - /* Create new Xauth keys and add them to the .Xauthority file */ - - make_auth_keys (server_name); - write_auth_file (TRUE); - } - - /* Construct our new argv */ - - i = j = 0; - - xargv[i++] = argv[j++]; - - if (auth_file != NULL) - { - xargv[i++] = "-auth"; - xargv[i++] = auth_file; - } - - /* By default, don't listen on tcp sockets if Xauth is disabled. */ - - if (read_boolean_pref (CFSTR ("nolisten_tcp"), auth_file == NULL)) - { - xargv[i++] = "-nolisten"; - xargv[i++] = "tcp"; - } - - while (j < argc) - { - if (argv[j++][0] != ':') - xargv[i++] = argv[j-1]; - } - - xargv[i++] = (char *) server_name; - xargv[i++] = NULL; - - /* Detach from any controlling terminal and connect stdin to /dev/null */ - -#ifdef TIOCNOTTY - fd = open ("/dev/tty", O_RDONLY); - if (fd != -1) - { - ioctl (fd, TIOCNOTTY, 0); - close (fd); - } -#endif - - fd = open ("/dev/null", O_RDWR, 0); - if (fd >= 0) - { - dup2 (fd, 0); - if (fd > 0) - close (fd); - } - - if (!start_server (xargv)) - return 1; - - if (!wait_for_server ()) - { - kill (server_pid, SIGTERM); - return 1; - } - - if (!start_client ()) - { - kill (server_pid, SIGTERM); - return 1; - } - - signal (SIGCHLD, sigchld_handler); - - signal (SIGTERM, termination_signal_handler); - signal (SIGHUP, termination_signal_handler); - signal (SIGINT, termination_signal_handler); - signal (SIGQUIT, termination_signal_handler); - - if (setjmp (exit_continuation) == 0) - { - if (install_ipaddr_source ()) - CFRunLoopRun (); - else - while (1) pause (); - } - - signal (SIGCHLD, SIG_IGN); - - if (client_pid >= 0) kill (client_pid, SIGTERM); - if (server_pid >= 0) kill (server_pid, SIGTERM); - - if (auth_file != NULL) - { - /* Remove our Xauth keys */ - - write_auth_file (FALSE); - } - - free_auth_items (); - - return 0; + /* First check if launchd started us */ + if(argc == 2 && !strncmp(argv[1], "--launchd", 9)) { + argc--; + argv[1] = argv[0]; + argv++; + fprintf(stderr, "X11.app: main(): launchd called us, running server_main()"); + return server_main(argc, argv); + } + + /* If we have a process serial number and it's our only arg, act as if + * the user double clicked the app bundle: launch app_to_run if possible + */ + if(argc == 1 || (argc == 2 && !strncmp(argv[1], "-psn_", 5))) { + /* Now, try to open a display, if so, run the launcher */ + display = XOpenDisplay(NULL); + if(display) { + fprintf(stderr, "X11.app: main(): closing the display"); + /* Could open the display, start the launcher */ + XCloseDisplay(display); + + /* Give 2 seconds for the server to start... + * TODO: *Really* fix this race condition + */ + usleep(2000); + fprintf(stderr, "X11.app: main(): running launcher_main()"); + return launcher_main(argc, argv); + } + } + + /* Couldn't open the display or we were called with arguments, + * just want to start a server. + */ + fprintf(stderr, "X11.app: main(): running server_main()"); + return server_main(argc, argv); } diff --git a/hw/darwin/launcher/bundle-main.c b/hw/darwin/apple/launcher-main.c similarity index 98% rename from hw/darwin/launcher/bundle-main.c rename to hw/darwin/apple/launcher-main.c index ca6255307..60a1624b9 100644 --- a/hw/darwin/launcher/bundle-main.c +++ b/hw/darwin/apple/launcher-main.c @@ -35,7 +35,7 @@ #define DEFAULT_APP "/usr/X11/bin/xterm" -int main (int argc, char **argv) { +int launcher_main (int argc, char **argv) { char *command = DEFAULT_APP; const char *newargv[7]; int child; diff --git a/hw/darwin/apple/org.x.X11.plist b/hw/darwin/apple/org.x.X11.plist new file mode 100644 index 000000000..6c6be91ab --- /dev/null +++ b/hw/darwin/apple/org.x.X11.plist @@ -0,0 +1,25 @@ + + + + + Label + org.x.X11 + Program + /Applications/Utilities/X11.app/Contents/MacOS/X11 + ProgramArguments + + /Applications/Utilities/X11.app/Contents/MacOS/X11 + --launchd + + Sockets + + :0 + + SecureSocketWithKey + DISPLAY + + + ServiceIPC + + + diff --git a/hw/darwin/apple/server-main.c b/hw/darwin/apple/server-main.c new file mode 100644 index 000000000..26fcbb0ab --- /dev/null +++ b/hw/darwin/apple/server-main.c @@ -0,0 +1,904 @@ +/* bundle-main.c -- X server launcher + + Copyright (c) 2002-2007 Apple Inc. All rights reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT + HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name(s) of the above + copyright holders shall not be used in advertising or otherwise to + promote the sale, use or other dealings in this Software without + prior written authorization. + + Parts of this file are derived from xdm, which has this copyright: + + Copyright 1988, 1998 The Open Group + + Permission to use, copy, modify, distribute, and sell this software + and its documentation for any purpose is hereby granted without fee, + provided that the above copyright notice appear in all copies and + that both that copyright notice and this permission notice appear in + supporting documentation. + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of The Open Group shall + not be used in advertising or otherwise to promote the sale, use or + other dealings in this Software without prior written authorization + from The Open Group. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define X_SERVER "/usr/X11/bin/Xquartz" +#define XTERM_PATH "/usr/X11/bin/xterm" +#define WM_PATH "/usr/bin/quartz-wm" +#define DEFAULT_XINITRC "/usr/X11/lib/X11/xinit/xinitrc" +#define DEFAULT_PATH "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/X11/bin" + +/* what xinit does */ +#ifndef SHELL +# define SHELL "sh" +#endif + +#undef FALSE +#define FALSE 0 +#undef TRUE +#define TRUE 1 + +#define MAX_DISPLAYS 64 + +static int server_pid = -1, client_pid = -1; +static int xinit_kills_server = FALSE; +static jmp_buf exit_continuation; +static const char *server_name = NULL; +static Display *server_dpy; + +static char *auth_file; + +typedef struct addr_list_struct addr_list; + +struct addr_list_struct { + addr_list *next; + Xauth auth; +}; + +static addr_list *addresses; + + +/* Utility functions. */ + +/* Return the current host name. Matches what Xlib does. */ +static char * +host_name (void) +{ +#ifdef NEED_UTSNAME + static struct utsname name; + + uname(&name); + + return name.nodename; +#else + static char buf[100]; + + gethostname(buf, sizeof(buf)); + + return buf; +#endif +} + +static int +read_boolean_pref (CFStringRef name, int default_) +{ + int value; + Boolean ok; + + value = CFPreferencesGetAppBooleanValue (name, + CFSTR ("com.apple.x11"), &ok); + return ok ? value : default_; +} + +static inline int +binary_equal (const void *a, const void *b, int length) +{ + return memcmp (a, b, length) == 0; +} + +static inline void * +binary_dup (const void *a, int length) +{ + void *b = malloc (length); + if (b != NULL) + memcpy (b, a, length); + return b; +} + +static inline void +binary_free (void *data, int length) +{ + if (data != NULL) + free (data); +} + + +/* Functions for managing the authentication entries. */ + +/* Returns true if something matching AUTH is in our list of auth items */ +static int +check_auth_item (Xauth *auth) +{ + addr_list *a; + + for (a = addresses; a != NULL; a = a->next) + { + if (a->auth.family == auth->family + && a->auth.address_length == auth->address_length + && binary_equal (a->auth.address, auth->address, auth->address_length) + && a->auth.number_length == auth->number_length + && binary_equal (a->auth.number, auth->number, auth->number_length) + && a->auth.name_length == auth->name_length + && binary_equal (a->auth.name, auth->name, auth->name_length)) + { + return TRUE; + } + } + + return FALSE; +} + +/* Add one item to our list of auth items. */ +static void +add_auth_item (Xauth *auth) +{ + addr_list *a = malloc (sizeof (addr_list)); + + a->auth.family = auth->family; + a->auth.address_length = auth->address_length; + a->auth.address = binary_dup (auth->address, auth->address_length); + a->auth.number_length = auth->number_length; + a->auth.number = binary_dup (auth->number, auth->number_length); + a->auth.name_length = auth->name_length; + a->auth.name = binary_dup (auth->name, auth->name_length); + a->auth.data_length = auth->data_length; + a->auth.data = binary_dup (auth->data, auth->data_length); + + a->next = addresses; + addresses = a; +} + +/* Free all allocated auth items. */ +static void +free_auth_items (void) +{ + addr_list *a; + + while ((a = addresses) != NULL) + { + addresses = a->next; + + binary_free (a->auth.address, a->auth.address_length); + binary_free (a->auth.number, a->auth.number_length); + binary_free (a->auth.name, a->auth.name_length); + binary_free (a->auth.data, a->auth.data_length); + free (a); + } +} + +/* Add the unix domain auth item. */ +static void +define_local (Xauth *auth) +{ + char *host = host_name (); + +#ifdef DEBUG + fprintf (stderr, "x11: hostname is %s\n", host); +#endif + + auth->family = FamilyLocal; + auth->address_length = strlen (host); + auth->address = host; + + add_auth_item (auth); +} + +/* Add the tcp auth item. */ +static void +define_named (Xauth *auth, const char *name) +{ + struct ifaddrs *addrs, *ptr; + + if (getifaddrs (&addrs) != 0) + return; + + for (ptr = addrs; ptr != NULL; ptr = ptr->ifa_next) + { + if (ptr->ifa_addr->sa_family != AF_INET) + continue; + + auth->family = FamilyInternet; + auth->address_length = sizeof (struct in_addr); + auth->address = (char *) &(((struct sockaddr_in *) ptr->ifa_addr)->sin_addr); + +#ifdef DEBUG + fprintf (stderr, "x11: ipaddr is %d.%d.%d.%d\n", + (unsigned char) auth->address[0], + (unsigned char) auth->address[1], + (unsigned char) auth->address[2], + (unsigned char) auth->address[3]); +#endif + + add_auth_item (auth); + } + + freeifaddrs (addrs); +} + +/* Parse the display number from NAME and add it to AUTH. */ +static void +set_auth_number (Xauth *auth, const char *name) +{ + char *colon; + char *dot, *number; + + colon = strrchr(name, ':'); + if (colon != NULL) + { + colon++; + dot = strchr(colon, '.'); + + if (dot != NULL) + auth->number_length = dot - colon; + else + auth->number_length = strlen (colon); + + number = malloc (auth->number_length + 1); + if (number != NULL) + { + strncpy (number, colon, auth->number_length); + number[auth->number_length] = '\0'; + } + else + { + auth->number_length = 0; + } + + auth->number = number; + } +} + +/* Put 128 bits of random data into DATA. If possible, it will be "high + quality" */ +static int +generate_mit_magic_cookie (char data[16]) +{ + int fd, ret, i; + long *ldata = (long *) data; + + fd = open ("/dev/random", O_RDONLY); + if (fd > 0) { + ret = read (fd, data, 16); + close (fd); + if (ret == 16) return TRUE; + } + + /* fall back to the usual crappy rng */ + + srand48 (getpid () ^ time (NULL)); + + for (i = 0; i < 4; i++) + ldata[i] = lrand48 (); + + return TRUE; +} + +/* Create the keys we'll be using for the display named NAME. */ +static int +make_auth_keys (const char *name) +{ + Xauth auth; + char key[16]; + + if (auth_file == NULL) + return FALSE; + + auth.name = "MIT-MAGIC-COOKIE-1"; + auth.name_length = strlen (auth.name); + + if (!generate_mit_magic_cookie (key)) + { + auth_file = NULL; + return FALSE; + } + + auth.data = key; + auth.data_length = 16; + + set_auth_number (&auth, name); + + define_named (&auth, host_name ()); + define_local (&auth); + + free (auth.number); + + return TRUE; +} + +/* If ADD-ENTRIES is true, merge our auth entries into the existing + Xauthority file. If ADD-ENTRIES is false, remove our entries. */ +static int +write_auth_file (int add_entries) +{ + char *home, newname[1024]; + int fd, ret; + FILE *new_fh, *old_fh; + addr_list *addr; + Xauth *auth; + + if (auth_file == NULL) + return FALSE; + + home = getenv ("HOME"); + if (home == NULL) + { + auth_file = NULL; + return FALSE; + } + + snprintf (newname, sizeof (newname), "%s/.XauthorityXXXXXX", home); + mktemp (newname); + + if (XauLockAuth (auth_file, 1, 2, 10) != LOCK_SUCCESS) + { + /* FIXME: do something here? */ + + auth_file = NULL; + return FALSE; + } + + fd = open (newname, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd >= 0) + { + new_fh = fdopen (fd, "w"); + if (new_fh != NULL) + { + if (add_entries) + { + for (addr = addresses; addr != NULL; addr = addr->next) + { + XauWriteAuth (new_fh, &addr->auth); + } + } + + old_fh = fopen (auth_file, "r"); + if (old_fh != NULL) + { + while ((auth = XauReadAuth (old_fh)) != NULL) + { + if (!check_auth_item (auth)) + XauWriteAuth (new_fh, auth); + XauDisposeAuth (auth); + } + fclose (old_fh); + } + + fclose (new_fh); + unlink (auth_file); + + ret = rename (newname, auth_file); + + if (ret != 0) + auth_file = NULL; + + XauUnlockAuth (auth_file); + return ret == 0; + } + + close (fd); + } + + XauUnlockAuth (auth_file); + auth_file = NULL; + return FALSE; +} + + +/* Subprocess management functions. */ + +static int +start_server (char **xargv) +{ + int child; + + child = fork (); + + switch (child) + { + case -1: /* error */ + perror ("fork"); + return FALSE; + + case 0: /* child */ + execv (X_SERVER, xargv); + perror ("Couldn't exec " X_SERVER); + _exit (1); + + default: /* parent */ + server_pid = child; + return TRUE; + } +} + +static int +wait_for_server (void) +{ + int count = 100; + + while (count-- > 0) + { + int status; + + server_dpy = XOpenDisplay (server_name); + if (server_dpy != NULL) + return TRUE; + + if (waitpid (server_pid, &status, WNOHANG) == server_pid) + return FALSE; + + sleep (1); + } + + return FALSE; +} + +static int +start_client (void) +{ + int child; + + child = fork(); + + switch (child) { + char *temp, buf[1024]; + + case -1: /* error */ + perror("fork"); + return FALSE; + + case 0: /* child */ + /* Setup environment */ + temp = getenv("DISPLAY"); +// if (temp == NULL && temp[0] != 0) + setenv("DISPLAY", server_name, TRUE); + + temp = getenv("PATH"); + if (temp == NULL || temp[0] == 0) + setenv ("PATH", DEFAULT_PATH, TRUE); + else if (strnstr(temp, "/usr/X11/bin", sizeof(temp)) == NULL) { + snprintf(buf, sizeof(buf), "%s:/usr/X11/bin", temp); + setenv("PATH", buf, TRUE); + } + + /* First try value of $XINITRC, if set. */ + temp = getenv("XINITRC"); + if (temp != NULL && temp[0] != 0 && access(temp, R_OK) == 0) + execlp (SHELL, SHELL, temp, NULL); + + /* Then look for .xinitrc in user's home directory. */ + temp = getenv("HOME"); + if (temp != NULL && temp[0] != 0) { + chdir(temp); + snprintf (buf, sizeof (buf), "%s/.xinitrc", temp); + if (access(buf, R_OK) == 0) + execlp(SHELL, SHELL, buf, NULL); + } + + /* Then try the default xinitrc in the lib directory. */ + + if (access(DEFAULT_XINITRC, R_OK) == 0) + execlp(SHELL, SHELL, DEFAULT_XINITRC, NULL); + + /* Then fallback to hardcoding an xterm and the window manager. */ + + // system(XTERM_PATH " &"); + execl(WM_PATH, WM_PATH, NULL); + + perror("exec"); + _exit(1); + + default: /* parent */ + client_pid = child; + return TRUE; + } +} + +static void +sigchld_handler (int sig) +{ + int pid, status; + + again: + pid = waitpid (WAIT_ANY, &status, WNOHANG); + + if (pid > 0) + { + if (pid == server_pid) + { + server_pid = -1; + + if (client_pid >= 0) + kill (client_pid, SIGTERM); + } + else if (pid == client_pid) + { + client_pid = -1; + + if (server_pid >= 0 && xinit_kills_server) + kill (server_pid, SIGTERM); + } + goto again; + } + + if (server_pid == -1 && client_pid == -1) + longjmp (exit_continuation, 1); + + signal (SIGCHLD, sigchld_handler); +} + + +/* Server utilities. */ + +static Boolean +display_exists_p (int number) +{ + char buf[64]; + xcb_connection_t *conn; + char *fullname = NULL; + int idisplay, iscreen; + char *conn_auth_name, *conn_auth_data; + int conn_auth_namelen, conn_auth_datalen; + + // extern void *_X11TransConnectDisplay (); + // extern void _XDisconnectDisplay (); + + /* Since connecting to the display waits for a few seconds if the + display doesn't exist, check for trivial non-existence - if the + socket in /tmp exists or not.. (note: if the socket exists, the + server may still not, so we need to try to connect in that case..) */ + + sprintf (buf, "/tmp/.X11-unix/X%d", number); + if (access (buf, F_OK) != 0) + return FALSE; + + sprintf (buf, ":%d", number); + conn = xcb_connect(buf, NULL); + if (xcb_connection_has_error(conn)) return FALSE; + + xcb_disconnect(conn); + return TRUE; +} + + +/* Monitoring when the system's ip addresses change. */ + +static Boolean pending_timer; + +static void +timer_callback (CFRunLoopTimerRef timer, void *info) +{ + pending_timer = FALSE; + + /* Update authentication names. Need to write .Xauthority file first + without the existing entries, then again with the new entries.. */ + + write_auth_file (FALSE); + + free_auth_items (); + make_auth_keys (server_name); + + write_auth_file (TRUE); +} + +/* This function is called when the system's ip addresses may have changed. */ +static void +ipaddr_callback (SCDynamicStoreRef store, CFArrayRef changed_keys, void *info) +{ +#if DEBUG + if (changed_keys != NULL) { + fprintf (stderr, "x11: changed sc keys: "); + CFShow (changed_keys); + } +#endif + + if (auth_file != NULL && !pending_timer) + { + CFRunLoopTimerRef timer; + + timer = CFRunLoopTimerCreate (NULL, CFAbsoluteTimeGetCurrent () + 1.0, + 0.0, 0, 0, timer_callback, NULL); + CFRunLoopAddTimer (CFRunLoopGetCurrent (), timer, + kCFRunLoopDefaultMode); + CFRelease (timer); + + pending_timer = TRUE; + } +} + +/* This code adapted from "Living in a Dynamic TCP/IP Environment" technote. */ +static Boolean +install_ipaddr_source (void) +{ + CFRunLoopSourceRef source = NULL; + + SCDynamicStoreContext context = {0}; + SCDynamicStoreRef ref; + + ref = SCDynamicStoreCreate (NULL, + CFSTR ("AddIPAddressListChangeCallbackSCF"), + ipaddr_callback, &context); + + if (ref != NULL) + { + const void *keys[4], *patterns[2]; + int i; + + keys[0] = SCDynamicStoreKeyCreateNetworkGlobalEntity (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); + keys[1] = SCDynamicStoreKeyCreateNetworkGlobalEntity (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); + keys[2] = SCDynamicStoreKeyCreateComputerName (NULL); + keys[3] = SCDynamicStoreKeyCreateHostNames (NULL); + + patterns[0] = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + patterns[1] = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); + + if (keys[0] != NULL && keys[1] != NULL && keys[2] != NULL + && keys[3] != NULL && patterns[0] != NULL && patterns[1] != NULL) + { + CFArrayRef key_array, pattern_array; + + key_array = CFArrayCreate (NULL, keys, 4, &kCFTypeArrayCallBacks); + pattern_array = CFArrayCreate (NULL, patterns, 2, &kCFTypeArrayCallBacks); + + if (key_array != NULL || pattern_array != NULL) + { + SCDynamicStoreSetNotificationKeys (ref, key_array, pattern_array); + source = SCDynamicStoreCreateRunLoopSource (NULL, ref, 0); + } + + if (key_array != NULL) + CFRelease (key_array); + if (pattern_array != NULL) + CFRelease (pattern_array); + } + + + for (i = 0; i < 4; i++) + if (keys[i] != NULL) + CFRelease (keys[i]); + for (i = 0; i < 2; i++) + if (patterns[i] != NULL) + CFRelease (patterns[i]); + + CFRelease (ref); + } + + if (source != NULL) + { + CFRunLoopAddSource (CFRunLoopGetCurrent (), + source, kCFRunLoopDefaultMode); + CFRelease (source); + } + + return source != NULL; +} + + +/* Entrypoint. */ + +void +termination_signal_handler (int unused_sig) +{ + signal (SIGTERM, SIG_DFL); + signal (SIGHUP, SIG_DFL); + signal (SIGINT, SIG_DFL); + signal (SIGQUIT, SIG_DFL); + + longjmp (exit_continuation, 1); +} + +int +server_main (int argc, char **argv) +{ + char **xargv; + int i, j; + int fd; + + xargv = alloca (sizeof (char *) * (argc + 32)); + + if (!read_boolean_pref (CFSTR ("no_auth"), FALSE)) + auth_file = XauFileName (); + + /* The standard X11 behaviour is for the server to quit when the first + client exits. But it can be useful for debugging (and to mimic our + behaviour in the beta releases) to not do that. */ + + xinit_kills_server = read_boolean_pref (CFSTR ("xinit_kills_server"), TRUE); + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == ':') + server_name = argv[i]; + } + + if (server_name == NULL) + { + static char name[8]; + + /* No display number specified, so search for the first unused. + + There's a big old race condition here if two servers start at + the same time, but that's fairly unlikely. We could create + lockfiles or something, but that's seems more likely to cause + problems than the race condition itself.. */ + + for (i = 0; i < MAX_DISPLAYS; i++) + { + if (!display_exists_p (i)) + break; + } + + if (i == MAX_DISPLAYS) + { + fprintf (stderr, "%s: couldn't allocate a display number", argv[0]); + exit (1); + } + + sprintf (name, ":%d", i); + server_name = name; + } + + if (auth_file != NULL) + { + /* Create new Xauth keys and add them to the .Xauthority file */ + + make_auth_keys (server_name); + write_auth_file (TRUE); + } + + /* Construct our new argv */ + + i = j = 0; + + xargv[i++] = argv[j++]; + + if (auth_file != NULL) + { + xargv[i++] = "-auth"; + xargv[i++] = auth_file; + } + + /* By default, don't listen on tcp sockets if Xauth is disabled. */ + + if (read_boolean_pref (CFSTR ("nolisten_tcp"), auth_file == NULL)) + { + xargv[i++] = "-nolisten"; + xargv[i++] = "tcp"; + } + + while (j < argc) + { + if (argv[j++][0] != ':') + xargv[i++] = argv[j-1]; + } + + xargv[i++] = (char *) server_name; + xargv[i++] = NULL; + + /* Detach from any controlling terminal and connect stdin to /dev/null */ + +#ifdef TIOCNOTTY + fd = open ("/dev/tty", O_RDONLY); + if (fd != -1) + { + ioctl (fd, TIOCNOTTY, 0); + close (fd); + } +#endif + + fd = open ("/dev/null", O_RDWR, 0); + if (fd >= 0) + { + dup2 (fd, 0); + if (fd > 0) + close (fd); + } + + if (!start_server (xargv)) + return 1; + + if (!wait_for_server ()) + { + kill (server_pid, SIGTERM); + return 1; + } + + if (!start_client ()) + { + kill (server_pid, SIGTERM); + return 1; + } + + signal (SIGCHLD, sigchld_handler); + + signal (SIGTERM, termination_signal_handler); + signal (SIGHUP, termination_signal_handler); + signal (SIGINT, termination_signal_handler); + signal (SIGQUIT, termination_signal_handler); + + if (setjmp (exit_continuation) == 0) + { + if (install_ipaddr_source ()) + CFRunLoopRun (); + else + while (1) pause (); + } + + signal (SIGCHLD, SIG_IGN); + + if (client_pid >= 0) kill (client_pid, SIGTERM); + if (server_pid >= 0) kill (server_pid, SIGTERM); + + if (auth_file != NULL) + { + /* Remove our Xauth keys */ + + write_auth_file (FALSE); + } + + free_auth_items (); + + return 0; +} diff --git a/hw/darwin/launcher/Info.plist b/hw/darwin/launcher/Info.plist deleted file mode 100644 index b5385b61a..000000000 --- a/hw/darwin/launcher/Info.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - X11 - CFBundleGetInfoString - 2.0, Copyright © 2003-2007, Apple Inc. - CFBundleIconFile - X11.icns - CFBundleIdentifier - org.x.X11_launcher - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - X11 - CFBundlePackageType - APPL - CFBundleShortVersionString - 2.0 - CFBundleSignature - x11l - LSUIElement - 1 - NSHumanReadableCopyright - Copyright © 2007, Apple Inc. - - diff --git a/hw/darwin/launcher/Makefile.am b/hw/darwin/launcher/Makefile.am deleted file mode 100644 index c291731d1..000000000 --- a/hw/darwin/launcher/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -bin_SCRIPTS = x11launcher - -.PHONY: x11launcher - -x11launcher: - xcodebuild CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" ARCHS="$(X11APP_ARCHS)" - -install-data-hook: - xcodebuild install DSTROOT=$(DESTDIR) INSTALL_PATH=$(APPLE_APPLICATIONS_DIR) DEPLOYMENT_LOCATION=YES SKIP_INSTALL=NO ARCHS="$(X11APP_ARCHS)" - -clean-local: - rm -rf build - -EXTRA_DIST = \ - bundle-main.c \ - Info.plist \ - X11.icns \ - X11.xcodeproj/project.pbxproj diff --git a/hw/darwin/launcher/X11.icns b/hw/darwin/launcher/X11.icns deleted file mode 100644 index d770e617ddb455d8a499550e712f93a06a69f871..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65908 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