1 /* 2 * This wrapper program executes a python executable hidden inside an 3 * application bundle inside the Python framework. This is needed to run 4 * GUI code: some GUI API's don't work unless the program is inside an 5 * application bundle. 6 * 7 * This program uses posix_spawn rather than plain execv because we need 8 * slightly more control over how the "real" interpreter is executed. 9 * 10 * On OSX 10.4 (and earlier) this falls back to using exec because the 11 * posix_spawnv functions aren't available there. 12 */ 13 #pragma weak_import posix_spawnattr_init 14 #pragma weak_import posix_spawnattr_setbinpref_np 15 #pragma weak_import posix_spawnattr_setflags 16 #pragma weak_import posix_spawn 17 18 #include <Python.h> 19 #include <unistd.h> 20 #ifdef HAVE_SPAWN_H 21 #include <spawn.h> 22 #endif 23 #include <stdio.h> 24 #include <string.h> 25 #include <errno.h> 26 #include <err.h> 27 #include <dlfcn.h> 28 #include <stdlib.h> 29 30 31 extern char** environ; 32 33 /* 34 * Locate the python framework by looking for the 35 * library that contains Py_Initialize. 36 * 37 * In a regular framework the structure is: 38 * 39 * Python.framework/Versions/2.7 40 * /Python 41 * /Resources/Python.app/Contents/MacOS/Python 42 * 43 * In a virtualenv style structure the expected 44 * structure is: 45 * 46 * ROOT 47 * /bin/pythonw 48 * /.Python <- the dylib 49 * /.Resources/Python.app/Contents/MacOS/Python 50 * 51 * NOTE: virtualenv's are not an officially supported 52 * feature, support for that structure is provided as 53 * a convenience. 54 */ 55 static char* get_python_path(void) 56 { 57 size_t len; 58 Dl_info info; 59 char* end; 60 char* g_path; 61 62 if (dladdr(Py_Initialize, &info) == 0) { 63 return NULL; 64 } 65 66 len = strlen(info.dli_fname); 67 68 g_path = malloc(len+60); 69 if (g_path == NULL) { 70 return NULL; 71 } 72 73 strcpy(g_path, info.dli_fname); 74 end = g_path + len - 1; 75 while (end != g_path && *end != '/') { 76 end --; 77 } 78 end++; 79 if (*end == '.') { 80 end++; 81 } 82 strcpy(end, "Resources/Python.app/Contents/MacOS/" PYTHONFRAMEWORK); 83 84 return g_path; 85 } 86 87 #ifdef HAVE_SPAWN_H 88 static void 89 setup_spawnattr(posix_spawnattr_t* spawnattr) 90 { 91 size_t ocount; 92 size_t count; 93 cpu_type_t cpu_types[1]; 94 short flags = 0; 95 #ifdef __LP64__ 96 int ch; 97 #endif 98 99 if ((errno = posix_spawnattr_init(spawnattr)) != 0) { 100 err(2, "posix_spawnattr_int"); 101 /* NOTREACHTED */ 102 } 103 104 count = 1; 105 106 /* Run the real python executable using the same architecture as this 107 * executable, this allows users to control the architecture using 108 * "arch -ppc python" 109 */ 110 111 #if defined(__ppc64__) 112 cpu_types[0] = CPU_TYPE_POWERPC64; 113 114 #elif defined(__x86_64__) 115 cpu_types[0] = CPU_TYPE_X86_64; 116 117 #elif defined(__ppc__) 118 cpu_types[0] = CPU_TYPE_POWERPC; 119 #elif defined(__i386__) 120 cpu_types[0] = CPU_TYPE_X86; 121 #else 122 # error "Unknown CPU" 123 #endif 124 125 if (posix_spawnattr_setbinpref_np(spawnattr, count, 126 cpu_types, &ocount) == -1) { 127 err(1, "posix_spawnattr_setbinpref"); 128 /* NOTREACHTED */ 129 } 130 if (count != ocount) { 131 fprintf(stderr, "posix_spawnattr_setbinpref failed to copy\n"); 132 exit(1); 133 /* NOTREACHTED */ 134 } 135 136 137 /* 138 * Set flag that causes posix_spawn to behave like execv 139 */ 140 flags |= POSIX_SPAWN_SETEXEC; 141 if ((errno = posix_spawnattr_setflags(spawnattr, flags)) != 0) { 142 err(1, "posix_spawnattr_setflags"); 143 /* NOTREACHTED */ 144 } 145 } 146 #endif 147 148 int 149 main(int argc, char **argv) { 150 char* exec_path = get_python_path(); 151 152 /* 153 * Let argv[0] refer to the new interpreter. This is needed to 154 * get the effect we want on OSX 10.5 or earlier. That is, without 155 * changing argv[0] the real interpreter won't have access to 156 * the Window Server. 157 */ 158 argv[0] = exec_path; 159 160 #ifdef HAVE_SPAWN_H 161 162 /* We're weak-linking to posix-spawnv to ensure that 163 * an executable build on 10.5 can work on 10.4. 164 */ 165 if (posix_spawn != NULL) { 166 posix_spawnattr_t spawnattr = NULL; 167 168 169 setup_spawnattr(&spawnattr); 170 posix_spawn(NULL, exec_path, NULL, 171 &spawnattr, argv, environ); 172 err(1, "posix_spawn: %s", exec_path); 173 } 174 #endif 175 execve(exec_path, argv, environ); 176 err(1, "execve: %s", argv[0]); 177 /* NOTREACHED */ 178 } 179