diff options
38 files changed, 1219 insertions, 740 deletions
diff --git a/base/base.gyp b/base/base.gyp index 96deb53..5d09098 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -133,8 +133,6 @@ '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', @@ -326,7 +324,6 @@ 'tracked_objects.cc', 'tracked_objects.h', 'tuple.h', - 'unix_domain_socket_posix.cc', 'values.cc', 'values.h', 'version.cc', @@ -421,6 +418,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', @@ -685,6 +685,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 diff --git a/base/command_line.cc b/base/command_line.cc index 582c8b8..735620d 100644 --- a/base/command_line.cc +++ b/base/command_line.cc @@ -168,12 +168,6 @@ bool CommandLine::IsSwitch(const StringType& parameter_string, } // 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) current_process_commandline_ = new CommandLine; diff --git a/base/command_line.h b/base/command_line.h index d128d49..c895b81 100644 --- a/base/command_line.h +++ b/base/command_line.h @@ -45,9 +45,6 @@ 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 diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index aca0ff9..92b9d00 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -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 { @@ -645,8 +646,19 @@ 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. (OK at start of ui_tests.)"; + } + } +#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; diff --git a/base/global_descriptors_posix.cc b/base/global_descriptors_posix.cc deleted file mode 100644 index 869d196..0000000 --- a/base/global_descriptors_posix.cc +++ /dev/null @@ -1,46 +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. - -#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 diff --git a/base/global_descriptors_posix.h b/base/global_descriptors_posix.h deleted file mode 100644 index f606a82..0000000 --- a/base/global_descriptors_posix.h +++ /dev/null @@ -1,66 +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_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_ diff --git a/base/logging.cc b/base/logging.cc index b5af671..77e9583 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -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" @@ -204,6 +208,20 @@ 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) diff --git a/base/process_util.h b/base/process_util.h index 41d0470..4fe081f 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -141,6 +141,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 diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc index 7560257..ba2d8cd 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -17,6 +17,7 @@ #include "base/logging.h" #include "base/string_tokenizer.h" #include "base/string_util.h" +#include "base/zygote_manager.h" namespace { @@ -85,6 +86,22 @@ 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) { diff --git a/base/reserved_file_descriptors.h b/base/reserved_file_descriptors.h new file mode 100644 index 0000000..5236a08 --- /dev/null +++ b/base/reserved_file_descriptors.h @@ -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 diff --git a/base/unix_domain_socket_posix.cc b/base/unix_domain_socket_posix.cc deleted file mode 100644 index 8d53d2a..0000000 --- a/base/unix_domain_socket_posix.cc +++ /dev/null @@ -1,98 +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. - -#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 diff --git a/base/unix_domain_socket_posix.h b/base/unix_domain_socket_posix.h deleted file mode 100644 index 46c473a..0000000 --- a/base/unix_domain_socket_posix.h +++ /dev/null @@ -1,22 +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_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_ diff --git a/base/zygote_manager.cc b/base/zygote_manager.cc new file mode 100644 index 0000000..7b2c026 --- /dev/null +++ b/base/zygote_manager.cc @@ -0,0 +1,832 @@ +// 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) diff --git a/base/zygote_manager.h b/base/zygote_manager.h new file mode 100644 index 0000000..5b94533 --- /dev/null +++ b/base/zygote_manager.h @@ -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_ diff --git a/chrome/app/breakpad_linux.cc b/chrome/app/breakpad_linux.cc index ef3dfa2..0ee0bed 100644 --- a/chrome/app/breakpad_linux.cc +++ b/chrome/app/breakpad_linux.cc @@ -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,11 +501,13 @@ RendererCrashHandler(const void* crash_context, size_t crash_context_size, } void EnableRendererCrashDumping() { - const int fd = Singleton<base::GlobalDescriptors>()->Get(kCrashDumpSignal); + // When the browser forks off our process, it installs the crash signal file + // descriptor in slot kMagicCrashSignalFd. + // We deliberately leak this object. google_breakpad::ExceptionHandler* handler = new google_breakpad::ExceptionHandler("" /* unused */, NULL, NULL, - (void*) fd, true); + (void*) kMagicCrashSignalFd, true); handler->set_crash_handler(RendererCrashHandler); } diff --git a/chrome/app/chrome_dll_main.cc b/chrome/app/chrome_dll_main.cc index 33e774c..b6ba22f 100644 --- a/chrome/app/chrome_dll_main.cc +++ b/chrome/app/chrome_dll_main.cc @@ -34,9 +34,6 @@ #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" @@ -48,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) @@ -57,7 +57,6 @@ #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" @@ -76,7 +75,6 @@ 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? @@ -294,19 +292,21 @@ 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 @@ -339,13 +339,11 @@ 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); -#endif #if defined(OS_POSIX) // When you hit Ctrl-C in a terminal running the browser @@ -491,13 +489,6 @@ int ChromeMain(int argc, const char** argv) { #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) // Glib type system initialization. Needed at least for gconf, diff --git a/chrome/app/chrome_main_uitest.cc b/chrome/app/chrome_main_uitest.cc index f229751..5ce05c2 100644 --- a/chrome/app/chrome_main_uitest.cc +++ b/chrome/app/chrome_main_uitest.cc @@ -18,14 +18,9 @@ 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 } } diff --git a/chrome/browser/plugin_process_host.cc b/chrome/browser/plugin_process_host.cc index 5216755..921f860 100644 --- a/chrome/browser/plugin_process_host.cc +++ b/chrome/browser/plugin_process_host.cc @@ -14,9 +14,6 @@ #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" @@ -33,7 +30,6 @@ #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" @@ -397,10 +393,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; - const int ipcfd = channel().GetClientFileDescriptor(); - if (ipcfd > -1) - fds_to_map.push_back(std::pair<int, int>( - ipcfd, kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); + 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)); base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process); #endif diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 29e7ef0..829ef47 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -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" @@ -34,6 +35,9 @@ #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" @@ -41,7 +45,6 @@ #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" @@ -51,11 +54,6 @@ #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) @@ -136,8 +134,7 @@ BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile) backgrounded_(true), ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_( base::TimeDelta::FromSeconds(5), - this, &BrowserRenderProcessHost::ClearTransportDIBCache)), - zygote_child_(false) { + this, &BrowserRenderProcessHost::ClearTransportDIBCache)) { widget_helper_ = new RenderWidgetHelper(); registrar_.Add(this, NotificationType::USER_SCRIPTS_UPDATED, @@ -173,13 +170,7 @@ BrowserRenderProcessHost::~BrowserRenderProcessHost() { audio_renderer_host_->Destroy(); if (process_.handle() && !run_renderer_in_process()) { - if (zygote_child_) { -#if defined(OS_LINUX) - Singleton<ZygoteHost>()->EnsureProcessTerminated(process_.handle()); -#endif - } else { - ProcessWatcher::EnsureProcessTerminated(process_.handle()); - } + ProcessWatcher::EnsureProcessTerminated(process_.handle()); } ClearTransportDIBCache(); @@ -303,9 +294,7 @@ bool BrowserRenderProcessHost::Init() { ASCIIToWide(field_trial->MakePersistentString())); #if defined(OS_POSIX) - const bool has_cmd_prefix = - browser_command_line.HasSwitch(switches::kRendererCmdPrefix); - if (has_cmd_prefix) { + if (browser_command_line.HasSwitch(switches::kRendererCmdPrefix)) { // launch the renderer child with some prefix (usually "gdb --args") const std::wstring prefix = browser_command_line.GetSwitchValue(switches::kRendererCmdPrefix); @@ -345,42 +334,24 @@ bool BrowserRenderProcessHost::Init() { base::ProcessHandle process = 0; #if defined(OS_WIN) process = sandbox::StartProcess(&cmd_line); -#elif defined(OS_POSIX) -#if defined(OS_LINUX) - 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; +#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)); #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); #endif #endif - if (!process) { channel_.reset(); return false; @@ -695,7 +666,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()) << peer_pid << " " << process_.pid(); + CHECK(peer_pid == process_.pid()); } } } diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index 6c259f0..b380211 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -154,9 +154,6 @@ 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); }; diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc deleted file mode 100644 index f56e6e93..0000000 --- a/chrome/browser/zygote_host_linux.cc +++ /dev/null @@ -1,90 +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. - -#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())); -} diff --git a/chrome/browser/zygote_host_linux.h b/chrome/browser/zygote_host_linux.h deleted file mode 100644 index 279918d..0000000 --- a/chrome/browser/zygote_host_linux.h +++ /dev/null @@ -1,41 +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 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_ diff --git a/chrome/browser/zygote_main_linux.cc b/chrome/browser/zygote_main_linux.cc deleted file mode 100644 index d450c39..0000000 --- a/chrome/browser/zygote_main_linux.cc +++ /dev/null @@ -1,168 +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. - -#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(); -} diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index c1aefab..d9ea3ea 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1613,8 +1613,6 @@ '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', diff --git a/chrome/common/chrome_descriptors.h b/chrome/common/chrome_descriptors.h deleted file mode 100644 index 3d6be45..0000000 --- a/chrome/common/chrome_descriptors.h +++ /dev/null @@ -1,15 +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 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_ diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index e5bce97..48511d3 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -63,9 +63,6 @@ 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"; @@ -493,9 +490,6 @@ 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"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 33a0239..8259d72 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -26,7 +26,6 @@ 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[]; @@ -186,8 +185,6 @@ extern const wchar_t kEnableBenchmarking[]; extern const wchar_t kNoDefaultBrowserCheck[]; -extern const wchar_t kZygoteCmdPrefix[]; - extern const wchar_t kThumbnailStore[]; } // namespace switches diff --git a/chrome/common/ipc_channel.h b/chrome/common/ipc_channel.h index 85b35fa..f619508 100644 --- a/chrome/common/ipc_channel.h +++ b/chrome/common/ipc_channel.h @@ -87,13 +87,14 @@ 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. + // FD # for the client end of the socket and the equivalent FD# to use for + // mapping it into the Child process. // 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. - int GetClientFileDescriptor() const; + // socketpair() in which case this method returns -1 for both parameters. + void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; #endif // defined(OS_POSIX) private: diff --git a/chrome/common/ipc_channel_posix.cc b/chrome/common/ipc_channel_posix.cc index babc16c..52cad9d 100644 --- a/chrome/common/ipc_channel_posix.cc +++ b/chrome/common/ipc_channel_posix.cc @@ -17,16 +17,15 @@ #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" @@ -41,7 +40,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 kPrimaryIPCChannel in the child. Future +// magic file descriptor kClientChannelFd in the child. Future // connections (file descriptors) can then be passed via that // connection via sendmsg(). @@ -51,7 +50,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 (@kPrimaryIPCChannel). However, we +// specific file descriptor number (@kClientChannelFd). However, we // also have to support the case where we are running unittests in the // same process. (We do not support forking without execing.) // @@ -59,7 +58,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 (@kPrimaryIPCChannel). The client will search for the +// the magic slot (@kClientChannelFd). 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. @@ -125,7 +124,10 @@ 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. - return Singleton<base::GlobalDescriptors>()->Get(kPrimaryIPCChannel); + // kClientChannelFd is the file descriptor number that a client process + // expects to find its IPC socket; see reserved_file_descriptors.h. + + return kClientChannelFd; } //------------------------------------------------------------------------------ @@ -669,8 +671,11 @@ bool Channel::ChannelImpl::Send(Message* message) { return true; } -int Channel::ChannelImpl::GetClientFileDescriptor() const { - return client_pipe_; +void Channel::ChannelImpl::GetClientFileDescriptorMapping(int *src_fd, + int *dest_fd) const { + DCHECK(mode_ == MODE_SERVER); + *src_fd = client_pipe_; + *dest_fd = kClientChannelFd; } // Called by libevent when we can read from th pipe without blocking. @@ -798,8 +803,8 @@ bool Channel::Send(Message* message) { return channel_impl_->Send(message); } -int Channel::GetClientFileDescriptor() const { - return channel_impl_->GetClientFileDescriptor(); +void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const { + return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd); } } // namespace IPC diff --git a/chrome/common/ipc_channel_posix.h b/chrome/common/ipc_channel_posix.h index ed3d727..414730a 100644 --- a/chrome/common/ipc_channel_posix.h +++ b/chrome/common/ipc_channel_posix.h @@ -29,7 +29,7 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher { void Close(); void set_listener(Listener* listener) { listener_ = listener; } bool Send(Message* message); - int GetClientFileDescriptor() const; + void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; private: bool CreatePipe(const std::string& channel_id, Mode mode); diff --git a/chrome/common/ipc_channel_proxy.cc b/chrome/common/ipc_channel_proxy.cc index c77ee7e..6e8919a3 100644 --- a/chrome/common/ipc_channel_proxy.cc +++ b/chrome/common/ipc_channel_proxy.cc @@ -289,10 +289,11 @@ 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. -int ChannelProxy::GetClientFileDescriptor() const { +void ChannelProxy::GetClientFileDescriptorMapping(int *src_fd, + int *dest_fd) const { Channel *channel = context_.get()->channel_; DCHECK(channel); // Channel must have been created first. - return channel->GetClientFileDescriptor(); + channel->GetClientFileDescriptorMapping(src_fd, dest_fd); } #endif diff --git a/chrome/common/ipc_channel_proxy.h b/chrome/common/ipc_channel_proxy.h index 8adcb5b..a395b1f 100644 --- a/chrome/common/ipc_channel_proxy.h +++ b/chrome/common/ipc_channel_proxy.h @@ -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. - int GetClientFileDescriptor() const; + void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; #endif // defined(OS_POSIX) protected: diff --git a/chrome/common/ipc_tests.cc b/chrome/common/ipc_tests.cc index c2e0baa..d347fb5 100644 --- a/chrome/common/ipc_tests.cc +++ b/chrome/common/ipc_tests.cc @@ -21,16 +21,11 @@ #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" @@ -91,9 +86,11 @@ base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type, CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren); base::file_handle_mapping_vector fds_to_map; - const int ipcfd = channel->GetClientFileDescriptor(); - if (ipcfd > -1) { - fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3)); + 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)); } base::ProcessHandle ret = NULL; @@ -261,9 +258,11 @@ TEST_F(IPCChannelTest, ChannelProxyTest) { bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch( switches::kDebugChildren); base::file_handle_mapping_vector fds_to_map; - const int ipcfd = chan.GetClientFileDescriptor(); - if (ipcfd > -1) { - fds_to_map.push_back(std::pair<int,int>(ipcfd, kPrimaryIPCChannel + 3)); + 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)); } base::ProcessHandle process_handle = MultiProcessTest::SpawnChild( diff --git a/chrome/common/process_watcher_posix.cc b/chrome/common/process_watcher_posix.cc index f1ae4f4..497b80b 100644 --- a/chrome/common/process_watcher_posix.cc +++ b/chrome/common/process_watcher_posix.cc @@ -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); diff --git a/chrome/plugin/plugin_main.cc b/chrome/plugin/plugin_main.cc index fb8880f..1bbb624 100644 --- a/chrome/plugin/plugin_main.cc +++ b/chrome/plugin/plugin_main.cc @@ -22,9 +22,6 @@ #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. diff --git a/chrome/renderer/renderer_main_unittest.cc b/chrome/renderer/renderer_main_unittest.cc index 605101e..18e67fa 100644 --- a/chrome/renderer/renderer_main_unittest.cc +++ b/chrome/renderer/renderer_main_unittest.cc @@ -53,9 +53,11 @@ void RendererMainTest::TearDown() { ProcessHandle RendererMainTest::SpawnChild(const std::wstring &procname, IPC::Channel *channel) { base::file_handle_mapping_vector fds_to_map; - const int ipcfd = channel->GetClientFileDescriptor(); - if (ipcfd > -1) { - fds_to_map.push_back(std::pair<int,int>(ipcfd, 3)); + 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)); } return MultiProcessTest::SpawnChild(procname, fds_to_map, false); diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc index 4410e2e..e54f6a6 100644 --- a/chrome/test/automation/automation_proxy.cc +++ b/chrome/test/automation/automation_proxy.cc @@ -13,7 +13,6 @@ #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" @@ -441,9 +440,10 @@ 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; - const int ipcfd = channel_->GetClientFileDescriptor(); - if (ipcfd > -1) - map.push_back(std::make_pair(ipcfd, kPrimaryIPCChannel + 3)); + 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)); return map; } #endif // defined(OS_POSIX) diff --git a/chrome/test/chrome_process_util.cc b/chrome/test/chrome_process_util.cc index 4a5f5bb..296b291 100644 --- a/chrome/test/chrome_process_util.cc +++ b/chrome/test/chrome_process_util.cc @@ -5,7 +5,6 @@ #include "chrome/test/chrome_process_util.h" #include <vector> -#include <set> #include "base/process_util.h" #include "base/time.h" @@ -15,6 +14,26 @@ 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. @@ -51,24 +70,6 @@ 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; @@ -76,32 +77,37 @@ ChromeProcessList GetRunningChromeProcesses(const FilePath& data_dir) { if (browser_pid < 0) return result; - ChildProcessFilter filter(browser_pid); + // 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); 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; } |