diff --git a/src/core/nginx.c b/src/core/nginx.c index 0deb27b7f..11e5994f5 100644 --- a/src/core/nginx.c +++ b/src/core/nginx.c @@ -183,6 +183,7 @@ ngx_module_t ngx_core_module = { static ngx_uint_t ngx_show_help; static ngx_uint_t ngx_show_version; static ngx_uint_t ngx_show_configure; +static ngx_uint_t ngx_service = 0; static u_char *ngx_prefix; static u_char *ngx_error_log; static u_char *ngx_conf_file; @@ -388,6 +389,56 @@ main(int argc, char *const *argv) } +#ifdef NGX_WIN32 +static ngx_int_t +ngx_install_windows_service() +{ + wchar_t *install_commandline = NULL; + wchar_t *i_opt_ptr = NULL; + SC_HANDLE ScManagerHandle = NULL; + SC_HANDLE NginxServiceHandle = NULL; + + // Replace install commandline by service run commandline by + // replacing the -i (install) option to a -w (windows service mode) + install_commandline = _wcsdup(GetCommandLineW()); + i_opt_ptr = wcsstr(install_commandline, L" -i"); + i_opt_ptr[2] = 'w'; + printf("Installing NGINX service with commandline: %ls\n", install_commandline); + + ScManagerHandle = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); + if (ScManagerHandle == NULL) { + ngx_log_stderr(0, "OpenSCManagerW failed: errno == %d", ngx_errno); + } + + //"nginx" as service name is hardcoded is ngx_service : use a define ? + NginxServiceHandle = CreateServiceW(ScManagerHandle, + L"nginx", + L"Nginx server service", + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, // Auto-start ? let user change it manually in windows interface ? + SERVICE_ERROR_NORMAL, + install_commandline, + NULL, + NULL, + NULL, + NULL, //L"NT AUTHORITY\\NetworkService", ? + NULL); + + if (NginxServiceHandle == NULL) { + ngx_log_stderr(0, "CreateServiceW failed: errno == %d", ngx_errno); + CloseServiceHandle(ScManagerHandle); + } else { + printf("Service created !\n"); + } + + CloseServiceHandle(NginxServiceHandle); + CloseServiceHandle(ScManagerHandle); + return 0; +} +#endif + + static void ngx_show_version_info(void) { @@ -929,6 +980,18 @@ ngx_get_options(int argc, char *const *argv) ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal); return NGX_ERROR; +#ifdef NGX_WIN32 + case 'i': + ngx_install_windows_service(); + exit(0); // Do something else ? + + case 'w': + // Run as windows service + ngx_service = 1; + goto next; + +#endif + default: ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1)); return NGX_ERROR; @@ -1086,6 +1149,12 @@ ngx_process_options(ngx_cycle_t *cycle) cycle->log->log_level = NGX_LOG_INFO; } +#if NGX_WIN32 + if (ngx_service) { + cycle->service = 1; + } +#endif + return NGX_OK; } diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c index a75bdf878..53eb4e343 100644 --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -82,6 +82,8 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) cycle->log = log; cycle->old_cycle = old_cycle; + cycle->service = old_cycle->service; + cycle->conf_prefix.len = old_cycle->conf_prefix.len; cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix); if (cycle->conf_prefix.data == NULL) { diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h index 0c47f25fe..cc8e14a5b 100644 --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -83,6 +83,7 @@ struct ngx_cycle_s { ngx_str_t error_log; ngx_str_t lock_file; ngx_str_t hostname; + ngx_uint_t service; }; diff --git a/src/os/win32/ngx_process_cycle.c b/src/os/win32/ngx_process_cycle.c index a39335fd1..78c744c38 100644 --- a/src/os/win32/ngx_process_cycle.c +++ b/src/os/win32/ngx_process_cycle.c @@ -26,7 +26,11 @@ static ngx_thread_value_t __stdcall ngx_worker_thread(void *data); static ngx_thread_value_t __stdcall ngx_cache_manager_thread(void *data); static void ngx_cache_manager_process_handler(void); static ngx_thread_value_t __stdcall ngx_cache_loader_thread(void *data); +static ngx_thread_value_t __stdcall ngx_service_thread(void *data); +/* TODO: ngx_service.h */ + +ngx_int_t ngx_service(ngx_log_t *log); ngx_uint_t ngx_process; ngx_uint_t ngx_worker; @@ -46,7 +50,7 @@ ngx_uint_t ngx_exiting; HANDLE ngx_master_process_event; char ngx_master_process_event_name[NGX_PROCESS_SYNC_NAME]; -static HANDLE ngx_stop_event; +HANDLE ngx_stop_event; static char ngx_stop_event_name[NGX_PROCESS_SYNC_NAME]; static HANDLE ngx_quit_event; static char ngx_quit_event_name[NGX_PROCESS_SYNC_NAME]; @@ -68,6 +72,7 @@ ngx_master_process_cycle(ngx_cycle_t *cycle) ngx_int_t n; ngx_msec_t timer; ngx_uint_t live; + ngx_tid_t servicetid; HANDLE events[MAXIMUM_WAIT_OBJECTS]; ngx_sprintf((u_char *) ngx_master_process_event_name, @@ -116,6 +121,14 @@ ngx_master_process_cycle(ngx_cycle_t *cycle) ngx_close_listening_sockets(cycle); + if (cycle->service){ + // Create only if this thread does not already exists ? + if (ngx_create_thread(&servicetid, ngx_service_thread, NULL, cycle->log) != 0) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "Creating ngx_service_thread failed"); + exit(2); + } + } + if (ngx_start_worker_processes(cycle, NGX_PROCESS_RESPAWN) == 0) { exit(2); } @@ -754,7 +767,6 @@ ngx_worker_process_cycle(ngx_cycle_t *cycle, char *mevn) ngx_worker_process_exit(cycle); failed: - exit(2); } @@ -981,6 +993,11 @@ ngx_cache_loader_thread(void *data) return 0; } +static ngx_thread_value_t __stdcall +ngx_service_thread(void *data) +{ + return ngx_service((ngx_log_t*)data); +} void ngx_single_process_cycle(ngx_cycle_t *cycle) diff --git a/src/os/win32/ngx_service.c b/src/os/win32/ngx_service.c index 835d9cf00..dbb947275 100644 --- a/src/os/win32/ngx_service.c +++ b/src/os/win32/ngx_service.c @@ -4,131 +4,154 @@ * Copyright (C) Nginx, Inc. */ - +#include +#include #define NGX_SERVICE_CONTROL_SHUTDOWN 128 #define NGX_SERVICE_CONTROL_REOPEN 129 +HANDLE ngx_stop_event; // Defined in src\os\win32\ngx_process_cycle.c +/* +void LpserviceMainFunctiona( + [in] DWORD dwNumServicesArgs, + [in] LPSTR *lpServiceArgVectors +) +*/ +void WINAPI service_main(DWORD dwNumServicesArgs, LPSTR *lpServiceArgVectors); -SERVICE_TABLE_ENTRY st[] = { - { "nginx", service_main }, +/* + +DWORD LphandlerFunctionEx( + [in] DWORD dwControl, + [in] DWORD dwEventType, + [in] LPVOID lpEventData, + [in] LPVOID lpContext +) + +*/ +DWORD WINAPI service_handler(DWORD control, DWORD type, void *data, void *ctx); + +SERVICE_STATUS_HANDLE service = 0; // Put this field in ngx_cycle ? + +SERVICE_TABLE_ENTRY service_table[] = { + { "nginx", service_main}, { NULL, NULL } }; ngx_int_t -ngx_service(ngx_log_t *log) +ngx_service() { /* primary thread */ - /* StartServiceCtrlDispatcher() should be called within 30 seconds */ - if (StartServiceCtrlDispatcher(st) == 0) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + if (StartServiceCtrlDispatcher(service_table) == 0) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, "StartServiceCtrlDispatcher() failed"); return NGX_ERROR; } - return NGX_OK; } -void -service_main(u_int argc, char **argv) +void WINAPI +service_main(DWORD dwNumServicesArgs, LPSTR *lpServiceArgVectors) { SERVICE_STATUS status; - SERVICE_STATUS_HANDLE service; /* thread spawned by SCM */ - - service = RegisterServiceCtrlHandlerEx("nginx", service_handler, ctx); + service = RegisterServiceCtrlHandlerEx("nginx", service_handler, NULL); if (service == INVALID_HANDLE_VALUE) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, "RegisterServiceCtrlHandlerEx() failed"); return; } + /* Use a more generic report_service_status ? */ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; status.dwCurrentState = SERVICE_START_PENDING; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP - |SERVICE_ACCEPT_PARAMCHANGE; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP; status.dwWin32ExitCode = NO_ERROR; status.dwServiceSpecificExitCode = 0; status.dwCheckPoint = 1; status.dwWaitHint = 2000; /* SetServiceStatus() should be called within 80 seconds */ - if (SetServiceStatus(service, &status) == 0) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, "SetServiceStatus() failed"); return; } /* init */ + // Do we have any init to wait for ? + // Init seems preety well advanced when the service thread is created status.dwCurrentState = SERVICE_RUNNING; status.dwCheckPoint = 0; status.dwWaitHint = 0; if (SetServiceStatus(service, &status) == 0) { - ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, "SetServiceStatus() failed"); return; } - /* call master or worker loop */ +} - /* - * master should use event notification and look status - * single should use iocp to get notifications from service handler - */ +int report_service_stop_status(DWORD dwCurrentState) +{ + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = dwCurrentState; + status.dwControlsAccepted = 0; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + /* SetServiceStatus() should be called within 80 seconds */ + if (SetServiceStatus(service, &status) == 0) { + return 1; + } + return 0; } -u_int -service_handler(u_int control, u_int type, void *data, void *ctx) + +DWORD WINAPI +service_handler(DWORD control, DWORD type, void *data, void *ctx) { - /* primary thread */ - - switch (control) { - - case SERVICE_CONTROL_INTERROGATE: - status = NGX_IOCP_INTERROGATE; - break; + switch (control) { + // case SERVICE_CONTROL_INTERROGATE: + // status = NGX_IOCP_INTERROGATE; + // break; + // case SERVICE_CONTROL_STOP: - status = NGX_IOCP_STOP; - break; - - case SERVICE_CONTROL_PARAMCHANGE: - status = NGX_IOCP_RECONFIGURE; - break; - - case NGX_SERVICE_CONTROL_SHUTDOWN: - status = NGX_IOCP_REOPEN; - break; - - case NGX_SERVICE_CONTROL_REOPEN: - status = NGX_IOCP_REOPEN; + report_service_stop_status(SERVICE_STOP_PENDING); + if (SetEvent(ngx_stop_event) == 0) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + "SetEvent(ngx_stop_event) failed"); + } + report_service_stop_status(SERVICE_STOPPED); break; + // + // case SERVICE_CONTROL_PARAMCHANGE: + // status = NGX_IOCP_RECONFIGURE; + // break; + // + // case NGX_SERVICE_CONTROL_SHUTDOWN: + // status = NGX_IOCP_REOPEN; + // break; + // + // case NGX_SERVICE_CONTROL_REOPEN: + // status = NGX_IOCP_REOPEN; + // break; default: return ERROR_CALL_NOT_IMPLEMENTED; } - - if (ngx_single) { - if (PostQueuedCompletionStatus(iocp, ... status, ...) == 0) { - err = ngx_errno; - ngx_log_error(NGX_LOG_ALERT, log, err, - "PostQueuedCompletionStatus() failed"); - return err; - } - - } else { - Event - } - return NO_ERROR; }