| /* | |
| * Copyright (C) Igor Sysoev | |
| * Copyright (C) Nginx, Inc. | |
| */ | |
| #include <ngx_config.h> | |
| #include <ngx_core.h> | |
| int ngx_argc; | |
| char **ngx_argv; | |
| char **ngx_os_argv; | |
| ngx_int_t ngx_last_process; | |
| ngx_process_t ngx_processes[NGX_MAX_PROCESSES]; | |
| ngx_pid_t | |
| ngx_spawn_process(ngx_cycle_t *cycle, char *name, ngx_int_t respawn) | |
| { | |
| u_long rc, n, code; | |
| ngx_int_t s; | |
| ngx_pid_t pid; | |
| ngx_exec_ctx_t ctx; | |
| HANDLE events[2]; | |
| char file[MAX_PATH + 1]; | |
| if (respawn >= 0) { | |
| s = respawn; | |
| } else { | |
| for (s = 0; s < ngx_last_process; s++) { | |
| if (ngx_processes[s].handle == NULL) { | |
| 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; | |
| } | |
| } | |
| n = GetModuleFileName(NULL, file, MAX_PATH); | |
| if (n == 0) { | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | |
| "GetModuleFileName() failed"); | |
| return NGX_INVALID_PID; | |
| } | |
| file[n] = '\0'; | |
| ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, | |
| "GetModuleFileName: \"%s\"", file); | |
| ctx.path = file; | |
| ctx.name = name; | |
| ctx.args = GetCommandLine(); | |
| ctx.argv = NULL; | |
| ctx.envp = NULL; | |
| pid = ngx_execute(cycle, &ctx); | |
| if (pid == NGX_INVALID_PID) { | |
| return pid; | |
| } | |
| ngx_memzero(&ngx_processes[s], sizeof(ngx_process_t)); | |
| ngx_processes[s].handle = ctx.child; | |
| ngx_processes[s].pid = pid; | |
| ngx_processes[s].name = name; | |
| ngx_sprintf(ngx_processes[s].term_event, "ngx_%s_term_%P%Z", name, pid); | |
| ngx_sprintf(ngx_processes[s].quit_event, "ngx_%s_quit_%P%Z", name, pid); | |
| ngx_sprintf(ngx_processes[s].reopen_event, "ngx_%s_reopen_%P%Z", | |
| name, pid); | |
| events[0] = ngx_master_process_event; | |
| events[1] = ctx.child; | |
| rc = WaitForMultipleObjects(2, events, 0, 5000); | |
| ngx_time_update(); | |
| ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, | |
| "WaitForMultipleObjects: %ul", rc); | |
| switch (rc) { | |
| case WAIT_OBJECT_0: | |
| ngx_processes[s].term = OpenEvent(EVENT_MODIFY_STATE, 0, | |
| (char *) ngx_processes[s].term_event); | |
| if (ngx_processes[s].term == NULL) { | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | |
| "OpenEvent(\"%s\") failed", | |
| ngx_processes[s].term_event); | |
| goto failed; | |
| } | |
| ngx_processes[s].quit = OpenEvent(EVENT_MODIFY_STATE, 0, | |
| (char *) ngx_processes[s].quit_event); | |
| if (ngx_processes[s].quit == NULL) { | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | |
| "OpenEvent(\"%s\") failed", | |
| ngx_processes[s].quit_event); | |
| goto failed; | |
| } | |
| ngx_processes[s].reopen = OpenEvent(EVENT_MODIFY_STATE, 0, | |
| (char *) ngx_processes[s].reopen_event); | |
| if (ngx_processes[s].reopen == NULL) { | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | |
| "OpenEvent(\"%s\") failed", | |
| ngx_processes[s].reopen_event); | |
| goto failed; | |
| } | |
| if (ResetEvent(ngx_master_process_event) == 0) { | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | |
| "ResetEvent(\"%s\") failed", | |
| ngx_master_process_event_name); | |
| goto failed; | |
| } | |
| break; | |
| case WAIT_OBJECT_0 + 1: | |
| if (GetExitCodeProcess(ctx.child, &code) == 0) { | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | |
| "GetExitCodeProcess(%P) failed", pid); | |
| } | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | |
| "%s process %P exited with code %Xl", | |
| name, pid, code); | |
| goto failed; | |
| case WAIT_TIMEOUT: | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, | |
| "the event \"%s\" was not signaled for 5s", | |
| ngx_master_process_event_name); | |
| goto failed; | |
| case WAIT_FAILED: | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | |
| "WaitForSingleObject(\"%s\") failed", | |
| ngx_master_process_event_name); | |
| goto failed; | |
| } | |
| if (respawn >= 0) { | |
| return pid; | |
| } | |
| switch (respawn) { | |
| case NGX_PROCESS_RESPAWN: | |
| ngx_processes[s].just_spawn = 0; | |
| break; | |
| case NGX_PROCESS_JUST_RESPAWN: | |
| ngx_processes[s].just_spawn = 1; | |
| break; | |
| } | |
| if (s == ngx_last_process) { | |
| ngx_last_process++; | |
| } | |
| return pid; | |
| failed: | |
| if (ngx_processes[s].reopen) { | |
| ngx_close_handle(ngx_processes[s].reopen); | |
| } | |
| if (ngx_processes[s].quit) { | |
| ngx_close_handle(ngx_processes[s].quit); | |
| } | |
| if (ngx_processes[s].term) { | |
| ngx_close_handle(ngx_processes[s].term); | |
| } | |
| TerminateProcess(ngx_processes[s].handle, 2); | |
| if (ngx_processes[s].handle) { | |
| ngx_close_handle(ngx_processes[s].handle); | |
| ngx_processes[s].handle = NULL; | |
| } | |
| return NGX_INVALID_PID; | |
| } | |
| ngx_pid_t | |
| ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx) | |
| { | |
| STARTUPINFO si; | |
| PROCESS_INFORMATION pi; | |
| ngx_memzero(&si, sizeof(STARTUPINFO)); | |
| si.cb = sizeof(STARTUPINFO); | |
| ngx_memzero(&pi, sizeof(PROCESS_INFORMATION)); | |
| if (CreateProcess(ctx->path, ctx->args, | |
| NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &si, &pi) | |
| == 0) | |
| { | |
| ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno, | |
| "CreateProcess(\"%s\") failed", ngx_argv[0]); | |
| return 0; | |
| } | |
| ctx->child = pi.hProcess; | |
| if (CloseHandle(pi.hThread) == 0) { | |
| ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, | |
| "CloseHandle(pi.hThread) failed"); | |
| } | |
| ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, | |
| "start %s process %P", ctx->name, pi.dwProcessId); | |
| return pi.dwProcessId; | |
| } |