diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-10 23:04:55 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-10 23:04:55 +0000 |
commit | 361e25c6d29fdae737b5ddf846606093b5502187 (patch) | |
tree | 3114ddee1266088582391d5be9d0ab7aecbd5b8d /base | |
parent | c6b652b9298b037d8a18dd241061122200019dba (diff) | |
download | chromium_src-361e25c6d29fdae737b5ddf846606093b5502187.zip chromium_src-361e25c6d29fdae737b5ddf846606093b5502187.tar.gz chromium_src-361e25c6d29fdae737b5ddf846606093b5502187.tar.bz2 |
Linux: refactor zygote support
http://code.google.com/p/chromium/wiki/LinuxZygote
* Move Chrome specific bits out of base
* Move away from the idea of reserved file descriptors (which don't
really work with zygotes)
* Load resources before forking renderers (means that we don't need
communication between the zygote process and the renderers)
* Make sure that gdb works against the browser again
* Make sure that we have different ASLR between the renderers and the
browser.
http://codereview.chromium.org/119335
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18109 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/base.gyp | 9 | ||||
-rw-r--r-- | base/command_line.cc | 6 | ||||
-rw-r--r-- | base/command_line.h | 3 | ||||
-rw-r--r-- | base/file_util_posix.cc | 16 | ||||
-rw-r--r-- | base/global_descriptors_posix.cc | 46 | ||||
-rw-r--r-- | base/global_descriptors_posix.h | 66 | ||||
-rw-r--r-- | base/logging.cc | 18 | ||||
-rw-r--r-- | base/process_util.h | 8 | ||||
-rw-r--r-- | base/process_util_linux.cc | 17 | ||||
-rw-r--r-- | base/reserved_file_descriptors.h | 36 | ||||
-rw-r--r-- | base/unix_domain_socket_posix.cc | 98 | ||||
-rw-r--r-- | base/unix_domain_socket_posix.h | 22 | ||||
-rw-r--r-- | base/zygote_manager.cc | 832 | ||||
-rw-r--r-- | base/zygote_manager.h | 143 |
14 files changed, 246 insertions, 1074 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_ |