1cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao/*
2cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * Copyright 2016, The Android Open Source Project
3cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao *
4cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * Licensed under the Apache License, Version 2.0 (the "License");
5cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * you may not use this file except in compliance with the License.
6cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * You may obtain a copy of the License at
7cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao *
8cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao *     http://www.apache.org/licenses/LICENSE-2.0
9cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao *
10cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * Unless required by applicable law or agreed to in writing, software
11cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * distributed under the License is distributed on an "AS IS" BASIS,
12cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * See the License for the specific language governing permissions and
14cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * limitations under the License.
15cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao */
16cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
17cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include "intercept_manager.h"
18cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
19cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <inttypes.h>
20cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <sys/types.h>
21cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
22cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <unordered_map>
23cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
24cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <event2/event.h>
25cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <event2/listener.h>
26cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
27cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <android-base/logging.h>
28cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <android-base/unique_fd.h>
29cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <cutils/sockets.h>
30cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
312d377cd6888775fe682e49a1ac34a3a6feb78708Narayan Kamath#include "protocol.h"
322d377cd6888775fe682e49a1ac34a3a6feb78708Narayan Kamath#include "util.h"
33cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
34cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaousing android::base::unique_fd;
35cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
36cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaostatic void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
37cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  auto intercept = reinterpret_cast<Intercept*>(arg);
38cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  InterceptManager* intercept_manager = intercept->intercept_manager;
39cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
40cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  CHECK_EQ(sockfd, intercept->sockfd.get());
41cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
42cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  // If we can read, either we received unexpected data from the other side, or the other side
43cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  // closed their end of the socket. Either way, kill the intercept.
44cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
45cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  // Ownership of intercept differs based on whether we've registered it with InterceptManager.
46cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  if (!intercept->registered) {
47cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    delete intercept;
48cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  } else {
49cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
50cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    if (it == intercept_manager->intercepts.end()) {
51cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      LOG(FATAL) << "intercept close callback called after intercept was already removed?";
52cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    }
53cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    if (it->second.get() != intercept) {
54cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
55cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    }
56cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
57cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    const char* reason;
58cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    if ((event & EV_TIMEOUT) != 0) {
59cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      reason = "due to timeout";
60cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    } else {
61cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      reason = "due to input";
62cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    }
63cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
64a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
65a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath              << intercept->dump_type << " terminated: " << reason;
66cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    intercept_manager->intercepts.erase(it);
67cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  }
68cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao}
69cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
70a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamathstatic bool is_intercept_request_valid(const InterceptRequest& request) {
71a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
72a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    return false;
73a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  }
74a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath
75a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
76a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    return false;
77a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  }
78a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath
79a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  return true;
80a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath}
81a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath
82cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaostatic void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
83cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  auto intercept = reinterpret_cast<Intercept*>(arg);
84cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  InterceptManager* intercept_manager = intercept->intercept_manager;
85cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
86cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  CHECK_EQ(sockfd, intercept->sockfd.get());
87cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
88cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  if ((ev & EV_TIMEOUT) != 0) {
89cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
90cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    goto fail;
91cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  } else if ((ev & EV_READ) == 0) {
92cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
93cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    goto fail;
94cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  }
95cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
96cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  {
97cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    unique_fd rcv_fd;
98cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    InterceptRequest intercept_request;
99cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
100cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
101cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    if (result == -1) {
102cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      PLOG(WARNING) << "failed to read from intercept socket";
103cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      goto fail;
104cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    } else if (result != sizeof(intercept_request)) {
105cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
106cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao                   << sizeof(intercept_request) << ")";
107cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      goto fail;
108cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    }
109cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
110cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    // Move the received FD to the upper half, in order to more easily notice FD leaks.
111cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
112cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    if (moved_fd == -1) {
113cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
114cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      goto fail;
115cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    }
116cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    rcv_fd.reset(moved_fd);
117cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
118cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    // We trust the other side, so only do minimal validity checking.
119a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    if (!is_intercept_request_valid(intercept_request)) {
120cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      InterceptResponse response = {};
121460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao      response.status = InterceptStatus::kFailed;
122a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath      snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
123cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
124cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      goto fail;
125cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    }
126cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
127cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    intercept->intercept_pid = intercept_request.pid;
128a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    intercept->dump_type = intercept_request.dump_type;
129cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
130460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao    // Check if it's already registered.
131cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
132cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      InterceptResponse response = {};
133a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath      response.status = InterceptStatus::kFailedAlreadyRegistered;
134cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      snprintf(response.error_message, sizeof(response.error_message),
135a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath               "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
136a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath               intercept_request.dump_type);
137cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
138cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      LOG(WARNING) << response.error_message;
139cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao      goto fail;
140cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    }
141cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
142460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao    // Let the other side know that the intercept has been registered, now that we know we can't
143460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao    // fail. tombstoned is single threaded, so this isn't racy.
144460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao    InterceptResponse response = {};
145460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao    response.status = InterceptStatus::kRegistered;
146460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao    if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
147460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao      PLOG(WARNING) << "failed to notify interceptor of registration";
148460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao      goto fail;
149460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao    }
150460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao
151cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    intercept->output_fd = std::move(rcv_fd);
152cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
153cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    intercept->registered = true;
154cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
155a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
156a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath              << intercept_request.dump_type;
157cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
158cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    // Register a different read event on the socket so that we can remove intercepts if the socket
159cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    // closes (e.g. if a user CTRL-C's the process that requested the intercept).
160cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
161cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao                 intercept_close_cb, arg);
162cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
163cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
164cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    event_add(intercept->intercept_event, &timeout);
165cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  }
166cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
167cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  return;
168cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
169cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaofail:
170cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  delete intercept;
171cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao}
172cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
173cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaostatic void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
174cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao                                void* arg) {
175cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  Intercept* intercept = new Intercept();
176cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  intercept->intercept_manager = static_cast<InterceptManager*>(arg);
177cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  intercept->sockfd.reset(sockfd);
178cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
179cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  struct timeval timeout = { 1, 0 };
180cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  event_base* base = evconnlistener_get_base(listener);
181cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  event* intercept_event =
182cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
183cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  intercept->intercept_event = intercept_event;
184cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  event_add(intercept_event, &timeout);
185cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao}
186cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
187cbe70cb0a8cb0171f3802273050e851a47b090edJosh GaoInterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
188cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
189cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao                                      intercept_socket);
190cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao}
191cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
192a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamathbool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
193a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath                                    android::base::unique_fd* out_fd) {
194cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  auto it = this->intercepts.find(pid);
195cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  if (it == this->intercepts.end()) {
196cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao    return false;
197cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  }
198cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
199a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  if (dump_type == kDebuggerdAnyIntercept) {
200a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    LOG(INFO) << "found registered intercept of type " << it->second->dump_type
201a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath              << " for requested type kDebuggerdAnyIntercept";
202a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  } else if (it->second->dump_type != dump_type) {
203a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
204a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath                 << " for requested type: " << dump_type;
205a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath    return false;
206a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  }
207a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath
208cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  auto intercept = std::move(it->second);
209cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  this->intercepts.erase(it);
210cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
211a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath  LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
212a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath            << " and type " << intercept->dump_type;
213cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  InterceptResponse response = {};
214460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao  response.status = InterceptStatus::kStarted;
215cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
216cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  *out_fd = std::move(intercept->output_fd);
217cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao
218cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao  return true;
219cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao}
220