1/*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2015 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25#include "shrpx_worker_process.h"
26
27#include <sys/types.h>
28#ifdef HAVE_UNISTD_H
29#  include <unistd.h>
30#endif // HAVE_UNISTD_H
31#include <sys/resource.h>
32#include <sys/wait.h>
33#include <grp.h>
34
35#include <cinttypes>
36#include <cstdlib>
37
38#include <openssl/rand.h>
39
40#include <ev.h>
41
42#include <ares.h>
43
44#include "shrpx_config.h"
45#include "shrpx_connection_handler.h"
46#include "shrpx_log_config.h"
47#include "shrpx_worker.h"
48#include "shrpx_accept_handler.h"
49#include "shrpx_http2_upstream.h"
50#include "shrpx_http2_session.h"
51#include "shrpx_memcached_dispatcher.h"
52#include "shrpx_memcached_request.h"
53#include "shrpx_process.h"
54#include "shrpx_tls.h"
55#include "shrpx_log.h"
56#include "util.h"
57#include "app_helper.h"
58#include "template.h"
59#include "xsi_strerror.h"
60
61using namespace nghttp2;
62
63namespace shrpx {
64
65namespace {
66void drop_privileges(
67#ifdef HAVE_NEVERBLEED
68    neverbleed_t *nb
69#endif // HAVE_NEVERBLEED
70) {
71  std::array<char, STRERROR_BUFSIZE> errbuf;
72  auto config = get_config();
73
74  if (getuid() == 0 && config->uid != 0) {
75#ifdef HAVE_NEVERBLEED
76    if (nb) {
77      neverbleed_setuidgid(nb, config->user.c_str(), 1);
78    }
79#endif // HAVE_NEVERBLEED
80
81    if (initgroups(config->user.c_str(), config->gid) != 0) {
82      auto error = errno;
83      LOG(FATAL) << "Could not change supplementary groups: "
84                 << xsi_strerror(error, errbuf.data(), errbuf.size());
85      exit(EXIT_FAILURE);
86    }
87    if (setgid(config->gid) != 0) {
88      auto error = errno;
89      LOG(FATAL) << "Could not change gid: "
90                 << xsi_strerror(error, errbuf.data(), errbuf.size());
91      exit(EXIT_FAILURE);
92    }
93    if (setuid(config->uid) != 0) {
94      auto error = errno;
95      LOG(FATAL) << "Could not change uid: "
96                 << xsi_strerror(error, errbuf.data(), errbuf.size());
97      exit(EXIT_FAILURE);
98    }
99    if (setuid(0) != -1) {
100      LOG(FATAL) << "Still have root privileges?";
101      exit(EXIT_FAILURE);
102    }
103  }
104}
105} // namespace
106
107namespace {
108void graceful_shutdown(ConnectionHandler *conn_handler) {
109  if (conn_handler->get_graceful_shutdown()) {
110    return;
111  }
112
113  LOG(NOTICE) << "Graceful shutdown signal received";
114
115  conn_handler->set_graceful_shutdown(true);
116
117  // TODO What happens for the connections not established in the
118  // kernel?
119  conn_handler->accept_pending_connection();
120  conn_handler->delete_acceptor();
121
122  conn_handler->graceful_shutdown_worker();
123
124  auto single_worker = conn_handler->get_single_worker();
125  if (single_worker) {
126    auto worker_stat = single_worker->get_worker_stat();
127    if (worker_stat->num_connections == 0 &&
128        worker_stat->num_close_waits == 0) {
129      ev_break(conn_handler->get_loop());
130    }
131
132    return;
133  }
134}
135} // namespace
136
137namespace {
138void reopen_log(ConnectionHandler *conn_handler) {
139  LOG(NOTICE) << "Reopening log files: worker process (thread main)";
140
141  auto config = get_config();
142  auto &loggingconf = config->logging;
143
144  (void)reopen_log_files(loggingconf);
145  redirect_stderr_to_errorlog(loggingconf);
146
147  conn_handler->worker_reopen_log_files();
148}
149} // namespace
150
151namespace {
152void ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) {
153  auto conn_handler = static_cast<ConnectionHandler *>(w->data);
154  std::array<uint8_t, 1024> buf;
155  ssize_t nread;
156  while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR)
157    ;
158  if (nread == -1) {
159    auto error = errno;
160    LOG(ERROR) << "Failed to read data from ipc channel: errno=" << error;
161    return;
162  }
163
164  if (nread == 0) {
165    // IPC socket closed.  Perform immediate shutdown.
166    LOG(FATAL) << "IPC socket is closed.  Perform immediate shutdown.";
167    nghttp2_Exit(EXIT_FAILURE);
168  }
169
170  for (ssize_t i = 0; i < nread; ++i) {
171    switch (buf[i]) {
172    case SHRPX_IPC_GRACEFUL_SHUTDOWN:
173      graceful_shutdown(conn_handler);
174      break;
175    case SHRPX_IPC_REOPEN_LOG:
176      reopen_log(conn_handler);
177      break;
178    }
179  }
180}
181} // namespace
182
183#ifdef ENABLE_HTTP3
184namespace {
185void quic_ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) {
186  auto conn_handler = static_cast<ConnectionHandler *>(w->data);
187
188  if (conn_handler->quic_ipc_read() != 0) {
189    LOG(ERROR) << "Failed to read data from QUIC IPC channel";
190
191    return;
192  }
193}
194} // namespace
195#endif // ENABLE_HTTP3
196
197namespace {
198int generate_ticket_key(TicketKey &ticket_key) {
199  ticket_key.cipher = get_config()->tls.ticket.cipher;
200  ticket_key.hmac = EVP_sha256();
201  ticket_key.hmac_keylen = EVP_MD_size(ticket_key.hmac);
202
203  assert(static_cast<size_t>(EVP_CIPHER_key_length(ticket_key.cipher)) <=
204         ticket_key.data.enc_key.size());
205  assert(ticket_key.hmac_keylen <= ticket_key.data.hmac_key.size());
206
207  if (LOG_ENABLED(INFO)) {
208    LOG(INFO) << "enc_keylen=" << EVP_CIPHER_key_length(ticket_key.cipher)
209              << ", hmac_keylen=" << ticket_key.hmac_keylen;
210  }
211
212  if (RAND_bytes(reinterpret_cast<unsigned char *>(&ticket_key.data),
213                 sizeof(ticket_key.data)) == 0) {
214    return -1;
215  }
216
217  return 0;
218}
219} // namespace
220
221namespace {
222void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) {
223  auto conn_handler = static_cast<ConnectionHandler *>(w->data);
224  const auto &old_ticket_keys = conn_handler->get_ticket_keys();
225
226  auto ticket_keys = std::make_shared<TicketKeys>();
227  LOG(NOTICE) << "Renew new ticket keys";
228
229  // If old_ticket_keys is not empty, it should contain at least 2
230  // keys: one for encryption, and last one for the next encryption
231  // key but decryption only.  The keys in between are old keys and
232  // decryption only.  The next key is provided to ensure to mitigate
233  // possible problem when one worker encrypt new key, but one worker,
234  // which did not take the that key yet, and cannot decrypt it.
235  //
236  // We keep keys for get_config()->tls_session_timeout seconds.  The
237  // default is 12 hours.  Thus the maximum ticket vector size is 12.
238  if (old_ticket_keys) {
239    auto &old_keys = old_ticket_keys->keys;
240    auto &new_keys = ticket_keys->keys;
241
242    assert(!old_keys.empty());
243
244    auto max_tickets =
245        static_cast<size_t>(std::chrono::duration_cast<std::chrono::hours>(
246                                get_config()->tls.session_timeout)
247                                .count());
248
249    new_keys.resize(std::min(max_tickets, old_keys.size() + 1));
250    std::copy_n(std::begin(old_keys), new_keys.size() - 1,
251                std::begin(new_keys) + 1);
252  } else {
253    ticket_keys->keys.resize(1);
254  }
255
256  auto &new_key = ticket_keys->keys[0];
257
258  if (generate_ticket_key(new_key) != 0) {
259    if (LOG_ENABLED(INFO)) {
260      LOG(INFO) << "failed to generate ticket key";
261    }
262    conn_handler->set_ticket_keys(nullptr);
263    conn_handler->set_ticket_keys_to_worker(nullptr);
264    return;
265  }
266
267  if (LOG_ENABLED(INFO)) {
268    LOG(INFO) << "ticket keys generation done";
269    assert(ticket_keys->keys.size() >= 1);
270    LOG(INFO) << 0 << " enc+dec: "
271              << util::format_hex(ticket_keys->keys[0].data.name);
272    for (size_t i = 1; i < ticket_keys->keys.size(); ++i) {
273      auto &key = ticket_keys->keys[i];
274      LOG(INFO) << i << " dec: " << util::format_hex(key.data.name);
275    }
276  }
277
278  conn_handler->set_ticket_keys(ticket_keys);
279  conn_handler->set_ticket_keys_to_worker(ticket_keys);
280}
281} // namespace
282
283namespace {
284void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w,
285                                 int revents) {
286  auto conn_handler = static_cast<ConnectionHandler *>(w->data);
287  auto dispatcher = conn_handler->get_tls_ticket_key_memcached_dispatcher();
288
289  auto req = std::make_unique<MemcachedRequest>();
290  req->key = "nghttpx:tls-ticket-key";
291  req->op = MemcachedOp::GET;
292  req->cb = [conn_handler, w](MemcachedRequest *req, MemcachedResult res) {
293    switch (res.status_code) {
294    case MemcachedStatusCode::NO_ERROR:
295      break;
296    case MemcachedStatusCode::EXT_NETWORK_ERROR:
297      conn_handler->on_tls_ticket_key_network_error(w);
298      return;
299    default:
300      conn_handler->on_tls_ticket_key_not_found(w);
301      return;
302    }
303
304    // |version (4bytes)|len (2bytes)|key (variable length)|...
305    // (len, key) pairs are repeated as necessary.
306
307    auto &value = res.value;
308    if (value.size() < 4) {
309      LOG(WARN) << "Memcached: tls ticket key value is too small: got "
310                << value.size();
311      conn_handler->on_tls_ticket_key_not_found(w);
312      return;
313    }
314    auto p = value.data();
315    auto version = util::get_uint32(p);
316    // Currently supported version is 1.
317    if (version != 1) {
318      LOG(WARN) << "Memcached: tls ticket key version: want 1, got " << version;
319      conn_handler->on_tls_ticket_key_not_found(w);
320      return;
321    }
322
323    auto end = p + value.size();
324    p += 4;
325
326    auto &ticketconf = get_config()->tls.ticket;
327
328    size_t expectedlen;
329    size_t enc_keylen;
330    size_t hmac_keylen;
331    if (ticketconf.cipher == EVP_aes_128_cbc()) {
332      expectedlen = 48;
333      enc_keylen = 16;
334      hmac_keylen = 16;
335    } else if (ticketconf.cipher == EVP_aes_256_cbc()) {
336      expectedlen = 80;
337      enc_keylen = 32;
338      hmac_keylen = 32;
339    } else {
340      return;
341    }
342
343    auto ticket_keys = std::make_shared<TicketKeys>();
344
345    for (; p != end;) {
346      if (end - p < 2) {
347        LOG(WARN) << "Memcached: tls ticket key data is too small";
348        conn_handler->on_tls_ticket_key_not_found(w);
349        return;
350      }
351      auto len = util::get_uint16(p);
352      p += 2;
353      if (len != expectedlen) {
354        LOG(WARN) << "Memcached: wrong tls ticket key size: want "
355                  << expectedlen << ", got " << len;
356        conn_handler->on_tls_ticket_key_not_found(w);
357        return;
358      }
359      if (p + len > end) {
360        LOG(WARN) << "Memcached: too short tls ticket key payload: want " << len
361                  << ", got " << (end - p);
362        conn_handler->on_tls_ticket_key_not_found(w);
363        return;
364      }
365      auto key = TicketKey();
366      key.cipher = ticketconf.cipher;
367      key.hmac = EVP_sha256();
368      key.hmac_keylen = hmac_keylen;
369
370      std::copy_n(p, key.data.name.size(), std::begin(key.data.name));
371      p += key.data.name.size();
372
373      std::copy_n(p, enc_keylen, std::begin(key.data.enc_key));
374      p += enc_keylen;
375
376      std::copy_n(p, hmac_keylen, std::begin(key.data.hmac_key));
377      p += hmac_keylen;
378
379      ticket_keys->keys.push_back(std::move(key));
380    }
381
382    conn_handler->on_tls_ticket_key_get_success(ticket_keys, w);
383  };
384
385  if (LOG_ENABLED(INFO)) {
386    LOG(INFO) << "Memcached: tls ticket key get request sent";
387  }
388
389  dispatcher->add_request(std::move(req));
390}
391
392} // namespace
393
394#ifdef HAVE_NEVERBLEED
395namespace {
396void nb_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
397  log_chld(w->rpid, w->rstatus, "neverbleed process");
398
399  ev_child_stop(loop, w);
400
401  LOG(FATAL) << "neverbleed process exitted; aborting now";
402
403  nghttp2_Exit(EXIT_FAILURE);
404}
405} // namespace
406#endif // HAVE_NEVERBLEED
407
408namespace {
409int send_ready_event(int ready_ipc_fd) {
410  std::array<char, STRERROR_BUFSIZE> errbuf;
411  auto pid = getpid();
412  ssize_t nwrite;
413
414  while ((nwrite = write(ready_ipc_fd, &pid, sizeof(pid))) == -1 &&
415         errno == EINTR)
416    ;
417
418  if (nwrite < 0) {
419    auto error = errno;
420
421    LOG(ERROR) << "Writing PID to ready IPC channel failed: "
422               << xsi_strerror(error, errbuf.data(), errbuf.size());
423
424    return -1;
425  }
426
427  return 0;
428}
429} // namespace
430
431int worker_process_event_loop(WorkerProcessConfig *wpconf) {
432  int rv;
433  std::array<char, STRERROR_BUFSIZE> errbuf;
434  (void)errbuf;
435
436  auto config = get_config();
437
438  if (reopen_log_files(config->logging) != 0) {
439    LOG(FATAL) << "Failed to open log file";
440    return -1;
441  }
442
443  rv = ares_library_init(ARES_LIB_INIT_ALL);
444  if (rv != 0) {
445    LOG(FATAL) << "ares_library_init failed: " << ares_strerror(rv);
446    return -1;
447  }
448
449  auto loop = EV_DEFAULT;
450
451  auto gen = util::make_mt19937();
452
453#ifdef HAVE_NEVERBLEED
454  std::array<char, NEVERBLEED_ERRBUF_SIZE> nb_errbuf;
455  auto nb = std::make_unique<neverbleed_t>();
456  if (neverbleed_init(nb.get(), nb_errbuf.data()) != 0) {
457    LOG(FATAL) << "neverbleed_init failed: " << nb_errbuf.data();
458    return -1;
459  }
460
461  LOG(NOTICE) << "neverbleed process [" << nb->daemon_pid << "] spawned";
462
463  ev_child nb_childev;
464
465  ev_child_init(&nb_childev, nb_child_cb, nb->daemon_pid, 0);
466  nb_childev.data = nullptr;
467  ev_child_start(loop, &nb_childev);
468#endif // HAVE_NEVERBLEED
469
470  auto conn_handler = std::make_unique<ConnectionHandler>(loop, gen);
471
472#ifdef HAVE_NEVERBLEED
473  conn_handler->set_neverbleed(nb.get());
474#endif // HAVE_NEVERBLEED
475
476#ifdef ENABLE_HTTP3
477  conn_handler->set_quic_ipc_fd(wpconf->quic_ipc_fd);
478  conn_handler->set_quic_lingering_worker_processes(
479      wpconf->quic_lingering_worker_processes);
480#endif // ENABLE_HTTP3
481
482  for (auto &addr : config->conn.listener.addrs) {
483    conn_handler->add_acceptor(
484        std::make_unique<AcceptHandler>(&addr, conn_handler.get()));
485  }
486
487  MemchunkPool mcpool;
488
489  ev_timer renew_ticket_key_timer;
490  if (tls::upstream_tls_enabled(config->conn)) {
491    auto &ticketconf = config->tls.ticket;
492    auto &memcachedconf = ticketconf.memcached;
493
494    if (!memcachedconf.host.empty()) {
495      SSL_CTX *ssl_ctx = nullptr;
496
497      if (memcachedconf.tls) {
498        ssl_ctx = conn_handler->create_tls_ticket_key_memcached_ssl_ctx();
499      }
500
501      conn_handler->set_tls_ticket_key_memcached_dispatcher(
502          std::make_unique<MemcachedDispatcher>(
503              &ticketconf.memcached.addr, loop, ssl_ctx,
504              StringRef{memcachedconf.host}, &mcpool, gen));
505
506      ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0.,
507                    0.);
508      renew_ticket_key_timer.data = conn_handler.get();
509      // Get first ticket keys.
510      memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
511    } else {
512      bool auto_tls_ticket_key = true;
513      if (!ticketconf.files.empty()) {
514        if (!ticketconf.cipher_given) {
515          LOG(WARN)
516              << "It is strongly recommended to specify "
517                 "--tls-ticket-key-cipher=aes-128-cbc (or "
518                 "tls-ticket-key-cipher=aes-128-cbc in configuration file) "
519                 "when --tls-ticket-key-file is used for the smooth "
520                 "transition when the default value of --tls-ticket-key-cipher "
521                 "becomes aes-256-cbc";
522        }
523        auto ticket_keys = read_tls_ticket_key_file(
524            ticketconf.files, ticketconf.cipher, EVP_sha256());
525        if (!ticket_keys) {
526          LOG(WARN) << "Use internal session ticket key generator";
527        } else {
528          conn_handler->set_ticket_keys(std::move(ticket_keys));
529          auto_tls_ticket_key = false;
530        }
531      }
532      if (auto_tls_ticket_key) {
533        // Generate new ticket key every 1hr.
534        ev_timer_init(&renew_ticket_key_timer, renew_ticket_key_cb, 0., 1_h);
535        renew_ticket_key_timer.data = conn_handler.get();
536        ev_timer_again(loop, &renew_ticket_key_timer);
537
538        // Generate first session ticket key before running workers.
539        renew_ticket_key_cb(loop, &renew_ticket_key_timer, 0);
540      }
541    }
542  }
543
544#ifdef ENABLE_HTTP3
545  auto &quicconf = config->quic;
546
547  std::shared_ptr<QUICKeyingMaterials> qkms;
548
549  if (!quicconf.upstream.secret_file.empty()) {
550    qkms = read_quic_secret_file(quicconf.upstream.secret_file);
551    if (!qkms) {
552      LOG(WARN) << "Use QUIC keying materials generated internally";
553    }
554  }
555
556  if (!qkms) {
557    qkms = std::make_shared<QUICKeyingMaterials>();
558    qkms->keying_materials.resize(1);
559
560    auto &qkm = qkms->keying_materials.front();
561
562    if (RAND_bytes(qkm.reserved.data(), qkm.reserved.size()) != 1) {
563      LOG(ERROR) << "Failed to generate QUIC secret reserved data";
564      return -1;
565    }
566
567    if (RAND_bytes(qkm.secret.data(), qkm.secret.size()) != 1) {
568      LOG(ERROR) << "Failed to generate QUIC secret";
569      return -1;
570    }
571
572    if (RAND_bytes(qkm.salt.data(), qkm.salt.size()) != 1) {
573      LOG(ERROR) << "Failed to generate QUIC salt";
574      return -1;
575    }
576  }
577
578  for (auto &qkm : qkms->keying_materials) {
579    if (generate_quic_connection_id_encryption_key(
580            qkm.cid_encryption_key.data(), qkm.cid_encryption_key.size(),
581            qkm.secret.data(), qkm.secret.size(), qkm.salt.data(),
582            qkm.salt.size()) != 0) {
583      LOG(ERROR) << "Failed to generate QUIC Connection ID encryption key";
584      return -1;
585    }
586  }
587
588  conn_handler->set_quic_keying_materials(std::move(qkms));
589
590  conn_handler->set_cid_prefixes(wpconf->cid_prefixes);
591  conn_handler->set_quic_lingering_worker_processes(
592      wpconf->quic_lingering_worker_processes);
593#endif // ENABLE_HTTP3
594
595  if (config->single_thread) {
596    rv = conn_handler->create_single_worker();
597    if (rv != 0) {
598      return -1;
599    }
600  } else {
601#ifndef NOTHREADS
602    sigset_t set;
603    sigemptyset(&set);
604    sigaddset(&set, SIGCHLD);
605
606    rv = pthread_sigmask(SIG_BLOCK, &set, nullptr);
607    if (rv != 0) {
608      LOG(ERROR) << "Blocking SIGCHLD failed: "
609                 << xsi_strerror(rv, errbuf.data(), errbuf.size());
610      return -1;
611    }
612#endif // !NOTHREADS
613
614    rv = conn_handler->create_worker_thread(config->num_worker);
615    if (rv != 0) {
616      return -1;
617    }
618
619#ifndef NOTHREADS
620    rv = pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
621    if (rv != 0) {
622      LOG(ERROR) << "Unblocking SIGCHLD failed: "
623                 << xsi_strerror(rv, errbuf.data(), errbuf.size());
624      return -1;
625    }
626#endif // !NOTHREADS
627  }
628
629#if defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF)
630  conn_handler->unload_bpf_objects();
631#endif // defined(ENABLE_HTTP3) && defined(HAVE_LIBBPF)
632
633  drop_privileges(
634#ifdef HAVE_NEVERBLEED
635      nb.get()
636#endif // HAVE_NEVERBLEED
637  );
638
639  ev_io ipcev;
640  ev_io_init(&ipcev, ipc_readcb, wpconf->ipc_fd, EV_READ);
641  ipcev.data = conn_handler.get();
642  ev_io_start(loop, &ipcev);
643
644#ifdef ENABLE_HTTP3
645  ev_io quic_ipcev;
646  ev_io_init(&quic_ipcev, quic_ipc_readcb, wpconf->quic_ipc_fd, EV_READ);
647  quic_ipcev.data = conn_handler.get();
648  ev_io_start(loop, &quic_ipcev);
649#endif // ENABLE_HTTP3
650
651  if (tls::upstream_tls_enabled(config->conn) && !config->tls.ocsp.disabled) {
652    if (config->tls.ocsp.startup) {
653      conn_handler->set_enable_acceptor_on_ocsp_completion(true);
654      conn_handler->disable_acceptor();
655    }
656
657    conn_handler->proceed_next_cert_ocsp();
658  }
659
660  if (LOG_ENABLED(INFO)) {
661    LOG(INFO) << "Entering event loop";
662  }
663
664  if (send_ready_event(wpconf->ready_ipc_fd) != 0) {
665    return -1;
666  }
667
668  ev_run(loop, 0);
669
670  conn_handler->cancel_ocsp_update();
671
672  // Destroy SSL_CTX held in conn_handler before killing neverbleed
673  // daemon.  Otherwise priv_rsa_finish yields "write error" and
674  // worker process aborts.
675  conn_handler.reset();
676
677#ifdef HAVE_NEVERBLEED
678  assert(nb->daemon_pid > 0);
679
680  rv = kill(nb->daemon_pid, SIGTERM);
681  if (rv != 0) {
682    auto error = errno;
683    LOG(ERROR) << "Could not send signal to neverbleed daemon: errno=" << error;
684  }
685
686  while ((rv = waitpid(nb->daemon_pid, nullptr, 0)) == -1 && errno == EINTR)
687    ;
688  if (rv == -1) {
689    auto error = errno;
690    LOG(ERROR) << "Error occurred while we were waiting for the completion "
691                  "of neverbleed process: errno="
692               << error;
693  }
694#endif // HAVE_NEVERBLEED
695
696  ares_library_cleanup();
697
698  return 0;
699}
700
701} // namespace shrpx
702