| |
| #include <ngx_config.h> |
| #include <ngx_core.h> |
| |
| |
| static void ngx_exec_proc(ngx_cycle_t *cycle, void *data); |
| |
| ngx_uint_t ngx_last_process; |
| ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; |
| |
| |
| ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, |
| ngx_spawn_proc_pt proc, void *data, |
| char *name, ngx_int_t respawn) |
| { |
| sigset_t set, oset; |
| ngx_pid_t pid; |
| |
| if (respawn < 0) { |
| sigemptyset(&set); |
| sigaddset(&set, SIGCHLD); |
| if (sigprocmask(SIG_BLOCK, &set, &oset) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, |
| "sigprocmask() failed while spawning %s", name); |
| return NGX_ERROR; |
| } |
| } |
| |
| pid = fork(); |
| |
| if (pid == -1) { |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, |
| "fork() failed while spawning \"%s\"", name); |
| } |
| |
| if (pid == -1 || pid == 0) { |
| if (sigprocmask(SIG_SETMASK, &oset, &set) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, |
| "sigprocmask() failed while spawning %s", name); |
| return NGX_ERROR; |
| } |
| } |
| |
| switch (pid) { |
| case -1: |
| return NGX_ERROR; |
| |
| case 0: |
| ngx_pid = ngx_getpid(); |
| proc(cycle, data); |
| break; |
| |
| default: |
| break; |
| } |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, |
| "spawn %s: " PID_T_FMT, name, pid); |
| |
| if (respawn >= 0) { |
| ngx_processes[respawn].pid = pid; |
| ngx_processes[respawn].exited = 0; |
| return pid; |
| } |
| |
| ngx_processes[ngx_last_process].pid = pid; |
| ngx_processes[ngx_last_process].proc = proc; |
| ngx_processes[ngx_last_process].data = data; |
| ngx_processes[ngx_last_process].name = name; |
| ngx_processes[ngx_last_process].respawn = |
| (respawn == NGX_PROCESS_RESPAWN) ? 1 : 0; |
| ngx_processes[ngx_last_process].detached = |
| (respawn == NGX_PROCESS_DETACHED) ? 1 : 0; |
| ngx_processes[ngx_last_process].exited = 0; |
| ngx_processes[ngx_last_process].exiting = 0; |
| ngx_last_process++; |
| |
| if (sigprocmask(SIG_SETMASK, &oset, &set) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, |
| "sigprocmask() failed while spawning %s", name); |
| return NGX_ERROR; |
| } |
| |
| return pid; |
| } |
| |
| |
| ngx_pid_t ngx_exec(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx) |
| { |
| return ngx_spawn_process(cycle, ngx_exec_proc, ctx, ctx->name, |
| NGX_PROCESS_DETACHED); |
| } |
| |
| |
| static void ngx_exec_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); |
| } |
| |
| |
| #if 0 |
| |
| void ngx_signal_processes(ngx_cycle_t *cycle) |
| { |
| ngx_uint_t i; |
| |
| for (i = 0; i < ngx_last_process; i++) { |
| |
| if (ngx_processes[i].signal0 == 0) { |
| continue; |
| } |
| |
| #if 0 |
| if (ngx_processes[i].exited) { |
| if (i != --ngx_last_process) { |
| ngx_processes[i--] = ngx_processes[ngx_last_process]; |
| } |
| continue; |
| } |
| #endif |
| |
| ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, |
| "kill (" PID_T_FMT ", %d)" , |
| ngx_processes[i].pid, ngx_processes[i].signal0); |
| |
| if (kill(ngx_processes[i].pid, ngx_processes[i].signal0) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, |
| "kill(%d, %d) failed", |
| ngx_processes[i].pid, ngx_processes[i].signal0); |
| continue; |
| } |
| |
| if (ngx_processes[i].signal0 != ngx_signal_value(NGX_REOPEN_SIGNAL)) { |
| ngx_processes[i].exiting = 1; |
| } |
| } |
| } |
| |
| #endif |
| |
| |
| void ngx_respawn_processes(ngx_cycle_t *cycle) |
| { |
| ngx_uint_t i; |
| |
| for (i = 0; i < ngx_last_process; i++) { |
| |
| if (ngx_processes[i].exiting || !ngx_processes[i].exited) { |
| continue; |
| } |
| |
| if (!ngx_processes[i].respawn) { |
| if (i != --ngx_last_process) { |
| ngx_processes[i--] = ngx_processes[ngx_last_process]; |
| } |
| continue; |
| } |
| |
| if (ngx_spawn_process(cycle, |
| ngx_processes[i].proc, ngx_processes[i].data, |
| ngx_processes[i].name, i) == NGX_ERROR) |
| { |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, |
| "can not respawn %s", ngx_processes[i].name); |
| } |
| } |
| } |
| |
| |
| void ngx_process_get_status() |
| { |
| int status; |
| char *process; |
| ngx_pid_t pid; |
| ngx_err_t err; |
| ngx_uint_t i, one; |
| struct timeval tv; |
| 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; |
| } |
| |
| ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, errno, |
| "waitpid() failed"); |
| return; |
| } |
| |
| one = 1; |
| 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 (i == ngx_last_process) { |
| process = "unknown process"; |
| } |
| |
| if (WTERMSIG(status)) { |
| ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, |
| "%s " PID_T_FMT " exited on signal %d%s", |
| process, pid, WTERMSIG(status), |
| WCOREDUMP(status) ? " (core dumped)" : ""); |
| |
| } else { |
| ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, |
| "%s " PID_T_FMT " 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 " PID_T_FMT |
| " exited with fatal code %d and could not respawn", |
| process, pid, WEXITSTATUS(status)); |
| ngx_processes[i].respawn = 0; |
| } |
| } |
| } |