|  | 
 | /* | 
 |  * 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_%ul%Z", name, pid); | 
 |     ngx_sprintf(ngx_processes[s].quit_event, "ngx_%s_quit_%ul%Z", name, pid); | 
 |     ngx_sprintf(ngx_processes[s].reopen_event, "ngx_%s_reopen_%ul%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 %Xul", | 
 |                       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; | 
 | } |