| |
| /* |
| * Copyright (C) Igor Sysoev |
| */ |
| |
| |
| #include <ngx_config.h> |
| #include <ngx_core.h> |
| #include <ngx_event.h> |
| #include <ngx_channel.h> |
| |
| |
| static void ngx_execute_proc(ngx_cycle_t *cycle, void *data); |
| |
| |
| 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_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_ERROR; |
| } |
| } |
| |
| |
| 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_ERROR; |
| } |
| |
| 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_ERROR; |
| } |
| |
| 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_ERROR; |
| } |
| |
| 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_ERROR; |
| } |
| |
| 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_ERROR; |
| } |
| |
| 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_ERROR; |
| } |
| |
| 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_ERROR; |
| } |
| |
| 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_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: %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_RESPAWN: |
| ngx_processes[s].respawn = 1; |
| ngx_processes[s].just_respawn = 0; |
| ngx_processes[s].detached = 0; |
| break; |
| |
| case NGX_PROCESS_JUST_RESPAWN: |
| ngx_processes[s].respawn = 1; |
| ngx_processes[s].just_respawn = 1; |
| ngx_processes[s].detached = 0; |
| break; |
| |
| case NGX_PROCESS_DETACHED: |
| ngx_processes[s].respawn = 0; |
| ngx_processes[s].just_respawn = 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); |
| } |
| |
| |
| void ngx_process_get_status() |
| { |
| int status; |
| char *process; |
| ngx_pid_t pid; |
| ngx_err_t err; |
| ngx_int_t i; |
| ngx_uint_t 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; |
| } |
| |
| #if (NGX_SOLARIS) |
| |
| /* |
| * Solaris always calls the signal handler for each exited process |
| * despite waitpid() may be already called for this process |
| */ |
| |
| if (err == NGX_ECHILD) { |
| ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, errno, |
| "waitpid() failed"); |
| } |
| |
| #endif |
| |
| ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, errno, |
| "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)) { |
| 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_INFO, 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 could not respawn", |
| process, pid, WEXITSTATUS(status)); |
| ngx_processes[i].respawn = 0; |
| } |
| } |
| } |
| |
| |
| void ngx_debug_point() |
| { |
| 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: |
| abort(); |
| } |
| } |