Home | History | Annotate | Download | only in update_engine
      1 //
      2 // Copyright (C) 2012 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 // This file implements a simple HTTP server. It can exhibit odd behavior
     18 // that's useful for testing. For example, it's useful to test that
     19 // the updater can continue a connection if it's dropped, or that it
     20 // handles very slow data transfers.
     21 
     22 // To use this, simply make an HTTP connection to localhost:port and
     23 // GET a url.
     24 
     25 #include <err.h>
     26 #include <errno.h>
     27 #include <fcntl.h>
     28 #include <inttypes.h>
     29 #include <netinet/in.h>
     30 #include <signal.h>
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <sys/socket.h>
     35 #include <sys/stat.h>
     36 #include <sys/types.h>
     37 #include <unistd.h>
     38 
     39 #include <algorithm>
     40 #include <string>
     41 #include <vector>
     42 
     43 #include <base/logging.h>
     44 #include <base/posix/eintr_wrapper.h>
     45 #include <base/strings/string_split.h>
     46 #include <base/strings/string_util.h>
     47 #include <base/strings/stringprintf.h>
     48 
     49 #include "update_engine/common/http_common.h"
     50 
     51 
     52 // HTTP end-of-line delimiter; sorry, this needs to be a macro.
     53 #define EOL "\r\n"
     54 
     55 using std::string;
     56 using std::vector;
     57 
     58 
     59 namespace chromeos_update_engine {
     60 
     61 static const char* kListeningMsgPrefix = "listening on port ";
     62 
     63 enum {
     64   RC_OK = 0,
     65   RC_BAD_ARGS,
     66   RC_ERR_READ,
     67   RC_ERR_SETSOCKOPT,
     68   RC_ERR_BIND,
     69   RC_ERR_LISTEN,
     70   RC_ERR_GETSOCKNAME,
     71   RC_ERR_REPORT,
     72 };
     73 
     74 struct HttpRequest {
     75   string raw_headers;
     76   string host;
     77   string url;
     78   off_t start_offset{0};
     79   off_t end_offset{0};  // non-inclusive, zero indicates unspecified.
     80   HttpResponseCode return_code{kHttpResponseOk};
     81 };
     82 
     83 bool ParseRequest(int fd, HttpRequest* request) {
     84   string headers;
     85   do {
     86     char buf[1024];
     87     ssize_t r = read(fd, buf, sizeof(buf));
     88     if (r < 0) {
     89       perror("read");
     90       exit(RC_ERR_READ);
     91     }
     92     headers.append(buf, r);
     93   } while (!base::EndsWith(headers, EOL EOL, base::CompareCase::SENSITIVE));
     94 
     95   LOG(INFO) << "got headers:\n--8<------8<------8<------8<----\n"
     96             << headers
     97             << "\n--8<------8<------8<------8<----";
     98   request->raw_headers = headers;
     99 
    100   // Break header into lines.
    101   vector<string> lines;
    102   base::SplitStringUsingSubstr(
    103       headers.substr(0, headers.length() - strlen(EOL EOL)), EOL, &lines);
    104 
    105   // Decode URL line.
    106   vector<string> terms = base::SplitString(lines[0], base::kWhitespaceASCII,
    107                                            base::KEEP_WHITESPACE,
    108                                            base::SPLIT_WANT_NONEMPTY);
    109   CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(3));
    110   CHECK_EQ(terms[0], "GET");
    111   request->url = terms[1];
    112   LOG(INFO) << "URL: " << request->url;
    113 
    114   // Decode remaining lines.
    115   size_t i;
    116   for (i = 1; i < lines.size(); i++) {
    117     terms = base::SplitString(lines[i], base::kWhitespaceASCII,
    118                               base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
    119 
    120     if (terms[0] == "Range:") {
    121       CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
    122       string &range = terms[1];
    123       LOG(INFO) << "range attribute: " << range;
    124       CHECK(base::StartsWith(range, "bytes=", base::CompareCase::SENSITIVE) &&
    125             range.find('-') != string::npos);
    126       request->start_offset = atoll(range.c_str() + strlen("bytes="));
    127       // Decode end offset and increment it by one (so it is non-inclusive).
    128       if (range.find('-') < range.length() - 1)
    129         request->end_offset = atoll(range.c_str() + range.find('-') + 1) + 1;
    130       request->return_code = kHttpResponsePartialContent;
    131       string tmp_str = base::StringPrintf("decoded range offsets: "
    132                                                "start=%jd end=",
    133                                                (intmax_t)request->start_offset);
    134       if (request->end_offset > 0)
    135         base::StringAppendF(&tmp_str, "%jd (non-inclusive)",
    136                             (intmax_t)request->end_offset);
    137       else
    138         base::StringAppendF(&tmp_str, "unspecified");
    139       LOG(INFO) << tmp_str;
    140     } else if (terms[0] == "Host:") {
    141       CHECK_EQ(terms.size(), static_cast<vector<string>::size_type>(2));
    142       request->host = terms[1];
    143       LOG(INFO) << "host attribute: " << request->host;
    144     } else {
    145       LOG(WARNING) << "ignoring HTTP attribute: `" << lines[i] << "'";
    146     }
    147   }
    148 
    149   return true;
    150 }
    151 
    152 string Itoa(off_t num) {
    153   char buf[100] = {0};
    154   snprintf(buf, sizeof(buf), "%" PRIi64, num);
    155   return buf;
    156 }
    157 
    158 // Writes a string into a file. Returns total number of bytes written or -1 if a
    159 // write error occurred.
    160 ssize_t WriteString(int fd, const string& str) {
    161   const size_t total_size = str.size();
    162   size_t remaining_size = total_size;
    163   char const *data = str.data();
    164 
    165   while (remaining_size) {
    166     ssize_t written = write(fd, data, remaining_size);
    167     if (written < 0) {
    168       perror("write");
    169       LOG(INFO) << "write failed";
    170       return -1;
    171     }
    172     data += written;
    173     remaining_size -= written;
    174   }
    175 
    176   return total_size;
    177 }
    178 
    179 // Writes the headers of an HTTP response into a file.
    180 ssize_t WriteHeaders(int fd, const off_t start_offset, const off_t end_offset,
    181                      HttpResponseCode return_code) {
    182   ssize_t written = 0, ret;
    183 
    184   ret = WriteString(fd,
    185                     string("HTTP/1.1 ") + Itoa(return_code) + " " +
    186                     GetHttpResponseDescription(return_code) +
    187                     EOL
    188                     "Content-Type: application/octet-stream" EOL);
    189   if (ret < 0)
    190     return -1;
    191   written += ret;
    192 
    193   // Compute content legnth.
    194   const off_t content_length = end_offset - start_offset;;
    195 
    196   // A start offset that equals the end offset indicates that the response
    197   // should contain the full range of bytes in the requested resource.
    198   if (start_offset || start_offset == end_offset) {
    199     ret = WriteString(fd,
    200                       string("Accept-Ranges: bytes" EOL
    201                              "Content-Range: bytes ") +
    202                       Itoa(start_offset == end_offset ? 0 : start_offset) +
    203                       "-" + Itoa(end_offset - 1) + "/" + Itoa(end_offset) +
    204                       EOL);
    205     if (ret < 0)
    206       return -1;
    207     written += ret;
    208   }
    209 
    210   ret = WriteString(fd, string("Content-Length: ") + Itoa(content_length) +
    211                     EOL EOL);
    212   if (ret < 0)
    213     return -1;
    214   written += ret;
    215 
    216   return written;
    217 }
    218 
    219 // Writes a predetermined payload of lines of ascending bytes to a file. The
    220 // first byte of output is appropriately offset with respect to the request line
    221 // length.  Returns the number of successfully written bytes.
    222 size_t WritePayload(int fd, const off_t start_offset, const off_t end_offset,
    223                     const char first_byte, const size_t line_len) {
    224   CHECK_LE(start_offset, end_offset);
    225   CHECK_GT(line_len, static_cast<size_t>(0));
    226 
    227   LOG(INFO) << "writing payload: " << line_len << "-byte lines starting with `"
    228             << first_byte << "', offset range " << start_offset << " -> "
    229             << end_offset;
    230 
    231   // Populate line of ascending characters.
    232   string line;
    233   line.reserve(line_len);
    234   char byte = first_byte;
    235   size_t i;
    236   for (i = 0; i < line_len; i++)
    237     line += byte++;
    238 
    239   const size_t total_len = end_offset - start_offset;
    240   size_t remaining_len = total_len;
    241   bool success = true;
    242 
    243   // If start offset is not aligned with line boundary, output partial line up
    244   // to the first line boundary.
    245   size_t start_modulo = start_offset % line_len;
    246   if (start_modulo) {
    247     string partial = line.substr(start_modulo, remaining_len);
    248     ssize_t ret = WriteString(fd, partial);
    249     if ((success = (ret >= 0 && (size_t) ret == partial.length())))
    250       remaining_len -= partial.length();
    251   }
    252 
    253   // Output full lines up to the maximal line boundary below the end offset.
    254   while (success && remaining_len >= line_len) {
    255     ssize_t ret = WriteString(fd, line);
    256     if ((success = (ret >= 0 && (size_t) ret == line_len)))
    257       remaining_len -= line_len;
    258   }
    259 
    260   // Output a partial line up to the end offset.
    261   if (success && remaining_len) {
    262     string partial = line.substr(0, remaining_len);
    263     ssize_t ret = WriteString(fd, partial);
    264     if ((success = (ret >= 0 && (size_t) ret == partial.length())))
    265       remaining_len -= partial.length();
    266   }
    267 
    268   return (total_len - remaining_len);
    269 }
    270 
    271 // Write default payload lines of the form 'abcdefghij'.
    272 inline size_t WritePayload(int fd, const off_t start_offset,
    273                            const off_t end_offset) {
    274   return WritePayload(fd, start_offset, end_offset, 'a', 10);
    275 }
    276 
    277 // Send an empty response, then kill the server.
    278 void HandleQuit(int fd) {
    279   WriteHeaders(fd, 0, 0, kHttpResponseOk);
    280   LOG(INFO) << "pid(" << getpid() <<  "): HTTP server exiting ...";
    281   exit(RC_OK);
    282 }
    283 
    284 
    285 // Generates an HTTP response with payload corresponding to requested offsets
    286 // and length.  Optionally, truncate the payload at a given length and add a
    287 // pause midway through the transfer.  Returns the total number of bytes
    288 // delivered or -1 for error.
    289 ssize_t HandleGet(int fd, const HttpRequest& request, const size_t total_length,
    290                   const size_t truncate_length, const int sleep_every,
    291                   const int sleep_secs) {
    292   ssize_t ret;
    293   size_t written = 0;
    294 
    295   // Obtain start offset, make sure it is within total payload length.
    296   const size_t start_offset = request.start_offset;
    297   if (start_offset >= total_length) {
    298     LOG(WARNING) << "start offset (" << start_offset
    299                  << ") exceeds total length (" << total_length
    300                  << "), generating error response ("
    301                  << kHttpResponseReqRangeNotSat << ")";
    302     return WriteHeaders(fd, total_length, total_length,
    303                         kHttpResponseReqRangeNotSat);
    304   }
    305 
    306   // Obtain end offset, adjust to fit in total payload length and ensure it does
    307   // not preceded the start offset.
    308   size_t end_offset = (request.end_offset > 0 ?
    309                        request.end_offset : total_length);
    310   if (end_offset < start_offset) {
    311     LOG(WARNING) << "end offset (" << end_offset << ") precedes start offset ("
    312                  << start_offset << "), generating error response";
    313     return WriteHeaders(fd, 0, 0, kHttpResponseBadRequest);
    314   }
    315   if (end_offset > total_length) {
    316     LOG(INFO) << "requested end offset (" << end_offset
    317               << ") exceeds total length (" << total_length << "), adjusting";
    318     end_offset = total_length;
    319   }
    320 
    321   // Generate headers
    322   LOG(INFO) << "generating response header: range=" << start_offset << "-"
    323             << (end_offset - 1) << "/" << (end_offset - start_offset)
    324             << ", return code=" << request.return_code;
    325   if ((ret = WriteHeaders(fd, start_offset, end_offset,
    326                           request.return_code)) < 0)
    327     return -1;
    328   LOG(INFO) << ret << " header bytes written";
    329   written += ret;
    330 
    331   // Compute payload length, truncate as necessary.
    332   size_t payload_length = end_offset - start_offset;
    333   if (truncate_length > 0 && truncate_length < payload_length) {
    334     LOG(INFO) << "truncating request payload length (" << payload_length
    335               << ") at " << truncate_length;
    336     payload_length = truncate_length;
    337     end_offset = start_offset + payload_length;
    338   }
    339 
    340   LOG(INFO) << "generating response payload: range=" << start_offset << "-"
    341             << (end_offset - 1) << "/" << (end_offset - start_offset);
    342 
    343   // Decide about optional midway delay.
    344   if (truncate_length > 0 && sleep_every > 0 && sleep_secs >= 0 &&
    345       start_offset % (truncate_length * sleep_every) == 0) {
    346     const off_t midway_offset = start_offset + payload_length / 2;
    347 
    348     if ((ret = WritePayload(fd, start_offset, midway_offset)) < 0)
    349       return -1;
    350     LOG(INFO) << ret << " payload bytes written (first chunk)";
    351     written += ret;
    352 
    353     LOG(INFO) << "sleeping for " << sleep_secs << " seconds...";
    354     sleep(sleep_secs);
    355 
    356     if ((ret = WritePayload(fd, midway_offset, end_offset)) < 0)
    357       return -1;
    358     LOG(INFO) << ret << " payload bytes written (second chunk)";
    359     written += ret;
    360   } else {
    361     if ((ret = WritePayload(fd, start_offset, end_offset)) < 0)
    362       return -1;
    363     LOG(INFO) << ret << " payload bytes written";
    364     written += ret;
    365   }
    366 
    367   LOG(INFO) << "response generation complete, " << written
    368             << " total bytes written";
    369   return written;
    370 }
    371 
    372 ssize_t HandleGet(int fd, const HttpRequest& request,
    373                   const size_t total_length) {
    374   return HandleGet(fd, request, total_length, 0, 0, 0);
    375 }
    376 
    377 // Handles /redirect/<code>/<url> requests by returning the specified
    378 // redirect <code> with a location pointing to /<url>.
    379 void HandleRedirect(int fd, const HttpRequest& request) {
    380   LOG(INFO) << "Redirecting...";
    381   string url = request.url;
    382   CHECK_EQ(static_cast<size_t>(0), url.find("/redirect/"));
    383   url.erase(0, strlen("/redirect/"));
    384   string::size_type url_start = url.find('/');
    385   CHECK_NE(url_start, string::npos);
    386   HttpResponseCode code = StringToHttpResponseCode(url.c_str());
    387   url.erase(0, url_start);
    388   url = "http://" + request.host + url;
    389   const char *status = GetHttpResponseDescription(code);
    390   if (!status)
    391     CHECK(false) << "Unrecognized redirection code: " << code;
    392   LOG(INFO) << "Code: " << code << " " << status;
    393   LOG(INFO) << "New URL: " << url;
    394 
    395   ssize_t ret;
    396   if ((ret = WriteString(fd, "HTTP/1.1 " + Itoa(code) + " " +
    397                          status + EOL)) < 0)
    398     return;
    399   WriteString(fd, "Location: " + url + EOL);
    400 }
    401 
    402 // Generate a page not found error response with actual text payload. Return
    403 // number of bytes written or -1 for error.
    404 ssize_t HandleError(int fd, const HttpRequest& request) {
    405   LOG(INFO) << "Generating error HTTP response";
    406 
    407   ssize_t ret;
    408   size_t written = 0;
    409 
    410   const string data("This is an error page.");
    411 
    412   if ((ret = WriteHeaders(fd, 0, data.size(), kHttpResponseNotFound)) < 0)
    413     return -1;
    414   written += ret;
    415 
    416   if ((ret = WriteString(fd, data)) < 0)
    417     return -1;
    418   written += ret;
    419 
    420   return written;
    421 }
    422 
    423 // Generate an error response if the requested offset is nonzero, up to a given
    424 // maximal number of successive failures.  The error generated is an "Internal
    425 // Server Error" (500).
    426 ssize_t HandleErrorIfOffset(int fd, const HttpRequest& request,
    427                             size_t end_offset, int max_fails) {
    428   static int num_fails = 0;
    429 
    430   if (request.start_offset > 0 && num_fails < max_fails) {
    431     LOG(INFO) << "Generating error HTTP response";
    432 
    433     ssize_t ret;
    434     size_t written = 0;
    435 
    436     const string data("This is an error page.");
    437 
    438     if ((ret = WriteHeaders(fd, 0, data.size(),
    439                             kHttpResponseInternalServerError)) < 0)
    440       return -1;
    441     written += ret;
    442 
    443     if ((ret = WriteString(fd, data)) < 0)
    444       return -1;
    445     written += ret;
    446 
    447     num_fails++;
    448     return written;
    449   } else {
    450     num_fails = 0;
    451     return HandleGet(fd, request, end_offset);
    452   }
    453 }
    454 
    455 // Returns a valid response echoing in the body of the response all the headers
    456 // sent by the client.
    457 void HandleEchoHeaders(int fd, const HttpRequest& request) {
    458   WriteHeaders(fd, 0, request.raw_headers.size(), kHttpResponseOk);
    459   WriteString(fd, request.raw_headers);
    460 }
    461 
    462 void HandleHang(int fd) {
    463   LOG(INFO) << "Hanging until the other side of the connection is closed.";
    464   char c;
    465   while (HANDLE_EINTR(read(fd, &c, 1)) > 0) {}
    466 }
    467 
    468 void HandleDefault(int fd, const HttpRequest& request) {
    469   const off_t start_offset = request.start_offset;
    470   const string data("unhandled path");
    471   const size_t size = data.size();
    472   ssize_t ret;
    473 
    474   if ((ret = WriteHeaders(fd, start_offset, size, request.return_code)) < 0)
    475     return;
    476   WriteString(fd, (start_offset < static_cast<off_t>(size) ?
    477                    data.substr(start_offset) : ""));
    478 }
    479 
    480 
    481 // Break a URL into terms delimited by slashes.
    482 class UrlTerms {
    483  public:
    484   UrlTerms(const string &url, size_t num_terms) {
    485     // URL must be non-empty and start with a slash.
    486     CHECK_GT(url.size(), static_cast<size_t>(0));
    487     CHECK_EQ(url[0], '/');
    488 
    489     // Split it into terms delimited by slashes, omitting the preceding slash.
    490     terms = base::SplitString(url.substr(1), "/", base::KEEP_WHITESPACE,
    491                               base::SPLIT_WANT_ALL);
    492 
    493     // Ensure expected length.
    494     CHECK_EQ(terms.size(), num_terms);
    495   }
    496 
    497   inline string Get(const off_t index) const {
    498     return terms[index];
    499   }
    500   inline const char *GetCStr(const off_t index) const {
    501     return Get(index).c_str();
    502   }
    503   inline int GetInt(const off_t index) const {
    504     return atoi(GetCStr(index));
    505   }
    506   inline size_t GetSizeT(const off_t index) const {
    507     return static_cast<size_t>(atol(GetCStr(index)));
    508   }
    509 
    510  private:
    511   vector<string> terms;
    512 };
    513 
    514 void HandleConnection(int fd) {
    515   HttpRequest request;
    516   ParseRequest(fd, &request);
    517 
    518   string &url = request.url;
    519   LOG(INFO) << "pid(" << getpid() <<  "): handling url " << url;
    520   if (url == "/quitquitquit") {
    521     HandleQuit(fd);
    522   } else if (base::StartsWith(
    523                  url, "/download/", base::CompareCase::SENSITIVE)) {
    524     const UrlTerms terms(url, 2);
    525     HandleGet(fd, request, terms.GetSizeT(1));
    526   } else if (base::StartsWith(url, "/flaky/", base::CompareCase::SENSITIVE)) {
    527     const UrlTerms terms(url, 5);
    528     HandleGet(fd, request, terms.GetSizeT(1), terms.GetSizeT(2),
    529               terms.GetInt(3), terms.GetInt(4));
    530   } else if (url.find("/redirect/") == 0) {
    531     HandleRedirect(fd, request);
    532   } else if (url == "/error") {
    533     HandleError(fd, request);
    534   } else if (base::StartsWith(url, "/error-if-offset/",
    535                               base::CompareCase::SENSITIVE)) {
    536     const UrlTerms terms(url, 3);
    537     HandleErrorIfOffset(fd, request, terms.GetSizeT(1), terms.GetInt(2));
    538   } else if (url == "/echo-headers") {
    539     HandleEchoHeaders(fd, request);
    540   } else if (url == "/hang") {
    541     HandleHang(fd);
    542   } else {
    543     HandleDefault(fd, request);
    544   }
    545 
    546   close(fd);
    547 }
    548 
    549 }  // namespace chromeos_update_engine
    550 
    551 using namespace chromeos_update_engine;  // NOLINT(build/namespaces)
    552 
    553 
    554 void usage(const char *prog_arg) {
    555   fprintf(
    556       stderr,
    557       "Usage: %s [ FILE ]\n"
    558       "Once accepting connections, the following is written to FILE (or "
    559       "stdout):\n"
    560       "\"%sN\" (where N is an integer port number)\n",
    561       basename(prog_arg), kListeningMsgPrefix);
    562 }
    563 
    564 int main(int argc, char** argv) {
    565   // Check invocation.
    566   if (argc > 2)
    567     errx(RC_BAD_ARGS, "unexpected number of arguments (use -h for usage)");
    568 
    569   // Parse (optional) argument.
    570   int report_fd = STDOUT_FILENO;
    571   if (argc == 2) {
    572     if (!strcmp(argv[1], "-h")) {
    573       usage(argv[0]);
    574       exit(RC_OK);
    575     }
    576 
    577     report_fd = open(argv[1], O_WRONLY | O_CREAT, 00644);
    578   }
    579 
    580   // Ignore SIGPIPE on write() to sockets.
    581   signal(SIGPIPE, SIG_IGN);
    582 
    583   int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    584   if (listen_fd < 0)
    585     LOG(FATAL) << "socket() failed";
    586 
    587   struct sockaddr_in server_addr = sockaddr_in();
    588   server_addr.sin_family = AF_INET;
    589   server_addr.sin_addr.s_addr = INADDR_ANY;
    590   server_addr.sin_port = 0;
    591 
    592   {
    593     // Get rid of "Address in use" error
    594     int tr = 1;
    595     if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &tr,
    596                    sizeof(int)) == -1) {
    597       perror("setsockopt");
    598       exit(RC_ERR_SETSOCKOPT);
    599     }
    600   }
    601 
    602   // Bind the socket and set for listening.
    603   if (bind(listen_fd, reinterpret_cast<struct sockaddr *>(&server_addr),
    604            sizeof(server_addr)) < 0) {
    605     perror("bind");
    606     exit(RC_ERR_BIND);
    607   }
    608   if (listen(listen_fd, 5) < 0) {
    609     perror("listen");
    610     exit(RC_ERR_LISTEN);
    611   }
    612 
    613   // Check the actual port.
    614   struct sockaddr_in bound_addr = sockaddr_in();
    615   socklen_t bound_addr_len = sizeof(bound_addr);
    616   if (getsockname(listen_fd, reinterpret_cast<struct sockaddr*>(&bound_addr),
    617                   &bound_addr_len) < 0) {
    618     perror("getsockname");
    619     exit(RC_ERR_GETSOCKNAME);
    620   }
    621   in_port_t port = ntohs(bound_addr.sin_port);
    622 
    623   // Output the listening port, indicating that the server is processing
    624   // requests. IMPORTANT! (a) the format of this message is as expected by some
    625   // unit tests, avoid unilateral changes; (b) it is necessary to flush/sync the
    626   // file to prevent the spawning process from waiting indefinitely for this
    627   // message.
    628   string listening_msg = base::StringPrintf("%s%hu", kListeningMsgPrefix, port);
    629   LOG(INFO) << listening_msg;
    630   CHECK_EQ(write(report_fd, listening_msg.c_str(), listening_msg.length()),
    631            static_cast<int>(listening_msg.length()));
    632   CHECK_EQ(write(report_fd, "\n", 1), 1);
    633   if (report_fd == STDOUT_FILENO)
    634     fsync(report_fd);
    635   else
    636     close(report_fd);
    637 
    638   while (1) {
    639     LOG(INFO) << "pid(" << getpid() <<  "): waiting to accept new connection";
    640     int client_fd = accept(listen_fd, nullptr, nullptr);
    641     LOG(INFO) << "got past accept";
    642     if (client_fd < 0)
    643       LOG(FATAL) << "ERROR on accept";
    644     HandleConnection(client_fd);
    645   }
    646   return 0;
    647 }
    648