Prototype implementation of zygotes.

Limitations that need addressing still:
- Doesn't forcibly terminate children that should have exited but haven't

Enable with env var ENABLE_ZYGOTE_MANAGER=1.

BUG=11841
TEST=
start the browser, then make chrome and all .pak files unreadable; or alternately, start an installed browser, and uninstall the browser while it's running.  Then create a new tab and browse to two new sites. 

Here's an example script to hide and unhide the .pak files (note: do not move the directory they're in, that doesn't work):

#!/bin/sh
chmod_all() {
   chmod $1 sconsbuild/Debug/chrome
   for path in . locales obj/chrome/app/intermediate/repack obj/global_intermediate/* themes
   do
     chmod $1 sconsbuild/Debug/$path/*.pak
   done
}
case $1 in
hide) chmod_all 000 ;;
show) chmod_all 755 ;;
esac


Review URL: http://codereview.chromium.org/115773

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17840 0039d316-1c4b-4281-b951-d872f2087c98
Esse commit está contido em:
dkegel@google.com
2009-06-06 19:59:36 +00:00
commit 9c85299ea2
18 arquivos alterados com 1415 adições e 15 exclusões
+6
Ver Arquivo
@@ -417,6 +417,9 @@
# 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',
@@ -681,6 +684,9 @@
],
'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
+11
Ver Arquivo
@@ -177,6 +177,17 @@ void CommandLine::Init(int argc, const char* const* argv) {
#endif
}
// static
void CommandLine::Init(const std::vector<std::string>& argv) {
DCHECK(current_process_commandline_ == NULL);
#if defined(OS_WIN)
current_process_commandline_ = new CommandLine;
current_process_commandline_->ParseFromString(::GetCommandLineW());
#elif defined(OS_POSIX)
current_process_commandline_ = new CommandLine(argv);
#endif
}
void CommandLine::Terminate() {
DCHECK(current_process_commandline_ != NULL);
delete current_process_commandline_;
+1
Ver Arquivo
@@ -50,6 +50,7 @@ class CommandLine {
// directly) because we don't trust the CRT's parsing of the command
// line, but it still must be called to set up the command line.
static void Init(int argc, const char* const* argv);
static void Init(const std::vector<std::string>& argv);
// Destroys the current process CommandLine singleton. This is necessary if
// you want to reset the base library to its initial state (for example in an
+4 -2
Ver Arquivo
@@ -21,8 +21,10 @@ bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
int temp_fd = -1;
// We DCHECK the injectiveness of the mapping.
for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j)
DCHECK(i->dest != j->dest);
for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j) {
DCHECK(i->dest != j->dest) << "Both fd " << i->source
<< " and " << j->source << " map to " << i->dest;
}
const bool is_identity = i->source == i->dest;
+22 -4
Ver Arquivo
@@ -27,6 +27,7 @@
#include "base/logging.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/zygote_manager.h"
namespace file_util {
@@ -625,20 +626,37 @@ MemoryMappedFile::MemoryMappedFile()
}
bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
file_ = open(file_name.value().c_str(), O_RDONLY);
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";
}
}
#endif // defined(OS_LINUX)
if (file_ == -1)
file_ = open(file_name.value().c_str(), O_RDONLY);
if (file_ == -1) {
LOG(ERROR) << "Couldn't open " << file_name.value();
return false;
}
struct stat file_stat;
if (fstat(file_, &file_stat) == -1)
if (fstat(file_, &file_stat) == -1) {
LOG(ERROR) << "Couldn't fstat " << file_name.value() << ", errno " << errno;
return false;
}
length_ = file_stat.st_size;
data_ = static_cast<uint8*>(
mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
if (data_ == MAP_FAILED)
data_ = NULL;
return data_ != NULL;
LOG(ERROR) << "Couldn't mmap " << file_name.value() << ", errno " << errno;
return data_ != MAP_FAILED;
}
void MemoryMappedFile::CloseHandles() {
+24
Ver Arquivo
@@ -19,9 +19,12 @@ 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;
@@ -37,6 +40,7 @@ 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"
@@ -205,7 +209,17 @@ bool InitializeLogFileHandle() {
}
SetFilePointer(log_file, 0, 0, FILE_END);
#elif defined(OS_POSIX)
// 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;
#endif
@@ -214,6 +228,16 @@ bool InitializeLogFileHandle() {
return true;
}
#if defined(OS_LINUX)
int GetLoggingFileDescriptor() {
// No locking needed, since this is only called by the zygote server,
// which is single-threaded.
if (log_file)
return fileno(log_file);
return -1;
}
#endif
void InitLogMutex() {
#if defined(OS_WIN)
if (!log_mutex) {
+7
Ver Arquivo
@@ -141,6 +141,13 @@ void SetMinLogLevel(int level);
// Gets the current log level.
int GetMinLogLevel();
#if defined(OS_LINUX)
// Get the file descriptor used for logging.
// Returns -1 if none open.
// Needed by ZygoteManager.
int GetLoggingFileDescriptor();
#endif
// Sets the log filter prefix. Any log message below LOG_ERROR severity that
// doesn't start with this prefix with be silently ignored. The filter defaults
// to NULL (everything is logged) if this function is not called. Messages
+8
Ver Arquivo
@@ -132,6 +132,14 @@ 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
+19
Ver Arquivo
@@ -17,6 +17,7 @@
#include "base/logging.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
#include "base/zygote_manager.h"
namespace {
@@ -41,6 +42,22 @@ void GetProcStats(pid_t pid, std::vector<std::string>* proc_stats) {
namespace base {
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) {
@@ -65,6 +82,8 @@ bool LaunchApp(const std::vector<std::string>& argv,
argv_cstr[i] = const_cast<char*>(argv[i].c_str());
argv_cstr[argv.size()] = NULL;
execvp(argv_cstr[0], argv_cstr.get());
LOG(ERROR) << "LaunchApp: exec failed!, argv_cstr[0] " << argv_cstr[0]
<< ", errno " << errno;
exit(127);
} else {
if (wait)
+36
Ver Arquivo
@@ -0,0 +1,36 @@
// 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
+828
Ver Arquivo
@@ -0,0 +1,828 @@
// 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;
}
}
// Runs in client process
ZygoteManager* ZygoteManager::Get() {
static bool checked = false;
static bool enabled = false;
if (!checked) {
enabled = (getenv("ENABLE_ZYGOTE_MANAGER") != NULL);
// sanity check - make sure all the places that relaunch chrome
// have been zygotified.
if (enabled)
DCHECK(getenv("ZYGOTE_MANAGER_STARTED") == NULL)
<< "fork/exec used instead of LongFork";
(void) setenv("ZYGOTE_MANAGER_STARTED", "1", 1);
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) {
LOG(INFO) << "poll interrupted";
// 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);
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 parent. 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 child. 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.
// Chrome doesn't seem to get here in practice.
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(-1);
}
}
}
#endif // defined(OS_LINUX)
+143
Ver Arquivo
@@ -0,0 +1,143 @@
// 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_
+266
Ver Arquivo
@@ -0,0 +1,266 @@
// 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/zygote_manager.h"
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/process_util.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
using file_util::Delete;
using file_util::WriteFile;
using file_util::ReadFileToString;
using file_util::GetCurrentDirectory;
#if defined(OS_LINUX)
// ZygoteManager is only used on Linux at the moment
typedef testing::Test ZygoteManagerTest;
TEST_F(ZygoteManagerTest, Ping) {
base::ZygoteManager zm;
scoped_ptr< std::vector<std::string> > new_argv;
new_argv.reset(zm.Start());
EXPECT_TRUE(new_argv.get() == NULL);
// Measure round trip time
base::TimeDelta delta;
EXPECT_EQ(zm.Ping(&delta), true);
EXPECT_LT(delta.InMilliseconds(), 5000);
}
TEST_F(ZygoteManagerTest, SpawnChild) {
base::ZygoteManager zm;
const int kDummyChildExitCode = 39;
scoped_ptr< std::vector<std::string> > new_argv;
new_argv.reset(zm.Start());
if (new_argv.get() == NULL) {
// original process
// Launch a child process
std::vector<std::string> myargs;
myargs.push_back(std::string("0thArg"));
myargs.push_back(std::string("1stArg"));
base::file_handle_mapping_vector no_files;
pid_t child = zm.LongFork(myargs, no_files);
EXPECT_NE(child, -1);
EXPECT_NE(child, 0);
LOG(INFO) << "child pid " << child;
// ZygoteManager doesn't support waiting for exit status
int status;
int err = HANDLE_EINTR(waitpid(child, &status, 0));
EXPECT_EQ(-1, err);
EXPECT_EQ(ECHILD, errno);
} else {
LOG(INFO) << "Hello from child!";
// child process
std::string arg0(new_argv.get()->at(0));
std::string arg1(new_argv.get()->at(1));
EXPECT_EQ(arg0, std::string("0thArg"));
EXPECT_EQ(arg1, std::string("1stArg"));
exit(kDummyChildExitCode);
}
}
TEST_F(ZygoteManagerTest, MapFile) {
base::ZygoteManager zm;
const int kDummyChildExitCode = 39;
const int kSpecialFDSlot = 5;
scoped_ptr< std::vector<std::string> > new_argv;
new_argv.reset(zm.Start());
if (new_argv.get() == NULL) {
// original process
// Launch a child process
std::vector<std::string> myargs;
myargs.push_back(std::string("0thArg"));
myargs.push_back(std::string("1stArg"));
base::file_handle_mapping_vector fds_to_map;
int fd = open("/tmp/zygote_manager_unittest.tmp", O_CREAT|O_RDWR|O_TRUNC, 0644);
fds_to_map.push_back(std::pair<int, int>(fd, kSpecialFDSlot));
pid_t child = zm.LongFork(myargs, fds_to_map);
EXPECT_NE(child, -1);
EXPECT_NE(child, 0);
// FIXME: really wait for child
sleep(3);
char buf[100];
// Expect fd to be seeked to end, so reading without seeking should fail
off_t loc = lseek(fd, 0L, SEEK_CUR);
EXPECT_EQ(3, loc);
memset(buf, 0, sizeof(buf));
int nread = read(fd, buf, 5);
EXPECT_EQ(0, nread);
// Try again from beginning
lseek(fd, 0L, SEEK_SET);
memset(buf, 0, sizeof(buf));
nread = read(fd, buf, 2);
EXPECT_EQ(2, nread);
EXPECT_EQ(strncmp(buf, "hi", 2), 0);
close(fd);
} else {
// child process
// Write three bytes; this happens to seek the file descriptor to the end.
int nwritten = write(kSpecialFDSlot, "hi\n", 3);
EXPECT_EQ(3, nwritten);
exit(kDummyChildExitCode);
}
}
TEST_F(ZygoteManagerTest, OpenFile) {
base::ZygoteManager zm;
scoped_ptr< std::vector<std::string> > new_argv;
new_argv.reset(zm.Start());
EXPECT_EQ(NULL, new_argv.get());
const char kSomeText[] = "foobar\n";
// Verify that we disallow nonabsolute paths.
FilePath badfilepath(FilePath::kCurrentDirectory);
badfilepath = badfilepath.AppendASCII("zygote_manager_test.pak");
ASSERT_FALSE(badfilepath.IsAbsolute());
EXPECT_NE(-1, WriteFile(badfilepath, kSomeText, strlen(kSomeText)));
std::string badfilename = WideToASCII(badfilepath.ToWStringHack());
int fd = zm.OpenFile(badfilename);
EXPECT_EQ(-1, fd);
EXPECT_TRUE(Delete(badfilepath, false));
// Verify that we disallow non-plain files.
ASSERT_TRUE(GetCurrentDirectory(&badfilepath));
badfilepath = badfilepath.AppendASCII("zygote_manager_test.pak");
std::string badfilenameA = WideToASCII(badfilepath.ToWStringHack());
EXPECT_EQ(0, mkfifo(badfilenameA.c_str(), 0644));
badfilename = WideToASCII(badfilepath.ToWStringHack());
fd = zm.OpenFile(badfilename);
ASSERT_EQ(-1, fd);
EXPECT_TRUE(Delete(badfilepath, false));
// Verify that we disallow files not ending in .pak.
ASSERT_TRUE(GetCurrentDirectory(&badfilepath));
badfilepath = badfilepath.AppendASCII("zygote_manager_test.tmp");
ASSERT_NE(-1, WriteFile(badfilepath, kSomeText, strlen(kSomeText)));
badfilename = WideToASCII(badfilepath.ToWStringHack());
fd = zm.OpenFile(badfilename);
ASSERT_EQ(-1, fd);
EXPECT_TRUE(Delete(badfilepath, false));
// Verify that we disallow files in /etc
badfilepath = FilePath(FILE_PATH_LITERAL("/"));
badfilepath = badfilepath.AppendASCII("etc");
badfilepath = badfilepath.AppendASCII("hosts");
EXPECT_TRUE(badfilepath.IsAbsolute());
badfilename = WideToASCII(badfilepath.ToWStringHack());
fd = zm.OpenFile(badfilename);
ASSERT_EQ(-1, fd);
// Verify that we disallow files in /dev
badfilepath = FilePath(FILE_PATH_LITERAL("/"));
badfilepath = badfilepath.AppendASCII("dev");
badfilepath = badfilepath.AppendASCII("tty");
EXPECT_TRUE(badfilepath.IsAbsolute());
badfilename = WideToASCII(badfilepath.ToWStringHack());
fd = zm.OpenFile(badfilename);
ASSERT_EQ(-1, fd);
// Verify that we allow absolute paths with filename ending in .pak,
// and that we can open them a second time even if they were
// deleted after we opened them the first time.
// Because of our restrictive filename checks, can't put
// test file in /tmp, so put it in current directory.
FilePath goodfilepath;
ASSERT_TRUE(GetCurrentDirectory(&goodfilepath));
goodfilepath = goodfilepath.AppendASCII("zygote_manager_test.pak");
ASSERT_NE(-1, WriteFile(goodfilepath, kSomeText, strlen(kSomeText)));
std::string goodfilename = WideToASCII(goodfilepath.ToWStringHack());
for (int i = 0; i < 2; i++) {
fd = zm.OpenFile(goodfilename);
ASSERT_NE(-1, fd);
char buf[sizeof(kSomeText)];
// Can't use read because it depends on file position.
// (In practice these files are mmapped.)
int nread = pread(fd, buf, strlen(kSomeText), 0);
ASSERT_EQ(strlen(kSomeText), static_cast<size_t>(nread));
EXPECT_EQ(0, strncmp(buf, kSomeText, strlen(kSomeText)));
EXPECT_EQ(0, close(fd));
// oddly, our Delete returns true for nonexistant files.
EXPECT_EQ(true, Delete(goodfilepath, false));
}
}
TEST_F(ZygoteManagerTest, ChildOpenFile) {
base::ZygoteManager zm;
const int kDummyChildExitCode = 39;
const char kSomeText[] = "foobar\n";
FilePath resultfilepath(FILE_PATH_LITERAL("/tmp"));
resultfilepath = resultfilepath.AppendASCII("zygote_manager_test_result.tmp");
EXPECT_EQ(true, Delete(resultfilepath, false));
scoped_ptr< std::vector<std::string> > new_argv;
new_argv.reset(zm.Start());
if (new_argv.get() == NULL) {
// original process
// Launch a child process
std::vector<std::string> myargs;
base::file_handle_mapping_vector no_files;
pid_t child = zm.LongFork(myargs, no_files);
EXPECT_NE(child, -1);
EXPECT_NE(child, 0);
LOG(INFO) << "child pid " << child;
// Wait for resultfile to be created
std::string result;
int nloops = 0;
while (!ReadFileToString(resultfilepath, &result)) {
sleep(1);
++nloops;
ASSERT_NE(10, nloops);
}
ASSERT_EQ(result, std::string(kSomeText));
EXPECT_EQ(true, Delete(resultfilepath, false));
} else {
LOG(INFO) << "Hello from child!";
FilePath goodfilepath;
ASSERT_TRUE(GetCurrentDirectory(&goodfilepath));
goodfilepath = goodfilepath.AppendASCII("zygote_manager_test.pak");
ASSERT_NE(-1, WriteFile(goodfilepath, kSomeText, strlen(kSomeText)));
std::string goodfilename = WideToASCII(goodfilepath.ToWStringHack());
int fd = zm.OpenFile(goodfilename);
EXPECT_EQ(true, Delete(goodfilepath, false));
ASSERT_NE(-1, fd);
char buf[sizeof(kSomeText)];
// Can't use read because it depends on file position.
// (In practice these files are mmapped.)
int nread = pread(fd, buf, strlen(kSomeText), 0);
ASSERT_EQ(strlen(kSomeText), static_cast<size_t>(nread));
EXPECT_EQ(0, strncmp(buf, kSomeText, strlen(kSomeText)));
EXPECT_EQ(0, close(fd));
ASSERT_NE(-1, WriteFile(resultfilepath, kSomeText, strlen(kSomeText)));
exit(kDummyChildExitCode);
}
}
#endif
+2 -2
Ver Arquivo
@@ -14,6 +14,7 @@
#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"
@@ -501,8 +502,7 @@ 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 this slot:
static const int kMagicCrashSignalFd = 4;
// descriptor in slot kMagicCrashSignalFd.
// We deliberately leak this object.
google_breakpad::ExceptionHandler* handler =
+15
Ver Arquivo
@@ -45,6 +45,9 @@
#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)
@@ -292,6 +295,18 @@ int ChromeMain(int argc, const char** argv) {
// 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
@@ -22,6 +22,7 @@
#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"
@@ -345,9 +346,11 @@ bool BrowserRenderProcessHost::Init() {
const int crash_signal_fd =
Singleton<RenderCrashHandlerHostLinux>()->GetDeathSignalSocket();
if (crash_signal_fd >= 0)
fds_to_map.push_back(std::make_pair(crash_signal_fd, 4));
#endif
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);
#endif
#endif
if (!process) {
channel_.reset();
+4 -4
Ver Arquivo
@@ -20,6 +20,7 @@
#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"
@@ -114,10 +115,6 @@ class PipeMap {
ChannelToFDMap map_;
};
// This is the file descriptor number that a client process expects to find its
// IPC socket.
static const int kClientChannelFd = 3;
// Used to map a channel name to the equivalent FD # in the client process.
int ChannelNameToClientFD(const std::string& channel_id) {
// See the large block comment above PipeMap for the reasoning here.
@@ -127,6 +124,9 @@ 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;
}
+14 -1
Ver Arquivo
@@ -11,6 +11,7 @@
#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.
@@ -69,8 +70,20 @@ class BackgroundReaper : public PlatformThread::Delegate {
// static
void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) {
// If the child is already dead, then there's nothing to do
if (IsChildDead(process))
const int result = HANDLE_EINTR(waitpid(process, NULL, WNOHANG));
if (result > 0)
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);