Linux: refactor zygote support
http://code.google.com/p/chromium/wiki/LinuxZygote * Move Chrome specific bits out of base * Move away from the idea of reserved file descriptors (which don't really work with zygotes) * Load resources before forking renderers (means that we don't need communication between the zygote process and the renderers) * Make sure that gdb works against the browser again * Make sure that we have different ASLR between the renderers and the browser. http://codereview.chromium.org/119335 (This is a reland. First landed in r18109, reverted in r18112.) git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18291 0039d316-1c4b-4281-b951-d872f2087c98
Esse commit está contido em:
+3
-6
@@ -133,6 +133,8 @@
|
||||
'fix_wp64.h',
|
||||
'float_util.h',
|
||||
'foundation_utils_mac.h',
|
||||
'global_descriptors_posix.h',
|
||||
'global_descriptors_posix.cc',
|
||||
'hash_tables.h',
|
||||
'histogram.cc',
|
||||
'histogram.h',
|
||||
@@ -324,6 +326,7 @@
|
||||
'tracked_objects.cc',
|
||||
'tracked_objects.h',
|
||||
'tuple.h',
|
||||
'unix_domain_socket_posix.cc',
|
||||
'values.cc',
|
||||
'values.h',
|
||||
'version.cc',
|
||||
@@ -418,9 +421,6 @@
|
||||
# so use idle_timer_none.cc instead.
|
||||
'idle_timer.cc',
|
||||
],
|
||||
'sources': [
|
||||
'zygote_manager.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'../build/util/build_util.gyp:lastchange',
|
||||
'../build/linux/system.gyp:gtk',
|
||||
@@ -685,9 +685,6 @@
|
||||
],
|
||||
'conditions': [
|
||||
['OS == "linux"', {
|
||||
'sources': [
|
||||
'zygote_manager_unittest.cc',
|
||||
],
|
||||
'sources!': [
|
||||
'file_version_info_unittest.cc',
|
||||
# Linux has an implementation of idle_timer, but it's unclear
|
||||
|
||||
@@ -167,6 +167,12 @@ bool CommandLine::IsSwitch(const StringType& parameter_string,
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
void CommandLine::Reset() {
|
||||
delete current_process_commandline_;
|
||||
current_process_commandline_ = NULL;
|
||||
}
|
||||
|
||||
// static
|
||||
void CommandLine::Init(int argc, const char* const* argv) {
|
||||
#if defined(OS_WIN)
|
||||
|
||||
@@ -45,6 +45,9 @@ class CommandLine {
|
||||
// TODO(port): should be a FilePath.
|
||||
explicit CommandLine(const std::wstring& program);
|
||||
|
||||
// Uninit and free the current process's command line.
|
||||
static void Reset();
|
||||
|
||||
// Initialize the current process CommandLine singleton. On Windows,
|
||||
// ignores its arguments (we instead parse GetCommandLineW()
|
||||
// directly) because we don't trust the CRT's parsing of the command
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/time.h"
|
||||
#include "base/zygote_manager.h"
|
||||
|
||||
namespace file_util {
|
||||
|
||||
@@ -646,19 +645,8 @@ MemoryMappedFile::MemoryMappedFile()
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
|
||||
file_ = -1;
|
||||
#if defined(OS_LINUX)
|
||||
base::ZygoteManager* zm = base::ZygoteManager::Get();
|
||||
if (zm) {
|
||||
file_ = zm->OpenFile(file_name.value().c_str());
|
||||
if (file_ == -1) {
|
||||
LOG(INFO) << "Zygote manager can't open " << file_name.value()
|
||||
<< ", retrying locally. (OK at start of ui_tests.)";
|
||||
}
|
||||
}
|
||||
#endif // defined(OS_LINUX)
|
||||
if (file_ == -1)
|
||||
file_ = open(file_name.value().c_str(), O_RDONLY);
|
||||
file_ = open(file_name.value().c_str(), O_RDONLY);
|
||||
|
||||
if (file_ == -1) {
|
||||
LOG(ERROR) << "Couldn't open " << file_name.value();
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/global_descriptors_posix.h"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
int GlobalDescriptors::MaybeGet(Key key) const {
|
||||
for (Mapping::const_iterator
|
||||
i = descriptors_.begin(); i != descriptors_.end(); ++i) {
|
||||
if (i->first == key)
|
||||
return i->second;
|
||||
}
|
||||
|
||||
// In order to make unittests pass, we define a default mapping from keys to
|
||||
// descriptors by adding a fixed offset:
|
||||
return kBaseDescriptor + key;
|
||||
}
|
||||
|
||||
int GlobalDescriptors::Get(Key key) const {
|
||||
const int ret = MaybeGet(key);
|
||||
|
||||
if (ret == -1)
|
||||
LOG(FATAL) << "Unknown global descriptor: " << key;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GlobalDescriptors::Set(Key key, int fd) {
|
||||
for (Mapping::iterator
|
||||
i = descriptors_.begin(); i != descriptors_.end(); ++i) {
|
||||
if (i->first == key) {
|
||||
i->second = fd;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
descriptors_.push_back(std::make_pair(key, fd));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_GLOBAL_DESCRIPTORS_POSIX_H_
|
||||
#define BASE_GLOBAL_DESCRIPTORS_POSIX_H_
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "base/singleton.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// It's common practice to install file descriptors into well known slot
|
||||
// numbers before execing a child; stdin, stdout and stderr are ubiqutous
|
||||
// examples.
|
||||
//
|
||||
// However, when using a zygote model, this becomes troublesome. Since the
|
||||
// descriptors which need to be in these slots generally aren't known, any code
|
||||
// could open a resource and take one of the reserved descriptors. Simply
|
||||
// overwriting the slot isn't a viable solution.
|
||||
//
|
||||
// We could try to fill the reserved slots as soon as possible, but this is a
|
||||
// fragile solution since global constructors etc are able to open files.
|
||||
//
|
||||
// Instead, we retreat from the idea of installing descriptors in specific
|
||||
// slots and add a layer of indirection in the form of this singleton object.
|
||||
// It maps from an abstract key to a descriptor. If independent modules each
|
||||
// need to define keys, then values should be chosen randomly so as not to
|
||||
// collide.
|
||||
class GlobalDescriptors {
|
||||
public:
|
||||
typedef uint32_t Key;
|
||||
// Often we want a canonical descriptor for a given Key. In this case, we add
|
||||
// the following constant to the key value:
|
||||
static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken.
|
||||
|
||||
// Get a descriptor given a key. It is a fatal error if the key is not known.
|
||||
int Get(Key key) const;
|
||||
// Get a descriptor give a key. Returns -1 on error.
|
||||
int MaybeGet(Key key) const;
|
||||
|
||||
typedef std::vector<std::pair<Key, int> > Mapping;
|
||||
|
||||
// Set the descriptor for the given key.
|
||||
void Set(Key key, int fd);
|
||||
|
||||
void Reset(const Mapping& mapping) {
|
||||
descriptors_ = mapping;
|
||||
}
|
||||
|
||||
private:
|
||||
GlobalDescriptors() { }
|
||||
friend struct DefaultSingletonTraits<GlobalDescriptors>;
|
||||
|
||||
Mapping descriptors_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_GLOBAL_DESCRIPTORS_POSIX_H_
|
||||
@@ -19,12 +19,9 @@ typedef HANDLE MutexHandle;
|
||||
#endif
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#define MAX_PATH PATH_MAX
|
||||
typedef FILE* FileHandle;
|
||||
@@ -40,7 +37,6 @@ typedef pthread_mutex_t* MutexHandle;
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug_util.h"
|
||||
#include "base/lock_impl.h"
|
||||
#include "base/reserved_file_descriptors.h"
|
||||
#include "base/string_piece.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/sys_string_conversions.h"
|
||||
@@ -208,20 +204,6 @@ bool InitializeLogFileHandle() {
|
||||
}
|
||||
}
|
||||
SetFilePointer(log_file, 0, 0, FILE_END);
|
||||
#elif defined(OS_LINUX)
|
||||
// Reserve global fd slots.
|
||||
int reserved_fds[kReservedFds];
|
||||
for (int i=0; i < kReservedFds; i++)
|
||||
reserved_fds[i] = open("/dev/null", O_RDONLY, 0);
|
||||
|
||||
log_file = fopen(log_file_name->c_str(), "a");
|
||||
|
||||
// Release the reserved fds.
|
||||
for (int i=0; i < kReservedFds; i++)
|
||||
close(reserved_fds[i]);
|
||||
|
||||
if (log_file == NULL)
|
||||
return false;
|
||||
#elif defined(OS_POSIX)
|
||||
log_file = fopen(log_file_name->c_str(), "a");
|
||||
if (log_file == NULL)
|
||||
|
||||
@@ -141,14 +141,6 @@ typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
|
||||
bool LaunchApp(const std::vector<std::string>& argv,
|
||||
const file_handle_mapping_vector& fds_to_remap,
|
||||
bool wait, ProcessHandle* process_handle);
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
// Like LaunchApp, but if zygote manager is enabled,
|
||||
// forks the zygote instead of forking and exec'ing.
|
||||
bool ForkApp(const std::vector<std::string>& argv,
|
||||
const file_handle_mapping_vector& fds_to_remap,
|
||||
ProcessHandle* process_handle);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Executes the application specified by cl. This function delegates to one
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/string_tokenizer.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/zygote_manager.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -86,22 +85,6 @@ FilePath GetProcessExecutablePath(ProcessHandle process) {
|
||||
return FilePath(std::string(exename, len));
|
||||
}
|
||||
|
||||
bool ForkApp(const std::vector<std::string>& argv,
|
||||
const file_handle_mapping_vector& fds_to_remap,
|
||||
ProcessHandle* process_handle) {
|
||||
ZygoteManager* zm = ZygoteManager::Get();
|
||||
if (!zm)
|
||||
return LaunchApp(argv, fds_to_remap, false, process_handle);
|
||||
|
||||
pid_t pid = zm->LongFork(argv, fds_to_remap);
|
||||
if (pid < 0)
|
||||
return false;
|
||||
|
||||
if (process_handle)
|
||||
*process_handle = pid;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LaunchApp(const std::vector<std::string>& argv,
|
||||
const file_handle_mapping_vector& fds_to_remap,
|
||||
bool wait, ProcessHandle* process_handle) {
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_RESERVED_FILE_DESCRIPTORS_H_
|
||||
#define BASE_RESERVED_FILE_DESCRIPTORS_H_
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
|
||||
// Chrome uses predefined file descriptors to communicate with child processes.
|
||||
// Normally this is a private contract between code that does fork/exec and the
|
||||
// code it invokes, but in zygote mode, things get a little more interesting.
|
||||
// It's a huge layering violation for this to be in base, but
|
||||
// logging and ZygoteManager need kReservedFileDescriptors, so there.
|
||||
|
||||
enum GlobalReservedFds {
|
||||
// Classic unix file descriptors.
|
||||
// Let's leave them alone even if we don't use them.
|
||||
kStdinFd = 0,
|
||||
kStdoutFd = 1,
|
||||
kStderrFd = 2,
|
||||
|
||||
// See chrome/common/ipc_channel_posix.cc
|
||||
kClientChannelFd = 3,
|
||||
|
||||
// See chrome/app/breakpad_linux.cc and
|
||||
// chrome/browser/renderer_host/browser_render_process_host.cc
|
||||
kMagicCrashSignalFd = 4,
|
||||
|
||||
// One plus highest fd mentioned in this enum.
|
||||
kReservedFds = 5
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/unix_domain_socket_posix.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
bool SendMsg(int fd, const void* buf, size_t length, std::vector<int>& fds) {
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
struct iovec iov = {const_cast<void*>(buf), length};
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
char* control_buffer = NULL;
|
||||
if (fds.size()) {
|
||||
const unsigned control_len = CMSG_SPACE(sizeof(int) * fds.size());
|
||||
control_buffer = new char[control_len];
|
||||
if (!control_buffer)
|
||||
return false;
|
||||
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
msg.msg_control = control_buffer;
|
||||
msg.msg_controllen = control_len;
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds.size());
|
||||
memcpy(CMSG_DATA(cmsg), &fds[0], sizeof(int) * fds.size());
|
||||
msg.msg_controllen = cmsg->cmsg_len;
|
||||
}
|
||||
|
||||
const ssize_t r = HANDLE_EINTR(sendmsg(fd, &msg, 0));
|
||||
const bool ret = static_cast<ssize_t>(length) == r;
|
||||
delete[] control_buffer;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t RecvMsg(int fd, void* buf, size_t length, std::vector<int>* fds) {
|
||||
static const unsigned kMaxDescriptors = 16;
|
||||
|
||||
fds->clear();
|
||||
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
struct iovec iov = {buf, length};
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
char control_buffer[CMSG_SPACE(sizeof(int) * kMaxDescriptors)];
|
||||
msg.msg_control = control_buffer;
|
||||
msg.msg_controllen = sizeof(control_buffer);
|
||||
|
||||
const ssize_t r = HANDLE_EINTR(recvmsg(fd, &msg, 0));
|
||||
if (r == -1)
|
||||
return -1;
|
||||
|
||||
int* wire_fds = NULL;
|
||||
unsigned wire_fds_len = 0;
|
||||
|
||||
if (msg.msg_controllen > 0) {
|
||||
struct cmsghdr* cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
|
||||
DCHECK(payload_len % sizeof(int) == 0);
|
||||
wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
|
||||
wire_fds_len = payload_len / sizeof(int);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) {
|
||||
for (unsigned i = 0; i < wire_fds_len; ++i)
|
||||
close(wire_fds[i]);
|
||||
errno = EMSGSIZE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fds->resize(wire_fds_len);
|
||||
memcpy(&(*fds)[0], wire_fds, sizeof(int) * wire_fds_len);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_UNIX_DOMAIN_SOCKET_POSIX_H_
|
||||
#define BASE_UNIX_DOMAIN_SOCKET_POSIX_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace base {
|
||||
|
||||
// Use sendmsg to write the given msg and include a vector
|
||||
// of file descriptors. Returns true iff successful.
|
||||
bool SendMsg(int fd, const void* msg, size_t length,
|
||||
std::vector<int>& fds);
|
||||
// Use recvmsg to read a message and an array of file descriptors. Returns
|
||||
// -1 on failure. Note: will read, at most, 16 descriptors.
|
||||
ssize_t RecvMsg(int fd, void* msg, size_t length, std::vector<int>* fds);
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_UNIX_DOMAIN_SOCKET_POSIX_H_
|
||||
@@ -1,832 +0,0 @@
|
||||
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/zygote_manager.h"
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/file.h> // for flock()
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h> // for struct iovec
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h> // for ssize_t
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/file_descriptor_shuffle.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/process_util.h"
|
||||
#include "base/reserved_file_descriptors.h"
|
||||
#include "base/singleton.h"
|
||||
|
||||
using file_util::Delete;
|
||||
|
||||
// See comment below, where sigaction is called.
|
||||
static void SIGCHLDHandler(int signal) {
|
||||
}
|
||||
|
||||
namespace base {
|
||||
|
||||
const char ZygoteManager::kZMagic[] = "zygo";
|
||||
|
||||
ZygoteManager::~ZygoteManager() {
|
||||
if (server_fd_ != -1) {
|
||||
close(server_fd_);
|
||||
server_fd_ = -1;
|
||||
}
|
||||
if (client_fd_ != -1) {
|
||||
close(client_fd_);
|
||||
client_fd_ = -1;
|
||||
}
|
||||
if (lockfd_ != -1) {
|
||||
close(lockfd_);
|
||||
lockfd_ = -1;
|
||||
}
|
||||
if (canary_fd_ != -1) {
|
||||
// Wake up the poll() in ReadAndHandleMessage()
|
||||
close(canary_fd_);
|
||||
canary_fd_ = -1;
|
||||
}
|
||||
#ifndef OFFICIAL_BUILD
|
||||
// Closing the canary kills the server,
|
||||
// so after this it's ok for e.g. unit tests
|
||||
// to start a new zygote server.
|
||||
(void) unsetenv("ZYGOTE_MANAGER_STARTED");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Runs in client process
|
||||
ZygoteManager* ZygoteManager::Get() {
|
||||
static bool checked = false;
|
||||
static bool enabled = false;
|
||||
if (!checked) {
|
||||
enabled = (getenv("ENABLE_ZYGOTE_MANAGER") != NULL);
|
||||
checked = true;
|
||||
}
|
||||
if (!enabled)
|
||||
return NULL;
|
||||
return Singleton<ZygoteManager>::get();
|
||||
}
|
||||
|
||||
// Runs in zygote manager process
|
||||
int ZygoteManager::UnpickleHeader(const Pickle& reply, void** iter) {
|
||||
std::string magic;
|
||||
if (!reply.ReadString(iter, &magic) || magic != std::string(kZMagic)) {
|
||||
LOG(ERROR) << "reply didn't start with " << kZMagic;
|
||||
return ZMBAD;
|
||||
}
|
||||
pid_t clientpid = (pid_t) -1;
|
||||
if (!reply.ReadInt(iter, &clientpid)) {
|
||||
LOG(ERROR) << "Can't read client pid";
|
||||
return ZMBAD;
|
||||
}
|
||||
if (clientpid != getpid()) {
|
||||
LOG(ERROR) << "got client pid " << clientpid << ", expected " << getpid();
|
||||
DCHECK(clientpid == getpid());
|
||||
return ZMBAD;
|
||||
}
|
||||
int kind;
|
||||
if (!reply.ReadInt(iter, &kind)) {
|
||||
LOG(ERROR) << "can't read kind";
|
||||
return ZMBAD;
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
|
||||
// Runs in client process (only used in unit test)
|
||||
bool ZygoteManager::Ping(base::TimeDelta* delta) {
|
||||
if (client_fd_ == -1)
|
||||
return false;
|
||||
|
||||
Pickle pickle;
|
||||
pickle.WriteString(kZMagic);
|
||||
pickle.WriteInt(getpid());
|
||||
pickle.WriteInt(ZMPING);
|
||||
|
||||
int bytes_sent;
|
||||
int bytes_read = -1;
|
||||
|
||||
TimeTicks time_sent = TimeTicks::HighResNow();
|
||||
|
||||
// Lock fork server, send the pickle, wait for the reply, unlock
|
||||
if (flock(lockfd_, LOCK_EX))
|
||||
LOG(ERROR) << "flock failed, errno " << errno;
|
||||
bytes_sent = HANDLE_EINTR(write(client_fd_,
|
||||
const_cast<void *>(pickle.data()), pickle.size()));
|
||||
if (bytes_sent > 0) {
|
||||
bytes_read = HANDLE_EINTR(read(client_fd_, msg_buf_, kMAX_MSG_LEN));
|
||||
}
|
||||
if (flock(lockfd_, LOCK_UN))
|
||||
LOG(ERROR) << "flock failed, errno " << errno;
|
||||
|
||||
TimeTicks time_received = TimeTicks::HighResNow();
|
||||
|
||||
if (bytes_sent < 1) {
|
||||
LOG(ERROR) << "Can't send to zm, errno " << errno;
|
||||
return false;
|
||||
}
|
||||
if (bytes_read < 1) {
|
||||
LOG(ERROR) << "Can't get from zm, errno " << errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unpickle the reply
|
||||
Pickle reply(msg_buf_, bytes_read);
|
||||
void* iter = NULL;
|
||||
int kind = UnpickleHeader(reply, &iter);
|
||||
if (kind != ZMPINGED) {
|
||||
LOG(ERROR) << "reply wrong kind " << kind;
|
||||
return false;
|
||||
}
|
||||
*delta = TimeTicks::HighResNow() - time_sent;
|
||||
LOG(INFO) << "Round trip time in microseconds: " << delta->InMicroseconds();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Runs in zygote manager process
|
||||
void ZygoteManager::PingHandler(const Pickle& request, void* iter,
|
||||
Pickle* reply, std::vector<std::string>** newargv) {
|
||||
reply->WriteInt(ZMPINGED);
|
||||
}
|
||||
|
||||
// Runs in browser process, called only by base::ForkApp()
|
||||
pid_t ZygoteManager::LongFork(const std::vector<std::string>& argv,
|
||||
const file_handle_mapping_vector& fds_to_remap) {
|
||||
if (client_fd_ == -1)
|
||||
return -1;
|
||||
|
||||
Pickle pickle;
|
||||
|
||||
// Encode the arguments and the desired remote fd numbers in the pickle,
|
||||
// and the fds in a separate buffer
|
||||
pickle.WriteString(kZMagic);
|
||||
pickle.WriteInt(getpid());
|
||||
pickle.WriteInt(ZMFORK);
|
||||
pickle.WriteInt(argv.size());
|
||||
std::vector<std::string>::const_iterator argi;
|
||||
for (argi = argv.begin(); argi != argv.end(); ++argi)
|
||||
pickle.WriteString(*argi);
|
||||
pickle.WriteInt(fds_to_remap.size());
|
||||
|
||||
// Wrap the pickle and the fds together in a msghdr
|
||||
::msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
struct iovec iov;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsg_buf_;
|
||||
msg.msg_controllen = CMSG_LEN(sizeof(int) * fds_to_remap.size());
|
||||
struct cmsghdr* cmsg;
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = msg.msg_controllen;
|
||||
int* wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
|
||||
for (size_t i = 0; i < fds_to_remap.size(); i++) {
|
||||
pickle.WriteInt(fds_to_remap[i].second);
|
||||
wire_fds[i] = fds_to_remap[i].first;
|
||||
}
|
||||
iov.iov_base = const_cast<void *>(pickle.data());
|
||||
iov.iov_len = pickle.size();
|
||||
|
||||
int bytes_sent;
|
||||
int bytes_read = -1;
|
||||
|
||||
// Lock fork server, send the pickle, wait for the reply, unlock
|
||||
if (flock(lockfd_, LOCK_EX))
|
||||
LOG(ERROR) << "flock failed, errno " << errno;
|
||||
bytes_sent = HANDLE_EINTR(sendmsg(client_fd_, &msg, MSG_WAITALL));
|
||||
if (bytes_sent > 0) {
|
||||
bytes_read = HANDLE_EINTR(read(client_fd_, msg_buf_, kMAX_MSG_LEN));
|
||||
}
|
||||
if (flock(lockfd_, LOCK_UN))
|
||||
LOG(ERROR) << "flock failed, errno " << errno;
|
||||
|
||||
if (bytes_sent < 1) {
|
||||
LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_;
|
||||
return (pid_t) -1;
|
||||
}
|
||||
if (bytes_read < 1) {
|
||||
LOG(ERROR) << "Can't get from zm, errno " << errno;
|
||||
return (pid_t) -1;
|
||||
}
|
||||
|
||||
// Unpickle the reply
|
||||
Pickle reply(msg_buf_, bytes_read);
|
||||
void* iter = NULL;
|
||||
int kind = UnpickleHeader(reply, &iter);
|
||||
if (kind != ZMFORKED) {
|
||||
LOG(ERROR) << "reply wrong kind " << kind;
|
||||
return (pid_t) -1;
|
||||
}
|
||||
pid_t newpid = (pid_t) -1;
|
||||
int pid_errno;
|
||||
if (!reply.ReadInt(&iter, &newpid) || !reply.ReadInt(&iter, &pid_errno)) {
|
||||
LOG(ERROR) << "fork failed, can't read pid/errno";
|
||||
return (pid_t) -1;
|
||||
}
|
||||
if ((newpid == (pid_t) -1) || pid_errno != 0) {
|
||||
LOG(ERROR) << "fork failed, pid " << newpid << ", errno " << pid_errno;
|
||||
return (pid_t) -1;
|
||||
}
|
||||
return newpid;
|
||||
}
|
||||
|
||||
// Runs in zygote manager process
|
||||
bool ZygoteManager::LongForkHandler(const Pickle& request, void* iter,
|
||||
Pickle* reply, std::vector<std::string>** newargv,
|
||||
const int wire_fds[], int num_wire_fds) {
|
||||
file_handle_mapping_vector fds_to_remap;
|
||||
pid_t childpid;
|
||||
|
||||
reply->WriteInt(ZMFORKED);
|
||||
|
||||
// Unpickle commandline for new child
|
||||
std::vector<std::string>* argv = new std::vector<std::string>;
|
||||
int argc;
|
||||
request.ReadInt(&iter, &argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
std::string arg;
|
||||
if (!request.ReadString(&iter, &arg)) {
|
||||
LOG(ERROR) << "can't read arg?";
|
||||
goto error_reply;
|
||||
}
|
||||
argv->push_back(arg);
|
||||
}
|
||||
// Unpickle file descriptor map for new child
|
||||
int numfds;
|
||||
request.ReadInt(&iter, &numfds);
|
||||
DCHECK(numfds == num_wire_fds);
|
||||
if (numfds != num_wire_fds) {
|
||||
LOG(ERROR) << "numfds " << numfds << " != num_wire_fds " << num_wire_fds;
|
||||
goto error_reply;
|
||||
}
|
||||
for (int i = 0; i < numfds; i++) {
|
||||
int fd;
|
||||
if (!request.ReadInt(&iter, &fd)) {
|
||||
LOG(ERROR) << "can't read fd?";
|
||||
goto error_reply;
|
||||
}
|
||||
fds_to_remap.push_back(std::pair<int, int>(wire_fds[i], fd));
|
||||
}
|
||||
|
||||
// Mitosis!
|
||||
childpid = fork();
|
||||
|
||||
if (childpid != 0) {
|
||||
// parent
|
||||
// first off, close our copy of the child's file descriptors
|
||||
for (file_handle_mapping_vector::const_iterator
|
||||
it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
|
||||
close(it->first);
|
||||
}
|
||||
|
||||
// Finish formatting the reply
|
||||
reply->WriteInt(childpid);
|
||||
if (childpid == (pid_t) -1) {
|
||||
reply->WriteInt(errno);
|
||||
return false;
|
||||
} else {
|
||||
reply->WriteInt(0);
|
||||
}
|
||||
} else {
|
||||
// child
|
||||
// Apply file descriptor map
|
||||
InjectiveMultimap fd_shuffle;
|
||||
for (file_handle_mapping_vector::const_iterator
|
||||
it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
|
||||
fd_shuffle.push_back(InjectionArc(it->first, it->second, false));
|
||||
}
|
||||
|
||||
// Avoid closing descriptor children will need to contact fork server.
|
||||
fd_shuffle.push_back(InjectionArc(client_fd_, client_fd_, false));
|
||||
// Avoid closing log descriptor we're using
|
||||
int logfd = logging::GetLoggingFileDescriptor();
|
||||
if (logfd != -1)
|
||||
fd_shuffle.push_back(InjectionArc(logfd, logfd, false));
|
||||
// And of course avoid closing the cached fds.
|
||||
std::map<std::string, int>::iterator i;
|
||||
for (i = cached_fds_.begin(); i != cached_fds_.end(); ++i) {
|
||||
fd_shuffle.push_back(InjectionArc(i->second, i->second, false));
|
||||
}
|
||||
|
||||
// If there is any clash in the mapping, this function will DCHECK.
|
||||
if (!ShuffleFileDescriptors(fd_shuffle))
|
||||
exit(127);
|
||||
|
||||
// Open this after shuffle to avoid using reserved slots.
|
||||
lockfd_ = open(lockfile_.c_str(), O_RDWR, 0);
|
||||
if (lockfd_ == -1) {
|
||||
// TODO(dkegel): real error handling
|
||||
exit(126);
|
||||
}
|
||||
// Mark it as not to be closed.
|
||||
fd_shuffle.push_back(InjectionArc(lockfd_, lockfd_, false));
|
||||
|
||||
// Also closes reserved fds.
|
||||
CloseSuperfluousFds(fd_shuffle);
|
||||
|
||||
*newargv = argv;
|
||||
// Because *newargv is set, we will return to main instead of looping
|
||||
}
|
||||
return true;
|
||||
|
||||
error_reply:
|
||||
reply->WriteInt(-1);
|
||||
reply->WriteInt(-1);
|
||||
for (int i=0; i<num_wire_fds; i++)
|
||||
close(wire_fds[i]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Runs in browser process, called by ProcessWatcher::EnsureProcessTerminated().
|
||||
void ZygoteManager::EnsureProcessTerminated(pid_t childpid) {
|
||||
if (client_fd_ == -1)
|
||||
return;
|
||||
|
||||
Pickle pickle;
|
||||
|
||||
pickle.WriteString(kZMagic);
|
||||
pickle.WriteInt(getpid());
|
||||
pickle.WriteInt(ZMREAP);
|
||||
pickle.WriteInt(childpid);
|
||||
|
||||
int bytes_sent = HANDLE_EINTR(
|
||||
write(client_fd_, const_cast<void*>(pickle.data()), pickle.size()));
|
||||
|
||||
if (bytes_sent < 1) {
|
||||
LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_;
|
||||
}
|
||||
}
|
||||
|
||||
// Runs in zygote manager process
|
||||
void ZygoteManager::EnsureProcessTerminatedHandler(const Pickle& request,
|
||||
void* iter) {
|
||||
pid_t childpid;
|
||||
request.ReadInt(&iter, &childpid);
|
||||
NOTIMPLEMENTED();
|
||||
// TODO(dkegel): put childpid on a watch list, and terminate it
|
||||
// after a while as chrome/common/process_watcher does.
|
||||
}
|
||||
|
||||
static bool ValidateFilename(const std::string& filename) {
|
||||
// We only have to open one kind of file, but we don't know
|
||||
// the directory it's in, so be as restrictive as we can within
|
||||
// those bounds.
|
||||
|
||||
static const char* allowed_prefix = "/";
|
||||
if (filename.compare(0, strlen(allowed_prefix), allowed_prefix) != 0) {
|
||||
LOG(ERROR) << "filename did not start with " << allowed_prefix;
|
||||
return false;
|
||||
}
|
||||
static const char* allowed_suffix = ".pak";
|
||||
if ((filename.length() <= strlen(allowed_suffix)) ||
|
||||
(filename.compare(filename.length() - strlen(allowed_suffix),
|
||||
strlen(allowed_suffix), allowed_suffix) != 0)) {
|
||||
LOG(ERROR) << "filename did not end in " << allowed_suffix;
|
||||
return false;
|
||||
}
|
||||
if (filename.find("../") != std::string::npos) {
|
||||
LOG(ERROR) << "filename contained relative component";
|
||||
return false;
|
||||
}
|
||||
static const char* forbidden_prefixes[] = {
|
||||
"/var/", "/tmp/", "/etc/", "/dev/", "/proc/", 0 };
|
||||
for (const char** p = forbidden_prefixes;
|
||||
*p; p++) {
|
||||
if (filename.compare(0, strlen(*p), *p) == 0) {
|
||||
LOG(ERROR) << "filename began with " << *p;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Runs in browser process
|
||||
int ZygoteManager::OpenFile(const std::string& filename) {
|
||||
// For security reasons, we only support .pak files,
|
||||
// and only in certain locations.
|
||||
if (!ValidateFilename(filename)) {
|
||||
LOG(INFO) << "ZygoteManager: filename " << filename << " disallowed.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::map<std::string, int>::iterator mapiter = cached_fds_.find(filename);
|
||||
if (mapiter != cached_fds_.end())
|
||||
return mapiter->second;
|
||||
|
||||
if (client_fd_ == -1)
|
||||
return -1;
|
||||
|
||||
Pickle pickle;
|
||||
|
||||
pickle.WriteString(kZMagic);
|
||||
pickle.WriteInt(getpid());
|
||||
pickle.WriteInt(ZMOPEN);
|
||||
pickle.WriteString(filename);
|
||||
|
||||
// Get ready to receive fds
|
||||
::msghdr msg = {0};
|
||||
struct iovec iov = {msg_buf_, kMAX_MSG_LEN};
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsg_buf_;
|
||||
msg.msg_controllen = kMAX_CMSG_LEN;
|
||||
|
||||
ssize_t bytes_sent;
|
||||
ssize_t bytes_read = 0;
|
||||
|
||||
if (flock(lockfd_, LOCK_EX))
|
||||
LOG(ERROR) << "flock failed, errno " << errno;
|
||||
bytes_sent = HANDLE_EINTR(
|
||||
write(client_fd_, const_cast<void *>(pickle.data()), pickle.size()));
|
||||
if (bytes_sent > 0) {
|
||||
bytes_read = HANDLE_EINTR(recvmsg(client_fd_, &msg, MSG_WAITALL));
|
||||
}
|
||||
if (flock(lockfd_, LOCK_UN))
|
||||
LOG(ERROR) << "flock failed, errno " << errno;
|
||||
|
||||
if (bytes_sent < 1) {
|
||||
LOG(ERROR) << "Can't send to zm, errno " << errno << ", fd " << client_fd_;
|
||||
return -1;
|
||||
}
|
||||
if (bytes_read < 1) {
|
||||
LOG(ERROR) << "Can't get from zm, errno " << errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Locate the sole block of sent file descriptors within the list of
|
||||
// control messages
|
||||
const int* wire_fds = NULL;
|
||||
unsigned num_wire_fds = 0;
|
||||
if (msg.msg_controllen > 0) {
|
||||
struct cmsghdr* cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
|
||||
assert(payload_len % sizeof(int) == 0);
|
||||
wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
|
||||
num_wire_fds = payload_len / sizeof(int);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DCHECK(!(msg.msg_flags & MSG_CTRUNC));
|
||||
|
||||
// Unpickle the reply
|
||||
Pickle reply(msg_buf_, bytes_read);
|
||||
void* iter = NULL;
|
||||
int kind = UnpickleHeader(reply, &iter);
|
||||
if (kind != ZMOPENED) {
|
||||
LOG(ERROR) << "reply wrong kind " << kind;
|
||||
goto error_close;
|
||||
}
|
||||
int newfd_errno;
|
||||
if (!reply.ReadInt(&iter, &newfd_errno)) {
|
||||
LOG(ERROR) << "open failed, can't read errno";
|
||||
goto error_close;
|
||||
}
|
||||
if (newfd_errno != 0) {
|
||||
LOG(ERROR) << "open failed, errno " << newfd_errno;
|
||||
goto error_close;
|
||||
}
|
||||
if (num_wire_fds != 1) {
|
||||
LOG(ERROR) << "open failed, reply wrong number fds " << num_wire_fds;
|
||||
goto error_close;
|
||||
}
|
||||
if (wire_fds[0] == -1) {
|
||||
LOG(ERROR) << "open failed, fd -1";
|
||||
NOTREACHED();
|
||||
return -1;
|
||||
}
|
||||
return wire_fds[0];
|
||||
|
||||
error_close:
|
||||
for (unsigned i=0; i<num_wire_fds; i++)
|
||||
close(wire_fds[i]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Runs in zygote manager process
|
||||
bool ZygoteManager::OpenFileHandler(const Pickle& request, void* iter,
|
||||
Pickle* reply, ::msghdr* msg) {
|
||||
reply->WriteInt(ZMOPENED);
|
||||
|
||||
std::string filename;
|
||||
if (!request.ReadString(&iter, &filename)) {
|
||||
LOG(ERROR) << "no filename?";
|
||||
return false;
|
||||
}
|
||||
if (!ValidateFilename(filename)) {
|
||||
// Fake a unix error code
|
||||
reply->WriteInt(EPERM);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, int>::iterator i;
|
||||
int newfd;
|
||||
std::map<std::string, int>::iterator mapiter = cached_fds_.find(filename);
|
||||
if (mapiter == cached_fds_.end()) {
|
||||
// Verify that file is a plain file
|
||||
struct stat statbuf;
|
||||
if (lstat(filename.c_str(), &statbuf) != 0) {
|
||||
LOG(ERROR) << "can't stat " << filename << ", errno " << errno;
|
||||
return false;
|
||||
}
|
||||
if (!S_ISREG(statbuf.st_mode)) {
|
||||
LOG(ERROR) << "not regular file " << filename;
|
||||
// Fake a unix error code
|
||||
reply->WriteInt(EISDIR);
|
||||
return false;
|
||||
}
|
||||
newfd = open(filename.c_str(), O_RDONLY, 0);
|
||||
if (newfd != -1) {
|
||||
cached_fds_[filename] = newfd;
|
||||
} else {
|
||||
LOG(ERROR) << "can't open " << filename << ", errno " << errno;
|
||||
}
|
||||
} else {
|
||||
newfd = mapiter->second;
|
||||
}
|
||||
if (newfd == -1) {
|
||||
reply->WriteInt(errno);
|
||||
} else {
|
||||
reply->WriteInt(0);
|
||||
msg->msg_control = cmsg_buf_;
|
||||
msg->msg_controllen = CMSG_LEN(sizeof(int));
|
||||
struct cmsghdr* cmsg;
|
||||
cmsg = CMSG_FIRSTHDR(msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = msg->msg_controllen;
|
||||
int* wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
|
||||
wire_fds[0] = newfd;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Runs in zygote manager process
|
||||
bool ZygoteManager::ReadAndHandleMessage(std::vector<std::string>** newargv) {
|
||||
// Wait for activity either on canary fd or main fd.
|
||||
struct pollfd watcher[2];
|
||||
memset(watcher, 0, sizeof(watcher));
|
||||
watcher[0].fd = canary_fd_;
|
||||
watcher[0].events = POLLIN|POLLHUP;
|
||||
watcher[1].fd = server_fd_;
|
||||
watcher[1].events = POLLIN;
|
||||
// Wait at most one minute. This lets us detect case where
|
||||
// canary socket is closed abruptly because the main client aborted.
|
||||
// Also lets us reap dead children once a minute even if we don't get SIGCHLD.
|
||||
// We'd like to wait less time, but that's hard on battery life.
|
||||
// Note: handle EINTR manually here, not with wrapper, as we need
|
||||
// to return when we're interrupted so caller can reap promptly.
|
||||
int nactive = poll(watcher, 2, 60*1000);
|
||||
|
||||
if (nactive == -1) {
|
||||
if (errno == EINTR) {
|
||||
// Probably SIGCHLD. Return to main loop so it can reap.
|
||||
return true;
|
||||
}
|
||||
LOG(ERROR) << "poll failed, errno " << errno << ", aborting";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it was the canary, exit
|
||||
if (watcher[0].revents != 0) {
|
||||
LOG(INFO) << "notified of peer destruction, exiting";
|
||||
return false;
|
||||
}
|
||||
if ((watcher[1].revents & POLLIN) != POLLIN) {
|
||||
// spurious wakeup?
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = 0;
|
||||
struct msghdr msg = {0};
|
||||
struct iovec iov = {msg_buf_, kMAX_MSG_LEN};
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsg_buf_;
|
||||
msg.msg_controllen = kMAX_CMSG_LEN;
|
||||
bytes_read = HANDLE_EINTR(recvmsg(server_fd_, &msg, MSG_WAITALL));
|
||||
if (bytes_read == 0) {
|
||||
LOG(ERROR) << "got EOF, aborting";
|
||||
return false;
|
||||
}
|
||||
if (bytes_read == -1) {
|
||||
LOG(ERROR) << "got errno " << errno << ", aborting";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Locate the sole block of sent file descriptors within the list of
|
||||
// control messages
|
||||
const int* wire_fds = NULL;
|
||||
unsigned num_wire_fds = 0;
|
||||
if (msg.msg_controllen > 0) {
|
||||
struct cmsghdr* cmsg;
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
|
||||
assert(payload_len % sizeof(int) == 0);
|
||||
wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
|
||||
num_wire_fds = payload_len / sizeof(int);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
DCHECK(!(msg.msg_flags & MSG_CTRUNC));
|
||||
|
||||
// Unpickle/parse message
|
||||
Pickle pickle(msg_buf_, bytes_read);
|
||||
void* iter = NULL;
|
||||
std::string magic;
|
||||
if (!pickle.ReadString(&iter, &magic) || magic != std::string(kZMagic)) {
|
||||
LOG(ERROR) << "msg didn't start with " << kZMagic << ", got " << magic;
|
||||
for (unsigned i=0; i<num_wire_fds; i++)
|
||||
close(wire_fds[i]);
|
||||
return true;
|
||||
}
|
||||
pid_t clientpid = (pid_t) -1;
|
||||
pickle.ReadInt(&iter, &clientpid);
|
||||
int kind;
|
||||
pickle.ReadInt(&iter, &kind);
|
||||
|
||||
Pickle reply;
|
||||
reply.WriteString(kZMagic);
|
||||
reply.WriteInt(clientpid);
|
||||
|
||||
struct msghdr replymsg = {0};
|
||||
memset(&replymsg, 0, sizeof(replymsg));
|
||||
|
||||
switch (kind) {
|
||||
case ZMPING:
|
||||
DCHECK_EQ(0U, num_wire_fds);
|
||||
PingHandler(pickle, iter, &reply, newargv);
|
||||
break;
|
||||
case ZMFORK:
|
||||
// TODO(dkegel): real error handling
|
||||
(void) LongForkHandler(pickle, iter, &reply, newargv, wire_fds,
|
||||
num_wire_fds);
|
||||
if (*newargv != NULL) {
|
||||
// Child. Just return to caller, who will return from SetLongFork.
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case ZMREAP:
|
||||
DCHECK_EQ(0U, num_wire_fds);
|
||||
EnsureProcessTerminatedHandler(pickle, iter);
|
||||
// no reply to this message
|
||||
return true;
|
||||
case ZMOPEN:
|
||||
DCHECK_EQ(0U, num_wire_fds);
|
||||
// TODO(dkegel): real error handling
|
||||
(void) OpenFileHandler(pickle, iter, &reply, &replymsg);
|
||||
break;
|
||||
default:
|
||||
// TODO(dkegel): real error handling
|
||||
LOG(ERROR) << "Unknown message kind " << kind;
|
||||
DCHECK_EQ(0U, num_wire_fds);
|
||||
break;
|
||||
}
|
||||
|
||||
struct iovec riov = {const_cast<void *>(reply.data()), reply.size()};
|
||||
replymsg.msg_iov = &riov;
|
||||
replymsg.msg_iovlen = 1;
|
||||
|
||||
int bytes_sent;
|
||||
bytes_sent = HANDLE_EINTR(sendmsg(server_fd_, &replymsg, MSG_WAITALL));
|
||||
if (bytes_sent != static_cast<int>(riov.iov_len)) {
|
||||
// TODO(dkegel): real error handling
|
||||
LOG(ERROR) << "Can't send reply.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called only by ChromeMain(), forks the zygote manager process.
|
||||
std::vector<std::string>* ZygoteManager::Start() {
|
||||
DCHECK(lockfd_ == -1);
|
||||
DCHECK(canary_fd_ == -1);
|
||||
DCHECK(server_fd_ == -1);
|
||||
DCHECK(client_fd_ == -1);
|
||||
|
||||
#ifndef OFFICIAL_BUILD
|
||||
// Disallow nested ZygoteManager servers
|
||||
CHECK(getenv("ZYGOTE_MANAGER_STARTED") == NULL) << "already started?!";
|
||||
(void) setenv("ZYGOTE_MANAGER_STARTED", "1", 1);
|
||||
#endif
|
||||
|
||||
int pipe_fds[2];
|
||||
|
||||
// Avoid using the reserved fd slots.
|
||||
int reserved_fds[kReservedFds];
|
||||
for (int i=0; i < kReservedFds; i++)
|
||||
reserved_fds[i] = open("/dev/null", O_RDONLY, 0);
|
||||
|
||||
// Create the main communications pipe.
|
||||
int err = HANDLE_EINTR(socketpair(AF_UNIX, SOCK_DGRAM, 0, pipe_fds));
|
||||
if (err != 0) {
|
||||
// TODO(dkegel): real error handling
|
||||
exit(99);
|
||||
}
|
||||
server_fd_ = pipe_fds[1];
|
||||
client_fd_ = pipe_fds[0];
|
||||
|
||||
// Create the pipe used only to relay destruction event server.
|
||||
// Must be SOCK_STREAM so close() is sensed by poll().
|
||||
err = HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds));
|
||||
if (err != 0) {
|
||||
// TODO(dkegel): real error handling
|
||||
exit(99);
|
||||
}
|
||||
|
||||
// Create lock file.
|
||||
// TODO(dkegel): get rid of this
|
||||
char lockfile[256];
|
||||
strcpy(lockfile, "/tmp/zygote_manager_lock.XXXXXX");
|
||||
lockfd_ = mkstemp(lockfile);
|
||||
if (lockfd_ == -1) {
|
||||
// TODO(dkegel): real error handling
|
||||
exit(99);
|
||||
}
|
||||
lockfile_.assign(lockfile);
|
||||
|
||||
// Fork a fork server.
|
||||
pid_t childpid = fork();
|
||||
|
||||
if (!childpid) {
|
||||
for (int i=0; i < kReservedFds; i++)
|
||||
close(reserved_fds[i]);
|
||||
|
||||
// Original child. Continues on with the main program
|
||||
// and becomes the first client.
|
||||
close(server_fd_);
|
||||
server_fd_ = -1;
|
||||
|
||||
close(pipe_fds[1]);
|
||||
canary_fd_ = pipe_fds[0];
|
||||
|
||||
// Return now to indicate this is the original process.
|
||||
return NULL;
|
||||
} else {
|
||||
close(lockfd_);
|
||||
|
||||
close(pipe_fds[0]);
|
||||
canary_fd_ = pipe_fds[1];
|
||||
|
||||
// We need to accept SIGCHLD, even though our handler is a no-op because
|
||||
// otherwise we cannot wait on children. (According to POSIX 2001.)
|
||||
// (And otherwise poll() might not wake up on SIGCHLD.)
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = SIGCHLDHandler;
|
||||
CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
|
||||
|
||||
// Original process. Acts as the server.
|
||||
while (true) {
|
||||
std::vector<std::string>* newargv = NULL;
|
||||
if (!ReadAndHandleMessage(&newargv))
|
||||
break;
|
||||
if (newargv) {
|
||||
// Return new commandline to show caller this is a new child process.
|
||||
return newargv;
|
||||
}
|
||||
// Server process continues around loop.
|
||||
|
||||
// Reap children.
|
||||
while (true) {
|
||||
int status = -1;
|
||||
pid_t reaped = waitpid(-1, &status, WNOHANG);
|
||||
if (reaped != -1 && reaped != 0) {
|
||||
LOG(INFO) << "Reaped pid " << reaped;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Server cleanup after EOF or error reading from the socket.
|
||||
Delete(FilePath(lockfile_), false);
|
||||
// TODO(dkegel): real error handling
|
||||
LOG(INFO) << "exiting. " << cached_fds_.size() << " cached fds.";
|
||||
std::map<std::string, int>::iterator i;
|
||||
for (i = cached_fds_.begin(); i != cached_fds_.end(); ++i) {
|
||||
LOG(INFO) << "Closing fd " << i->second << " filename " << i->first;
|
||||
close(i->second);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // defined(OS_LINUX)
|
||||
@@ -1,143 +0,0 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_ZYGOTE_MANAGER_H_
|
||||
#define BASE_ZYGOTE_MANAGER_H_
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/pickle.h"
|
||||
#include "base/time.h"
|
||||
#include "base/process_util.h" // for file_handle_mapping_vector
|
||||
|
||||
namespace base {
|
||||
|
||||
class ZygoteManager {
|
||||
public:
|
||||
// The normal way to get a ZygoteManager is via this singleton factory.
|
||||
static ZygoteManager* Get();
|
||||
|
||||
ZygoteManager() :
|
||||
server_fd_(-1), client_fd_(-1), canary_fd_(-1), lockfd_(-1) {
|
||||
}
|
||||
|
||||
~ZygoteManager();
|
||||
|
||||
// Measure round trip time. Return true on success.
|
||||
// Only used during testing.
|
||||
bool Ping(base::TimeDelta* delta);
|
||||
|
||||
// Start the zygote manager.
|
||||
// Called only once, but returns many times.
|
||||
// Returns once in original process and once in each spawned child.
|
||||
// In original process, returns NULL.
|
||||
// In child processes, returns the argv to use for the child.
|
||||
// In Chromium, called from ChromeMain().
|
||||
std::vector<std::string>* Start();
|
||||
|
||||
// Like longjmp() and base::LaunchApp().
|
||||
// Ask the fork server to spawn a new process with
|
||||
// the given commandline and the given file descriptors.
|
||||
// Returns process id of copy, or -1 on failure.
|
||||
// In Chromium, called from base::ForkApp(), which is
|
||||
// called from BrowserRenderProcessHost::Init().
|
||||
pid_t LongFork(const std::vector<std::string>& argv,
|
||||
const file_handle_mapping_vector& fds_to_remap);
|
||||
|
||||
// Tell ZygoteManager that we expect the given process to
|
||||
// exit on its own soon. If it doesn't die within a few
|
||||
// seconds, kill it. Does not block (unless pipe to server full).
|
||||
// In Chromium, called from ProcessWatcher::EnsureProcessTerminated().
|
||||
void EnsureProcessTerminated(pid_t childpid);
|
||||
|
||||
// Open a file, or retrieve a previously cached file descriptor
|
||||
// for this file. The files are opened for readonly access.
|
||||
// Caution: do not seek file descriptors returned
|
||||
// by this API, as all children share the same file objects, so
|
||||
// a seek on one is a seek on all.
|
||||
// Works even if the file is unlinked after the first call
|
||||
// (e.g. when an app is updated by the linux system autoupdater).
|
||||
// Returns file descriptor, or -1 for error.
|
||||
// In Chromium, called from MemoryMappedFile::MapFileToMemory().
|
||||
// Only allows openeing files named .pak in reasonable looking locations.
|
||||
int OpenFile(const std::string& filename);
|
||||
|
||||
private:
|
||||
int UnpickleHeader(const Pickle& reply, void** iter);
|
||||
|
||||
// Returns false on EOF
|
||||
// Sets *newargv to a new commandline if the remote side requested a fork.
|
||||
bool ReadAndHandleMessage(std::vector<std::string>** newargv);
|
||||
|
||||
void PingHandler(const Pickle& request, void* iter, Pickle* reply,
|
||||
std::vector<std::string>** newargv);
|
||||
|
||||
bool LongForkHandler(const Pickle& request, void* iter, Pickle* reply,
|
||||
std::vector<std::string>** newargv,
|
||||
const int wire_fds[], int num_wire_fds);
|
||||
|
||||
void EnsureProcessTerminatedHandler(const Pickle& request, void* iter);
|
||||
|
||||
bool OpenFileHandler(const Pickle& request, void* iter, Pickle* reply,
|
||||
::msghdr* msg);
|
||||
|
||||
// The fd used by the server to receive requests
|
||||
int server_fd_;
|
||||
|
||||
// The fd used by the clients to send requests
|
||||
int client_fd_;
|
||||
|
||||
// fd used only to notify server of destruction.
|
||||
int canary_fd_;
|
||||
|
||||
// Temporary file used only for locking.
|
||||
// Each client must do its own open for locking to work;
|
||||
// inherited file descriptors can't lock each other out.
|
||||
// FIXME: locking is lame.
|
||||
std::string lockfile_;
|
||||
int lockfd_;
|
||||
|
||||
enum message_kind_t { ZMPING, ZMPINGED,
|
||||
ZMFORK, ZMFORKED,
|
||||
ZMREAP,
|
||||
ZMOPEN, ZMOPENED,
|
||||
ZMBAD };
|
||||
|
||||
// See common/reserved_file_descriptors.h for who uses the reserved
|
||||
// file descriptors. kReservedFds is one plus the highest fd mentioned there.
|
||||
// TODO(dkegel): move kReservedFds to reserved_file_descriptors.h
|
||||
static const int kReservedFds = 5;
|
||||
|
||||
static const int kMAX_MSG_LEN = 2000;
|
||||
static const int kMAX_CMSG_LEN = 100;
|
||||
|
||||
static const char kZMagic[];
|
||||
|
||||
char msg_buf_[kMAX_MSG_LEN];
|
||||
char cmsg_buf_[kMAX_CMSG_LEN];
|
||||
|
||||
// Where we remember file descriptors for already-opened files.
|
||||
// Both client and server maintain this table.
|
||||
// Client should check the table before requesting the
|
||||
// server to open a file, as it might have been already
|
||||
// opened before this client was forked.
|
||||
std::map<std::string, int> cached_fds_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // defined(OS_LINUX)
|
||||
|
||||
#endif // BASE_ZYGOTE_MANAGER_H_
|
||||
@@ -14,13 +14,13 @@
|
||||
#include "base/file_version_info_linux.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/reserved_file_descriptors.h"
|
||||
#include "breakpad/linux/directory_reader.h"
|
||||
#include "breakpad/linux/exception_handler.h"
|
||||
#include "breakpad/linux/linux_libc_support.h"
|
||||
#include "breakpad/linux/linux_syscall_support.h"
|
||||
#include "breakpad/linux/memory.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/installer/util/google_update_settings.h"
|
||||
|
||||
static const char kUploadURL[] =
|
||||
@@ -501,13 +501,11 @@ RendererCrashHandler(const void* crash_context, size_t crash_context_size,
|
||||
}
|
||||
|
||||
void EnableRendererCrashDumping() {
|
||||
// When the browser forks off our process, it installs the crash signal file
|
||||
// descriptor in slot kMagicCrashSignalFd.
|
||||
|
||||
const int fd = Singleton<base::GlobalDescriptors>()->Get(kCrashDumpSignal);
|
||||
// We deliberately leak this object.
|
||||
google_breakpad::ExceptionHandler* handler =
|
||||
new google_breakpad::ExceptionHandler("" /* unused */, NULL, NULL,
|
||||
(void*) kMagicCrashSignalFd, true);
|
||||
(void*) fd, true);
|
||||
handler->set_crash_handler(RendererCrashHandler);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
#include "base/at_exit.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug_util.h"
|
||||
#if defined(OS_POSIX)
|
||||
#include "base/global_descriptors_posix.h"
|
||||
#endif
|
||||
#include "base/icu_util.h"
|
||||
#include "base/message_loop.h"
|
||||
#include "base/path_service.h"
|
||||
@@ -45,9 +48,6 @@
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win_util.h"
|
||||
#endif
|
||||
#if defined(OS_LINUX)
|
||||
#include "base/zygote_manager.h"
|
||||
#endif
|
||||
#if defined(OS_MACOSX)
|
||||
#include "chrome/app/breakpad_mac.h"
|
||||
#elif defined(OS_LINUX)
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "chrome/browser/renderer_host/render_process_host.h"
|
||||
#include "chrome/common/chrome_constants.h"
|
||||
#include "chrome/common/chrome_counters.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/logging_chrome.h"
|
||||
@@ -75,6 +76,7 @@ extern int RendererMain(const MainFunctionParams&);
|
||||
extern int PluginMain(const MainFunctionParams&);
|
||||
extern int WorkerMain(const MainFunctionParams&);
|
||||
extern int UtilityMain(const MainFunctionParams&);
|
||||
extern int ZygoteMain(const MainFunctionParams&);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// TODO(erikkay): isn't this already defined somewhere?
|
||||
@@ -299,21 +301,19 @@ int ChromeMain(int argc, const char** argv) {
|
||||
// its main event loop to get rid of the cruft.
|
||||
base::ScopedNSAutoreleasePool autorelease_pool;
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
base::GlobalDescriptors* g_fds = Singleton<base::GlobalDescriptors>::get();
|
||||
g_fds->Set(kPrimaryIPCChannel,
|
||||
kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
|
||||
#if defined(OS_LINUX)
|
||||
g_fds->Set(kCrashDumpSignal,
|
||||
kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Initialize the command line.
|
||||
#if defined(OS_WIN)
|
||||
CommandLine::Init(0, NULL);
|
||||
#elif defined(OS_LINUX)
|
||||
base::ZygoteManager* zm = base::ZygoteManager::Get();
|
||||
std::vector<std::string>* zargv = NULL;
|
||||
if (zm)
|
||||
zargv = zm->Start();
|
||||
if (zargv) {
|
||||
// Forked child.
|
||||
CommandLine::Init(*zargv);
|
||||
} else {
|
||||
// Original process.
|
||||
CommandLine::Init(argc, argv);
|
||||
}
|
||||
#else
|
||||
CommandLine::Init(argc, argv);
|
||||
#endif
|
||||
@@ -346,11 +346,15 @@ int ChromeMain(int argc, const char** argv) {
|
||||
CHECK(sigaction(SIGPIPE, &action, 0) == 0);
|
||||
#endif // OS_POSIX
|
||||
} else {
|
||||
#if defined(OS_WIN)
|
||||
std::wstring channel_name =
|
||||
parsed_command_line.GetSwitchValue(switches::kProcessChannelID);
|
||||
|
||||
browser_pid = StringToInt(WideToASCII(channel_name));
|
||||
DCHECK(browser_pid != 0);
|
||||
#else
|
||||
browser_pid = base::GetCurrentProcId();
|
||||
#endif
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
// When you hit Ctrl-C in a terminal running the browser
|
||||
@@ -495,6 +499,13 @@ int ChromeMain(int argc, const char** argv) {
|
||||
rv = WorkerMain(main_params);
|
||||
#else
|
||||
NOTIMPLEMENTED();
|
||||
#endif
|
||||
} else if (process_type == switches::kZygoteProcess) {
|
||||
#if defined(OS_LINUX)
|
||||
if (ZygoteMain(main_params))
|
||||
RendererMain(main_params);
|
||||
#else
|
||||
NOTIMPLEMENTED();
|
||||
#endif
|
||||
} else if (process_type.empty()) {
|
||||
#if defined(OS_LINUX)
|
||||
|
||||
@@ -18,9 +18,14 @@ TEST_F(ChromeMainTest, AppLaunch) {
|
||||
if (UITest::in_process_renderer()) {
|
||||
EXPECT_EQ(1, UITest::GetBrowserProcessCount());
|
||||
} else {
|
||||
#if defined(OS_LINUX)
|
||||
// On Linux we'll have three processes: browser, renderer and zygote.
|
||||
EXPECT_EQ(3, UITest::GetBrowserProcessCount());
|
||||
#else
|
||||
// We should have two instances of the browser process alive -
|
||||
// one is the Browser and the other is the Renderer.
|
||||
EXPECT_EQ(2, UITest::GetBrowserProcessCount());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
|
||||
#include "app/app_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#if defined(OS_POSIX)
|
||||
#include "base/global_descriptors_posix.h"
|
||||
#endif
|
||||
#include "base/file_path.h"
|
||||
#include "base/file_util.h"
|
||||
#include "base/file_version_info.h"
|
||||
@@ -30,6 +33,7 @@
|
||||
#include "chrome/browser/renderer_host/browser_render_process_host.h"
|
||||
#include "chrome/browser/renderer_host/render_process_host.h"
|
||||
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/chrome_plugin_lib.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
@@ -393,10 +397,10 @@ bool PluginProcessHost::Init(const WebPluginInfo& info,
|
||||
// This code is duplicated with browser_render_process_host.cc, but
|
||||
// there's not a good place to de-duplicate it.
|
||||
base::file_handle_mapping_vector fds_to_map;
|
||||
int src_fd = -1, dest_fd = -1;
|
||||
channel().GetClientFileDescriptorMapping(&src_fd, &dest_fd);
|
||||
if (src_fd > -1)
|
||||
fds_to_map.push_back(std::pair<int, int>(src_fd, dest_fd));
|
||||
const int ipcfd = channel().GetClientFileDescriptor();
|
||||
if (ipcfd > -1)
|
||||
fds_to_map.push_back(std::pair<int, int>(
|
||||
ipcfd, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor));
|
||||
base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "base/path_service.h"
|
||||
#include "base/process_util.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "base/reserved_file_descriptors.h"
|
||||
#include "base/scoped_ptr.h"
|
||||
#include "base/shared_memory.h"
|
||||
#include "base/singleton.h"
|
||||
@@ -35,9 +34,6 @@
|
||||
#include "chrome/browser/history/history.h"
|
||||
#include "chrome/browser/plugin_service.h"
|
||||
#include "chrome/browser/profile.h"
|
||||
#if defined(OS_LINUX)
|
||||
#include "chrome/browser/renderer_host/render_crash_handler_host_linux.h"
|
||||
#endif
|
||||
#include "chrome/browser/renderer_host/render_view_host.h"
|
||||
#include "chrome/browser/renderer_host/render_widget_helper.h"
|
||||
#include "chrome/browser/renderer_host/render_widget_host.h"
|
||||
@@ -45,6 +41,7 @@
|
||||
#include "chrome/browser/renderer_host/web_cache_manager.h"
|
||||
#include "chrome/browser/visitedlink_master.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/common/child_process_info.h"
|
||||
#include "chrome/common/logging_chrome.h"
|
||||
#include "chrome/common/notification_service.h"
|
||||
@@ -54,6 +51,11 @@
|
||||
#include "chrome/renderer/render_process.h"
|
||||
#include "grit/generated_resources.h"
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
#include "chrome/browser/zygote_host_linux.h"
|
||||
#include "chrome/browser/renderer_host/render_crash_handler_host_linux.h"
|
||||
#endif
|
||||
|
||||
using WebKit::WebCache;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
@@ -134,7 +136,8 @@ BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile)
|
||||
backgrounded_(true),
|
||||
ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_(
|
||||
base::TimeDelta::FromSeconds(5),
|
||||
this, &BrowserRenderProcessHost::ClearTransportDIBCache)) {
|
||||
this, &BrowserRenderProcessHost::ClearTransportDIBCache)),
|
||||
zygote_child_(false) {
|
||||
widget_helper_ = new RenderWidgetHelper();
|
||||
|
||||
registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
|
||||
@@ -170,7 +173,13 @@ BrowserRenderProcessHost::~BrowserRenderProcessHost() {
|
||||
audio_renderer_host_->Destroy();
|
||||
|
||||
if (process_.handle() && !run_renderer_in_process()) {
|
||||
ProcessWatcher::EnsureProcessTerminated(process_.handle());
|
||||
if (zygote_child_) {
|
||||
#if defined(OS_LINUX)
|
||||
Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle());
|
||||
#endif
|
||||
} else {
|
||||
ProcessWatcher::EnsureProcessTerminated(process_.handle());
|
||||
}
|
||||
}
|
||||
|
||||
ClearTransportDIBCache();
|
||||
@@ -294,7 +303,9 @@ bool BrowserRenderProcessHost::Init() {
|
||||
ASCIIToWide(field_trial->MakePersistentString()));
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
if (browser_command_line.HasSwitch(switches::kRendererCmdPrefix)) {
|
||||
const bool has_cmd_prefix =
|
||||
browser_command_line.HasSwitch(switches::kRendererCmdPrefix);
|
||||
if (has_cmd_prefix) {
|
||||
// launch the renderer child with some prefix (usually "gdb --args")
|
||||
const std::wstring prefix =
|
||||
browser_command_line.GetSwitchValue(switches::kRendererCmdPrefix);
|
||||
@@ -334,24 +345,42 @@ bool BrowserRenderProcessHost::Init() {
|
||||
base::ProcessHandle process = 0;
|
||||
#if defined(OS_WIN)
|
||||
process = sandbox::StartProcess(&cmd_line);
|
||||
#else
|
||||
// NOTE: This code is duplicated with plugin_process_host.cc, but
|
||||
// there's not a good place to de-duplicate it.
|
||||
base::file_handle_mapping_vector fds_to_map;
|
||||
int src_fd = -1, dest_fd = -1;
|
||||
channel_->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
|
||||
if (src_fd > -1)
|
||||
fds_to_map.push_back(std::pair<int, int>(src_fd, dest_fd));
|
||||
#elif defined(OS_POSIX)
|
||||
#if defined(OS_LINUX)
|
||||
const int crash_signal_fd =
|
||||
Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
|
||||
if (crash_signal_fd >= 0)
|
||||
fds_to_map.push_back(std::make_pair(crash_signal_fd, kMagicCrashSignalFd));
|
||||
base::ForkApp(cmd_line.argv(), fds_to_map, &process);
|
||||
#else
|
||||
base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
|
||||
if (!has_cmd_prefix) {
|
||||
base::GlobalDescriptors::Mapping mapping;
|
||||
const int ipcfd = channel_->GetClientFileDescriptor();
|
||||
mapping.push_back(std::pair<uint32_t, int>(kPrimaryIPCChannel, ipcfd));
|
||||
const int crash_signal_fd =
|
||||
Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
|
||||
if (crash_signal_fd >= 0) {
|
||||
mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal,
|
||||
crash_signal_fd));
|
||||
}
|
||||
process = Singleton<ZygoteHost>()->ForkRenderer(cmd_line.argv(), mapping);
|
||||
zygote_child_ = true;
|
||||
} else {
|
||||
#endif
|
||||
// NOTE: This code is duplicated with plugin_process_host.cc, but
|
||||
// there's not a good place to de-duplicate it.
|
||||
base::file_handle_mapping_vector fds_to_map;
|
||||
const int ipcfd = channel_->GetClientFileDescriptor();
|
||||
fds_to_map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3));
|
||||
#if defined(OS_LINUX)
|
||||
const int crash_signal_fd =
|
||||
Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
|
||||
if (crash_signal_fd >= 0) {
|
||||
fds_to_map.push_back(std::make_pair(crash_signal_fd,
|
||||
kCrashDumpSignal + 3));
|
||||
}
|
||||
#endif
|
||||
base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
|
||||
zygote_child_ = false;
|
||||
#if defined(OS_LINUX)
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!process) {
|
||||
channel_.reset();
|
||||
return false;
|
||||
@@ -666,7 +695,7 @@ void BrowserRenderProcessHost::OnChannelConnected(int32 peer_pid) {
|
||||
const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
|
||||
if (cmd_line.HasSwitch(switches::kRendererCmdPrefix))
|
||||
return;
|
||||
CHECK(peer_pid == process_.pid());
|
||||
CHECK(peer_pid == process_.pid()) << peer_pid << " " << process_.pid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,9 @@ class BrowserRenderProcessHost : public RenderProcessHost,
|
||||
// Used in single-process mode.
|
||||
scoped_ptr<RendererMainThread> in_process_renderer_;
|
||||
|
||||
// True iff the renderer is a child of a zygote process.
|
||||
bool zygote_child_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BrowserRenderProcessHost);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/zygote_host_linux.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/pickle.h"
|
||||
#include "base/process_util.h"
|
||||
#include "base/unix_domain_socket_posix.h"
|
||||
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
|
||||
ZygoteHost::ZygoteHost() {
|
||||
std::wstring chrome_path;
|
||||
CHECK(PathService::Get(base::FILE_EXE, &chrome_path));
|
||||
CommandLine cmd_line(chrome_path);
|
||||
|
||||
cmd_line.AppendSwitchWithValue(switches::kProcessType,
|
||||
switches::kZygoteProcess);
|
||||
|
||||
int fds[2];
|
||||
CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
|
||||
base::file_handle_mapping_vector fds_to_map;
|
||||
fds_to_map.push_back(std::make_pair(fds[1], 3));
|
||||
|
||||
const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
|
||||
if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) {
|
||||
const std::wstring prefix =
|
||||
browser_command_line.GetSwitchValue(switches::kZygoteCmdPrefix);
|
||||
cmd_line.PrependWrapper(prefix);
|
||||
}
|
||||
|
||||
base::ProcessHandle process;
|
||||
base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
|
||||
CHECK(process != -1) << "Failed to launch zygote process";
|
||||
|
||||
close(fds[1]);
|
||||
control_fd_ = fds[0];
|
||||
}
|
||||
|
||||
ZygoteHost::~ZygoteHost() {
|
||||
close(control_fd_);
|
||||
}
|
||||
|
||||
pid_t ZygoteHost::ForkRenderer(
|
||||
const std::vector<std::string>& argv,
|
||||
const base::GlobalDescriptors::Mapping& mapping) {
|
||||
Pickle pickle;
|
||||
|
||||
pickle.WriteInt(kCmdFork);
|
||||
pickle.WriteInt(argv.size());
|
||||
for (std::vector<std::string>::const_iterator
|
||||
i = argv.begin(); i != argv.end(); ++i)
|
||||
pickle.WriteString(*i);
|
||||
|
||||
pickle.WriteInt(mapping.size());
|
||||
|
||||
std::vector<int> fds;
|
||||
for (base::GlobalDescriptors::Mapping::const_iterator
|
||||
i = mapping.begin(); i != mapping.end(); ++i) {
|
||||
pickle.WriteUInt32(i->first);
|
||||
fds.push_back(i->second);
|
||||
}
|
||||
|
||||
if (!base::SendMsg(control_fd_, pickle.data(), pickle.size(), fds))
|
||||
return -1;
|
||||
|
||||
pid_t pid;
|
||||
if (HANDLE_EINTR(read(control_fd_, &pid, sizeof(pid))) != sizeof(pid))
|
||||
return -1;
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
void ZygoteHost::EnsureProcessTerminated(pid_t process) {
|
||||
Pickle pickle;
|
||||
|
||||
pickle.WriteInt(kCmdReap);
|
||||
pickle.WriteInt(process);
|
||||
|
||||
HANDLE_EINTR(write(control_fd_, pickle.data(), pickle.size()));
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
|
||||
#define CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/global_descriptors_posix.h"
|
||||
#include "base/singleton.h"
|
||||
|
||||
// http://code.google.com/p/chromium/wiki/LinuxZygote
|
||||
|
||||
// The zygote host is the interface, in the browser process, to the zygote
|
||||
// process.
|
||||
class ZygoteHost {
|
||||
public:
|
||||
~ZygoteHost();
|
||||
|
||||
pid_t ForkRenderer(const std::vector<std::string>& command_line,
|
||||
const base::GlobalDescriptors::Mapping& mapping);
|
||||
void EnsureProcessTerminated(pid_t process);
|
||||
|
||||
// These are the command codes used on the wire between the browser and the
|
||||
// zygote.
|
||||
enum {
|
||||
kCmdFork = 0, // Fork off a new renderer.
|
||||
kCmdReap = 1, // Reap a renderer child.
|
||||
};
|
||||
|
||||
private:
|
||||
friend struct DefaultSingletonTraits<ZygoteHost>;
|
||||
ZygoteHost();
|
||||
void LaunchZygoteProcess();
|
||||
|
||||
int control_fd_; // the socket to the zygote
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/global_descriptors_posix.h"
|
||||
#include "base/pickle.h"
|
||||
#include "base/unix_domain_socket_posix.h"
|
||||
|
||||
#include "chrome/browser/zygote_host_linux.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/common/main_function_params.h"
|
||||
#include "chrome/common/process_watcher.h"
|
||||
|
||||
// http://code.google.com/p/chromium/wiki/LinuxZygote
|
||||
|
||||
// This is the object which implements the zygote. The ZygoteMain function,
|
||||
// which is called from ChromeMain, at the the bottom and simple constructs one
|
||||
// of these objects and runs it.
|
||||
class Zygote {
|
||||
public:
|
||||
bool ProcessRequests() {
|
||||
// A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
|
||||
// browser on it.
|
||||
|
||||
// We need to accept SIGCHLD, even though our handler is a no-op because
|
||||
// otherwise we cannot wait on children. (According to POSIX 2001.)
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
action.sa_handler = SIGCHLDHandler;
|
||||
CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
|
||||
|
||||
for (;;) {
|
||||
if (HandleRequestFromBrowser(3))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// See comment below, where sigaction is called.
|
||||
static void SIGCHLDHandler(int signal) { }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Requests from the browser...
|
||||
|
||||
// Read and process a request from the browser. Returns true if we are in a
|
||||
// new process and thus need to unwind back into ChromeMain.
|
||||
bool HandleRequestFromBrowser(int fd) {
|
||||
std::vector<int> fds;
|
||||
static const unsigned kMaxMessageLength = 2048;
|
||||
char buf[kMaxMessageLength];
|
||||
const ssize_t len = base::RecvMsg(fd, buf, sizeof(buf), &fds);
|
||||
if (len == -1) {
|
||||
LOG(WARNING) << "Error reading message from browser: " << errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
// EOF from the browser. We should die.
|
||||
_exit(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
Pickle pickle(buf, len);
|
||||
void* iter = NULL;
|
||||
|
||||
int kind;
|
||||
if (!pickle.ReadInt(&iter, &kind))
|
||||
goto error;
|
||||
|
||||
if (kind == ZygoteHost::kCmdFork) {
|
||||
return HandleForkRequest(fd, pickle, iter, fds);
|
||||
} else if (kind == ZygoteHost::kCmdReap) {
|
||||
if (fds.size())
|
||||
goto error;
|
||||
return HandleReapRequest(fd, pickle, iter);
|
||||
}
|
||||
|
||||
error:
|
||||
LOG(WARNING) << "Error parsing message from browser";
|
||||
for (std::vector<int>::const_iterator
|
||||
i = fds.begin(); i != fds.end(); ++i)
|
||||
close(*i);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandleReapRequest(int fd, Pickle& pickle, void* iter) {
|
||||
pid_t child;
|
||||
|
||||
if (!pickle.ReadInt(&iter, &child)) {
|
||||
LOG(WARNING) << "Error parsing reap request from browser";
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessWatcher::EnsureProcessTerminated(child);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle a 'fork' request from the browser: this means that the browser
|
||||
// wishes to start a new renderer.
|
||||
bool HandleForkRequest(int fd, Pickle& pickle, void* iter,
|
||||
std::vector<int>& fds) {
|
||||
std::vector<std::string> args;
|
||||
int argc, numfds;
|
||||
base::GlobalDescriptors::Mapping mapping;
|
||||
pid_t child;
|
||||
|
||||
if (!pickle.ReadInt(&iter, &argc))
|
||||
goto error;
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
std::string arg;
|
||||
if (!pickle.ReadString(&iter, &arg))
|
||||
goto error;
|
||||
args.push_back(arg);
|
||||
}
|
||||
|
||||
if (!pickle.ReadInt(&iter, &numfds))
|
||||
goto error;
|
||||
if (numfds != static_cast<int>(fds.size()))
|
||||
goto error;
|
||||
|
||||
for (int i = 0; i < numfds; ++i) {
|
||||
base::GlobalDescriptors::Key key;
|
||||
if (!pickle.ReadUInt32(&iter, &key))
|
||||
goto error;
|
||||
mapping.push_back(std::make_pair(key, fds[i]));
|
||||
}
|
||||
|
||||
child = fork();
|
||||
|
||||
if (!child) {
|
||||
close(3); // our socket from the browser is in fd 3
|
||||
Singleton<base::GlobalDescriptors>()->Reset(mapping);
|
||||
CommandLine::Reset();
|
||||
CommandLine::Init(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (std::vector<int>::const_iterator
|
||||
i = fds.begin(); i != fds.end(); ++i)
|
||||
close(*i);
|
||||
|
||||
HANDLE_EINTR(write(fd, &child, sizeof(child)));
|
||||
return false;
|
||||
|
||||
error:
|
||||
LOG(WARNING) << "Error parsing fork request from browser";
|
||||
for (std::vector<int>::const_iterator
|
||||
i = fds.begin(); i != fds.end(); ++i)
|
||||
close(*i);
|
||||
return false;
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
};
|
||||
|
||||
bool ZygoteMain(const MainFunctionParams& params) {
|
||||
Zygote zygote;
|
||||
return zygote.ProcessRequests();
|
||||
}
|
||||
@@ -1623,6 +1623,8 @@
|
||||
'browser/worker_host/worker_process_host.h',
|
||||
'browser/worker_host/worker_service.cc',
|
||||
'browser/worker_host/worker_service.h',
|
||||
'browser/zygote_host_linux.cc',
|
||||
'browser/zygote_main_linux.cc',
|
||||
'tools/build/win/precompiled_wtl.h',
|
||||
'tools/build/win/precompiled_wtl.cc',
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_COMMON_CHROME_DESCRIPTORS_H_
|
||||
#define CHROME_COMMON_CHROME_DESCRIPTORS_H_
|
||||
|
||||
// This is a list of global descriptor keys to be used with the
|
||||
// base::GlobalDescriptors object (see base/global_descriptors_posix.h)
|
||||
enum {
|
||||
kPrimaryIPCChannel = 0,
|
||||
kCrashDumpSignal = 1,
|
||||
};
|
||||
|
||||
#endif // CHROME_COMMON_CHROME_DESCRIPTORS_H_
|
||||
@@ -63,6 +63,9 @@ const wchar_t kHomePage[] = L"homepage";
|
||||
// Causes the process to run as renderer instead of as browser.
|
||||
const wchar_t kRendererProcess[] = L"renderer";
|
||||
|
||||
// Causes the process to run as a renderer zygote.
|
||||
const wchar_t kZygoteProcess[] = L"zygote";
|
||||
|
||||
// Path to the exe to run for the renderer and plugin subprocesses.
|
||||
const wchar_t kBrowserSubprocessPath[] = L"browser-subprocess-path";
|
||||
|
||||
@@ -490,6 +493,9 @@ const wchar_t kNoDefaultBrowserCheck[] = L"no-default-browser-check";
|
||||
// Enables the benchmarking extensions.
|
||||
const wchar_t kEnableBenchmarking[] = L"enable-benchmarking";
|
||||
|
||||
// The prefix used when starting the zygote process. (i.e. 'gdb --args')
|
||||
const wchar_t kZygoteCmdPrefix[] = L"zygote-cmd-prefix";
|
||||
|
||||
// Enables using ThumbnailStore instead of ThumbnailDatabase for setting and
|
||||
// getting thumbnails for the new tab page.
|
||||
const wchar_t kThumbnailStore[] = L"thumbnail-store";
|
||||
|
||||
@@ -26,6 +26,7 @@ extern const wchar_t kProcessChannelID[];
|
||||
extern const wchar_t kTestingChannelID[];
|
||||
extern const wchar_t kHomePage[];
|
||||
extern const wchar_t kRendererProcess[];
|
||||
extern const wchar_t kZygoteProcess[];
|
||||
extern const wchar_t kBrowserSubprocessPath[];
|
||||
extern const wchar_t kPluginProcess[];
|
||||
extern const wchar_t kWorkerProcess[];
|
||||
@@ -185,6 +186,8 @@ extern const wchar_t kEnableBenchmarking[];
|
||||
|
||||
extern const wchar_t kNoDefaultBrowserCheck[];
|
||||
|
||||
extern const wchar_t kZygoteCmdPrefix[];
|
||||
|
||||
extern const wchar_t kThumbnailStore[];
|
||||
|
||||
extern const wchar_t kTryChromeAgain[];
|
||||
|
||||
@@ -87,14 +87,13 @@ class Channel : public Message::Sender {
|
||||
|
||||
#if defined(OS_POSIX)
|
||||
// On POSIX an IPC::Channel wraps a socketpair(), this method returns the
|
||||
// FD # for the client end of the socket and the equivalent FD# to use for
|
||||
// mapping it into the Child process.
|
||||
// FD # for the client end of the socket.
|
||||
// This method may only be called on the server side of a channel.
|
||||
//
|
||||
// If the kTestingChannelID flag is specified on the command line then
|
||||
// a named FIFO is used as the channel transport mechanism rather than a
|
||||
// socketpair() in which case this method returns -1 for both parameters.
|
||||
void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
|
||||
// socketpair() in which case this method returns -1.
|
||||
int GetClientFileDescriptor() const;
|
||||
#endif // defined(OS_POSIX)
|
||||
|
||||
private:
|
||||
|
||||
@@ -17,15 +17,16 @@
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/global_descriptors_posix.h"
|
||||
#include "base/lock.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/process_util.h"
|
||||
#include "base/reserved_file_descriptors.h"
|
||||
#include "base/scoped_ptr.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/singleton.h"
|
||||
#include "base/stats_counters.h"
|
||||
#include "chrome/common/chrome_counters.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/file_descriptor_set_posix.h"
|
||||
#include "chrome/common/ipc_logging.h"
|
||||
@@ -40,7 +41,7 @@ namespace IPC {
|
||||
//
|
||||
// When creating a child subprocess, the parent side of the fork
|
||||
// arranges it such that the initial control channel ends up on the
|
||||
// magic file descriptor kClientChannelFd in the child. Future
|
||||
// magic file descriptor kPrimaryIPCChannel in the child. Future
|
||||
// connections (file descriptors) can then be passed via that
|
||||
// connection via sendmsg().
|
||||
|
||||
@@ -50,7 +51,7 @@ namespace {
|
||||
// The PipeMap class works around this quirk related to unit tests:
|
||||
//
|
||||
// When running as a server, we install the client socket in a
|
||||
// specific file descriptor number (@kClientChannelFd). However, we
|
||||
// specific file descriptor number (@kPrimaryIPCChannel). However, we
|
||||
// also have to support the case where we are running unittests in the
|
||||
// same process. (We do not support forking without execing.)
|
||||
//
|
||||
@@ -58,7 +59,7 @@ namespace {
|
||||
// The IPC server object will install a mapping in PipeMap from the
|
||||
// name which it was given to the client pipe. When forking the client, the
|
||||
// GetClientFileDescriptorMapping will ensure that the socket is installed in
|
||||
// the magic slot (@kClientChannelFd). The client will search for the
|
||||
// the magic slot (@kPrimaryIPCChannel). The client will search for the
|
||||
// mapping, but it won't find any since we are in a new process. Thus the
|
||||
// magic fd number is returned. Once the client connects, the server will
|
||||
// close its copy of the client socket and remove the mapping.
|
||||
@@ -124,10 +125,7 @@ int ChannelNameToClientFD(const std::string& channel_id) {
|
||||
|
||||
// If we don't find an entry, we assume that the correct value has been
|
||||
// inserted in the magic slot.
|
||||
// kClientChannelFd is the file descriptor number that a client process
|
||||
// expects to find its IPC socket; see reserved_file_descriptors.h.
|
||||
|
||||
return kClientChannelFd;
|
||||
return Singleton<base::GlobalDescriptors>()->Get(kPrimaryIPCChannel);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -671,11 +669,8 @@ bool Channel::ChannelImpl::Send(Message* message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Channel::ChannelImpl::GetClientFileDescriptorMapping(int *src_fd,
|
||||
int *dest_fd) const {
|
||||
DCHECK(mode_ == MODE_SERVER);
|
||||
*src_fd = client_pipe_;
|
||||
*dest_fd = kClientChannelFd;
|
||||
int Channel::ChannelImpl::GetClientFileDescriptor() const {
|
||||
return client_pipe_;
|
||||
}
|
||||
|
||||
// Called by libevent when we can read from th pipe without blocking.
|
||||
@@ -803,8 +798,8 @@ bool Channel::Send(Message* message) {
|
||||
return channel_impl_->Send(message);
|
||||
}
|
||||
|
||||
void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const {
|
||||
return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
|
||||
int Channel::GetClientFileDescriptor() const {
|
||||
return channel_impl_->GetClientFileDescriptor();
|
||||
}
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
@@ -29,7 +29,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
|
||||
void Close();
|
||||
void set_listener(Listener* listener) { listener_ = listener; }
|
||||
bool Send(Message* message);
|
||||
void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
|
||||
int GetClientFileDescriptor() const;
|
||||
|
||||
private:
|
||||
bool CreatePipe(const std::string& channel_id, Mode mode);
|
||||
|
||||
@@ -289,11 +289,10 @@ void ChannelProxy::RemoveFilter(MessageFilter* filter) {
|
||||
// See the TODO regarding lazy initialization of the channel in
|
||||
// ChannelProxy::Init().
|
||||
// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe.
|
||||
void ChannelProxy::GetClientFileDescriptorMapping(int *src_fd,
|
||||
int *dest_fd) const {
|
||||
int ChannelProxy::GetClientFileDescriptor() const {
|
||||
Channel *channel = context_.get()->channel_;
|
||||
DCHECK(channel); // Channel must have been created first.
|
||||
channel->GetClientFileDescriptorMapping(src_fd, dest_fd);
|
||||
return channel->GetClientFileDescriptor();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ class ChannelProxy : public Message::Sender {
|
||||
// Calls through to the underlying channel's methods.
|
||||
// TODO(playmobil): For now this is only implemented in the case of
|
||||
// create_pipe_now = true, we need to figure this out for the latter case.
|
||||
void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
|
||||
int GetClientFileDescriptor() const;
|
||||
#endif // defined(OS_POSIX)
|
||||
|
||||
protected:
|
||||
|
||||
@@ -21,11 +21,16 @@
|
||||
#include "base/base_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug_on_start.h"
|
||||
#if defined(OS_POSIX)
|
||||
#include "base/at_exit.h"
|
||||
#include "base/global_descriptors_posix.h"
|
||||
#endif
|
||||
#include "base/perftimer.h"
|
||||
#include "base/perf_test_suite.h"
|
||||
#include "base/test_suite.h"
|
||||
#include "base/thread.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/common/ipc_channel.h"
|
||||
#include "chrome/common/ipc_channel_proxy.h"
|
||||
#include "chrome/common/ipc_message_utils.h"
|
||||
@@ -86,11 +91,9 @@ base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type,
|
||||
CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren);
|
||||
|
||||
base::file_handle_mapping_vector fds_to_map;
|
||||
int src_fd;
|
||||
int dest_fd;
|
||||
channel->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
|
||||
if (src_fd > -1) {
|
||||
fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
|
||||
const int ipcfd = channel->GetClientFileDescriptor();
|
||||
if (ipcfd > -1) {
|
||||
fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
|
||||
}
|
||||
|
||||
base::ProcessHandle ret = NULL;
|
||||
@@ -258,11 +261,9 @@ TEST_F(IPCChannelTest, ChannelProxyTest) {
|
||||
bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
switches::kDebugChildren);
|
||||
base::file_handle_mapping_vector fds_to_map;
|
||||
int src_fd;
|
||||
int dest_fd;
|
||||
chan.GetClientFileDescriptorMapping(&src_fd, &dest_fd);
|
||||
if (src_fd > -1) {
|
||||
fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
|
||||
const int ipcfd = chan.GetClientFileDescriptor();
|
||||
if (ipcfd > -1) {
|
||||
fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3));
|
||||
}
|
||||
|
||||
base::ProcessHandle process_handle = MultiProcessTest::SpawnChild(
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include "base/eintr_wrapper.h"
|
||||
#include "base/platform_thread.h"
|
||||
#include "base/zygote_manager.h"
|
||||
|
||||
// Return true if the given child is dead. This will also reap the process.
|
||||
// Doesn't block.
|
||||
@@ -70,20 +69,8 @@ class BackgroundReaper : public PlatformThread::Delegate {
|
||||
// static
|
||||
void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
|
||||
// If the child is already dead, then there's nothing to do
|
||||
const int result = HANDLE_EINTR(waitpid(process, NULL, WNOHANG));
|
||||
if (result > 0)
|
||||
if (IsChildDead(process))
|
||||
return;
|
||||
if (result == -1) {
|
||||
#if defined(OS_LINUX)
|
||||
// If it wasn't our child, maybe it was the zygote manager's child
|
||||
base::ZygoteManager* zm = base::ZygoteManager::Get();
|
||||
if (zm) {
|
||||
zm->EnsureProcessTerminated(process);
|
||||
return;
|
||||
}
|
||||
#endif // defined(OS_LINUX)
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
BackgroundReaper* reaper = new BackgroundReaper(process);
|
||||
PlatformThread::CreateNonJoinable(0, reaper);
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#if defined(OS_WIN)
|
||||
#include "chrome/test/injection_test_dll.h"
|
||||
#include "sandbox/src/sandbox.h"
|
||||
#elif defined(OS_LINUX)
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "base/global_descriptors_posix.h"
|
||||
#endif
|
||||
|
||||
// main() routine for running as the plugin process.
|
||||
|
||||
@@ -53,11 +53,9 @@ void RendererMainTest::TearDown() {
|
||||
ProcessHandle RendererMainTest::SpawnChild(const std::wstring &procname,
|
||||
IPC::Channel *channel) {
|
||||
base::file_handle_mapping_vector fds_to_map;
|
||||
int src_fd;
|
||||
int dest_fd;
|
||||
channel->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
|
||||
if (src_fd > -1) {
|
||||
fds_to_map.push_back(std::pair<int,int>(src_fd, dest_fd));
|
||||
const int ipcfd = channel->GetClientFileDescriptor();
|
||||
if (ipcfd > -1) {
|
||||
fds_to_map.push_back(std::pair<int,int>(ipcfd, 3));
|
||||
}
|
||||
|
||||
return MultiProcessTest::SpawnChild(procname, fds_to_map, false);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "base/process_util.h"
|
||||
#include "base/ref_counted.h"
|
||||
#include "base/waitable_event.h"
|
||||
#include "chrome/common/chrome_descriptors.h"
|
||||
#include "chrome/test/automation/automation_constants.h"
|
||||
#include "chrome/test/automation/automation_messages.h"
|
||||
#include "chrome/test/automation/browser_proxy.h"
|
||||
@@ -440,10 +441,9 @@ scoped_refptr<BrowserProxy> AutomationProxy::GetLastActiveBrowserWindow() {
|
||||
#if defined(OS_POSIX)
|
||||
base::file_handle_mapping_vector AutomationProxy::fds_to_map() const {
|
||||
base::file_handle_mapping_vector map;
|
||||
int src_fd = -1, dest_fd = -1;
|
||||
channel_->GetClientFileDescriptorMapping(&src_fd, &dest_fd);
|
||||
if (src_fd > -1)
|
||||
map.push_back(std::make_pair(src_fd, dest_fd));
|
||||
const int ipcfd = channel_->GetClientFileDescriptor();
|
||||
if (ipcfd > -1)
|
||||
map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3));
|
||||
return map;
|
||||
}
|
||||
#endif // defined(OS_POSIX)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "chrome/test/chrome_process_util.h"
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "base/process_util.h"
|
||||
#include "base/time.h"
|
||||
@@ -14,26 +15,6 @@
|
||||
using base::Time;
|
||||
using base::TimeDelta;
|
||||
|
||||
namespace {
|
||||
|
||||
class ChromeProcessFilter : public base::ProcessFilter {
|
||||
public:
|
||||
explicit ChromeProcessFilter(base::ProcessId browser_pid)
|
||||
: browser_pid_(browser_pid) {}
|
||||
|
||||
virtual bool Includes(base::ProcessId pid, base::ProcessId parent_pid) const {
|
||||
// Match browser process itself and its children.
|
||||
return browser_pid_ == pid || browser_pid_ == parent_pid;
|
||||
}
|
||||
|
||||
private:
|
||||
base::ProcessId browser_pid_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ChromeProcessFilter);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void TerminateAllChromeProcesses(const FilePath& data_dir) {
|
||||
// Total time the function will wait for chrome processes
|
||||
// to terminate after it told them to do so.
|
||||
@@ -70,44 +51,57 @@ void TerminateAllChromeProcesses(const FilePath& data_dir) {
|
||||
base::CloseProcessHandle(*it);
|
||||
}
|
||||
|
||||
class ChildProcessFilter : public base::ProcessFilter {
|
||||
public:
|
||||
explicit ChildProcessFilter(base::ProcessId parent_pid)
|
||||
: parent_pids_(&parent_pid, (&parent_pid) + 1) {}
|
||||
|
||||
explicit ChildProcessFilter(std::vector<base::ProcessId> parent_pids)
|
||||
: parent_pids_(parent_pids.begin(), parent_pids.end()) {}
|
||||
|
||||
virtual bool Includes(base::ProcessId pid, base::ProcessId parent_pid) const {
|
||||
return parent_pids_.find(parent_pid) != parent_pids_.end();
|
||||
}
|
||||
|
||||
private:
|
||||
const std::set<base::ProcessId> parent_pids_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ChildProcessFilter);
|
||||
};
|
||||
|
||||
ChromeProcessList GetRunningChromeProcesses(const FilePath& data_dir) {
|
||||
ChromeProcessList result;
|
||||
|
||||
base::ProcessId browser_pid = ChromeBrowserProcessId(data_dir);
|
||||
if (browser_pid < 0)
|
||||
if (browser_pid == (base::ProcessId) -1)
|
||||
return result;
|
||||
|
||||
// Normally, the browser is the parent process for all the renderers
|
||||
base::ProcessId parent_pid = browser_pid;
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
// But if the browser's parent is the same executable as the browser,
|
||||
// then it's the zygote manager, and it's the parent for all the renderers.
|
||||
base::ProcessId manager_pid = base::GetParentProcessId(browser_pid);
|
||||
FilePath selfPath = base::GetProcessExecutablePath(browser_pid);
|
||||
FilePath managerPath = base::GetProcessExecutablePath(manager_pid);
|
||||
if (!selfPath.empty() && !managerPath.empty() && selfPath == managerPath) {
|
||||
LOG(INFO) << "Zygote manager in use.";
|
||||
parent_pid = manager_pid;
|
||||
}
|
||||
#endif
|
||||
|
||||
ChromeProcessFilter filter(parent_pid);
|
||||
ChildProcessFilter filter(browser_pid);
|
||||
base::NamedProcessIterator it(chrome::kBrowserProcessExecutableName, &filter);
|
||||
|
||||
const ProcessEntry* process_entry;
|
||||
while ((process_entry = it.NextProcessEntry())) {
|
||||
#if defined(OS_WIN)
|
||||
result.push_back(process_entry->th32ProcessID);
|
||||
#elif defined(OS_LINUX)
|
||||
// Don't count the zygote manager, that screws up unit tests,
|
||||
// and it will exit cleanly on its own when first client exits.
|
||||
if (process_entry->pid != manager_pid)
|
||||
result.push_back(process_entry->pid);
|
||||
#elif defined(OS_POSIX)
|
||||
result.push_back(process_entry->pid);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
// On Linux we might be running with a zygote process for the renderers.
|
||||
// Because of that we sweep the list of processes again and pick those which
|
||||
// are children of one of the processes that we've already seen.
|
||||
{
|
||||
ChildProcessFilter filter(result);
|
||||
base::NamedProcessIterator it(chrome::kBrowserProcessExecutableName,
|
||||
&filter);
|
||||
while ((process_entry = it.NextProcessEntry()))
|
||||
result.push_back(process_entry->pid);
|
||||
}
|
||||
#endif
|
||||
|
||||
result.push_back(browser_pid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário