Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: chrome/browser/extensions/native_message_process.cc

Issue 10818013: Native Messaging! (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Everything is Different! Merged with MessageService Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/extensions/native_message_process.h"
6
7 #include <unistd.h>
8
9 #include "base/command_line.h"
10 #include "base/file_path.h"
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/logging.h"
14 #include "base/path_service.h"
15 #include "base/pickle.h"
16 #include "base/process_util.h"
17 #include "base/values.h"
18 #include "chrome/browser/extensions/message_service.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "content/public/browser/browser_thread.h"
21
22 namespace {
23
24 // TODO(eriq): Figure out good values for these constants.
25 const int kExitTimeoutMS = 100;
26 const uint32 kMaxMessageDataLength = 4096;
Matt Perry 2012/08/09 02:13:00 I imagine extensions will want much larger sizes,
eaugusti 2012/08/13 23:22:34 How about 10MB?
Matt Perry 2012/08/16 00:01:36 SGTM
27 const char kNativeClientDir[] = "Native Hosts";
28
29 } // namespace
30
31 namespace extensions {
32
33 NativeMessageProcess::MessageData::MessageData() {
34 }
35
36 NativeMessageProcess::MessageData::MessageData(MessageType message_type,
37 std::string message_data)
38 : type(message_type),
39 data(message_data) {
40 }
41
42 NativeMessageProcess::NativeMessageProcess(
43 MessageService* service,
44 int dest_port,
45 base::ProcessHandle handle,
46 int read_fd,
47 int write_fd,
48 bool is_send_message)
49 : service_(service),
50 dest_port_(dest_port),
51 handle_(handle),
52 watching_write_(false),
53 read_fd_(read_fd),
54 write_fd_(write_fd),
55 is_send_message_(is_send_message) {
56 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
57 scoped_read_fd_.reset(&read_fd_);
58 scoped_write_fd_.reset(&write_fd_);
59 // Always watch the read end.
60 MessageLoopForIO::current()->WatchFileDescriptor(read_fd_,
61 true, /* persistent */
62 MessageLoopForIO::WATCH_READ,
63 &read_watcher_,
64 this);
65 }
66
67 NativeMessageProcess::~NativeMessageProcess() {
68 read_watcher_.StopWatchingFileDescriptor();
69 write_watcher_.StopWatchingFileDescriptor();
70
71 // Close the fds now.
72 scoped_read_fd_.reset();
73 scoped_write_fd_.reset();
74
75 content::BrowserThread::PostTask(
76 content::BrowserThread::FILE,
77 FROM_HERE,
78 base::Bind(&NativeMessageProcess::KillProcess, handle_));
79 }
80
81 // static
82 NativeMessageProcess* NativeMessageProcess::Create(
83 const std::string& native_app_name,
84 const std::string& connection_message,
85 MessageService* service,
86 int dest_port,
87 MessageType type) {
88 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
89
90 DCHECK(type == TYPE_SEND_MESSAGE_REQUEST || type == TYPE_CONNECT);
91
92 base::FileHandleMappingVector fd_map;
93
94 int read_pipe_fds[2];
95 if (HANDLE_EINTR(pipe(read_pipe_fds)) != 0) {
96 LOG(ERROR) << "Bad read pipe";
97 return NULL;
98 }
99 file_util::ScopedFD read_pipe_read_fd(&read_pipe_fds[0]);
100 file_util::ScopedFD read_pipe_write_fd(&read_pipe_fds[1]);
101 fd_map.push_back(std::make_pair(*read_pipe_write_fd, STDOUT_FILENO));
102
103 int write_pipe_fds[2];
104 if (HANDLE_EINTR(pipe(write_pipe_fds)) != 0) {
105 LOG(ERROR) << "Bad write pipe";
106 return NULL;
107 }
108 file_util::ScopedFD write_pipe_read_fd(&write_pipe_fds[0]);
109 file_util::ScopedFD write_pipe_write_fd(&write_pipe_fds[1]);
110 fd_map.push_back(std::make_pair(*write_pipe_read_fd, STDIN_FILENO));
111
112 FilePath client_path;
113 FilePath registered_client_dir;
114 CHECK(PathService::Get(chrome::DIR_USER_DATA, &registered_client_dir));
115 registered_client_dir = registered_client_dir.Append(kNativeClientDir);
116 client_path = registered_client_dir.Append(native_app_name);
117
118 // Make sure that the client is not trying to invoke something outside of the
119 // proper directory. Eg. '../../dangerous_something.exe'.
120 if (!file_util::ContainsPath(registered_client_dir, client_path)) {
121 LOG(ERROR) << "Paths are not accepted as native app names.";
122 return NULL;
123 }
124
125 CommandLine line(client_path);
126 base::ProcessHandle handle;
127 base::LaunchOptions options;
128 options.fds_to_remap = &fd_map;
129 if (!base::LaunchProcess(line, options, &handle)) {
130 LOG(ERROR) << "Error launching process";
131 return NULL;
132 }
133
134 // We will not be reading from the write pipe, nor writing from the read pipe.
135 write_pipe_read_fd.reset();
136 read_pipe_write_fd.reset();
137
138 NativeMessageProcess* process = new NativeMessageProcess(
139 service, dest_port, handle,
140 *read_pipe_read_fd.release(), *write_pipe_write_fd.release(),
141 type == TYPE_SEND_MESSAGE_REQUEST);
142
143 process->Send(type, connection_message);
144
145 return process;
146 }
147
148 void NativeMessageProcess::WatchWrite() {
149 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
150 if (!MessageLoopForIO::current()->WatchFileDescriptor(
151 write_fd_, true, /* persistent */
152 MessageLoopForIO::WATCH_WRITE, &write_watcher_, this)) {
153 LOG(ERROR) << "Error watching for the FD.";
154 }
155 }
156
157 void NativeMessageProcess::Send(MessageType type, const std::string& json) {
Matt Perry 2012/08/09 02:13:00 What thread is this called on? Assuming the answer
eaugusti 2012/08/13 23:22:34 Done.
158 pending_messages_.push_back(MessageData(type, json));
159 if (!watching_write_) {
160 watching_write_ = true;
161 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
162 base::Bind(&NativeMessageProcess::WatchWrite, base::Unretained(this)));
163 }
164 }
165
166 void NativeMessageProcess::OnFileCanReadWithoutBlocking(int fd) {
167 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
168
169 // Make sure that the fd given to us is the same one we started with.
170 CHECK_EQ(fd, read_fd_);
171
172 // If this is a sendMessage request, stop trying to read after the first
173 // message.
174 if (is_send_message_)
175 read_watcher_.StopWatchingFileDescriptor();
176
177 MessageData data;
178 if (!ReadMessage(fd, &data)) {
179 LOG(ERROR) << "Bad Read";
180 return;
181 }
182
183 service_->PostMessageFromRenderer(dest_port_, data.data);
Matt Perry 2012/08/09 02:13:00 This must be called on the UI thread. Note you als
eaugusti 2012/08/13 23:22:34 Done.
184 }
185
186 void NativeMessageProcess::OnFileCanWriteWithoutBlocking(int fd) {
187 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
188
189 // Make sure that the fd given to us is the same one we started with.
190 CHECK_EQ(fd, write_fd_);
191
192 write_watcher_.StopWatchingFileDescriptor();
193 watching_write_ = false;
194
195 while (!pending_messages_.empty()) {
196 WriteMessage(fd, pending_messages_.back());
197 pending_messages_.pop_back();
198 }
199 }
200
201 // TODO(eriq): Update doc: byte (type) -> uint32.
202 bool NativeMessageProcess::WriteMessage(int fd, const MessageData& data) {
203 Pickle pickle;
204
205 // Pickles will always pad bytes to 32-bit alignment, so just use a unit32.
206 pickle.WriteUInt32(data.type);
207
208 // Pickles write the length of a string before it as a uint32.
209 pickle.WriteString(data.data);
210
211 // Make sure that the pickle doesn't do any unexpected padding.
212 DCHECK(8 + data.data.length() == pickle.payload_size());
213
214 if (!file_util::WriteFileDescriptor(
215 fd,
216 const_cast<const Pickle*>(&pickle)->payload(),
217 pickle.payload_size())) {
218 LOG(ERROR) << "Error writing message to the native client.";
219 return false;
220 }
221
222 return true;
223 }
224
225 bool NativeMessageProcess::ReadMessage(int fd, MessageData* message) {
226 char data[kMaxMessageDataLength];
Matt Perry 2012/08/09 02:13:00 Better to heap-allocate this, especially if you up
eaugusti 2012/08/13 23:22:34 Done.
227
228 // Read the type (uint32) and length (uint32).
229 errno = 0;
230 if (!file_util::ReadFromFD(fd, data, 8)) {
231 LOG(ERROR) << "Error reading the message type and length.";
232 return false;
233 }
234
235 Pickle pickle;
236 pickle.WriteBytes(data, 8);
237 PickleIterator pickle_it(pickle);
238 uint32 type;
239 uint32 data_length;
240 if (!pickle_it.ReadUInt32(&type) || !pickle_it.ReadUInt32(&data_length)) {
241 LOG(ERROR) << "Error getting the message type and length from the pickle.";
242 return false;
243 }
244
245 if (type < 0 || type >= NUM_MESSAGE_TYPES) {
246 LOG(ERROR) << type << " is not a valid message type.";
247 return false;
248 }
249
250 if ((is_send_message_ && (type != TYPE_SEND_MESSAGE_RESPONSE)) ||
251 (!is_send_message_ && (type != TYPE_CONNECT_MESSAGE))) {
252 LOG(ERROR) << "Recieved a message of type " << type << ". "
253 << "Expecting a message of type "
254 << (is_send_message_ ? TYPE_SEND_MESSAGE_RESPONSE :
255 TYPE_CONNECT_MESSAGE);
256 return false;
257 }
258 message->type = static_cast<MessageType>(type);
259
260 if (data_length > kMaxMessageDataLength) {
261 LOG(ERROR) << data_length << " is too large for the length of a message. "
262 << "Max message size is " << kMaxMessageDataLength;
263 return false;
264 }
265
266 if (!file_util::ReadFromFD(fd, data, data_length)) {
267 LOG(ERROR) << "Error reading the json data.";
268 return false;
269 }
270 std::string read_data(data, data_length);
271 message->data.swap(read_data);
272
273 return true;
274 }
275
276 // static
277 void NativeMessageProcess::KillProcess(base::ProcessHandle handle) {
Matt Perry 2012/08/09 02:13:00 This is a little draconian. Why do we need to be s
Aaron Boodman 2012/08/09 18:36:12 I don't think we need to kill it all. We can just
eaugusti 2012/08/13 23:22:34 Aaron and I were talking about it, and how about w
278 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
279 int exit_code = 0;
280 if (!base::WaitForExitCodeWithTimeout(
281 handle,
282 &exit_code,
283 base::TimeDelta::FromMilliseconds(kExitTimeoutMS))) {
284 // Kill the process.
285 if (!base::KillProcess(handle,
286 -1, /* error exit code */
287 false /* Don't wait for the process to exit */)) {
288 // Really kill the process.
289 base::EnsureProcessTerminated(handle);
290 }
291 }
292 }
293
294 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698