FILE* pyi_path_fopen(const char* filename, const char* mode) { wchar_t wfilename[MAX_PATH]; wchar_t wmode[10]; pyi_win32_utils_from_utf8(wfilename, filename, MAX_PATH); pyi_win32_utils_from_utf8(wmode, mode, 10); return _wfopen(wfilename, wmode); }
/* Set environment variable. */ int pyi_setenv(const char *variable, const char *value){ int rc; #ifdef _WIN32 wchar_t * wvar, *wval; wvar = pyi_win32_utils_from_utf8(NULL, variable, 0); wval = pyi_win32_utils_from_utf8(NULL, value, 0); rc = SetEnvironmentVariableW(wvar, wval); free(wvar); free(wval); #else rc = setenv(variable, value, true); #endif return rc; }
/* Load the shared dynamic library (DLL) */ dylib_t pyi_utils_dlopen(const char *dllpath) { #ifdef _WIN32 wchar_t * dllpath_w; dylib_t ret; #else int dlopenMode = RTLD_NOW | RTLD_GLOBAL; #endif #ifdef AIX /* Append the RTLD_MEMBER to the open mode for 'dlopen()' * in order to load shared object member from library. */ dlopenMode |= RTLD_MEMBER; #endif #ifdef _WIN32 dllpath_w = pyi_win32_utils_from_utf8(NULL, dllpath, 0); ret = LoadLibraryExW(dllpath_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); free(dllpath_w); return ret; #else return dlopen(dllpath, dlopenMode); #endif }
char * pyi_win32_utf8_to_mbs_ex(char * dst, const char * src, size_t max, int sfn) { wchar_t * wsrc; char * mbs; wsrc = pyi_win32_utils_from_utf8(NULL, src, 0); if(NULL == wsrc) { return NULL; } if(sfn) { mbs = pyi_win32_wcs_to_mbs_sfn(wsrc); } else { mbs = pyi_win32_wcs_to_mbs(wsrc); } free(wsrc); if(NULL == mbs) { return NULL; } if(dst){ strncpy(dst, mbs, max); free(mbs); return dst; } else { return mbs; } }
int pyi_utils_create_child(const char *thisfile, const int argc, char *const argv[]) { SECURITY_ATTRIBUTES sa; STARTUPINFOW si; PROCESS_INFORMATION pi; int rc = 0; wchar_t buffer[PATH_MAX]; // TODO is there a replacement for this conversion or just use wchar_t everywhere? /* Convert file name to wchar_t from utf8. */ pyi_win32_utils_from_utf8(buffer, thisfile, PATH_MAX); // the parent process should ignore all signals it can signal(SIGABRT, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGBREAK, SIG_IGN); VS("LOADER: Setting up to run child\n"); sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; GetStartupInfoW(&si); si.lpReserved = NULL; si.lpDesktop = NULL; si.lpTitle = NULL; si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; si.wShowWindow = SW_NORMAL; si.hStdInput = (void*)_get_osfhandle(fileno(stdin)); si.hStdOutput = (void*)_get_osfhandle(fileno(stdout)); si.hStdError = (void*)_get_osfhandle(fileno(stderr)); VS("LOADER: Creating child process\n"); if (CreateProcessW( buffer, // Pointer to name of executable module. GetCommandLineW(), // pointer to command line string &sa, // pointer to process security attributes NULL, // pointer to thread security attributes TRUE, // handle inheritance flag 0, // creation flags NULL, // pointer to new environment block NULL, // pointer to current directory name &si, // pointer to STARTUPINFO &pi // pointer to PROCESS_INFORMATION )) { VS("LOADER: Waiting for child process to finish...\n"); WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, (unsigned long *)&rc); } else { FATALERROR("Error creating child process!\n"); rc = -1; } return rc; }
int pyi_path_exists(char * path) { #ifdef _WIN32 wchar_t wpath[PATH_MAX + 1]; struct _stat result; pyi_win32_utils_from_utf8(wpath, path, PATH_MAX); return _wstat(wpath, &result) == 0; #else struct stat result; return stat(path, &result) == 0; #endif }
/* Unset environment variable. */ int pyi_unsetenv(const char *variable) { int rc; #ifdef _WIN32 wchar_t * wvar; wvar = pyi_win32_utils_from_utf8(NULL, variable, 0); rc = SetEnvironmentVariableW(wvar, NULL); free(wvar); #else /* _WIN32 */ #if HAVE_UNSETENV rc = unsetenv(variable); #else /* HAVE_UNSETENV */ rc = setenv(variable, ""); #endif /* HAVE_UNSETENV */ #endif /* _WIN32 */ return rc; }
int CreateActContext(const char *manifestpath) { wchar_t * manifestpath_w; ACTCTXW ctx; BOOL activated; HANDLE k32; HANDLE (WINAPI * CreateActCtx)(PACTCTXW pActCtx); BOOL (WINAPI * ActivateActCtx)(HANDLE hActCtx, ULONG_PTR * lpCookie); /* Setup activation context */ VS("LOADER: manifestpath: %s\n", manifestpath); manifestpath_w = pyi_win32_utils_from_utf8(NULL, manifestpath, 0); k32 = LoadLibraryA("kernel32"); CreateActCtx = (void*)GetProcAddress(k32, "CreateActCtxW"); ActivateActCtx = (void*)GetProcAddress(k32, "ActivateActCtx"); if (!CreateActCtx || !ActivateActCtx) { VS("LOADER: Cannot find CreateActCtx/ActivateActCtx exports in kernel32.dll\n"); return 0; } ZeroMemory(&ctx, sizeof(ctx)); ctx.cbSize = sizeof(ACTCTX); ctx.lpSource = manifestpath_w; ctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT; hCtx = CreateActCtx(&ctx); free(manifestpath_w); if (hCtx != INVALID_HANDLE_VALUE) { VS("LOADER: Activation context created\n"); activated = ActivateActCtx(hCtx, &actToken); if (activated) { VS("LOADER: Activation context activated\n"); return 1; } } hCtx = INVALID_HANDLE_VALUE; VS("LOADER: Error activating the context: ActivateActCtx: \n%s\n", GetWinErrorString(0)); return 0; }
/* Return string copy of environment variable. */ char *pyi_getenv(const char *variable) { char *env = NULL; #ifdef _WIN32 wchar_t * wenv = NULL; wchar_t * wvar = NULL; wchar_t buf1[PATH_MAX], buf2[PATH_MAX]; DWORD rc; wvar = pyi_win32_utils_from_utf8(NULL, variable, 0); rc = GetEnvironmentVariableW(wvar, buf1, sizeof(buf1)); if(rc > 0) { wenv = buf1; /* Expand environment variables like %VAR% in value. */ rc = ExpandEnvironmentStringsW(wenv, buf2, sizeof(buf2)); if(rc > 0) { wenv = buf1; } } if(wenv) { env = pyi_win32_utils_to_utf8(NULL, wenv, 0); } #else /* Standard POSIX function. */ env = getenv(variable); #endif /* If the Python program we are about to run invokes another PyInstaller * one-file program as subprocess, this subprocess must not be fooled into * thinking that it is already unpacked. Therefore, PyInstaller deletes * the _MEIPASS2 variable from the environment in pyi_main(). * * However, on some platforms (e.g. AIX) the Python function 'os.unsetenv()' * does not always exist. In these cases we cannot delete the _MEIPASS2 * environment variable from Python but only set it to the empty string. * The code below takes into account that a variable may exist while its * value is only the empty string. * * Return copy of string to avoid modification of the process environment. */ return (env && env[0]) ? strdup(env) : NULL; }
/* Convenience function to convert current locale to wchar_t on Linux/OS X * and convert UTF-8 to wchar_t on Windows. * * To be called when converting internal PyI strings to wchar_t for * Python 3's consumption */ wchar_t * pyi_locale_char2wchar(wchar_t * dst, char * src, size_t len) { #ifdef _WIN32 return pyi_win32_utils_from_utf8(dst, src, len); #else wchar_t * buffer; saved_locale = strdup(setlocale(LC_CTYPE, NULL)); setlocale(LC_CTYPE, ""); buffer = PI_Py_DecodeLocale(src, &len); setlocale(LC_CTYPE, saved_locale); if(!buffer) { return NULL; } wcsncpy(dst, buffer, len); free(buffer); return dst; #endif }
int pyi_main(int argc, char * argv[]) { /* archive_status contain status information of the main process. */ ARCHIVE_STATUS *archive_status = NULL; char executable[PATH_MAX]; char homepath[PATH_MAX]; char archivefile[PATH_MAX]; int rc = 0; char *extractionpath = NULL; wchar_t * dllpath_w; int i = 0; #ifdef _MSC_VER /* Visual C runtime incorrectly buffers stderr */ setbuf(stderr, (char *)NULL); #endif /* _MSC_VER */ VS("PyInstaller Bootloader 3.x\n"); // TODO create special function to allocate memory for archive status pyi_arch_status_alloc_memory(archive_status); archive_status = (ARCHIVE_STATUS *) calloc(1,sizeof(ARCHIVE_STATUS)); if (archive_status == NULL) { FATALERROR("Cannot allocate memory for ARCHIVE_STATUS\n"); return -1; } pyi_path_executable(executable, argv[0]); pyi_path_archivefile(archivefile, executable); pyi_path_homepath(homepath, executable); /* For the curious: * On Windows, the UTF-8 form of MEIPASS2 is passed to pyi_setenv, which * decodes to UTF-16 before passing it to the Windows API. So the var's value * is full unicode. * * On OS X/Linux, the MEIPASS2 value is passed as the bytes received from the OS. * Only Python will care about its encoding, and it is passed to Python using * PyUnicode_DecodeFSDefault. */ extractionpath = pyi_getenv("_MEIPASS2"); /* If the Python program we are about to run invokes another PyInstaller * one-file program as subprocess, this subprocess must not be fooled into * thinking that it is already unpacked. Therefore, PyInstaller deletes * the _MEIPASS2 variable from the environment. */ pyi_unsetenv("_MEIPASS2"); VS("LOADER: _MEIPASS2 is %s\n", (extractionpath ? extractionpath : "NULL")); if (pyi_arch_setup(archive_status, homepath, &executable[strlen(homepath)])) { if (pyi_arch_setup(archive_status, homepath, &archivefile[strlen(homepath)])) { FATALERROR("Cannot open self %s or archive %s\n", executable, archivefile); return -1; } } /* These are used only in pyi_pylib_set_sys_argv, which converts to wchar_t */ archive_status->argc = argc; archive_status->argv = argv; #ifdef _WIN32 /* On Windows use single-process for --onedir mode. */ if (!extractionpath && !pyi_launch_need_to_extract_binaries(archive_status)) { VS("LOADER: No need to extract files to run; setting extractionpath to homepath\n"); extractionpath = homepath; } if(extractionpath) { /* Add extraction folder to DLL search path */ dllpath_w = pyi_win32_utils_from_utf8(NULL, extractionpath, 0); SetDllDirectory(dllpath_w); VS("LOADER: SetDllDirectory(%s)\n", extractionpath); free(dllpath_w); } #endif if (extractionpath) { VS("LOADER: Already in the child - running user's code.\n"); /* If binaries were extracted to temppath, * we pass it through status variable */ if (strcmp(homepath, extractionpath) != 0) { strcpy(archive_status->temppath, extractionpath); /* * Temp path exits - set appropriate flag and change * status->mainpath to point to temppath. */ archive_status->has_temp_directory = true; strcpy(archive_status->mainpath, archive_status->temppath); } /* Main code to initialize Python and run user's code. */ pyi_launch_initialize(archive_status); rc = pyi_launch_execute(archive_status); pyi_launch_finalize(archive_status); } else { /* status->temppath is created if necessary. */ if (pyi_launch_extract_binaries(archive_status)) { VS("LOADER: temppath is %s\n", archive_status->temppath); VS("LOADER: Error extracting binaries\n"); return -1; } /* Run the 'child' process, then clean up. */ VS("LOADER: Executing self as child\n"); pyi_setenv("_MEIPASS2", archive_status->temppath[0] != 0 ? archive_status->temppath : homepath); VS("LOADER: set _MEIPASS2 to %s\n", pyi_getenv("_MEIPASS2")); if (pyi_utils_set_environment(archive_status) == -1) return -1; /* Transform parent to background process on OSX only. */ pyi_parent_to_background(); /* Run user's code in a subprocess and pass command line arguments to it. */ rc = pyi_utils_create_child(executable, argc, argv); VS("LOADER: Back to parent (RC: %d)\n", rc); VS("LOADER: Doing cleanup\n"); if (archive_status->has_temp_directory == true) pyi_remove_temp_path(archive_status->temppath); pyi_arch_status_free_memory(archive_status); if (extractionpath != NULL) free(extractionpath); } return rc; }