• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
61 using namespace nghttp2;
62 
63 namespace shrpx {
64 
65 namespace {
drop_privileges(neverbleed_t * nb)66 void 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 
107 namespace {
graceful_shutdown(ConnectionHandler * conn_handler)108 void 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 
137 namespace {
reopen_log(ConnectionHandler * conn_handler)138 void 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 
151 namespace {
ipc_readcb(struct ev_loop * loop,ev_io * w,int revents)152 void 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
184 namespace {
quic_ipc_readcb(struct ev_loop * loop,ev_io * w,int revents)185 void 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 
197 namespace {
generate_ticket_key(TicketKey & ticket_key)198 int 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 
221 namespace {
renew_ticket_key_cb(struct ev_loop * loop,ev_timer * w,int revents)222 void 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 
283 namespace {
memcached_get_ticket_key_cb(struct ev_loop * loop,ev_timer * w,int revents)284 void 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
395 namespace {
nb_child_cb(struct ev_loop * loop,ev_child * w,int revents)396 void 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 
408 namespace {
send_ready_event(int ready_ipc_fd)409 int 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 
worker_process_event_loop(WorkerProcessConfig * wpconf)431 int 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