Revert 285173 "Removed InProcessBrowserTest::CleanUpOnMainThread()"
[chromium-blink-merge.git] / chrome / browser / extensions / api / messaging / native_process_launcher_win.cc
blob59b1b39bb30b4e5896bf8d4ce72b6324d9e23722
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
7 #include <windows.h>
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/process/kill.h"
12 #include "base/process/launch.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/registry.h"
18 #include "base/win/scoped_handle.h"
19 #include "crypto/random.h"
21 namespace extensions {
23 const wchar_t kNativeMessagingRegistryKey[] =
24 L"SOFTWARE\\Google\\Chrome\\NativeMessagingHosts";
26 namespace {
28 // Reads path to the native messaging host manifest from the registry. Returns
29 // false if the path isn't found.
30 bool GetManifestPathWithFlags(HKEY root_key,
31 DWORD flags,
32 const base::string16& host_name,
33 base::string16* result) {
34 base::win::RegKey key;
36 if (key.Open(root_key, kNativeMessagingRegistryKey,
37 KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
38 key.OpenKey(host_name.c_str(),
39 KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
40 key.ReadValue(NULL, result) != ERROR_SUCCESS) {
41 return false;
44 return true;
47 bool GetManifestPath(HKEY root_key,
48 const base::string16& host_name,
49 base::string16* result) {
50 // First check 32-bit registry and then try 64-bit.
51 return GetManifestPathWithFlags(
52 root_key, KEY_WOW64_32KEY, host_name, result) ||
53 GetManifestPathWithFlags(
54 root_key, KEY_WOW64_64KEY, host_name, result);
57 } // namespace
59 // static
60 base::FilePath NativeProcessLauncher::FindManifest(
61 const std::string& host_name,
62 bool allow_user_level_hosts,
63 std::string* error_message) {
64 base::string16 host_name_wide = base::UTF8ToUTF16(host_name);
66 // Try to find the key in HKEY_LOCAL_MACHINE and then in HKEY_CURRENT_USER.
67 base::string16 path_str;
68 bool found = false;
69 if (allow_user_level_hosts)
70 found = GetManifestPath(HKEY_CURRENT_USER, host_name_wide, &path_str);
71 if (!found)
72 found = GetManifestPath(HKEY_LOCAL_MACHINE, host_name_wide, &path_str);
74 if (!found) {
75 *error_message =
76 "Native messaging host " + host_name + " is not registered.";
77 return base::FilePath();
80 base::FilePath manifest_path(path_str);
81 if (!manifest_path.IsAbsolute()) {
82 *error_message = "Path to native messaging host manifest must be absolute.";
83 return base::FilePath();
86 return manifest_path;
89 // static
90 bool NativeProcessLauncher::LaunchNativeProcess(
91 const CommandLine& command_line,
92 base::ProcessHandle* process_handle,
93 base::File* read_file,
94 base::File* write_file) {
95 // Timeout for the IO pipes.
96 const DWORD kTimeoutMs = 5000;
98 // Windows will use default buffer size when 0 is passed to
99 // CreateNamedPipeW().
100 const DWORD kBufferSize = 0;
102 if (!command_line.GetProgram().IsAbsolute()) {
103 LOG(ERROR) << "Native Messaging host path must be absolute.";
104 return false;
107 uint64 pipe_name_token;
108 crypto::RandBytes(&pipe_name_token, sizeof(pipe_name_token));
109 base::string16 out_pipe_name = base::StringPrintf(
110 L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", pipe_name_token);
111 base::string16 in_pipe_name = base::StringPrintf(
112 L"\\\\.\\pipe\\chrome.nativeMessaging.in.%llx", pipe_name_token);
114 // Create the pipes to read and write from.
115 base::win::ScopedHandle stdout_pipe(
116 CreateNamedPipeW(out_pipe_name.c_str(),
117 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED |
118 FILE_FLAG_FIRST_PIPE_INSTANCE,
119 PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
120 kTimeoutMs, NULL));
121 if (!stdout_pipe.IsValid()) {
122 LOG(ERROR) << "Failed to create pipe " << out_pipe_name;
123 return false;
126 base::win::ScopedHandle stdin_pipe(
127 CreateNamedPipeW(in_pipe_name.c_str(),
128 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
129 FILE_FLAG_FIRST_PIPE_INSTANCE,
130 PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
131 kTimeoutMs, NULL));
132 if (!stdin_pipe.IsValid()) {
133 LOG(ERROR) << "Failed to create pipe " << in_pipe_name;
134 return false;
137 DWORD comspec_length = ::GetEnvironmentVariable(L"COMSPEC", NULL, 0);
138 if (comspec_length == 0) {
139 LOG(ERROR) << "COMSPEC is not set";
140 return false;
142 scoped_ptr<wchar_t[]> comspec(new wchar_t[comspec_length]);
143 ::GetEnvironmentVariable(L"COMSPEC", comspec.get(), comspec_length);
145 base::string16 command_line_string = command_line.GetCommandLineString();
147 base::string16 command = base::StringPrintf(
148 L"%ls /c %ls < %ls > %ls",
149 comspec.get(), command_line_string.c_str(),
150 in_pipe_name.c_str(), out_pipe_name.c_str());
152 base::LaunchOptions options;
153 options.start_hidden = true;
154 base::win::ScopedHandle cmd_handle;
155 if (!base::LaunchProcess(command.c_str(), options, &cmd_handle)) {
156 LOG(ERROR) << "Error launching process "
157 << command_line.GetProgram().MaybeAsASCII();
158 return false;
161 bool stdout_connected = ConnectNamedPipe(stdout_pipe.Get(), NULL) ?
162 TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
163 bool stdin_connected = ConnectNamedPipe(stdin_pipe.Get(), NULL) ?
164 TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
165 if (!stdout_connected || !stdin_connected) {
166 base::KillProcess(cmd_handle.Get(), 0, false);
167 LOG(ERROR) << "Failed to connect IO pipes when starting "
168 << command_line.GetProgram().MaybeAsASCII();
169 return false;
172 *process_handle = cmd_handle.Take();
173 *read_file = base::File(stdout_pipe.Take());
174 *write_file = base::File(stdin_pipe.Take());
176 return true;
179 } // namespace extensions