nghttpx: Reload configuration with SIGHUP
This commit implements configuration reloading with SIGHUP. There are rough edges left: * Rename SignalServer with more meaningful name, say, WorkerProcess. * We should introduce global configuration object which is not affected by configuration reloading. It should hold cmdcfgs, argc, argv, and last worker PID. * We should close the listener file descriptor when some operation was failed after that.
This commit is contained in:
parent
a54cda22ab
commit
1214f9e23b
529
src/shrpx.cc
529
src/shrpx.cc
|
@ -130,22 +130,138 @@ constexpr auto ENV_ACCEPT_PREFIX = StringRef::from_lit("NGHTTPX_ACCEPT_");
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct InheritedAddr {
|
||||||
|
// IP address if TCP socket. Otherwise, UNIX domain socket path.
|
||||||
|
ImmutableString host;
|
||||||
|
uint16_t port;
|
||||||
|
// true if UNIX domain socket path
|
||||||
|
bool host_unix;
|
||||||
|
int fd;
|
||||||
|
bool used;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::random_device rd;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// This contains all options given in command-line. Make it static so
|
||||||
|
// that we can use it in reloading.
|
||||||
|
std::vector<std::pair<StringRef, StringRef>> cmdcfgs;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void signal_cb(struct ev_loop *loop, ev_signal *w, int revents);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
struct SignalServer {
|
struct SignalServer {
|
||||||
SignalServer() : ipc_fd{{-1, -1}}, worker_process_pid(-1) {}
|
SignalServer(struct ev_loop *loop, pid_t worker_pid, int ipc_fd)
|
||||||
~SignalServer() {
|
: loop(loop), worker_pid(worker_pid), ipc_fd(ipc_fd) {
|
||||||
if (ipc_fd[0] != -1) {
|
ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
|
||||||
close(ipc_fd[0]);
|
reopen_log_signalev.data = this;
|
||||||
|
ev_signal_start(loop, &reopen_log_signalev);
|
||||||
|
|
||||||
|
ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
|
||||||
|
exec_binary_signalev.data = this;
|
||||||
|
ev_signal_start(loop, &exec_binary_signalev);
|
||||||
|
|
||||||
|
ev_signal_init(&graceful_shutdown_signalev, signal_cb,
|
||||||
|
GRACEFUL_SHUTDOWN_SIGNAL);
|
||||||
|
graceful_shutdown_signalev.data = this;
|
||||||
|
ev_signal_start(loop, &graceful_shutdown_signalev);
|
||||||
|
|
||||||
|
ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
|
||||||
|
reload_signalev.data = this;
|
||||||
|
ev_signal_start(loop, &reload_signalev);
|
||||||
|
|
||||||
|
ev_child_init(&worker_process_childev, worker_process_child_cb, worker_pid,
|
||||||
|
0);
|
||||||
|
worker_process_childev.data = this;
|
||||||
|
ev_child_start(loop, &worker_process_childev);
|
||||||
}
|
}
|
||||||
if (ipc_fd[1] != -1) {
|
|
||||||
shutdown(ipc_fd[1], SHUT_WR);
|
~SignalServer() {
|
||||||
close(ipc_fd[1]);
|
shutdown_signal_watchers();
|
||||||
|
|
||||||
|
ev_child_stop(loop, &worker_process_childev);
|
||||||
|
|
||||||
|
if (ipc_fd != -1) {
|
||||||
|
shutdown(ipc_fd, SHUT_WR);
|
||||||
|
close(ipc_fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<int, 2> ipc_fd;
|
void shutdown_signal_watchers() {
|
||||||
pid_t worker_process_pid;
|
ev_signal_stop(loop, &reopen_log_signalev);
|
||||||
|
ev_signal_stop(loop, &exec_binary_signalev);
|
||||||
|
ev_signal_stop(loop, &graceful_shutdown_signalev);
|
||||||
|
ev_signal_stop(loop, &reload_signalev);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev_signal reopen_log_signalev;
|
||||||
|
ev_signal exec_binary_signalev;
|
||||||
|
ev_signal graceful_shutdown_signalev;
|
||||||
|
ev_signal reload_signalev;
|
||||||
|
ev_child worker_process_childev;
|
||||||
|
struct ev_loop *loop;
|
||||||
|
pid_t worker_pid;
|
||||||
|
int ipc_fd;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void reload_config(SignalServer *ssv);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::deque<std::unique_ptr<SignalServer>> signal_servers;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void signal_server_add(std::unique_ptr<SignalServer> ssv) {
|
||||||
|
signal_servers.push_back(std::move(ssv));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void signal_server_remove(const SignalServer *ssv) {
|
||||||
|
for (auto it = std::begin(signal_servers); it != std::end(signal_servers);
|
||||||
|
++it) {
|
||||||
|
auto &s = *it;
|
||||||
|
|
||||||
|
if (s.get() != ssv) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal_servers.erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void signal_server_remove_all() {
|
||||||
|
std::deque<std::unique_ptr<SignalServer>>().swap(signal_servers);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Send signal |signum| to all worker processes, and clears
|
||||||
|
// signal_servers.
|
||||||
|
void signal_server_kill(int signum) {
|
||||||
|
for (auto &s : signal_servers) {
|
||||||
|
if (s->worker_pid == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
kill(s->worker_pid, signum);
|
||||||
|
}
|
||||||
|
signal_servers.clear();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int chown_to_running_user(const char *path) {
|
int chown_to_running_user(const char *path) {
|
||||||
return chown(path, get_config()->uid, get_config()->gid);
|
return chown(path, get_config()->uid, get_config()->gid);
|
||||||
|
@ -346,8 +462,7 @@ void exec_binary(SignalServer *ssv) {
|
||||||
namespace {
|
namespace {
|
||||||
void ipc_send(SignalServer *ssv, uint8_t ipc_event) {
|
void ipc_send(SignalServer *ssv, uint8_t ipc_event) {
|
||||||
ssize_t nwrite;
|
ssize_t nwrite;
|
||||||
while ((nwrite = write(ssv->ipc_fd[1], &ipc_event, 1)) == -1 &&
|
while ((nwrite = write(ssv->ipc_fd, &ipc_event, 1)) == -1 && errno == EINTR)
|
||||||
errno == EINTR)
|
|
||||||
;
|
;
|
||||||
|
|
||||||
if (nwrite < 0) {
|
if (nwrite < 0) {
|
||||||
|
@ -377,7 +492,7 @@ void reopen_log(SignalServer *ssv) {
|
||||||
namespace {
|
namespace {
|
||||||
void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
|
void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
|
||||||
auto ssv = static_cast<SignalServer *>(w->data);
|
auto ssv = static_cast<SignalServer *>(w->data);
|
||||||
if (ssv->worker_process_pid == -1) {
|
if (ssv->worker_pid == -1) {
|
||||||
ev_break(loop);
|
ev_break(loop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -392,8 +507,11 @@ void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
|
||||||
case GRACEFUL_SHUTDOWN_SIGNAL:
|
case GRACEFUL_SHUTDOWN_SIGNAL:
|
||||||
ipc_send(ssv, SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
ipc_send(ssv, SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
||||||
return;
|
return;
|
||||||
|
case RELOAD_SIGNAL:
|
||||||
|
reload_config(ssv);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
kill(ssv->worker_process_pid, w->signum);
|
signal_server_kill(w->signum);
|
||||||
ev_break(loop);
|
ev_break(loop);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -402,24 +520,20 @@ void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
|
void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
|
||||||
|
auto ssv = static_cast<SignalServer *>(w->data);
|
||||||
|
|
||||||
log_chld(w->rpid, w->rstatus, "Worker process");
|
log_chld(w->rpid, w->rstatus, "Worker process");
|
||||||
|
|
||||||
ev_child_stop(loop, w);
|
auto pid = ssv->worker_pid;
|
||||||
|
|
||||||
|
signal_server_remove(ssv);
|
||||||
|
|
||||||
|
if (get_config()->last_worker_pid == pid) {
|
||||||
ev_break(loop);
|
ev_break(loop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct InheritedAddr {
|
|
||||||
// IP address if TCP socket. Otherwise, UNIX domain socket path.
|
|
||||||
ImmutableString host;
|
|
||||||
uint16_t port;
|
|
||||||
// true if UNIX domain socket path
|
|
||||||
bool host_unix;
|
|
||||||
int fd;
|
|
||||||
bool used;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int create_unix_domain_server_socket(UpstreamAddr &faddr,
|
int create_unix_domain_server_socket(UpstreamAddr &faddr,
|
||||||
std::vector<InheritedAddr> &iaddrs) {
|
std::vector<InheritedAddr> &iaddrs) {
|
||||||
|
@ -660,6 +774,70 @@ int create_tcp_server_socket(UpstreamAddr &faddr,
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
// Returns array of InheritedAddr constructed from |config|. This
|
||||||
|
// function is intended to be used when reloading configuration, and
|
||||||
|
// |config| is usually a current configuration.
|
||||||
|
std::vector<InheritedAddr>
|
||||||
|
get_inherited_addr_from_config(const Config *config) {
|
||||||
|
int rv;
|
||||||
|
std::vector<InheritedAddr> iaddrs;
|
||||||
|
|
||||||
|
auto &listenerconf = config->conn.listener;
|
||||||
|
|
||||||
|
for (auto &addr : listenerconf.addrs) {
|
||||||
|
iaddrs.emplace_back();
|
||||||
|
auto &iaddr = iaddrs.back();
|
||||||
|
|
||||||
|
if (addr.host_unix) {
|
||||||
|
iaddr.host = addr.host;
|
||||||
|
iaddr.host_unix = true;
|
||||||
|
iaddr.fd = addr.fd;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
iaddr.port = addr.port;
|
||||||
|
iaddr.fd = addr.fd;
|
||||||
|
|
||||||
|
// We have to getsockname/getnameinfo for fd, since we may have
|
||||||
|
// '*' appear in addr.host, which makes comparison against "real"
|
||||||
|
// address fail.
|
||||||
|
|
||||||
|
sockaddr_union su;
|
||||||
|
socklen_t salen = sizeof(su);
|
||||||
|
|
||||||
|
// We already added entry to iaddrs. Even if we got errors, we
|
||||||
|
// don't remove it. This is required because we have to close the
|
||||||
|
// socket if it is not reused. The empty host name usually does
|
||||||
|
// not match anything.
|
||||||
|
|
||||||
|
if (getsockname(addr.fd, &su.sa, &salen) != 0) {
|
||||||
|
auto error = errno;
|
||||||
|
LOG(WARN) << "getsockname() syscall failed (fd=" << addr.fd
|
||||||
|
<< "): " << strerror(error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<char, NI_MAXHOST> host;
|
||||||
|
rv = getnameinfo(&su.sa, salen, host.data(), host.size(), nullptr, 0,
|
||||||
|
NI_NUMERICHOST);
|
||||||
|
if (rv != 0) {
|
||||||
|
LOG(WARN) << "getnameinfo() failed (fd=" << addr.fd
|
||||||
|
<< "): " << gai_strerror(rv);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
iaddr.host = host.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
return iaddrs;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Returns array of InheritedAddr constructed from environment
|
||||||
|
// variables. This function handles the old environment variable
|
||||||
|
// names used in 1.7.0 or earlier.
|
||||||
std::vector<InheritedAddr> get_inherited_addr_from_env() {
|
std::vector<InheritedAddr> get_inherited_addr_from_env() {
|
||||||
int rv;
|
int rv;
|
||||||
std::vector<InheritedAddr> iaddrs;
|
std::vector<InheritedAddr> iaddrs;
|
||||||
|
@ -808,7 +986,8 @@ std::vector<InheritedAddr> get_inherited_addr_from_env() {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void closeUnusedInheritedAddr(const std::vector<InheritedAddr> &iaddrs) {
|
// Closes all sockets which are not reused.
|
||||||
|
void close_unused_inherited_addr(const std::vector<InheritedAddr> &iaddrs) {
|
||||||
for (auto &ia : iaddrs) {
|
for (auto &ia : iaddrs) {
|
||||||
if (ia.used) {
|
if (ia.used) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -820,8 +999,8 @@ void closeUnusedInheritedAddr(const std::vector<InheritedAddr> &iaddrs) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int create_acceptor_socket(std::vector<InheritedAddr> &iaddrs) {
|
int create_acceptor_socket(Config *config, std::vector<InheritedAddr> &iaddrs) {
|
||||||
auto &listenerconf = mod_config()->conn.listener;
|
auto &listenerconf = config->conn.listener;
|
||||||
|
|
||||||
for (auto &addr : listenerconf.addrs) {
|
for (auto &addr : listenerconf.addrs) {
|
||||||
if (addr.host_unix) {
|
if (addr.host_unix) {
|
||||||
|
@ -829,7 +1008,7 @@ int create_acceptor_socket(std::vector<InheritedAddr> &iaddrs) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_config()->uid != 0) {
|
if (config->uid != 0) {
|
||||||
// fd is not associated to inode, so we cannot use fchown(2)
|
// fd is not associated to inode, so we cannot use fchown(2)
|
||||||
// here. https://lkml.org/lkml/2004/11/1/84
|
// here. https://lkml.org/lkml/2004/11/1/84
|
||||||
if (chown_to_running_user(addr.host.c_str()) == -1) {
|
if (chown_to_running_user(addr.host.c_str()) == -1) {
|
||||||
|
@ -861,15 +1040,54 @@ int call_daemon() {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
pid_t fork_worker_process(SignalServer *ssv) {
|
// Opens IPC socket used to communicate with worker proess. The
|
||||||
|
// communication is unidirectional; that is main process sends
|
||||||
|
// messages to the worker process. On success, ipc_fd[0] is for
|
||||||
|
// reading, and ipc_fd[1] for writing, just like pipe(2).
|
||||||
|
int create_ipc_socket(std::array<int, 2> &ipc_fd) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = pipe(ipc_fd.data());
|
||||||
|
if (rv == -1) {
|
||||||
|
auto error = errno;
|
||||||
|
LOG(WARN) << "Failed to create pipe to communicate worker process: "
|
||||||
|
<< strerror(error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
auto fd = ipc_fd[i];
|
||||||
|
util::make_socket_nonblocking(fd);
|
||||||
|
util::make_socket_closeonexec(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Creates worker process, and returns PID of worker process. On
|
||||||
|
// success, file descriptor for IPC (send only) is assigned to
|
||||||
|
// |main_ipc_fd|.
|
||||||
|
pid_t fork_worker_process(int &main_ipc_fd) {
|
||||||
int rv;
|
int rv;
|
||||||
sigset_t oldset;
|
sigset_t oldset;
|
||||||
|
|
||||||
|
std::array<int, 2> ipc_fd;
|
||||||
|
|
||||||
|
rv = create_ipc_socket(ipc_fd);
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
rv = shrpx_signal_block_all(&oldset);
|
rv = shrpx_signal_block_all(&oldset);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
LOG(ERROR) << "Blocking all signals failed: " << strerror(error);
|
LOG(ERROR) << "Blocking all signals failed: " << strerror(error);
|
||||||
|
|
||||||
|
close(ipc_fd[0]);
|
||||||
|
close(ipc_fd[1]);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,6 +1096,10 @@ pid_t fork_worker_process(SignalServer *ssv) {
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
ev_loop_fork(EV_DEFAULT);
|
ev_loop_fork(EV_DEFAULT);
|
||||||
|
|
||||||
|
// Remove all SignalServers to stop any registered watcher on
|
||||||
|
// default loop.
|
||||||
|
signal_server_remove_all();
|
||||||
|
|
||||||
shrpx_signal_set_worker_proc_ign_handler();
|
shrpx_signal_set_worker_proc_ign_handler();
|
||||||
|
|
||||||
rv = shrpx_signal_unblock_all();
|
rv = shrpx_signal_unblock_all();
|
||||||
|
@ -888,8 +1110,8 @@ pid_t fork_worker_process(SignalServer *ssv) {
|
||||||
_Exit(EXIT_FAILURE);
|
_Exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
close(ssv->ipc_fd[1]);
|
close(ipc_fd[1]);
|
||||||
WorkerProcessConfig wpconf{ssv->ipc_fd[0]};
|
WorkerProcessConfig wpconf{ipc_fd[0]};
|
||||||
rv = worker_process_event_loop(&wpconf);
|
rv = worker_process_event_loop(&wpconf);
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
LOG(FATAL) << "Worker process returned error";
|
LOG(FATAL) << "Worker process returned error";
|
||||||
|
@ -918,10 +1140,15 @@ pid_t fork_worker_process(SignalServer *ssv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
|
close(ipc_fd[0]);
|
||||||
|
close(ipc_fd[1]);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(ssv->ipc_fd[0]);
|
close(ipc_fd[0]);
|
||||||
|
|
||||||
|
main_ipc_fd = ipc_fd[1];
|
||||||
|
|
||||||
LOG(NOTICE) << "Worker process [" << pid << "] spawned";
|
LOG(NOTICE) << "Worker process [" << pid << "] spawned";
|
||||||
|
|
||||||
|
@ -931,8 +1158,6 @@ pid_t fork_worker_process(SignalServer *ssv) {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int event_loop() {
|
int event_loop() {
|
||||||
int rv;
|
|
||||||
|
|
||||||
shrpx_signal_set_master_proc_ign_handler();
|
shrpx_signal_set_master_proc_ign_handler();
|
||||||
|
|
||||||
if (get_config()->daemon) {
|
if (get_config()->daemon) {
|
||||||
|
@ -950,55 +1175,27 @@ int event_loop() {
|
||||||
redirect_stderr_to_errorlog();
|
redirect_stderr_to_errorlog();
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalServer ssv;
|
|
||||||
|
|
||||||
rv = pipe(ssv.ipc_fd.data());
|
|
||||||
if (rv == -1) {
|
|
||||||
auto error = errno;
|
|
||||||
LOG(WARN) << "Failed to create pipe to communicate worker process: "
|
|
||||||
<< strerror(error);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; ++i) {
|
|
||||||
auto fd = ssv.ipc_fd[i];
|
|
||||||
util::make_socket_nonblocking(fd);
|
|
||||||
util::make_socket_closeonexec(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iaddrs = get_inherited_addr_from_env();
|
auto iaddrs = get_inherited_addr_from_env();
|
||||||
|
|
||||||
if (create_acceptor_socket(iaddrs) != 0) {
|
if (create_acceptor_socket(mod_config(), iaddrs) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
closeUnusedInheritedAddr(iaddrs);
|
close_unused_inherited_addr(iaddrs);
|
||||||
|
|
||||||
auto loop = ev_default_loop(get_config()->ev_loop_flags);
|
auto loop = ev_default_loop(get_config()->ev_loop_flags);
|
||||||
|
|
||||||
auto pid = fork_worker_process(&ssv);
|
int ipc_fd;
|
||||||
|
|
||||||
|
auto pid = fork_worker_process(ipc_fd);
|
||||||
|
|
||||||
if (pid == -1) {
|
if (pid == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssv.worker_process_pid = pid;
|
signal_server_add(make_unique<SignalServer>(loop, pid, ipc_fd));
|
||||||
|
|
||||||
constexpr auto signals = std::array<int, 3>{
|
mod_config()->last_worker_pid = pid;
|
||||||
{REOPEN_LOG_SIGNAL, EXEC_BINARY_SIGNAL, GRACEFUL_SHUTDOWN_SIGNAL}};
|
|
||||||
auto sigevs = std::array<ev_signal, signals.size()>();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < signals.size(); ++i) {
|
|
||||||
auto sigev = &sigevs[i];
|
|
||||||
ev_signal_init(sigev, signal_cb, signals[i]);
|
|
||||||
sigev->data = &ssv;
|
|
||||||
ev_signal_start(loop, sigev);
|
|
||||||
}
|
|
||||||
|
|
||||||
ev_child worker_process_childev;
|
|
||||||
ev_child_init(&worker_process_childev, worker_process_child_cb, pid, 0);
|
|
||||||
worker_process_childev.data = nullptr;
|
|
||||||
ev_child_start(loop, &worker_process_childev);
|
|
||||||
|
|
||||||
// Write PID file when we are ready to accept connection from peer.
|
// Write PID file when we are ready to accept connection from peer.
|
||||||
// This makes easier to write restart script for nghttpx. Because
|
// This makes easier to write restart script for nghttpx. Because
|
||||||
|
@ -1043,18 +1240,19 @@ constexpr auto DEFAULT_ACCESSLOG_FORMAT = StringRef::from_lit(
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void fill_default_config() {
|
void fill_default_config(Config *config) {
|
||||||
*mod_config() = {};
|
*config = {};
|
||||||
|
|
||||||
mod_config()->num_worker = 1;
|
config->num_worker = 1;
|
||||||
mod_config()->conf_path = "/etc/nghttpx/nghttpx.conf";
|
config->conf_path = "/etc/nghttpx/nghttpx.conf";
|
||||||
mod_config()->pid = getpid();
|
config->pid = getpid();
|
||||||
|
config->last_worker_pid = -1;
|
||||||
|
|
||||||
if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
|
if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
|
||||||
mod_config()->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE;
|
config->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &tlsconf = mod_config()->tls;
|
auto &tlsconf = config->tls;
|
||||||
{
|
{
|
||||||
auto &ticketconf = tlsconf.ticket;
|
auto &ticketconf = tlsconf.ticket;
|
||||||
{
|
{
|
||||||
|
@ -1089,7 +1287,7 @@ void fill_default_config() {
|
||||||
|
|
||||||
tlsconf.session_timeout = std::chrono::hours(12);
|
tlsconf.session_timeout = std::chrono::hours(12);
|
||||||
|
|
||||||
auto &httpconf = mod_config()->http;
|
auto &httpconf = config->http;
|
||||||
httpconf.server_name =
|
httpconf.server_name =
|
||||||
StringRef::from_lit("nghttpx nghttp2/" NGHTTP2_VERSION);
|
StringRef::from_lit("nghttpx nghttp2/" NGHTTP2_VERSION);
|
||||||
httpconf.no_host_rewrite = true;
|
httpconf.no_host_rewrite = true;
|
||||||
|
@ -1098,7 +1296,7 @@ void fill_default_config() {
|
||||||
httpconf.response_header_field_buffer = 64_k;
|
httpconf.response_header_field_buffer = 64_k;
|
||||||
httpconf.max_response_header_fields = 500;
|
httpconf.max_response_header_fields = 500;
|
||||||
|
|
||||||
auto &http2conf = mod_config()->http2;
|
auto &http2conf = config->http2;
|
||||||
{
|
{
|
||||||
auto &upstreamconf = http2conf.upstream;
|
auto &upstreamconf = http2conf.upstream;
|
||||||
|
|
||||||
|
@ -1143,7 +1341,7 @@ void fill_default_config() {
|
||||||
nghttp2_option_set_peer_max_concurrent_streams(downstreamconf.option, 100);
|
nghttp2_option_set_peer_max_concurrent_streams(downstreamconf.option, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &loggingconf = mod_config()->logging;
|
auto &loggingconf = config->logging;
|
||||||
{
|
{
|
||||||
auto &accessconf = loggingconf.access;
|
auto &accessconf = loggingconf.access;
|
||||||
accessconf.format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT);
|
accessconf.format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT);
|
||||||
|
@ -1154,7 +1352,7 @@ void fill_default_config() {
|
||||||
|
|
||||||
loggingconf.syslog_facility = LOG_DAEMON;
|
loggingconf.syslog_facility = LOG_DAEMON;
|
||||||
|
|
||||||
auto &connconf = mod_config()->conn;
|
auto &connconf = config->conn;
|
||||||
{
|
{
|
||||||
auto &listenerconf = connconf.listener;
|
auto &listenerconf = connconf.listener;
|
||||||
{
|
{
|
||||||
|
@ -1198,7 +1396,7 @@ void fill_default_config() {
|
||||||
downstreamconf.family = AF_UNSPEC;
|
downstreamconf.family = AF_UNSPEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &apiconf = mod_config()->api;
|
auto &apiconf = config->api;
|
||||||
apiconf.max_request_body = 16_k;
|
apiconf.max_request_body = 16_k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2038,23 +2236,17 @@ Misc:
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void process_options(int argc, char **argv,
|
int process_options(Config *config,
|
||||||
std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
|
std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
|
||||||
if (conf_exists(get_config()->conf_path.c_str())) {
|
if (conf_exists(config->conf_path.c_str())) {
|
||||||
std::set<StringRef> include_set;
|
std::set<StringRef> include_set;
|
||||||
if (load_config(get_config()->conf_path.c_str(), include_set) == -1) {
|
if (load_config(config, config->conf_path.c_str(), include_set) == -1) {
|
||||||
LOG(FATAL) << "Failed to load configuration from "
|
LOG(FATAL) << "Failed to load configuration from " << config->conf_path;
|
||||||
<< get_config()->conf_path;
|
return -1;
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
assert(include_set.empty());
|
assert(include_set.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc - optind >= 2) {
|
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, StringRef{argv[optind++]});
|
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, StringRef{argv[optind++]});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reopen log files using configurations in file
|
// Reopen log files using configurations in file
|
||||||
reopen_log_files();
|
reopen_log_files();
|
||||||
|
|
||||||
|
@ -2062,16 +2254,16 @@ void process_options(int argc, char **argv,
|
||||||
std::set<StringRef> include_set;
|
std::set<StringRef> include_set;
|
||||||
|
|
||||||
for (auto &p : cmdcfgs) {
|
for (auto &p : cmdcfgs) {
|
||||||
if (parse_config(mod_config(), p.first, p.second, include_set) == -1) {
|
if (parse_config(config, p.first, p.second, include_set) == -1) {
|
||||||
LOG(FATAL) << "Failed to parse command-line argument.";
|
LOG(FATAL) << "Failed to parse command-line argument.";
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(include_set.empty());
|
assert(include_set.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &loggingconf = get_config()->logging;
|
auto &loggingconf = config->logging;
|
||||||
|
|
||||||
if (loggingconf.access.syslog || loggingconf.error.syslog) {
|
if (loggingconf.access.syslog || loggingconf.error.syslog) {
|
||||||
openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
|
openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
|
||||||
|
@ -2080,29 +2272,27 @@ void process_options(int argc, char **argv,
|
||||||
|
|
||||||
if (reopen_log_files() != 0) {
|
if (reopen_log_files() != 0) {
|
||||||
LOG(FATAL) << "Failed to open log file";
|
LOG(FATAL) << "Failed to open log file";
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect_stderr_to_errorlog();
|
redirect_stderr_to_errorlog();
|
||||||
|
|
||||||
if (get_config()->uid != 0) {
|
if (config->uid != 0) {
|
||||||
if (log_config()->accesslog_fd != -1 &&
|
if (log_config()->accesslog_fd != -1 &&
|
||||||
fchown(log_config()->accesslog_fd, get_config()->uid,
|
fchown(log_config()->accesslog_fd, config->uid, config->gid) == -1) {
|
||||||
get_config()->gid) == -1) {
|
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
LOG(WARN) << "Changing owner of access log file failed: "
|
LOG(WARN) << "Changing owner of access log file failed: "
|
||||||
<< strerror(error);
|
<< strerror(error);
|
||||||
}
|
}
|
||||||
if (log_config()->errorlog_fd != -1 &&
|
if (log_config()->errorlog_fd != -1 &&
|
||||||
fchown(log_config()->errorlog_fd, get_config()->uid,
|
fchown(log_config()->errorlog_fd, config->uid, config->gid) == -1) {
|
||||||
get_config()->gid) == -1) {
|
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
LOG(WARN) << "Changing owner of error log file failed: "
|
LOG(WARN) << "Changing owner of error log file failed: "
|
||||||
<< strerror(error);
|
<< strerror(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &http2conf = mod_config()->http2;
|
auto &http2conf = config->http2;
|
||||||
{
|
{
|
||||||
auto &dumpconf = http2conf.upstream.debug.dump;
|
auto &dumpconf = http2conf.upstream.debug.dump;
|
||||||
|
|
||||||
|
@ -2113,12 +2303,12 @@ void process_options(int argc, char **argv,
|
||||||
if (f == nullptr) {
|
if (f == nullptr) {
|
||||||
LOG(FATAL) << "Failed to open http2 upstream request header file: "
|
LOG(FATAL) << "Failed to open http2 upstream request header file: "
|
||||||
<< path;
|
<< path;
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dumpconf.request_header = f;
|
dumpconf.request_header = f;
|
||||||
|
|
||||||
if (get_config()->uid != 0) {
|
if (config->uid != 0) {
|
||||||
if (chown_to_running_user(path) == -1) {
|
if (chown_to_running_user(path) == -1) {
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
LOG(WARN) << "Changing owner of http2 upstream request header file "
|
LOG(WARN) << "Changing owner of http2 upstream request header file "
|
||||||
|
@ -2134,12 +2324,12 @@ void process_options(int argc, char **argv,
|
||||||
if (f == nullptr) {
|
if (f == nullptr) {
|
||||||
LOG(FATAL) << "Failed to open http2 upstream response header file: "
|
LOG(FATAL) << "Failed to open http2 upstream response header file: "
|
||||||
<< path;
|
<< path;
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
dumpconf.response_header = f;
|
dumpconf.response_header = f;
|
||||||
|
|
||||||
if (get_config()->uid != 0) {
|
if (config->uid != 0) {
|
||||||
if (chown_to_running_user(path) == -1) {
|
if (chown_to_running_user(path) == -1) {
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
LOG(WARN) << "Changing owner of http2 upstream response header file"
|
LOG(WARN) << "Changing owner of http2 upstream response header file"
|
||||||
|
@ -2149,7 +2339,7 @@ void process_options(int argc, char **argv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &tlsconf = mod_config()->tls;
|
auto &tlsconf = config->tls;
|
||||||
|
|
||||||
if (tlsconf.npn_list.empty()) {
|
if (tlsconf.npn_list.empty()) {
|
||||||
tlsconf.npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST);
|
tlsconf.npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST);
|
||||||
|
@ -2165,8 +2355,8 @@ void process_options(int argc, char **argv,
|
||||||
|
|
||||||
tlsconf.bio_method = create_bio_method();
|
tlsconf.bio_method = create_bio_method();
|
||||||
|
|
||||||
auto &listenerconf = mod_config()->conn.listener;
|
auto &listenerconf = config->conn.listener;
|
||||||
auto &upstreamconf = mod_config()->conn.upstream;
|
auto &upstreamconf = config->conn.upstream;
|
||||||
|
|
||||||
if (listenerconf.addrs.empty()) {
|
if (listenerconf.addrs.empty()) {
|
||||||
UpstreamAddr addr{};
|
UpstreamAddr addr{};
|
||||||
|
@ -2187,7 +2377,7 @@ void process_options(int argc, char **argv,
|
||||||
(tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
|
(tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
|
||||||
print_usage(std::cerr);
|
print_usage(std::cerr);
|
||||||
LOG(FATAL) << "Too few arguments";
|
LOG(FATAL) << "Too few arguments";
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssl::upstream_tls_enabled() && !tlsconf.ocsp.disabled) {
|
if (ssl::upstream_tls_enabled() && !tlsconf.ocsp.disabled) {
|
||||||
|
@ -2200,18 +2390,18 @@ void process_options(int argc, char **argv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configure_downstream_group(mod_config(), get_config()->http2_proxy, false,
|
if (configure_downstream_group(config, config->http2_proxy, false, tlsconf) !=
|
||||||
tlsconf) != 0) {
|
0) {
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &proxy = mod_config()->downstream_http_proxy;
|
auto &proxy = config->downstream_http_proxy;
|
||||||
if (!proxy.host.empty()) {
|
if (!proxy.host.empty()) {
|
||||||
auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port);
|
auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port);
|
||||||
if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port,
|
if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port,
|
||||||
AF_UNSPEC) == -1) {
|
AF_UNSPEC) == -1) {
|
||||||
LOG(FATAL) << "Resolving backend HTTP proxy address failed: " << hostport;
|
LOG(FATAL) << "Resolving backend HTTP proxy address failed: " << hostport;
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
LOG(NOTICE) << "Backend HTTP proxy address: " << hostport << " -> "
|
LOG(NOTICE) << "Backend HTTP proxy address: " << hostport << " -> "
|
||||||
<< util::to_numeric_addr(&proxy.addr);
|
<< util::to_numeric_addr(&proxy.addr);
|
||||||
|
@ -2227,7 +2417,7 @@ void process_options(int argc, char **argv,
|
||||||
LOG(FATAL)
|
LOG(FATAL)
|
||||||
<< "Resolving memcached address for TLS session cache failed: "
|
<< "Resolving memcached address for TLS session cache failed: "
|
||||||
<< hostport;
|
<< hostport;
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
LOG(NOTICE) << "Memcached address for TLS session cache: " << hostport
|
LOG(NOTICE) << "Memcached address for TLS session cache: " << hostport
|
||||||
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
||||||
|
@ -2247,7 +2437,7 @@ void process_options(int argc, char **argv,
|
||||||
memcachedconf.port, memcachedconf.family) == -1) {
|
memcachedconf.port, memcachedconf.family) == -1) {
|
||||||
LOG(FATAL) << "Resolving memcached address for TLS ticket key failed: "
|
LOG(FATAL) << "Resolving memcached address for TLS ticket key failed: "
|
||||||
<< hostport;
|
<< hostport;
|
||||||
exit(EXIT_FAILURE);
|
return -1;
|
||||||
}
|
}
|
||||||
LOG(NOTICE) << "Memcached address for TLS ticket key: " << hostport
|
LOG(NOTICE) << "Memcached address for TLS ticket key: " << hostport
|
||||||
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
||||||
|
@ -2258,27 +2448,26 @@ void process_options(int argc, char **argv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_config()->rlimit_nofile) {
|
if (config->rlimit_nofile) {
|
||||||
struct rlimit lim = {static_cast<rlim_t>(get_config()->rlimit_nofile),
|
struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
|
||||||
static_cast<rlim_t>(get_config()->rlimit_nofile)};
|
static_cast<rlim_t>(config->rlimit_nofile)};
|
||||||
if (setrlimit(RLIMIT_NOFILE, &lim) != 0) {
|
if (setrlimit(RLIMIT_NOFILE, &lim) != 0) {
|
||||||
auto error = errno;
|
auto error = errno;
|
||||||
LOG(WARN) << "Setting rlimit-nofile failed: " << strerror(error);
|
LOG(WARN) << "Setting rlimit-nofile failed: " << strerror(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &fwdconf = mod_config()->http.forwarded;
|
auto &fwdconf = config->http.forwarded;
|
||||||
|
|
||||||
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED &&
|
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED &&
|
||||||
fwdconf.by_obfuscated.empty()) {
|
fwdconf.by_obfuscated.empty()) {
|
||||||
std::random_device rd;
|
|
||||||
std::mt19937 gen(rd());
|
std::mt19937 gen(rd());
|
||||||
auto &dst = fwdconf.by_obfuscated;
|
auto &dst = fwdconf.by_obfuscated;
|
||||||
dst = "_";
|
dst = "_";
|
||||||
dst += util::random_alpha_digit(gen, SHRPX_OBFUSCATED_NODE_LENGTH);
|
dst += util::random_alpha_digit(gen, SHRPX_OBFUSCATED_NODE_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_config()->http2.upstream.debug.frame_debug) {
|
if (config->http2.upstream.debug.frame_debug) {
|
||||||
// To make it sync to logging
|
// To make it sync to logging
|
||||||
set_output(stderr);
|
set_output(stderr);
|
||||||
if (isatty(fileno(stdout))) {
|
if (isatty(fileno(stdout))) {
|
||||||
|
@ -2287,13 +2476,88 @@ void process_options(int argc, char **argv,
|
||||||
reset_timer();
|
reset_timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
mod_config()->http2.upstream.callbacks = create_http2_upstream_callbacks();
|
config->http2.upstream.callbacks = create_http2_upstream_callbacks();
|
||||||
mod_config()->http2.downstream.callbacks =
|
config->http2.downstream.callbacks = create_http2_downstream_callbacks();
|
||||||
create_http2_downstream_callbacks();
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void reload_config(SignalServer *ssv) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
LOG(NOTICE) << "Reloading configuration";
|
||||||
|
|
||||||
|
auto cur_config = get_config();
|
||||||
|
auto new_config = make_unique<Config>();
|
||||||
|
|
||||||
|
fill_default_config(new_config.get());
|
||||||
|
|
||||||
|
new_config->conf_path = cur_config->conf_path;
|
||||||
|
new_config->argc = cur_config->argc;
|
||||||
|
new_config->argv = cur_config->argv;
|
||||||
|
new_config->original_argv = cur_config->original_argv;
|
||||||
|
new_config->cwd = cur_config->cwd;
|
||||||
|
|
||||||
|
rv = process_options(new_config.get(), cmdcfgs);
|
||||||
|
if (rv != 0) {
|
||||||
|
LOG(ERROR) << "Failed to process new configuration";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// daemon option is ignored here.
|
||||||
|
|
||||||
|
auto iaddrs = get_inherited_addr_from_config(cur_config);
|
||||||
|
|
||||||
|
if (create_acceptor_socket(new_config.get(), iaddrs) != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO may leak socket if we get error later in this function
|
||||||
|
|
||||||
|
// TODO loop is reused, and new_config->ev_loop_flags gets ignored
|
||||||
|
|
||||||
|
auto loop = ssv->loop;
|
||||||
|
|
||||||
|
int ipc_fd;
|
||||||
|
|
||||||
|
auto pid = fork_worker_process(ipc_fd);
|
||||||
|
|
||||||
|
if (pid == -1) {
|
||||||
|
LOG(ERROR) << "Failed to process new configuration";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_unused_inherited_addr(iaddrs);
|
||||||
|
|
||||||
|
// Send last worker process a graceful shutdown notice
|
||||||
|
auto &last_ssv = signal_servers.back();
|
||||||
|
ipc_send(last_ssv.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
||||||
|
// We no longer use signals for this worker.
|
||||||
|
last_ssv->shutdown_signal_watchers();
|
||||||
|
|
||||||
|
signal_server_add(make_unique<SignalServer>(loop, pid, ipc_fd));
|
||||||
|
|
||||||
|
new_config->last_worker_pid = pid;
|
||||||
|
|
||||||
|
auto old_config = replace_config(new_config.release());
|
||||||
|
|
||||||
|
assert(cur_config == old_config);
|
||||||
|
|
||||||
|
delete_config(old_config);
|
||||||
|
|
||||||
|
// Now we use new configuration
|
||||||
|
|
||||||
|
if (!get_config()->pid_file.empty()) {
|
||||||
|
save_pid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
nghttp2::ssl::libssl_init();
|
nghttp2::ssl::libssl_init();
|
||||||
|
|
||||||
#ifndef NOTHREADS
|
#ifndef NOTHREADS
|
||||||
|
@ -2302,7 +2566,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
Log::set_severity_level(NOTICE);
|
Log::set_severity_level(NOTICE);
|
||||||
create_config();
|
create_config();
|
||||||
fill_default_config();
|
fill_default_config(mod_config());
|
||||||
|
|
||||||
// make copy of stderr
|
// make copy of stderr
|
||||||
util::store_original_fds();
|
util::store_original_fds();
|
||||||
|
@ -2333,7 +2597,6 @@ int main(int argc, char **argv) {
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<StringRef, StringRef>> cmdcfgs;
|
|
||||||
while (1) {
|
while (1) {
|
||||||
static int flag = 0;
|
static int flag = 0;
|
||||||
static option long_options[] = {
|
static option long_options[] = {
|
||||||
|
@ -3121,7 +3384,15 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
process_options(argc, argv, cmdcfgs);
|
if (argc - optind >= 2) {
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, StringRef{argv[optind++]});
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, StringRef{argv[optind++]});
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = process_options(mod_config(), cmdcfgs);
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (event_loop() != 0) {
|
if (event_loop() != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -69,8 +69,44 @@ const Config *get_config() { return config; }
|
||||||
|
|
||||||
Config *mod_config() { return config; }
|
Config *mod_config() { return config; }
|
||||||
|
|
||||||
|
Config *replace_config(Config *new_config) {
|
||||||
|
std::swap(config, new_config);
|
||||||
|
return new_config;
|
||||||
|
}
|
||||||
|
|
||||||
void create_config() { config = new Config(); }
|
void create_config() { config = new Config(); }
|
||||||
|
|
||||||
|
void delete_config(Config *config) {
|
||||||
|
if (config == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &http2conf = config->http2;
|
||||||
|
|
||||||
|
auto &upstreamconf = http2conf.upstream;
|
||||||
|
|
||||||
|
nghttp2_option_del(upstreamconf.option);
|
||||||
|
nghttp2_option_del(upstreamconf.alt_mode_option);
|
||||||
|
nghttp2_session_callbacks_del(upstreamconf.callbacks);
|
||||||
|
|
||||||
|
auto &downstreamconf = http2conf.downstream;
|
||||||
|
|
||||||
|
nghttp2_option_del(downstreamconf.option);
|
||||||
|
nghttp2_session_callbacks_del(downstreamconf.callbacks);
|
||||||
|
|
||||||
|
auto &dumpconf = http2conf.upstream.debug.dump;
|
||||||
|
|
||||||
|
if (dumpconf.request_header) {
|
||||||
|
fclose(dumpconf.request_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dumpconf.response_header) {
|
||||||
|
fclose(dumpconf.response_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete config;
|
||||||
|
}
|
||||||
|
|
||||||
TicketKeys::~TicketKeys() {
|
TicketKeys::~TicketKeys() {
|
||||||
/* Erase keys from memory */
|
/* Erase keys from memory */
|
||||||
for (auto &key : keys) {
|
for (auto &key : keys) {
|
||||||
|
@ -2398,7 +2434,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
}
|
}
|
||||||
|
|
||||||
included_set.insert(optarg);
|
included_set.insert(optarg);
|
||||||
auto rv = load_config(optarg.c_str(), included_set);
|
auto rv = load_config(config, optarg.c_str(), included_set);
|
||||||
included_set.erase(optarg);
|
included_set.erase(optarg);
|
||||||
|
|
||||||
if (rv != 0) {
|
if (rv != 0) {
|
||||||
|
@ -2648,7 +2684,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int load_config(const char *filename, std::set<StringRef> &include_set) {
|
int load_config(Config *config, const char *filename,
|
||||||
|
std::set<StringRef> &include_set) {
|
||||||
std::ifstream in(filename, std::ios::binary);
|
std::ifstream in(filename, std::ios::binary);
|
||||||
if (!in) {
|
if (!in) {
|
||||||
LOG(ERROR) << "Could not open config file " << filename;
|
LOG(ERROR) << "Could not open config file " << filename;
|
||||||
|
@ -2669,7 +2706,7 @@ int load_config(const char *filename, std::set<StringRef> &include_set) {
|
||||||
}
|
}
|
||||||
*eq = '\0';
|
*eq = '\0';
|
||||||
|
|
||||||
if (parse_config(mod_config(), StringRef{std::begin(line), eq},
|
if (parse_config(config, StringRef{std::begin(line), eq},
|
||||||
StringRef{eq + 1, std::end(line)}, include_set) != 0) {
|
StringRef{eq + 1, std::end(line)}, include_set) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -727,6 +727,9 @@ struct Config {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
// With reloading feature, we may have multiple worker PIDs at the
|
||||||
|
// given moment. This field tracks the last worker PID.
|
||||||
|
pid_t last_worker_pid;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool daemon;
|
bool daemon;
|
||||||
bool http2_proxy;
|
bool http2_proxy;
|
||||||
|
@ -736,7 +739,11 @@ struct Config {
|
||||||
|
|
||||||
const Config *get_config();
|
const Config *get_config();
|
||||||
Config *mod_config();
|
Config *mod_config();
|
||||||
|
// Replaces the current config with given |new_config|. The old config is
|
||||||
|
// returned.
|
||||||
|
Config *replace_config(Config *new_config);
|
||||||
void create_config();
|
void create_config();
|
||||||
|
void delete_config(Config *config);
|
||||||
|
|
||||||
// generated by gennghttpxfun.py
|
// generated by gennghttpxfun.py
|
||||||
enum {
|
enum {
|
||||||
|
@ -890,10 +897,11 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg,
|
||||||
int parse_config(Config *config, int optid, const StringRef &opt,
|
int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
const StringRef &optarg, std::set<StringRef> &included_set);
|
const StringRef &optarg, std::set<StringRef> &included_set);
|
||||||
|
|
||||||
// Loads configurations from |filename| and stores them in statically
|
// Loads configurations from |filename| and stores them in |config|.
|
||||||
// allocated Config object. This function returns 0 if it succeeds, or
|
// This function returns 0 if it succeeds, or -1. See parse_config()
|
||||||
// -1. See parse_config() for |include_set|.
|
// for |include_set|.
|
||||||
int load_config(const char *filename, std::set<StringRef> &include_set);
|
int load_config(Config *config, const char *filename,
|
||||||
|
std::set<StringRef> &include_set);
|
||||||
|
|
||||||
// Parses header field in |optarg|. We expect header field is formed
|
// Parses header field in |optarg|. We expect header field is formed
|
||||||
// like "NAME: VALUE". We require that NAME is non empty string. ":"
|
// like "NAME: VALUE". We require that NAME is non empty string. ":"
|
||||||
|
|
|
@ -114,8 +114,9 @@ constexpr auto master_proc_ign_signals = std::array<int, 1>{{SIGPIPE}};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr auto worker_proc_ign_signals = std::array<int, 4>{
|
constexpr auto worker_proc_ign_signals =
|
||||||
{REOPEN_LOG_SIGNAL, EXEC_BINARY_SIGNAL, GRACEFUL_SHUTDOWN_SIGNAL, SIGPIPE}};
|
std::array<int, 5>{{REOPEN_LOG_SIGNAL, EXEC_BINARY_SIGNAL,
|
||||||
|
GRACEFUL_SHUTDOWN_SIGNAL, RELOAD_SIGNAL, SIGPIPE}};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void shrpx_signal_set_master_proc_ign_handler() {
|
void shrpx_signal_set_master_proc_ign_handler() {
|
||||||
|
|
|
@ -34,6 +34,7 @@ namespace shrpx {
|
||||||
constexpr int REOPEN_LOG_SIGNAL = SIGUSR1;
|
constexpr int REOPEN_LOG_SIGNAL = SIGUSR1;
|
||||||
constexpr int EXEC_BINARY_SIGNAL = SIGUSR2;
|
constexpr int EXEC_BINARY_SIGNAL = SIGUSR2;
|
||||||
constexpr int GRACEFUL_SHUTDOWN_SIGNAL = SIGQUIT;
|
constexpr int GRACEFUL_SHUTDOWN_SIGNAL = SIGQUIT;
|
||||||
|
constexpr int RELOAD_SIGNAL = SIGHUP;
|
||||||
|
|
||||||
// Blocks all signals. The previous signal mask is stored into
|
// Blocks all signals. The previous signal mask is stored into
|
||||||
// |oldset| if it is not nullptr. This function returns 0 if it
|
// |oldset| if it is not nullptr. This function returns 0 if it
|
||||||
|
|
Loading…
Reference in New Issue