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"
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";
28 // Reads path to the native messaging host manifest from the registry. Returns
29 // empty string if the path isn't found.
30 string16 GetManifestPath(const string16& native_host_name, DWORD flags) {
31 base::win::RegKey key;
34 if (key.Open(HKEY_LOCAL_MACHINE, kNativeMessagingRegistryKey,
35 KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
36 key.OpenKey(native_host_name.c_str(),
37 KEY_QUERY_VALUE | flags) != ERROR_SUCCESS ||
38 key.ReadValue(NULL, &result) != ERROR_SUCCESS) {
48 base::FilePath NativeProcessLauncher::FindManifest(
49 const std::string& native_host_name,
50 std::string* error_message) {
51 string16 native_host_name_wide = UTF8ToUTF16(native_host_name);
53 // First check 32-bit registry and then try 64-bit.
54 string16 manifest_path_str =
55 GetManifestPath(native_host_name_wide, KEY_WOW64_32KEY);
56 if (manifest_path_str.empty())
57 manifest_path_str = GetManifestPath(native_host_name_wide, KEY_WOW64_64KEY);
59 if (manifest_path_str.empty()) {
60 *error_message = "Native messaging host " + native_host_name +
62 return base::FilePath();
65 base::FilePath manifest_path(manifest_path_str);
66 if (!manifest_path.IsAbsolute()) {
67 *error_message = "Path to native messaging host manifest must be absolute.";
68 return base::FilePath();
75 bool NativeProcessLauncher::LaunchNativeProcess(
76 const CommandLine& command_line,
77 base::ProcessHandle* process_handle,
78 base::PlatformFile* read_file,
79 base::PlatformFile* write_file) {
80 // Timeout for the IO pipes.
81 const DWORD kTimeoutMs = 5000;
83 // Windows will use default buffer size when 0 is passed to
84 // CreateNamedPipeW().
85 const DWORD kBufferSize = 0;
87 if (!command_line.GetProgram().IsAbsolute()) {
88 LOG(ERROR) << "Native Messaging host path must be absolute.";
92 uint64 pipe_name_token;
93 crypto::RandBytes(&pipe_name_token, sizeof(pipe_name_token));
94 string16 out_pipe_name = base::StringPrintf(
95 L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", pipe_name_token);
96 string16 in_pipe_name = base::StringPrintf(
97 L"\\\\.\\pipe\\chrome.nativeMessaging.in.%llx", pipe_name_token);
99 // Create the pipes to read and write from.
100 base::win::ScopedHandle stdout_pipe(
101 CreateNamedPipeW(out_pipe_name.c_str(),
102 PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED |
103 FILE_FLAG_FIRST_PIPE_INSTANCE,
104 PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
106 if (!stdout_pipe.IsValid()) {
107 LOG(ERROR) << "Failed to create pipe " << out_pipe_name;
111 base::win::ScopedHandle stdin_pipe(
112 CreateNamedPipeW(in_pipe_name.c_str(),
113 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
114 FILE_FLAG_FIRST_PIPE_INSTANCE,
115 PIPE_TYPE_BYTE, 1, kBufferSize, kBufferSize,
117 if (!stdin_pipe.IsValid()) {
118 LOG(ERROR) << "Failed to create pipe " << in_pipe_name;
122 DWORD comspec_length = ::GetEnvironmentVariable(L"COMSPEC", NULL, 0);
123 if (comspec_length == 0) {
124 LOG(ERROR) << "COMSPEC is not set";
127 scoped_ptr<wchar_t[]> comspec(new wchar_t[comspec_length]);
128 ::GetEnvironmentVariable(L"COMSPEC", comspec.get(), comspec_length);
130 string16 command_line_string = command_line.GetCommandLineString();
132 string16 command = base::StringPrintf(
133 L"%ls /c %ls < %ls > %ls",
134 comspec.get(), command_line_string.c_str(),
135 in_pipe_name.c_str(), out_pipe_name.c_str());
137 base::LaunchOptions options;
138 options.start_hidden = true;
139 base::ProcessHandle cmd_handle;
140 if (!base::LaunchProcess(command.c_str(), options, &cmd_handle)) {
141 LOG(ERROR) << "Error launching process "
142 << command_line.GetProgram().MaybeAsASCII();
146 bool stdout_connected = ConnectNamedPipe(stdout_pipe.Get(), NULL) ?
147 TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
148 bool stdin_connected = ConnectNamedPipe(stdin_pipe.Get(), NULL) ?
149 TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
150 if (!stdout_connected || !stdin_connected) {
151 base::KillProcess(cmd_handle, 0, false);
152 base::CloseProcessHandle(cmd_handle);
153 LOG(ERROR) << "Failed to connect IO pipes when starting "
154 << command_line.GetProgram().MaybeAsASCII();
158 *process_handle = cmd_handle;
159 *read_file = stdout_pipe.Take();
160 *write_file = stdin_pipe.Take();
165 } // namespace extensions