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) |