Don't just blindly tell application X11 to do anything.
authorGuy Harris <guy@alum.mit.edu>
Thu, 21 May 2015 07:25:55 +0000 (00:25 -0700)
committerGuy Harris <guy@alum.mit.edu>
Thu, 21 May 2015 07:27:58 +0000 (07:27 +0000)
Determine what type of X11 (bundled from Apple, unbundled XQuartz) we
should have and what type we do have.

If we don't have any installed, don't tell X11 to do anything, as that
just pops up a "where is X11?" dialog; that's information the user
shouldn't need to tell the system if it is installed, and it's
information for which the user shouldn't be asked if it's not installed
- and if they're asked, they might answer incorrectly, leaving a system
that doesn't properly launch X11 for Wireshark.  (See various
ask.wireshark.org questions about this, for example.)

Pick up some changes from newer versions of Inkscape, such as using
unsigned char *, not using FSSpecs, and adding some comments, while
we're at it.

Change-Id: Ic9a2b25938c4eec5628d1c16c7db28aa0714203e
Reviewed-on: https://code.wireshark.org/review/8559
Reviewed-by: Guy Harris <guy@alum.mit.edu>
Reviewed-on: https://code.wireshark.org/review/8561

packaging/macosx/ScriptExec/main.c

index d41b3e2d784b1b0d91143f99d7ab304bb085a675..c4d90712ca6a2e792d68956b7348a4c2f9100287 100644 (file)
@@ -6,6 +6,7 @@
     With modifications by Aaron Voisine for gimp.app
     With modifications by Marianne gagnon for Wilber-loves-apple
     With modifications by Michael Wybrow for Inkscape.app
+    With modifications by ~suv for Inkscape.app
     With modifications by Gerald Combs for Wireshark.app
 
     This program is free software; you can redistribute it and/or modify
@@ -29,7 +30,8 @@
 
 /*
  * This app laucher basically takes care of:
- * - launching Wireshark and X11 when double-clicked
+ * - launching Wireshark (and, on OS X prior to Leopard, X11) when
+ *   double-clicked (it's auto-launched by launchd on Leopard and later)
  * - bringing X11 to the top when its icon is clicked in the dock (via a small applescript)
  * - catch file dropped on icon events (and double-clicked gimp documents) and notify gimp.
  * - catch quit events performed outside gimp, e.g. on the dock icon.
 #pragma mark Includes
 
 // Apple stuff
+
+// Note: including Carbon prevents building the launcher app in x86_64
+//       used for StandardAlert in RequestUserAttention(), 
+//       RedFatalAlert()
 #include <Carbon/Carbon.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <Security/Authorization.h>
@@ -52,6 +58,8 @@
 #include <sys/wait.h>
 #include <pthread.h>
 #include <stdio.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
 
 ///////////////////////////////////////
 // Definitions
 #define        kScriptFileName "script"
 #define kOpenDocFileName "openDoc"
 
-// custom carbon events
+// custom carbon event class
 #define kEventClassRedFatalAlert 911
+
+// custom carbon event types
 #define kEventKindX11Failed 911
 #define kEventKindFCCacheFailed 912
 
@@ -83,14 +93,14 @@ static void *OpenDoc(void *arg);
 static OSErr ExecuteScript(char *script, pid_t *pid);
 
 static void  GetParameters(void);
-static char* GetScript(void);
-static char* GetOpenDoc(void);
+static unsigned char* GetScript(void);
+static unsigned char* GetOpenDoc(void);
 
 OSErr LoadMenuBar(char *appName);
 
-static OSStatus FSMakePath(FSSpec file, char *path, long maxPathSize);
+static OSStatus FSMakePath(FSRef fileRef, unsigned char *path, long maxPathSize);
 static void RedFatalAlert(Str255 errorString, Str255 expStr);
-static short DoesFileExist(char *path);
+static short DoesFileExist(unsigned char *path);
 
 static OSErr AppQuitAEHandler(const AppleEvent *theAppleEvent,
                               AppleEvent *reply, long refCon);
@@ -135,6 +145,19 @@ short numArgs = 0;
 
 extern char **environ;
 
+//
+// 0 if we should expect a bundled X11, 1 if we should expect XQuartz
+//
+static int expect_xquartz;
+
+static enum {
+    BUNDLED_X11,    // bundled X11, in /usr/X11
+    XQUARTZ,        // unbundled XQuartz, in /opt/X11, with /usr/X11 linking to it
+    XQUARTZ_STUB,   // stub libraries in /usr/X11 that will tell user to install XQuartz
+    BROKEN_XQUARTZ, // unbundled XQuartz, in /opt/X11, with no /usr/X11
+    NO_X11          // no X11
+} x11_type;
+
 #pragma mark -
 
 ///////////////////////////////////////
@@ -145,6 +168,79 @@ int main(int argc, char* argv[])
     OSErr err = noErr;
     EventTypeSpec X11events = { kEventClassRedFatalAlert, kEventKindX11Failed };
     EventTypeSpec FCCacheEvents = { kEventClassRedFatalAlert, kEventKindFCCacheFailed };
+    struct utsname os_info;
+    struct stat statb_usr_x11, statb_opt_x11;
+    char symlink_target[MAXPATHLEN+1];
+    ssize_t link_length;
+
+    // Which version of OS X is this?  Use that to determine what sort
+    // of X11 we should expect to have installed
+    if (uname(&os_info) == -1) {
+        // Couldn't find out; this "should not happen",
+        // assume bundled X11 for the lulz
+        expect_xquartz = 0;
+    } else {
+        if (os_info.release[0] <= '9' && os_info.release[1] == '.') {
+            // Darwin 9.0 (Leopard) or earlier; expect bundled X11
+            expect_xquartz = 0;
+        } else if (os_info.release[0] == '1' &&
+                   (os_info.release[1] == '0' || os_info.release[1] == '1') &&
+                   os_info.release[2] == '.') {
+            // Darwin 10.0 (Snow Leopard) or 11.0 (Lion); expect bundled X11
+            expect_xquartz = 0;
+        } else {
+            // Mountain Lion or later; expect XQuartz
+            expect_xquartz = 1;
+        }
+    }
+
+    if (expect_xquartz) {
+        // Do we have /opt/X11?
+        if (lstat("/opt/X11", &statb_opt_x11) == -1) {
+            // No.  Is /usr/X11 a directory?
+            if (lstat("/usr/X11", &statb_usr_x11) != -1 &&
+                S_ISDIR(statb_opt_x11.st_mode)) {
+               // It's a directory; assume it contains the stub libraries.
+               x11_type = XQUARTZ_STUB;
+           } else {
+                // It's not a directory; assume we need X11 installed.
+                x11_type = NO_X11;
+           }
+        } else {
+            // Yes.  Is /usr/X11 a symbolic link to /opt/X11?
+            if (lstat("/usr/X11", &statb_usr_x11) != -1 &&
+                S_ISLNK(statb_opt_x11.st_mode)) {
+                // OK, it's a symlink; does it point to /opt/X11?
+                link_length = readlink("/usr/X11", symlink_target, MAXPATHLEN);
+                if (link_length == -1) {
+                    // Couldn't read it; broken X11
+                    x11_type = BROKEN_XQUARTZ;
+                } else {
+                    // Read it; nul-terminate the string
+                    symlink_target[link_length] = '\0';
+                    if (strcmp(symlink_target, "/opt/X11") == 0) {
+                        // Yes, it points to /opt/X11, so that's good
+                        x11_type = XQUARTZ;
+                    } else {
+                        // No, it doesn't - broken
+                        x11_type = BROKEN_XQUARTZ;
+                    }
+                }
+            } else {
+                // Non-existent or not a symlink
+                x11_type = BROKEN_XQUARTZ;
+            }
+        }
+    } else {
+        // Look for /usr/X11
+        if (lstat("/usr/X11", &statb_usr_x11) == -1) {
+            // No /usr/X11; tell the user to install X11
+            x11_type = NO_X11;
+        } else {
+            // Assume it's OK
+            x11_type = BUNDLED_X11;
+        }
+    }
 
     InitCursor();
 
@@ -180,7 +276,22 @@ int main(int argc, char* argv[])
     GetParameters(); //load data from files containing exec settings
 
     // compile "icon clicked" script so it's ready to execute
-    SimpleCompileAppleScript("tell application \"X11\" to activate");
+    // Don't tell it to activate if it's not installed;
+    // that will pop up the annoying "where is XQuartz?"/"where is X11?"
+    // dialog, and if somebody selects the wrong app, you end up with
+    // a messed-up system that fails to start X correctly.
+    switch (x11_type) {
+    case XQUARTZ:
+        SimpleCompileAppleScript("tell application \"XQuartz\" to activate");
+        break;
+
+    case BUNDLED_X11:
+        SimpleCompileAppleScript("tell application \"X11\" to activate");
+        break;
+
+    default:
+        break;
+    }
 
     RunApplicationEventLoop(); //Run the event loop
     return 0;
@@ -336,14 +447,13 @@ static void GetParameters (void)
 ///////////////////////////////////////
 // Get path to the script in Resources folder
 ///////////////////////////////////////
-static char* GetScript (void)
+static unsigned char* GetScript (void)
 {
     CFStringRef fileName;
     CFBundleRef appBundle;
     CFURLRef scriptFileURL;
     FSRef fileRef;
-    FSSpec fileSpec;
-    char *path;
+    unsigned char *path;
 
     //get CF URL for script
     if (! (appBundle = CFBundleGetMainBundle())) return NULL;
@@ -360,13 +470,9 @@ static char* GetScript (void)
     CFRelease(scriptFileURL);
     CFRelease(fileName);
 
-    //convert FSRef to FSSpec
-    if (FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec,
-                         NULL)) return NULL;
-
     //create path string
     if (! (path = malloc(kMaxPathLength))) return NULL;
-    if (FSMakePath(fileSpec, path, kMaxPathLength)) return NULL;
+    if (FSMakePath(fileRef, path, kMaxPathLength)) return NULL;
     if (! DoesFileExist(path)) return NULL;
 
     return path;
@@ -375,14 +481,13 @@ static char* GetScript (void)
 ///////////////////////////////////////
 // Gets the path to openDoc in Resources folder
 ///////////////////////////////////////
-static char* GetOpenDoc (void)
+static unsigned char* GetOpenDoc (void)
 {
     CFStringRef fileName;
     CFBundleRef appBundle;
     CFURLRef openDocFileURL;
     FSRef fileRef;
-    FSSpec fileSpec;
-    char *path;
+    unsigned char *path;
 
     //get CF URL for openDoc
     if (! (appBundle = CFBundleGetMainBundle())) return NULL;
@@ -399,13 +504,9 @@ static char* GetOpenDoc (void)
     CFRelease(openDocFileURL);
     CFRelease(fileName);
 
-    //convert FSRef to FSSpec
-    if (FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, NULL, &fileSpec,
-                         NULL)) return NULL;
-
     //create path string
     if (! (path = malloc(kMaxPathLength))) return NULL;
-    if (FSMakePath(fileSpec, path, kMaxPathLength)) return NULL;
+    if (FSMakePath(fileRef, path, kMaxPathLength)) return NULL;
     if (! DoesFileExist(path)) return NULL;
 
     return path;
@@ -433,14 +534,8 @@ OSErr LoadMenuBar (char *appName)
 ///////////////////////////////////////
 // Generate path string from FSSpec record
 ///////////////////////////////////////
-static OSStatus FSMakePath(FSSpec file, char *path, long maxPathSize)
+static OSStatus FSMakePath(FSRef fileRef, unsigned char *path, long maxPathSize)
 {
-    OSErr err = noErr;
-    FSRef fileRef;
-
-    //create file reference from file spec
-    if (err = FSpMakeFSRef(&file, &fileRef)) return err;
-
     // and then convert the FSRef to a path
     return FSRefMakePath(&fileRef, path, maxPathSize);
 }
@@ -457,9 +552,9 @@ static void RedFatalAlert (Str255 errorString, Str255 expStr)
 ///////////////////////////////////////
 // Determines whether file exists at path or not
 ///////////////////////////////////////
-static short DoesFileExist (char *path)
+static short DoesFileExist (unsigned char *path)
 {
-    if (access(path, F_OK) == -1) return false;
+    if (access((char *)path, F_OK) == -1) return false;
     return true;
 }
 
@@ -478,7 +573,7 @@ static OSErr AppQuitAEHandler(const AppleEvent *theAppleEvent,
 
     if (! taskDone && pid) { //kill the script process brutally
         kill(pid, 9);
-        printf("Platypus App: PID %d killed brutally\n", pid);
+        printf("Wireshark.app: PID %d killed brutally\n", pid);
     }
 
     pthread_cancel(tid);
@@ -498,31 +593,31 @@ static OSErr AppOpenDocAEHandler(const AppleEvent *theAppleEvent,
     #pragma unused (reply, refCon)
 
     OSErr err = noErr;
-    AEDescList fileSpecList;
+    AEDescList fileRefList;
     AEKeyword keyword;
     DescType type;
 
     short i;
     long count, actualSize;
 
-    FSSpec fileSpec;
-    char path[kMaxPathLength];
+    FSRef fileRef;
+    unsigned char path[kMaxPathLength];
 
     while (numArgs > 0) free(fileArgs[numArgs--]);
 
     //Read the AppleEvent
     err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList,
-                         &fileSpecList);
+                         &fileRefList);
 
-    err = AECountItems(&fileSpecList, &count); //Count number of files
+    err = AECountItems(&fileRefList, &count); //Count number of files
 
     for (i = 1; i <= count; i++) { //iteratively process each file
-        //get fsspec from apple event
-        if (! (err = AEGetNthPtr(&fileSpecList, i, typeFSS, &keyword, &type,
-                                 (Ptr)&fileSpec, sizeof(FSSpec), &actualSize)))
+        //get fsref from apple event
+        if (! (err = AEGetNthPtr(&fileRefList, i, typeFSRef, &keyword, &type,
+                                 (Ptr)&fileRef, sizeof(FSRef), &actualSize)))
         {
-            //get path from file spec
-            if ((err = FSMakePath(fileSpec, (unsigned char *)&path,
+            //get path from file ref
+            if ((err = FSMakePath(fileRef, (unsigned char *)&path,
                                   kMaxPathLength))) return err;
 
             if (numArgs == kMaxArgumentsToScript) break;
@@ -588,6 +683,8 @@ static void OpenURL(Str255 url)
 
 //////////////////////////////////
 // Handler for when X11 fails to start
+// Applies only to pre-Leopard OS X, as it's auto-started
+// through launchd in Leopard and later
 //////////////////////////////////
 static OSStatus X11FailedHandler(EventHandlerCallRef theHandlerCall,
                                  EventRef theEvent, void *userData)