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