summaryrefslogtreecommitdiffstats
path: root/usr.sbin/nginx/src/os/unix/ngx_process.c
diff options
context:
space:
mode:
authorrobert <robert@openbsd.org>2011-09-22 23:32:10 +0000
committerrobert <robert@openbsd.org>2011-09-22 23:32:10 +0000
commit40ffe171a7951d3019b3c8b488be87bfad405509 (patch)
tree7634bff211814a539a15e4e34db747e4d7b46006 /usr.sbin/nginx/src/os/unix/ngx_process.c
parentAlways install all manuals on all architectures. (diff)
downloadwireguard-openbsd-40ffe171a7951d3019b3c8b488be87bfad405509.tar.xz
wireguard-openbsd-40ffe171a7951d3019b3c8b488be87bfad405509.zip
import of nginx 1.0.6 with a bundled libpcre needed for pcre to work
properly. this is not yet linked to the build but we would like to work on it in tree to provide an apache replacement for base
Diffstat (limited to 'usr.sbin/nginx/src/os/unix/ngx_process.c')
-rw-r--r--usr.sbin/nginx/src/os/unix/ngx_process.c584
1 files changed, 584 insertions, 0 deletions
diff --git a/usr.sbin/nginx/src/os/unix/ngx_process.c b/usr.sbin/nginx/src/os/unix/ngx_process.c
new file mode 100644
index 00000000000..605558786f2
--- /dev/null
+++ b/usr.sbin/nginx/src/os/unix/ngx_process.c
@@ -0,0 +1,584 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_channel.h>
+
+
+typedef struct {
+ int signo;
+ char *signame;
+ char *name;
+ void (*handler)(int signo);
+} ngx_signal_t;
+
+
+
+static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);
+static void ngx_signal_handler(int signo);
+static void ngx_process_get_status(void);
+
+
+int ngx_argc;
+char **ngx_argv;
+char **ngx_os_argv;
+
+ngx_int_t ngx_process_slot;
+ngx_socket_t ngx_channel;
+ngx_int_t ngx_last_process;
+ngx_process_t ngx_processes[NGX_MAX_PROCESSES];
+
+
+ngx_signal_t signals[] = {
+ { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
+ "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
+ "reload",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_REOPEN_SIGNAL),
+ "SIG" ngx_value(NGX_REOPEN_SIGNAL),
+ "reopen",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
+ "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
+ "",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_TERMINATE_SIGNAL),
+ "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
+ "stop",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
+ "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
+ "quit",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
+ "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
+ "",
+ ngx_signal_handler },
+
+ { SIGALRM, "SIGALRM", "", ngx_signal_handler },
+
+ { SIGINT, "SIGINT", "", ngx_signal_handler },
+
+ { SIGIO, "SIGIO", "", ngx_signal_handler },
+
+ { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
+
+ { SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN },
+
+ { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },
+
+ { 0, NULL, "", NULL }
+};
+
+
+ngx_pid_t
+ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
+ char *name, ngx_int_t respawn)
+{
+ u_long on;
+ ngx_pid_t pid;
+ ngx_int_t s;
+
+ if (respawn >= 0) {
+ s = respawn;
+
+ } else {
+ for (s = 0; s < ngx_last_process; s++) {
+ if (ngx_processes[s].pid == -1) {
+ break;
+ }
+ }
+
+ if (s == NGX_MAX_PROCESSES) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "no more than %d processes can be spawned",
+ NGX_MAX_PROCESSES);
+ return NGX_INVALID_PID;
+ }
+ }
+
+
+ if (respawn != NGX_PROCESS_DETACHED) {
+
+ /* Solaris 9 still has no AF_LOCAL */
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "socketpair() failed while spawning \"%s\"", name);
+ return NGX_INVALID_PID;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "channel %d:%d",
+ ngx_processes[s].channel[0],
+ ngx_processes[s].channel[1]);
+
+ if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_nonblocking_n " failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_nonblocking_n " failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ on = 1;
+ if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "ioctl(FIOASYNC) failed while spawning \"%s\"", name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fcntl(F_SETOWN) failed while spawning \"%s\"", name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ ngx_channel = ngx_processes[s].channel[1];
+
+ } else {
+ ngx_processes[s].channel[0] = -1;
+ ngx_processes[s].channel[1] = -1;
+ }
+
+ ngx_process_slot = s;
+
+
+ pid = fork();
+
+ switch (pid) {
+
+ case -1:
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fork() failed while spawning \"%s\"", name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+
+ case 0:
+ ngx_pid = ngx_getpid();
+ proc(cycle, data);
+ break;
+
+ default:
+ break;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);
+
+ ngx_processes[s].pid = pid;
+ ngx_processes[s].exited = 0;
+
+ if (respawn >= 0) {
+ return pid;
+ }
+
+ ngx_processes[s].proc = proc;
+ ngx_processes[s].data = data;
+ ngx_processes[s].name = name;
+ ngx_processes[s].exiting = 0;
+
+ switch (respawn) {
+
+ case NGX_PROCESS_NORESPAWN:
+ ngx_processes[s].respawn = 0;
+ ngx_processes[s].just_spawn = 0;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_JUST_SPAWN:
+ ngx_processes[s].respawn = 0;
+ ngx_processes[s].just_spawn = 1;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_RESPAWN:
+ ngx_processes[s].respawn = 1;
+ ngx_processes[s].just_spawn = 0;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_JUST_RESPAWN:
+ ngx_processes[s].respawn = 1;
+ ngx_processes[s].just_spawn = 1;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_DETACHED:
+ ngx_processes[s].respawn = 0;
+ ngx_processes[s].just_spawn = 0;
+ ngx_processes[s].detached = 1;
+ break;
+ }
+
+ if (s == ngx_last_process) {
+ ngx_last_process++;
+ }
+
+ return pid;
+}
+
+
+ngx_pid_t
+ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
+{
+ return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,
+ NGX_PROCESS_DETACHED);
+}
+
+
+static void
+ngx_execute_proc(ngx_cycle_t *cycle, void *data)
+{
+ ngx_exec_ctx_t *ctx = data;
+
+ if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "execve() failed while executing %s \"%s\"",
+ ctx->name, ctx->path);
+ }
+
+ exit(1);
+}
+
+
+ngx_int_t
+ngx_init_signals(ngx_log_t *log)
+{
+ ngx_signal_t *sig;
+ struct sigaction sa;
+
+ for (sig = signals; sig->signo != 0; sig++) {
+ ngx_memzero(&sa, sizeof(struct sigaction));
+ sa.sa_handler = sig->handler;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig->signo, &sa, NULL) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "sigaction(%s) failed", sig->signame);
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_signal_handler(int signo)
+{
+ char *action;
+ ngx_int_t ignore;
+ ngx_err_t err;
+ ngx_signal_t *sig;
+
+ ignore = 0;
+
+ err = ngx_errno;
+
+ for (sig = signals; sig->signo != 0; sig++) {
+ if (sig->signo == signo) {
+ break;
+ }
+ }
+
+ ngx_time_sigsafe_update();
+
+ action = "";
+
+ switch (ngx_process) {
+
+ case NGX_PROCESS_MASTER:
+ case NGX_PROCESS_SINGLE:
+ switch (signo) {
+
+ case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+ ngx_quit = 1;
+ action = ", shutting down";
+ break;
+
+ case ngx_signal_value(NGX_TERMINATE_SIGNAL):
+ case SIGINT:
+ ngx_terminate = 1;
+ action = ", exiting";
+ break;
+
+ case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
+ ngx_noaccept = 1;
+ action = ", stop accepting connections";
+ break;
+
+ case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
+ ngx_reconfigure = 1;
+ action = ", reconfiguring";
+ break;
+
+ case ngx_signal_value(NGX_REOPEN_SIGNAL):
+ ngx_reopen = 1;
+ action = ", reopening logs";
+ break;
+
+ case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
+ if (getppid() > 1 || ngx_new_binary > 0) {
+
+ /*
+ * Ignore the signal in the new binary if its parent is
+ * not the init process, i.e. the old binary's process
+ * is still running. Or ignore the signal in the old binary's
+ * process if the new binary's process is already running.
+ */
+
+ action = ", ignoring";
+ ignore = 1;
+ break;
+ }
+
+ ngx_change_binary = 1;
+ action = ", changing binary";
+ break;
+
+ case SIGALRM:
+ ngx_sigalrm = 1;
+ break;
+
+ case SIGIO:
+ ngx_sigio = 1;
+ break;
+
+ case SIGCHLD:
+ ngx_reap = 1;
+ break;
+ }
+
+ break;
+
+ case NGX_PROCESS_WORKER:
+ case NGX_PROCESS_HELPER:
+ switch (signo) {
+
+ case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
+ ngx_debug_quit = 1;
+ case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+ ngx_quit = 1;
+ action = ", shutting down";
+ break;
+
+ case ngx_signal_value(NGX_TERMINATE_SIGNAL):
+ case SIGINT:
+ ngx_terminate = 1;
+ action = ", exiting";
+ break;
+
+ case ngx_signal_value(NGX_REOPEN_SIGNAL):
+ ngx_reopen = 1;
+ action = ", reopening logs";
+ break;
+
+ case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
+ case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
+ case SIGIO:
+ action = ", ignoring";
+ break;
+ }
+
+ break;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+ "signal %d (%s) received%s", signo, sig->signame, action);
+
+ if (ignore) {
+ ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,
+ "the changing binary signal is ignored: "
+ "you should shutdown or terminate "
+ "before either old or new binary's process");
+ }
+
+ if (signo == SIGCHLD) {
+ ngx_process_get_status();
+ }
+
+ ngx_set_errno(err);
+}
+
+
+static void
+ngx_process_get_status(void)
+{
+ int status;
+ char *process;
+ ngx_pid_t pid;
+ ngx_err_t err;
+ ngx_int_t i;
+ ngx_uint_t one;
+
+ one = 0;
+
+ for ( ;; ) {
+ pid = waitpid(-1, &status, WNOHANG);
+
+ if (pid == 0) {
+ return;
+ }
+
+ if (pid == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_EINTR) {
+ continue;
+ }
+
+ if (err == NGX_ECHILD && one) {
+ return;
+ }
+
+#if (NGX_SOLARIS || NGX_FREEBSD)
+
+ /*
+ * Solaris always calls the signal handler for each exited process
+ * despite waitpid() may be already called for this process.
+ *
+ * When several processes exit at the same time FreeBSD may
+ * erroneously call the signal handler for exited process
+ * despite waitpid() may be already called for this process.
+ */
+
+ if (err == NGX_ECHILD) {
+ ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, err,
+ "waitpid() failed");
+ return;
+ }
+
+#endif
+
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
+ "waitpid() failed");
+ return;
+ }
+
+
+ if (ngx_accept_mutex_ptr) {
+
+ /*
+ * unlock the accept mutex if the abnormally exited process
+ * held it
+ */
+
+ ngx_atomic_cmp_set(ngx_accept_mutex_ptr, pid, 0);
+ }
+
+
+ one = 1;
+ process = "unknown process";
+
+ for (i = 0; i < ngx_last_process; i++) {
+ if (ngx_processes[i].pid == pid) {
+ ngx_processes[i].status = status;
+ ngx_processes[i].exited = 1;
+ process = ngx_processes[i].name;
+ break;
+ }
+ }
+
+ if (WTERMSIG(status)) {
+#ifdef WCOREDUMP
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "%s %P exited on signal %d%s",
+ process, pid, WTERMSIG(status),
+ WCOREDUMP(status) ? " (core dumped)" : "");
+#else
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "%s %P exited on signal %d",
+ process, pid, WTERMSIG(status));
+#endif
+
+ } else {
+ ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+ "%s %P exited with code %d",
+ process, pid, WEXITSTATUS(status));
+ }
+
+ if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "%s %P exited with fatal code %d "
+ "and can not be respawn",
+ process, pid, WEXITSTATUS(status));
+ ngx_processes[i].respawn = 0;
+ }
+ }
+}
+
+
+void
+ngx_debug_point(void)
+{
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+ ngx_core_module);
+
+ switch (ccf->debug_points) {
+
+ case NGX_DEBUG_POINTS_STOP:
+ raise(SIGSTOP);
+ break;
+
+ case NGX_DEBUG_POINTS_ABORT:
+ ngx_abort();
+ }
+}
+
+
+ngx_int_t
+ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid)
+{
+ ngx_signal_t *sig;
+
+ for (sig = signals; sig->signo != 0; sig++) {
+ if (ngx_strcmp(name, sig->name) == 0) {
+ if (kill(pid, sig->signo) != -1) {
+ return 0;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "kill(%P, %d) failed", pid, sig->signo);
+ }
+ }
+
+ return 1;
+}