summaryrefslogtreecommitdiff
path: root/chromium/content/zygote/zygote_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/zygote/zygote_linux.cc')
-rw-r--r--chromium/content/zygote/zygote_linux.cc660
1 files changed, 660 insertions, 0 deletions
diff --git a/chromium/content/zygote/zygote_linux.cc b/chromium/content/zygote/zygote_linux.cc
new file mode 100644
index 00000000000..48ab8fce3e7
--- /dev/null
+++ b/chromium/content/zygote/zygote_linux.cc
@@ -0,0 +1,660 @@
+// Copyright (c) 2012 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 "content/zygote/zygote_linux.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_util.h"
+#include "base/linux_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/global_descriptors.h"
+#include "base/posix/unix_domain_socket.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "content/common/zygote/zygote_commands_linux.h"
+#include "content/public/common/zygote/send_zygote_child_ping_linux.h"
+#include "content/public/common/zygote/zygote_fork_delegate_linux.h"
+#include "ipc/ipc_channel.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_sandbox.h"
+#include "services/service_manager/embedder/descriptors.h"
+#include "services/service_manager/embedder/result_codes.h"
+#include "services/service_manager/embedder/set_process_title.h"
+#include "services/service_manager/embedder/switches.h"
+#include "services/service_manager/sandbox/linux/sandbox_linux.h"
+#include "services/service_manager/sandbox/sandbox.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+
+// See
+// https://chromium.googlesource.com/chromium/src/+/master/docs/linux/zygote.md
+
+namespace content {
+
+namespace {
+
+// NOP function. See below where this handler is installed.
+void SIGCHLDHandler(int signal) {}
+
+int LookUpFd(const base::GlobalDescriptors::Mapping& fd_mapping, uint32_t key) {
+ for (size_t index = 0; index < fd_mapping.size(); ++index) {
+ if (fd_mapping[index].key == key)
+ return fd_mapping[index].fd;
+ }
+ return -1;
+}
+
+void KillAndReap(pid_t pid, ZygoteForkDelegate* helper) {
+ if (helper) {
+ // Helper children may be forked in another PID namespace, so |pid| might
+ // be meaningless to us; or we just might not be able to directly send it
+ // signals. So we can't kill it.
+ // Additionally, we're not its parent, so we can't reap it anyway.
+ // TODO(mdempsky): Extend the ZygoteForkDelegate API to handle this.
+ LOG(WARNING) << "Unable to kill or reap helper children";
+ return;
+ }
+
+ // Kill the child process in case it's not already dead, so we can safely
+ // perform a blocking wait.
+ PCHECK(0 == kill(pid, SIGKILL));
+ PCHECK(pid == HANDLE_EINTR(waitpid(pid, nullptr, 0)));
+}
+
+} // namespace
+
+Zygote::Zygote(int sandbox_flags,
+ std::vector<std::unique_ptr<ZygoteForkDelegate>> helpers,
+ const base::GlobalDescriptors::Descriptor& ipc_backchannel)
+ : sandbox_flags_(sandbox_flags),
+ helpers_(std::move(helpers)),
+ initial_uma_index_(0),
+ to_reap_(),
+ ipc_backchannel_(ipc_backchannel) {}
+
+Zygote::~Zygote() {}
+
+bool Zygote::ProcessRequests() {
+ // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
+ // browser on it.
+ // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel.
+ // See
+ // https://chromium.googlesource.com/chromium/src/+/master/docs/linux/sandbox_ipc.md
+
+ // We need to accept SIGCHLD, even though our handler is a no-op because
+ // otherwise we cannot wait on children. (According to POSIX 2001.)
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = &SIGCHLDHandler;
+ PCHECK(sigaction(SIGCHLD, &action, nullptr) == 0);
+
+ // Block SIGCHLD until a child might be ready to reap.
+ sigset_t sigset;
+ sigset_t orig_sigmask;
+ PCHECK(sigemptyset(&sigset) == 0);
+ PCHECK(sigaddset(&sigset, SIGCHLD) == 0);
+ PCHECK(sigprocmask(SIG_BLOCK, &sigset, &orig_sigmask) == 0);
+
+ if (UsingSUIDSandbox() || UsingNSSandbox()) {
+ // Let the ZygoteHost know we are ready to go.
+ // The receiving code is in
+ // content/browser/zygote_host/zygote_host_impl_linux.cc.
+ bool r = base::UnixDomainSocket::SendMsg(
+ kZygoteSocketPairFd, kZygoteHelloMessage, sizeof(kZygoteHelloMessage),
+ std::vector<int>());
+#if defined(OS_CHROMEOS)
+ LOG_IF(WARNING, !r) << "Sending zygote magic failed";
+ // Exit normally on chromeos because session manager may send SIGTERM
+ // right after the process starts and it may fail to send zygote magic
+ // number to browser process.
+ if (!r)
+ _exit(service_manager::RESULT_CODE_NORMAL_EXIT);
+#else
+ CHECK(r) << "Sending zygote magic failed";
+#endif
+ }
+
+ sigset_t ppoll_sigmask = orig_sigmask;
+ PCHECK(sigdelset(&ppoll_sigmask, SIGCHLD) == 0);
+ struct pollfd pfd;
+ pfd.fd = kZygoteSocketPairFd;
+ pfd.events = POLLIN;
+
+ struct timespec timeout;
+ timeout.tv_sec = 2;
+ timeout.tv_nsec = 0;
+
+ for (;;) {
+ struct timespec* timeout_ptr = nullptr;
+ if (!to_reap_.empty())
+ timeout_ptr = &timeout;
+ int rc = ppoll(&pfd, 1, timeout_ptr, &ppoll_sigmask);
+ PCHECK(rc >= 0 || errno == EINTR);
+ ReapChildren();
+
+ if (pfd.revents & POLLIN) {
+ // This function call can return multiple times, once per fork().
+ if (HandleRequestFromBrowser(kZygoteSocketPairFd)) {
+ PCHECK(sigprocmask(SIG_SETMASK, &orig_sigmask, nullptr) == 0);
+ return true;
+ }
+ }
+ }
+ // The loop should not be exited unless a request was successfully processed.
+ NOTREACHED();
+ return false;
+}
+
+bool Zygote::ReapChild(const base::TimeTicks& now, ZygoteProcessInfo* child) {
+ pid_t pid = child->internal_pid;
+ pid_t r = HANDLE_EINTR(waitpid(pid, nullptr, WNOHANG));
+ if (r > 0) {
+ if (r != pid) {
+ DLOG(ERROR) << "While waiting for " << pid
+ << " to terminate, "
+ "waitpid returned "
+ << r;
+ }
+ return r == pid;
+ }
+ if ((now - child->time_of_reap_request).InSeconds() < 2) {
+ return false;
+ }
+ // If the process has been requested reaped >= 2 seconds ago, kill it.
+ if (!child->sent_sigkill) {
+ if (kill(pid, SIGKILL) != 0)
+ DPLOG(ERROR) << "Sending SIGKILL to process " << pid << " failed";
+
+ child->sent_sigkill = true;
+ }
+ return false;
+}
+
+void Zygote::ReapChildren() {
+ base::TimeTicks now = base::TimeTicks::Now();
+ std::vector<ZygoteProcessInfo>::iterator it = to_reap_.begin();
+ while (it != to_reap_.end()) {
+ if (ReapChild(now, &(*it))) {
+ it = to_reap_.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+bool Zygote::GetProcessInfo(base::ProcessHandle pid,
+ ZygoteProcessInfo* process_info) {
+ DCHECK(process_info);
+ const ZygoteProcessMap::const_iterator it = process_info_map_.find(pid);
+ if (it == process_info_map_.end()) {
+ return false;
+ }
+ *process_info = it->second;
+ return true;
+}
+
+bool Zygote::UsingSUIDSandbox() const {
+ return sandbox_flags_ & service_manager::SandboxLinux::kSUID;
+}
+
+bool Zygote::UsingNSSandbox() const {
+ return sandbox_flags_ & service_manager::SandboxLinux::kUserNS;
+}
+
+bool Zygote::HandleRequestFromBrowser(int fd) {
+ std::vector<base::ScopedFD> fds;
+ char buf[kZygoteMaxMessageLength];
+ const ssize_t len =
+ base::UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
+
+ if (len == 0 || (len == -1 && errno == ECONNRESET)) {
+ // EOF from the browser. We should die.
+ // TODO(eugenis): call __sanititizer_cov_dump() here to obtain code
+ // coverage for the Zygote. Currently it's not possible because of
+ // confusion over who is responsible for closing the file descriptor.
+ _exit(0);
+ return false;
+ }
+
+ if (len == -1) {
+ PLOG(ERROR) << "Error reading message from browser";
+ return false;
+ }
+
+ base::Pickle pickle(buf, len);
+ base::PickleIterator iter(pickle);
+
+ int kind;
+ if (iter.ReadInt(&kind)) {
+ switch (kind) {
+ case kZygoteCommandFork:
+ // This function call can return multiple times, once per fork().
+ return HandleForkRequest(fd, iter, std::move(fds));
+
+ case kZygoteCommandReap:
+ if (!fds.empty())
+ break;
+ HandleReapRequest(fd, iter);
+ return false;
+ case kZygoteCommandGetTerminationStatus:
+ if (!fds.empty())
+ break;
+ HandleGetTerminationStatus(fd, iter);
+ return false;
+ case kZygoteCommandGetSandboxStatus:
+ HandleGetSandboxStatus(fd, iter);
+ return false;
+ case kZygoteCommandForkRealPID:
+ // This shouldn't happen in practice, but some failure paths in
+ // HandleForkRequest (e.g., if ReadArgsAndFork fails during depickling)
+ // could leave this command pending on the socket.
+ LOG(ERROR) << "Unexpected real PID message from browser";
+ NOTREACHED();
+ return false;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ LOG(WARNING) << "Error parsing message from browser";
+ return false;
+}
+
+void Zygote::HandleReapRequest(int fd, base::PickleIterator iter) {
+ base::ProcessId child;
+
+ if (!iter.ReadInt(&child)) {
+ LOG(WARNING) << "Error parsing reap request from browser";
+ return;
+ }
+
+ ZygoteProcessInfo child_info;
+ if (!GetProcessInfo(child, &child_info)) {
+ LOG(ERROR) << "Child not found!";
+ NOTREACHED();
+ return;
+ }
+ child_info.time_of_reap_request = base::TimeTicks::Now();
+
+ if (!child_info.started_from_helper) {
+ to_reap_.push_back(child_info);
+ } else {
+ // For processes from the helper, send a GetTerminationStatus request
+ // with known_dead set to true.
+ // This is not perfect, as the process may be killed instantly, but is
+ // better than ignoring the request.
+ base::TerminationStatus status;
+ int exit_code;
+ bool got_termination_status =
+ GetTerminationStatus(child, true /* known_dead */, &status, &exit_code);
+ DCHECK(got_termination_status);
+ }
+ process_info_map_.erase(child);
+}
+
+bool Zygote::GetTerminationStatus(base::ProcessHandle real_pid,
+ bool known_dead,
+ base::TerminationStatus* status,
+ int* exit_code) {
+ ZygoteProcessInfo child_info;
+ if (!GetProcessInfo(real_pid, &child_info)) {
+ LOG(ERROR) << "Zygote::GetTerminationStatus for unknown PID " << real_pid;
+ NOTREACHED();
+ return false;
+ }
+ // We know about |real_pid|.
+ const base::ProcessHandle child = child_info.internal_pid;
+ if (child_info.started_from_helper) {
+ if (!child_info.started_from_helper->GetTerminationStatus(
+ child, known_dead, status, exit_code)) {
+ return false;
+ }
+ } else {
+ // Handle the request directly.
+ if (known_dead) {
+ *status = base::GetKnownDeadTerminationStatus(child, exit_code);
+ } else {
+ // We don't know if the process is dying, so get its status but don't
+ // wait.
+ *status = base::GetTerminationStatus(child, exit_code);
+ }
+ }
+ // Successfully got a status for |real_pid|.
+ if (*status != base::TERMINATION_STATUS_STILL_RUNNING) {
+ // Time to forget about this process.
+ process_info_map_.erase(real_pid);
+ }
+
+ if (WIFEXITED(*exit_code)) {
+ const int exit_status = WEXITSTATUS(*exit_code);
+ if (exit_status == sandbox::NamespaceSandbox::SignalExitCode(SIGINT) ||
+ exit_status == sandbox::NamespaceSandbox::SignalExitCode(SIGTERM)) {
+ *status = base::TERMINATION_STATUS_PROCESS_WAS_KILLED;
+ }
+ }
+
+ return true;
+}
+
+void Zygote::HandleGetTerminationStatus(int fd, base::PickleIterator iter) {
+ bool known_dead;
+ base::ProcessHandle child_requested;
+
+ if (!iter.ReadBool(&known_dead) || !iter.ReadInt(&child_requested)) {
+ LOG(WARNING) << "Error parsing GetTerminationStatus request "
+ << "from browser";
+ return;
+ }
+
+ base::TerminationStatus status;
+ int exit_code;
+
+ bool got_termination_status =
+ GetTerminationStatus(child_requested, known_dead, &status, &exit_code);
+ if (!got_termination_status) {
+ // Assume that if we can't find the child in the sandbox, then
+ // it terminated normally.
+ NOTREACHED();
+ status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
+ exit_code = service_manager::RESULT_CODE_NORMAL_EXIT;
+ }
+
+ base::Pickle write_pickle;
+ write_pickle.WriteInt(static_cast<int>(status));
+ write_pickle.WriteInt(exit_code);
+ ssize_t written =
+ HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size()));
+ if (written != static_cast<ssize_t>(write_pickle.size()))
+ PLOG(ERROR) << "write";
+}
+
+int Zygote::ForkWithRealPid(const std::string& process_type,
+ const base::GlobalDescriptors::Mapping& fd_mapping,
+ const std::string& channel_id,
+ base::ScopedFD pid_oracle,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ ZygoteForkDelegate* helper = nullptr;
+ for (auto i = helpers_.begin(); i != helpers_.end(); ++i) {
+ if ((*i)->CanHelp(process_type, uma_name, uma_sample, uma_boundary_value)) {
+ helper = i->get();
+ break;
+ }
+ }
+
+ base::ScopedFD read_pipe, write_pipe;
+ base::ProcessId pid = 0;
+ if (helper) {
+ int mojo_channel_fd =
+ LookUpFd(fd_mapping, service_manager::kMojoIPCChannel);
+ if (mojo_channel_fd < 0) {
+ DLOG(ERROR) << "Failed to find kMojoIPCChannel in FD mapping";
+ return -1;
+ }
+ std::vector<int> fds;
+ fds.push_back(mojo_channel_fd); // kBrowserFDIndex
+ fds.push_back(pid_oracle.get()); // kPIDOracleFDIndex
+ pid = helper->Fork(process_type, fds, channel_id);
+
+ // Helpers should never return in the child process.
+ CHECK_NE(pid, 0);
+ } else {
+ PCHECK(base::CreatePipe(&read_pipe, &write_pipe));
+ if (sandbox_flags_ & service_manager::SandboxLinux::kPIDNS &&
+ sandbox_flags_ & service_manager::SandboxLinux::kUserNS) {
+ pid = sandbox::NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ } else {
+ pid = sandbox::Credentials::ForkAndDropCapabilitiesInChild();
+ }
+ }
+
+ if (pid == 0) {
+ // In the child process.
+
+ // If the process is the init process inside a PID namespace, it must have
+ // explicit signal handlers.
+ if (getpid() == 1) {
+ static const int kTerminationSignals[] = {
+ SIGINT, SIGTERM, SIGHUP, SIGQUIT, SIGABRT, SIGPIPE, SIGUSR1, SIGUSR2};
+ for (const int sig : kTerminationSignals) {
+ sandbox::NamespaceSandbox::InstallTerminationSignalHandler(
+ sig, sandbox::NamespaceSandbox::SignalExitCode(sig));
+ }
+ }
+
+ write_pipe.reset();
+
+ // Ping the PID oracle socket so the browser can find our PID.
+ CHECK(SendZygoteChildPing(pid_oracle.get()));
+
+ // Now read back our real PID from the zygote.
+ base::ProcessId real_pid;
+ if (!base::ReadFromFD(read_pipe.get(), reinterpret_cast<char*>(&real_pid),
+ sizeof(real_pid))) {
+ LOG(FATAL) << "Failed to synchronise with parent zygote process";
+ }
+ if (real_pid <= 0) {
+ LOG(FATAL) << "Invalid pid from parent zygote";
+ }
+ // Sandboxed processes need to send the global, non-namespaced PID when
+ // setting up an IPC channel to their parent.
+ IPC::Channel::SetGlobalPid(real_pid);
+ // Force the real PID so chrome event data have a PID that corresponds
+ // to system trace event data.
+ base::trace_event::TraceLog::GetInstance()->SetProcessID(
+ static_cast<int>(real_pid));
+ base::InitUniqueIdForProcessInPidNamespace(real_pid);
+ return 0;
+ }
+
+ // In the parent process.
+ if (pid < 0) {
+ // Fork failed.
+ return -1;
+ }
+
+ read_pipe.reset();
+ pid_oracle.reset();
+
+ // Always receive a real PID from the zygote host, though it might
+ // be invalid (see below).
+ base::ProcessId real_pid = -1;
+ {
+ std::vector<base::ScopedFD> recv_fds;
+ char buf[kZygoteMaxMessageLength];
+ const ssize_t len = base::UnixDomainSocket::RecvMsg(
+ kZygoteSocketPairFd, buf, sizeof(buf), &recv_fds);
+
+ if (len > 0) {
+ CHECK(recv_fds.empty());
+
+ base::Pickle pickle(buf, len);
+ base::PickleIterator iter(pickle);
+
+ int kind;
+ CHECK(iter.ReadInt(&kind));
+ CHECK(kind == kZygoteCommandForkRealPID);
+ CHECK(iter.ReadInt(&real_pid));
+ }
+ }
+
+ // If we successfully forked a child, but it crashed without sending
+ // a message to the browser, the browser won't have found its PID.
+ if (real_pid < 0) {
+ KillAndReap(pid, helper);
+ return -1;
+ }
+
+ // If we're not using a helper, send the PID back to the child process.
+ if (!helper) {
+ ssize_t written =
+ HANDLE_EINTR(write(write_pipe.get(), &real_pid, sizeof(real_pid)));
+ if (written != sizeof(real_pid)) {
+ KillAndReap(pid, helper);
+ return -1;
+ }
+ }
+
+ // Now set-up this process to be tracked by the Zygote.
+ if (process_info_map_.find(real_pid) != process_info_map_.end()) {
+ LOG(ERROR) << "Already tracking PID " << real_pid;
+ NOTREACHED();
+ }
+ process_info_map_[real_pid].internal_pid = pid;
+ process_info_map_[real_pid].started_from_helper = helper;
+
+ return real_pid;
+}
+
+base::ProcessId Zygote::ReadArgsAndFork(base::PickleIterator iter,
+ std::vector<base::ScopedFD> fds,
+ std::string* uma_name,
+ int* uma_sample,
+ int* uma_boundary_value) {
+ std::vector<std::string> args;
+ int argc = 0;
+ int numfds = 0;
+ base::GlobalDescriptors::Mapping mapping;
+ std::string process_type;
+ std::string channel_id;
+ const std::string channel_id_prefix =
+ std::string("--") +
+ service_manager::switches::kServiceRequestChannelToken + std::string("=");
+
+ if (!iter.ReadString(&process_type))
+ return -1;
+ if (!iter.ReadInt(&argc))
+ return -1;
+
+ for (int i = 0; i < argc; ++i) {
+ std::string arg;
+ if (!iter.ReadString(&arg))
+ return -1;
+ args.push_back(arg);
+ if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0)
+ channel_id = arg.substr(channel_id_prefix.length());
+ }
+
+ // timezone_id is obtained from ICU in zygote host so that it can't be
+ // invalid. For an unknown reason, if an invalid ID is passed down here, the
+ // worst result would be that timezone would be set to Etc/Unknown.
+ base::string16 timezone_id;
+ if (!iter.ReadString16(&timezone_id))
+ return -1;
+ icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
+ icu::UnicodeString(FALSE, timezone_id.data(), timezone_id.length())));
+
+ if (!iter.ReadInt(&numfds))
+ return -1;
+ if (numfds != static_cast<int>(fds.size()))
+ return -1;
+
+ // First FD is the PID oracle socket.
+ if (fds.size() < 1)
+ return -1;
+ base::ScopedFD pid_oracle(std::move(fds[0]));
+
+ // Remaining FDs are for the global descriptor mapping.
+ for (int i = 1; i < numfds; ++i) {
+ base::GlobalDescriptors::Key key;
+ if (!iter.ReadUInt32(&key))
+ return -1;
+ mapping.push_back(base::GlobalDescriptors::Descriptor(key, fds[i].get()));
+ }
+
+ mapping.push_back(ipc_backchannel_);
+
+ // Returns twice, once per process.
+ base::ProcessId child_pid =
+ ForkWithRealPid(process_type, mapping, channel_id, std::move(pid_oracle),
+ uma_name, uma_sample, uma_boundary_value);
+ if (!child_pid) {
+ // This is the child process.
+
+ // Our socket from the browser.
+ PCHECK(0 == IGNORE_EINTR(close(kZygoteSocketPairFd)));
+
+ // Pass ownership of file descriptors from fds to GlobalDescriptors.
+ for (base::ScopedFD& fd : fds)
+ ignore_result(fd.release());
+ base::GlobalDescriptors::GetInstance()->Reset(mapping);
+
+ // Reset the process-wide command line to our new command line.
+ base::CommandLine::Reset();
+ base::CommandLine::Init(0, nullptr);
+ base::CommandLine::ForCurrentProcess()->InitFromArgv(args);
+
+ // Update the process title. The argv was already cached by the call to
+ // SetProcessTitleFromCommandLine in ChromeMain, so we can pass NULL here
+ // (we don't have the original argv at this point).
+ service_manager::SetProcessTitleFromCommandLine(nullptr);
+ } else if (child_pid < 0) {
+ LOG(ERROR) << "Zygote could not fork: process_type " << process_type
+ << " numfds " << numfds << " child_pid " << child_pid;
+ }
+ return child_pid;
+}
+
+bool Zygote::HandleForkRequest(int fd,
+ base::PickleIterator iter,
+ std::vector<base::ScopedFD> fds) {
+ std::string uma_name;
+ int uma_sample;
+ int uma_boundary_value;
+ base::ProcessId child_pid = ReadArgsAndFork(iter, std::move(fds), &uma_name,
+ &uma_sample, &uma_boundary_value);
+ if (child_pid == 0)
+ return true;
+ // If there's no UMA report for this particular fork, then check if any
+ // helpers have an initial UMA report for us to send instead.
+ while (uma_name.empty() && initial_uma_index_ < helpers_.size()) {
+ helpers_[initial_uma_index_++]->InitialUMA(&uma_name, &uma_sample,
+ &uma_boundary_value);
+ }
+ // Must always send reply, as ZygoteHost blocks while waiting for it.
+ base::Pickle reply_pickle;
+ reply_pickle.WriteInt(child_pid);
+ reply_pickle.WriteString(uma_name);
+ if (!uma_name.empty()) {
+ reply_pickle.WriteInt(uma_sample);
+ reply_pickle.WriteInt(uma_boundary_value);
+ }
+ if (HANDLE_EINTR(write(fd, reply_pickle.data(), reply_pickle.size())) !=
+ static_cast<ssize_t>(reply_pickle.size()))
+ PLOG(ERROR) << "write";
+ return false;
+}
+
+bool Zygote::HandleGetSandboxStatus(int fd, base::PickleIterator iter) {
+ if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) !=
+ sizeof(sandbox_flags_)) {
+ PLOG(ERROR) << "write";
+ }
+
+ return false;
+}
+
+} // namespace content