📄 service.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#ifdef WIN32#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <process.h>#include <direct.h>#include "httpd.h"#include "http_conf_globals.h"#include "http_log.h"#include "http_main.h"#include "multithread.h"#include "service.h"#include "registry.h"#include "Win9xConHook.h"#define SERVICE_APACHE_RESTART 128static struct{ int (*main_fn)(int, char **); int connected; SERVICE_STATUS_HANDLE hServiceStatus; char *name; int exit_status; SERVICE_STATUS ssStatus; FILE *logFile;} globdat;/* statics for atexit processing or shared between threads */static BOOL die_on_logoff = FALSE;static HWND console_wnd = NULL;static int is_service = -1;static void WINAPI service_main_fn(DWORD, LPTSTR *);static void WINAPI service_ctrl(DWORD ctrlCode);static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint);static int ap_start_service(SC_HANDLE, DWORD argc, char **argv);static int ap_stop_service(SC_HANDLE);static int ap_restart_service(SC_HANDLE);/* exit() for Win32 is macro mapped (horrible, we agree) that allows us * to catch the non-zero conditions and inform the console process that * the application died, and hang on to the console a bit longer. * * The macro only maps for http_main.c and other sources that include * the service.h header, so we best assume it's an error to exit from * _any_ other module. * * If real_exit_code is not set to 2, it will not be set or trigger this * behavior on exit. All service and child processes are expected to * reset this flag to zero to avoid undesireable side effects. The value * 1 simply tells the system it is safe to enable the feature (set to 2), * while 0 prohibits the feature from being enabled. */int real_exit_code = 1;void hold_console_open_on_error(void){ HANDLE hConIn; HANDLE hConErr; DWORD result; DWORD mode; time_t start; time_t remains; char *msg = "Note the errors or messages above, " "and press the <ESC> key to exit. "; CONSOLE_SCREEN_BUFFER_INFO coninfo; INPUT_RECORD in; char count[16];#ifdef WIN32 /* The service parent cannot just 'pop' out of the main thread, * as it is about to try to do... * We must end this thread properly so the service control * thread exits gracefully. atexit()s registered in the running * apache_main thread _should_ have already been handled, so now * we can exit this thread and allow the service thread to exit. */ if (isWindowsNT() && isProcessService() && globdat.connected) { service_set_status(SERVICE_STOPPED); ExitThread(0); }#endif if (!real_exit_code) return; hConIn = GetStdHandle(STD_INPUT_HANDLE); hConErr = GetStdHandle(STD_ERROR_HANDLE); if ((hConIn == INVALID_HANDLE_VALUE) || (hConErr == INVALID_HANDLE_VALUE)) return; if (!WriteConsole(hConErr, msg, strlen(msg), &result, NULL) || !result) return; if (!GetConsoleScreenBufferInfo(hConErr, &coninfo)) return; if (isWindowsNT()) mode = ENABLE_MOUSE_INPUT | 0x80; else mode = ENABLE_MOUSE_INPUT; if (!SetConsoleMode(hConIn, mode)) return; start = time(NULL); do { while (PeekConsoleInput(hConIn, &in, 1, &result) && result) { if (!ReadConsoleInput(hConIn, &in, 1, &result) || !result) return; if ((in.EventType == KEY_EVENT) && in.Event.KeyEvent.bKeyDown && (in.Event.KeyEvent.uChar.AsciiChar == 27)) return; if (in.EventType == MOUSE_EVENT && (in.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)) return; } remains = ((start + 30) - time(NULL)); sprintf (count, "%d...", remains); if (!SetConsoleCursorPosition(hConErr, coninfo.dwCursorPosition)) return; if (!WriteConsole(hConErr, count, strlen(count), &result, NULL) || !result) return; } while ((remains > 0) && WaitForSingleObject(hConIn, 1000) != WAIT_FAILED);}/* Console Control handler for processing Ctrl-C/Ctrl-Break and * on Windows NT also user logoff and system shutdown, * this also used for the Win9x hidden service and child process */static BOOL CALLBACK ap_control_handler(DWORD ctrl_type){ switch (ctrl_type) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Ctrl+C/Break initiated, shutting down server."); real_exit_code = 0; /* for Interrupt signals, shut down the server. * Tell the system we have dealt with the signal * without waiting for Apache to terminate. */ ap_start_shutdown(); return TRUE; case CTRL_LOGOFF_EVENT: if (!die_on_logoff) return TRUE; /* or fall through... */ case CTRL_CLOSE_EVENT: case CTRL_SHUTDOWN_EVENT: ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Close/Logoff/Shutdown initiated, shutting down server."); /* for Terminate signals, shut down the server. * Wait for Apache to terminate, but respond * after a reasonable time to tell the system * that we have already tried to shut down. */ real_exit_code = 0; fprintf(stderr, "Apache server shutdown initiated...\n"); ap_start_shutdown(); Sleep(30000); return TRUE; } /* We should never get here, but this is (mostly) harmless */ return FALSE;}/* Once we are running a child process in our tty, it can no longer * determine which console window is our own, since the window * reports that it is owned by the child process. */static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd){ char tmp[20], *tty; if (isWindowsNT()) tty = "ConsoleWindowClass"; else tty = "tty"; if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, tty)) { DWORD wndproc, thisproc = GetCurrentProcessId(); GetWindowThreadProcessId(wnd, &wndproc); if (wndproc == thisproc) { *((HWND*)retwnd) = wnd; return FALSE; } } return TRUE;}void stop_child_monitor(void){ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Unhooking the child process monitor for shutdown."); FixConsoleCtrlHandler(ap_control_handler, 0);}/* * The Win32 Apache child cannot loose its console since 16bit cgi * processes will hang (9x) or fail (NT) if they are not launched * from a 32bit console app into that app's console window. * Mark the 9x child as a service process and let the parent process * clean it up as necessary. */void ap_start_child_console(int is_child_of_service){ int maxwait = 100; ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Hooking up the child process monitor to watch for shutdown."); /* The child is never exactly a service */ is_service = 0; /* Prevent holding open the (hidden) console */ real_exit_code = 0; /* We only die on logoff if we not a service's child */ die_on_logoff = !is_child_of_service; if (isWindowsNT()) { if (!is_child_of_service) { /* * Console mode Apache/WinNT needs to detach from the parent * console and create and hide it's own console window. * Not only is logout and shutdown more stable under W2K, * but this eliminates the mystery 'flicker' that users see * when invoking CGI apps (e.g. the titlebar or icon of the * console window changing to the cgi process's identifiers.) */ FreeConsole(); AllocConsole(); EnumWindows(EnumttyWindow, (long)(&console_wnd)); if (console_wnd) ShowWindow(console_wnd, SW_HIDE); } /* * Apache/WinNT installs no child console handler, otherwise * logoffs interfere with the service's child process! * The child process must have a later shutdown priority * than the parent, or the parent cannot shut down the * child process properly. (The parent's default is 0x280.) */ SetProcessShutdownParameters(0x200, 0); return; } if (!is_child_of_service) { FreeConsole(); AllocConsole(); } while (!console_wnd && maxwait-- > 0) { EnumWindows(EnumttyWindow, (long)(&console_wnd)); Sleep(100); } if (console_wnd) { FixConsoleCtrlHandler(ap_control_handler, die_on_logoff ? 1 : 2); ShowWindow(console_wnd, SW_HIDE); atexit(stop_child_monitor); }}void stop_console_monitor(void){ /* Remove the control handler at the end of the day. */ SetConsoleCtrlHandler(ap_control_handler, FALSE); ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Unhooking the console monitor for shutdown."); if (!isWindowsNT()) FixConsoleCtrlHandler(ap_control_handler, 0);}void ap_start_console_monitor(void){ HANDLE console_input; ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Hooking up the console monitor to watch for shutdown."); die_on_logoff = TRUE; is_service = 0; console_input = GetStdHandle(STD_INPUT_HANDLE); /* Assure we properly accept Ctrl+C as an interrupt... * Win/2000 definately makes some odd assumptions about * ctrl+c and the reserved console mode bits! */ if (console_input != INVALID_HANDLE_VALUE) { /* The SetConsoleCtrlHandler(NULL... would fault under Win9x * WinNT also includes an undocumented 0x80 bit for console mode * that preserves the console window behavior, and prevents the * bogus 'selection' mode from being accedently triggered. */ if (isWindowsNT()) { SetConsoleCtrlHandler(NULL, FALSE); SetConsoleMode(console_input, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | 0x80); } else { SetConsoleMode(console_input, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); } } if (!isWindowsNT()) FixConsoleCtrlHandler(ap_control_handler, die_on_logoff ? 1 : 2); SetConsoleCtrlHandler(ap_control_handler, TRUE); atexit(stop_console_monitor);}void stop_service_monitor(void){ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Unhooking up the service monitor for shutdown."); Windows9xServiceCtrlHandler(ap_control_handler, FALSE);}int service95_main(int (*main_fn)(int, char **), int argc, char **argv, char *display_name){ /* Windows 95/98 */ char *service_name; ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, "Hooking up the service monitor to watch for shutdown."); is_service = 1; die_on_logoff = FALSE; /* Set up the Win9x server name, as WinNT would */ ap_server_argv0 = globdat.name = display_name; /* Remove spaces from display name to create service name */ service_name = strdup(display_name); ap_remove_spaces(service_name, display_name);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -