diff --git a/atom/app/atom_main.cc b/atom/app/atom_main.cc index 1e69ba4ba83..a3e70270026 100644 --- a/atom/app/atom_main.cc +++ b/atom/app/atom_main.cc @@ -111,6 +111,7 @@ int APIENTRY wWinMain(HINSTANCE instance, HINSTANCE, wchar_t* cmd, int) { params.instance = instance; params.sandbox_info = &sandbox_info; atom::AtomCommandLine::Init(argc, argv); + atom::AtomCommandLine::InitW(argc, wargv); return content::ContentMain(params); } diff --git a/atom/browser/browser.cc b/atom/browser/browser.cc index 92da10bc5a6..88eeaab986c 100644 --- a/atom/browser/browser.cc +++ b/atom/browser/browser.cc @@ -8,7 +8,6 @@ #include "atom/browser/atom_browser_main_parts.h" #include "atom/browser/native_window.h" -#include "atom/browser/relauncher.h" #include "atom/browser/window_list.h" #include "base/files/file_util.h" #include "base/message_loop/message_loop.h" @@ -34,15 +33,15 @@ Browser* Browser::Get() { return AtomBrowserMainParts::Get()->browser(); } -void Browser::Relaunch(const std::vector& args, - const std::string& app) { - base::FilePath exe_path; - PathService::Get(base::FILE_EXE, &exe_path); - - std::vector args_with_app(args); - args_with_app.insert(args_with_app.begin(), - app.empty() ? exe_path.value() : app); - relauncher::RelaunchApp(args_with_app); +bool Browser::Relaunch(const base::FilePath& app, + const relauncher::StringVector& args) { + if (app.empty()) { + base::FilePath exe_path; + PathService::Get(base::FILE_EXE, &exe_path); + return relauncher::RelaunchApp(exe_path, args); + } else { + return relauncher::RelaunchApp(app, args); + } } void Browser::Quit() { diff --git a/atom/browser/browser.h b/atom/browser/browser.h index 2b446d621fd..018709ff404 100644 --- a/atom/browser/browser.h +++ b/atom/browser/browser.h @@ -13,6 +13,7 @@ #include "base/observer_list.h" #include "base/strings/string16.h" #include "atom/browser/browser_observer.h" +#include "atom/browser/relauncher.h" #include "atom/browser/window_list_observer.h" #include "native_mate/arguments.h" @@ -54,8 +55,8 @@ class Browser : public WindowListObserver { void Shutdown(); // Restart the app. - void Relaunch(const std::vector& args, - const std::string& app); + bool Relaunch(const base::FilePath& app, + const relauncher::StringVector& args); // Focus the application. void Focus(); diff --git a/atom/browser/relauncher.cc b/atom/browser/relauncher.cc index 58c4d57df80..557725e6af7 100644 --- a/atom/browser/relauncher.cc +++ b/atom/browser/relauncher.cc @@ -12,7 +12,7 @@ #include "base/logging.h" #include "base/path_service.h" #include "base/process/launch.h" -#include "base/strings/stringprintf.h" +#include "base/strings/string_util.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" @@ -29,12 +29,13 @@ namespace internal { const int kRelauncherSyncFD = STDERR_FILENO + 1; #endif -const char* kRelauncherTypeArg = "--type=relauncher"; -const char* kRelauncherArgSeparator = "---"; +const CharType* kRelauncherTypeArg = FILE_PATH_LITERAL("--type=relauncher"); +const CharType* kRelauncherArgSeparator = FILE_PATH_LITERAL("---"); } // namespace internal -bool RelaunchApp(const std::vector& args) { +bool RelaunchApp(const base::FilePath& app, + const StringVector& args) { // Use the currently-running application's helper process. The automatic // update feature is careful to leave the currently-running version alone, // so this is safe even if the relaunch is the result of an update having @@ -47,15 +48,16 @@ bool RelaunchApp(const std::vector& args) { return false; } - std::vector relauncher_args; - return RelaunchAppWithHelper(child_path.value(), relauncher_args, args); + StringVector relauncher_args; + return RelaunchAppWithHelper(app, child_path, relauncher_args, args); } -bool RelaunchAppWithHelper(const std::string& helper, - const std::vector& relauncher_args, - const std::vector& args) { - std::vector relaunch_args; - relaunch_args.push_back(helper); +bool RelaunchAppWithHelper(const base::FilePath& app, + const base::FilePath& helper, + const StringVector& relauncher_args, + const StringVector& args) { + StringVector relaunch_args; + relaunch_args.push_back(helper.value()); relaunch_args.push_back(internal::kRelauncherTypeArg); relaunch_args.insert(relaunch_args.end(), @@ -63,6 +65,7 @@ bool RelaunchAppWithHelper(const std::string& helper, relaunch_args.push_back(internal::kRelauncherArgSeparator); + relaunch_args.push_back(app.value()); relaunch_args.insert(relaunch_args.end(), args.begin(), args.end()); #if defined(OS_POSIX) @@ -97,8 +100,12 @@ bool RelaunchAppWithHelper(const std::string& helper, base::LaunchOptions options; #if defined(OS_POSIX) options.fds_to_remap = &fd_map; + base::Process process = base::LaunchProcess(relaunch_args, options); +#elif defined(OS_WIN) + base::Process process = base::LaunchProcess( + base::JoinString(relaunch_args, L" "), options); #endif - if (!base::LaunchProcess(relaunch_args, options).IsValid()) { + if (!process.IsValid()) { LOG(ERROR) << "base::LaunchProcess failed"; return false; } @@ -106,7 +113,15 @@ bool RelaunchAppWithHelper(const std::string& helper, // The relauncher process is now starting up, or has started up. The // original parent process continues. -#if defined(OS_POSIX) +#if defined(OS_WIN) + // Synchronize with the relauncher process. + StringType name = internal::GetWaitEventName(process.Pid()); + HANDLE wait_event = ::CreateEventW(NULL, TRUE, FALSE, name.c_str()); + if (wait_event != NULL) { + WaitForSingleObject(wait_event, 1000); + CloseHandle(wait_event); + } +#elif defined(OS_POSIX) pipe_write_fd.reset(); // close(pipe_fds[1]); // Synchronize with the relauncher process. @@ -129,7 +144,11 @@ bool RelaunchAppWithHelper(const std::string& helper, } int RelauncherMain(const content::MainFunctionParams& main_parameters) { - const std::vector& argv = atom::AtomCommandLine::argv(); +#if defined(OS_WIN) + const StringVector& argv = atom::AtomCommandLine::wargv(); +#else + const StringVector& argv = atom::AtomCommandLine::argv(); +#endif if (argv.size() < 4 || argv[1] != internal::kRelauncherTypeArg) { LOG(ERROR) << "relauncher process invoked with unexpected arguments"; @@ -142,11 +161,11 @@ int RelauncherMain(const content::MainFunctionParams& main_parameters) { // start it in the background. bool in_relaunch_args = false; bool seen_relaunch_executable = false; - std::string relaunch_executable; - std::vector relauncher_args; - std::vector launch_args; + StringType relaunch_executable; + StringVector relauncher_args; + StringVector launch_args; for (size_t argv_index = 2; argv_index < argv.size(); ++argv_index) { - const std::string& arg(argv[argv_index]); + const StringType& arg(argv[argv_index]); if (!in_relaunch_args) { if (arg == internal::kRelauncherArgSeparator) { in_relaunch_args = true; diff --git a/atom/browser/relauncher.h b/atom/browser/relauncher.h index 624a68eaf24..6b725263f8b 100644 --- a/atom/browser/relauncher.h +++ b/atom/browser/relauncher.h @@ -32,12 +32,22 @@ #include #include +#include "base/command_line.h" + +#if defined(OS_WIN) +#include "base/process/process_handle.h" +#endif + namespace content { struct MainFunctionParams; } namespace relauncher { +using CharType = base::CommandLine::CharType; +using StringType = base::CommandLine::StringType; +using StringVector = base::CommandLine::StringVector; + // Relaunches the application using the helper application associated with the // currently running instance of Chrome in the parent browser process as the // executable for the relauncher process. |args| is an argv-style vector of @@ -49,7 +59,8 @@ namespace relauncher { // successfully. Returns true on success, although some failures can occur // after this function returns true if, for example, they occur within the // relauncher process. Returns false when the relaunch definitely failed. -bool RelaunchApp(const std::vector& args); +bool RelaunchApp(const base::FilePath& app, + const StringVector& args); // Identical to RelaunchApp, but uses |helper| as the path to the relauncher // process, and allows additional arguments to be supplied to the relauncher @@ -60,22 +71,25 @@ bool RelaunchApp(const std::vector& args); // able to communicate with one another. This variant can be useful to // relaunch the same version of Chrome from another location, using that // location's helper. -bool RelaunchAppWithHelper(const std::string& helper, - const std::vector& relauncher_args, - const std::vector& args); +bool RelaunchAppWithHelper(const base::FilePath& app, + const base::FilePath& helper, + const StringVector& relauncher_args, + const StringVector& args); // The entry point from ChromeMain into the relauncher process. int RelauncherMain(const content::MainFunctionParams& main_parameters); namespace internal { +#if defined(OS_POSIX) // The "magic" file descriptor that the relauncher process' write side of the // pipe shows up on. Chosen to avoid conflicting with stdin, stdout, and // stderr. extern const int kRelauncherSyncFD; +#endif // The "type" argument identifying a relauncher process ("--type=relauncher"). -extern const char* kRelauncherTypeArg; +extern const CharType* kRelauncherTypeArg; // The argument separating arguments intended for the relauncher process from // those intended for the relaunched process. "---" is chosen instead of "--" @@ -84,7 +98,11 @@ extern const char* kRelauncherTypeArg; // arguments intended for the relaunched process, to get the correct settings // for such things as logging and the user-data-dir in case it affects crash // reporting. -extern const char* kRelauncherArgSeparator; +extern const CharType* kRelauncherArgSeparator; + +#if defined(OS_WIN) +StringType GetWaitEventName(base::ProcessId pid); +#endif // In the relauncher process, performs the necessary synchronization steps // with the parent by setting up a kqueue to watch for it to exit, writing a @@ -94,9 +112,9 @@ extern const char* kRelauncherArgSeparator; // process and the best recovery approach is to attempt relaunch anyway. void RelauncherSynchronizeWithParent(); -int LaunchProgram(const std::vector& relauncher_args, - const std::string& program, - const std::vector& argv); +int LaunchProgram(const StringVector& relauncher_args, + const StringType& program, + const StringVector& argv); } // namespace internal diff --git a/atom/browser/relauncher_linux.cc b/atom/browser/relauncher_linux.cc index 7ad0c03cbd0..8a3c3050df5 100644 --- a/atom/browser/relauncher_linux.cc +++ b/atom/browser/relauncher_linux.cc @@ -8,7 +8,6 @@ #include #include -#include "atom/common/atom_command_line.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" diff --git a/atom/browser/relauncher_win.cc b/atom/browser/relauncher_win.cc new file mode 100644 index 00000000000..4c31466eb69 --- /dev/null +++ b/atom/browser/relauncher_win.cc @@ -0,0 +1,78 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/relauncher.h" + +#include + +#include "base/process/launch.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/win/scoped_handle.h" +#include "sandbox/win/src/nt_internals.h" +#include "sandbox/win/src/win_utils.h" +#include "ui/base/win/shell.h" + +namespace relauncher { + +namespace internal { + +namespace { + +const CharType* kWaitEventName = L"ElectronRelauncherWaitEvent"; + +HANDLE GetParentProcessHandle(base::ProcessHandle handle) { + NtQueryInformationProcessFunction NtQueryInformationProcess = nullptr; + ResolveNTFunctionPtr("NtQueryInformationProcess", &NtQueryInformationProcess); + if (!NtQueryInformationProcess) { + LOG(ERROR) << "Unable to get NtQueryInformationProcess"; + return NULL; + } + + PROCESS_BASIC_INFORMATION pbi; + LONG status = NtQueryInformationProcess( + handle, ProcessBasicInformation, + &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL); + if (!NT_SUCCESS(status)) { + LOG(ERROR) << "NtQueryInformationProcess failed"; + return NULL; + } + + return ::OpenProcess(PROCESS_ALL_ACCESS, TRUE, + pbi.InheritedFromUniqueProcessId); +} + +} // namespace + +StringType GetWaitEventName(base::ProcessId pid) { + return base::StringPrintf(L"%s-%d", kWaitEventName, static_cast(pid)); +} + +void RelauncherSynchronizeWithParent() { + base::Process process = base::Process::Current(); + base::win::ScopedHandle parent_process( + GetParentProcessHandle(process.Handle())); + + // Notify the parent process that it can quit now. + StringType name = internal::GetWaitEventName(process.Pid()); + base::win::ScopedHandle wait_event( + ::CreateEventW(NULL, TRUE, FALSE, name.c_str())); + ::SetEvent(wait_event.Get()); + + // Wait for parent process to quit. + WaitForSingleObject(parent_process.Get(), INFINITE); +} + +int LaunchProgram(const StringVector& relauncher_args, + const StringType& program, + const StringVector& args) { + base::LaunchOptions options; + base::Process process = base::LaunchProcess( + program + L" " + base::JoinString(args, L" "), options); + return process.IsValid() ? 0 : 1; +} + +} // namespace internal + +} // namespace relauncher diff --git a/atom/common/atom_command_line.cc b/atom/common/atom_command_line.cc index 2ac62385aea..08880ffe4a3 100644 --- a/atom/common/atom_command_line.cc +++ b/atom/common/atom_command_line.cc @@ -12,6 +12,11 @@ namespace atom { // static std::vector AtomCommandLine::argv_; +#if defined(OS_WIN) +// static +std::vector AtomCommandLine::wargv_; +#endif + // static void AtomCommandLine::Init(int argc, const char* const* argv) { // Hack around with the argv pointer. Used for process.title = "blah" @@ -21,6 +26,15 @@ void AtomCommandLine::Init(int argc, const char* const* argv) { } } +#if defined(OS_WIN) +// static +void AtomCommandLine::InitW(int argc, const wchar_t* const* argv) { + for (int i = 0; i < argc; ++i) { + wargv_.push_back(argv[i]); + } +} +#endif + #if defined(OS_LINUX) // static void AtomCommandLine::InitializeFromCommandLine() { diff --git a/atom/common/atom_command_line.h b/atom/common/atom_command_line.h index b5915533a41..a834ce92566 100644 --- a/atom/common/atom_command_line.h +++ b/atom/common/atom_command_line.h @@ -19,6 +19,11 @@ class AtomCommandLine { static void Init(int argc, const char* const* argv); static std::vector argv() { return argv_; } +#if defined(OS_WIN) + static void InitW(int argc, const wchar_t* const* argv); + static std::vector wargv() { return wargv_; } +#endif + #if defined(OS_LINUX) // On Linux the command line has to be read from base::CommandLine since // it is using zygote. @@ -28,6 +33,10 @@ class AtomCommandLine { private: static std::vector argv_; +#if defined(OS_WIN) + static std::vector wargv_; +#endif + DISALLOW_IMPLICIT_CONSTRUCTORS(AtomCommandLine); }; diff --git a/filenames.gypi b/filenames.gypi index 0317aef6b10..5f1fdc3b141 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -227,6 +227,7 @@ 'atom/browser/node_debugger.h', 'atom/browser/relauncher_linux.cc', 'atom/browser/relauncher_mac.cc', + 'atom/browser/relauncher_win.cc', 'atom/browser/relauncher.cc', 'atom/browser/relauncher.h', 'atom/browser/render_process_preferences.cc',