| Index: base/zygote_manager.cc
|
| diff --git a/base/zygote_manager.cc b/base/zygote_manager.cc
|
| deleted file mode 100644
|
| index 7f0043aa6c8ead81dda542dd72ed90a94f8eb3e7..0000000000000000000000000000000000000000
|
| --- 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("DISABLE_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)
|
|
|