diff options
Diffstat (limited to 'toolkit/xre/nsWindowsRestart.cpp')
-rw-r--r-- | toolkit/xre/nsWindowsRestart.cpp | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/toolkit/xre/nsWindowsRestart.cpp b/toolkit/xre/nsWindowsRestart.cpp new file mode 100644 index 0000000000..869ca7c92c --- /dev/null +++ b/toolkit/xre/nsWindowsRestart.cpp @@ -0,0 +1,197 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This file is not build directly. Instead, it is included in multiple +// shared objects. + +#ifdef nsWindowsRestart_cpp +# error \ + "nsWindowsRestart.cpp is not a header file, and must only be included once." +#else +# define nsWindowsRestart_cpp +#endif + +#include "mozilla/CmdLineAndEnvUtils.h" +#include "nsUTF8Utils.h" + +#include <shellapi.h> + +// Needed for CreateEnvironmentBlock +#include <userenv.h> +#ifndef __MINGW32__ +# pragma comment(lib, "userenv.lib") +#endif + +/** + * Convert UTF8 to UTF16 without using the normal XPCOM goop, which we + * can't link to updater.exe. + */ +static char16_t* AllocConvertUTF8toUTF16(const char* arg) { + // UTF16 can't be longer in units than UTF8 + size_t len = strlen(arg); + char16_t* s = new char16_t[(len + 1) * sizeof(char16_t)]; + if (!s) return nullptr; + + size_t dstLen = ::MultiByteToWideChar(CP_UTF8, 0, arg, len, + reinterpret_cast<wchar_t*>(s), len); + s[dstLen] = 0; + + return s; +} + +static void FreeAllocStrings(int argc, wchar_t** argv) { + while (argc) { + --argc; + delete[] argv[argc]; + } + + delete[] argv; +} + +static wchar_t** AllocConvertUTF8toUTF16Strings(int argc, char** argv) { + wchar_t** argvConverted = new wchar_t*[argc]; + if (!argvConverted) return nullptr; + + for (int i = 0; i < argc; ++i) { + argvConverted[i] = + reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i])); + if (!argvConverted[i]) { + FreeAllocStrings(i, argvConverted); + return nullptr; + } + } + return argvConverted; +} + +/** + * Return true if we are in a job with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE and + * we can break away from it. + * CreateProcess fails if we try to break away from a job but it's not allowed. + * So if we cannot determine the result due to a failure, we assume we don't + * need to break away and this returns false. + */ +static bool NeedToBreakAwayFromJob() { + // If we can't determine if we are in a job, we assume we're not in a job. + BOOL inJob = FALSE; + if (!::IsProcessInJob(::GetCurrentProcess(), nullptr, &inJob)) { + return false; + } + + // If there is no job, there is nothing to worry about. + if (!inJob) { + return false; + } + + // If we can't get the job object flags, we assume no need to break away from + // it. + JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {}; + if (!::QueryInformationJobObject(nullptr, JobObjectExtendedLimitInformation, + &job_info, sizeof(job_info), nullptr)) { + return false; + } + + // If JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE is not set, no need to worry about + // the job. + if (!(job_info.BasicLimitInformation.LimitFlags & + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE)) { + return false; + } + + // If we can't break away from the current job, there is nothing we can do. + if (!(job_info.BasicLimitInformation.LimitFlags & + JOB_OBJECT_LIMIT_BREAKAWAY_OK)) { + return false; + } + + // We can and need to break away from the job. + return true; +} + +/** + * Launch a child process with the specified arguments. + * @note argv[0] is ignored + * @note The form of this function that takes char **argv expects UTF-8 + */ + +BOOL WinLaunchChild(const wchar_t* exePath, int argc, wchar_t** argv, + HANDLE userToken = nullptr, HANDLE* hProcess = nullptr); + +BOOL WinLaunchChild(const wchar_t* exePath, int argc, char** argv, + HANDLE userToken, HANDLE* hProcess) { + wchar_t** argvConverted = AllocConvertUTF8toUTF16Strings(argc, argv); + if (!argvConverted) return FALSE; + + BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess); + FreeAllocStrings(argc, argvConverted); + return ok; +} + +BOOL WinLaunchChild(const wchar_t* exePath, int argc, wchar_t** argv, + HANDLE userToken, HANDLE* hProcess) { + BOOL ok; + + mozilla::UniquePtr<wchar_t[]> cl(mozilla::MakeCommandLine(argc, argv)); + if (!cl) { + return FALSE; + } + + DWORD creationFlags = + NeedToBreakAwayFromJob() ? CREATE_BREAKAWAY_FROM_JOB : 0; + + STARTUPINFOW si = {0}; + si.cb = sizeof(STARTUPINFOW); + si.lpDesktop = const_cast<LPWSTR>(L"winsta0\\Default"); + PROCESS_INFORMATION pi = {0}; + + if (userToken == nullptr) { + ok = CreateProcessW(exePath, cl.get(), + nullptr, // no special security attributes + nullptr, // no special thread attributes + FALSE, // don't inherit filehandles + creationFlags, + nullptr, // inherit my environment + nullptr, // use my current directory + &si, &pi); + } else { + // Create an environment block for the process we're about to start using + // the user's token. + LPVOID environmentBlock = nullptr; + if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) { + environmentBlock = nullptr; + } + + ok = CreateProcessAsUserW(userToken, exePath, cl.get(), + nullptr, // no special security attributes + nullptr, // no special thread attributes + FALSE, // don't inherit filehandles + creationFlags, environmentBlock, + nullptr, // use my current directory + &si, &pi); + + if (environmentBlock) { + DestroyEnvironmentBlock(environmentBlock); + } + } + + if (ok) { + if (hProcess) { + *hProcess = pi.hProcess; // the caller now owns the HANDLE + } else { + CloseHandle(pi.hProcess); + } + CloseHandle(pi.hThread); + } else { + LPVOID lpMsgBuf = nullptr; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, + 0, nullptr); + wprintf(L"Error restarting: %s\n", + lpMsgBuf ? static_cast<const wchar_t*>(lpMsgBuf) : L"(null)"); + if (lpMsgBuf) LocalFree(lpMsgBuf); + } + + return ok; +} |