| /* 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 128 |
| |
| static 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); |
| |
| /* Prevent holding open the (hidden) console */ |
| real_exit_code = 0; |
| |
| Windows9xServiceCtrlHandler(ap_control_handler, service_name); |
| |
| atexit(stop_service_monitor); |
| |
| /* Run the service */ |
| globdat.exit_status = main_fn(argc, argv); |
| |
| return (globdat.exit_status); |
| } |
| |
| static HANDLE eventlog_pipewrite = NULL; |
| static HANDLE eventlog_thread = NULL; |
| |
| int service_main(int (*main_fn)(int, char **), int argc, char **argv ) |
| { |
| SERVICE_TABLE_ENTRY dispatchTable[] = |
| { |
| { "", service_main_fn }, |
| { NULL, NULL } |
| }; |
| |
| /* Prevent holding open the (nonexistant) console and allow us past |
| * the first NT service to parse the service's args in apache_main() |
| */ |
| ap_server_argv0 = argv[0]; |
| real_exit_code = 0; |
| |
| /* keep the server from going to any real effort, since we know */ |
| is_service = 1; |
| |
| globdat.main_fn = main_fn; |
| globdat.connected = 1; |
| |
| if(!StartServiceCtrlDispatcher(dispatchTable)) |
| { |
| /* This is a genuine failure of the SCM. */ |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Error starting service control dispatcher"); |
| } |
| |
| globdat.connected = 0; |
| |
| if (eventlog_pipewrite) |
| { |
| CloseHandle(eventlog_pipewrite); |
| WaitForSingleObject(eventlog_thread, 10000); |
| eventlog_pipewrite = NULL; |
| } |
| |
| return(globdat.exit_status); |
| } |
| |
| long __stdcall service_stderr_thread(LPVOID hPipe) |
| { |
| HANDLE hPipeRead = (HANDLE) hPipe; |
| HANDLE hEventSource; |
| char errbuf[256]; |
| char *errmsg = errbuf; |
| char *errarg[9]; |
| DWORD errlen = 0; |
| DWORD errres; |
| HKEY hk; |
| |
| errarg[0] = "The Apache service named"; |
| errarg[1] = ap_server_argv0; |
| errarg[2] = "reported the following error:\r\n>>>"; |
| errarg[3] = errmsg; |
| errarg[4] = "<<<\r\n before the error.log file could be opened.\r\n"; |
| errarg[5] = "More information may be available in the error.log file."; |
| errarg[6] = NULL; |
| errarg[7] = NULL; |
| errarg[8] = NULL; |
| |
| /* What are we going to do in here, bail on the user? not. */ |
| if (!RegCreateKey(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services" |
| "\\EventLog\\Application\\Apache Service", &hk)) |
| { |
| /* The stock message file */ |
| char *netmsgkey = "%SystemRoot%\\System32\\netmsg.dll"; |
| DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | |
| EVENTLOG_INFORMATION_TYPE; |
| |
| RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, |
| (LPBYTE) netmsgkey, strlen(netmsgkey) + 1); |
| |
| RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD, |
| (LPBYTE) &dwData, sizeof(dwData)); |
| RegCloseKey(hk); |
| } |
| |
| hEventSource = RegisterEventSource(NULL, "Apache Service"); |
| |
| while (ReadFile(hPipeRead, errmsg, 1, &errres, NULL) && (errres == 1)) |
| { |
| if ((errmsg > errbuf) || !isspace(*errmsg)) |
| { |
| ++errlen; |
| ++errmsg; |
| if ((*(errmsg - 1) == '\n') || (errlen == sizeof(errbuf) - 1)) |
| { |
| while (errlen && isspace(errbuf[errlen - 1])) |
| --errlen; |
| errbuf[errlen] = '\0'; |
| |
| /* Generic message: '%1 %2 %3 %4 %5 %6 %7 %8 %9' |
| * The event code in netmsg.dll is 3299 |
| */ |
| ReportEvent(hEventSource, EVENTLOG_ERROR_TYPE, 0, |
| 3299, NULL, 9, 0, errarg, NULL); |
| errmsg = errbuf; |
| errlen = 0; |
| } |
| } |
| } |
| |
| ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, |
| "Shut down the Service Error Event Logger."); |
| |
| CloseHandle(eventlog_pipewrite); |
| eventlog_pipewrite = NULL; |
| |
| CloseHandle(hPipeRead); |
| |
| CloseHandle(eventlog_thread); |
| eventlog_thread = NULL; |
| return 0; |
| } |
| |
| void __stdcall service_main_fn(DWORD argc, LPTSTR *argv) |
| { |
| HANDLE hCurrentProcess; |
| HANDLE hPipeRead = NULL; |
| HANDLE hPipeReadDup; |
| HANDLE hNullFile; |
| DWORD threadid; |
| SECURITY_ATTRIBUTES sa = {0}; |
| char **newargv; |
| |
| if(!(globdat.hServiceStatus = RegisterServiceCtrlHandler(argv[0], |
| service_ctrl))) |
| { |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Failure registering service handler"); |
| return; |
| } |
| |
| ReportStatusToSCMgr( |
| SERVICE_START_PENDING, // service state |
| NO_ERROR, // exit code |
| 3000); // wait hint |
| |
| /* Create a pipe to send stderr messages to the system error log */ |
| hCurrentProcess = GetCurrentProcess(); |
| if (CreatePipe(&hPipeRead, &eventlog_pipewrite, &sa, 0)) |
| { |
| if (DuplicateHandle(hCurrentProcess, hPipeRead, hCurrentProcess, |
| &hPipeReadDup, 0, FALSE, DUPLICATE_SAME_ACCESS)) |
| { |
| CloseHandle(hPipeRead); |
| hPipeRead = hPipeReadDup; |
| eventlog_thread = CreateThread(NULL, 0, service_stderr_thread, |
| (LPVOID) hPipeRead, 0, &threadid); |
| if (eventlog_thread) |
| { |
| int fh; |
| FILE *fl; |
| fflush(stderr); |
| SetStdHandle(STD_ERROR_HANDLE, eventlog_pipewrite); |
| |
| fh = _open_osfhandle((long) STD_ERROR_HANDLE, |
| _O_WRONLY | _O_BINARY); |
| dup2(fh, STDERR_FILENO); |
| fl = _fdopen(STDERR_FILENO, "wcb"); |
| memcpy(stderr, fl, sizeof(FILE)); |
| } |
| else |
| { |
| CloseHandle(hPipeRead); |
| CloseHandle(eventlog_pipewrite); |
| eventlog_pipewrite = NULL; |
| } |
| } |
| else |
| { |
| CloseHandle(hPipeRead); |
| CloseHandle(eventlog_pipewrite); |
| eventlog_pipewrite = NULL; |
| } |
| } |
| |
| /* Open a null handle to nak our stdin */ |
| hNullFile = CreateFile("nul", GENERIC_READ | GENERIC_WRITE, |
| FILE_SHARE_READ | FILE_SHARE_WRITE, |
| &sa, OPEN_EXISTING, 0, NULL); |
| if (hNullFile == INVALID_HANDLE_VALUE) { |
| ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, NULL, |
| "Parent: Unable to create null stdin pipe for this service process.\n"); |
| } |
| else { |
| int fh; |
| FILE *fl; |
| fflush(stdin); |
| SetStdHandle(STD_INPUT_HANDLE, hNullFile); |
| fh = _open_osfhandle((long) STD_INPUT_HANDLE, |
| _O_RDONLY | _O_BINARY); |
| dup2(fh, STDIN_FILENO); |
| fl = _fdopen(STDIN_FILENO, "rcb"); |
| memcpy(stdin, fl, sizeof(FILE)); |
| } |
| |
| /* Open a null handle to soak our stdout */ |
| hNullFile = CreateFile("nul", GENERIC_READ | GENERIC_WRITE, |
| FILE_SHARE_READ | FILE_SHARE_WRITE, |
| &sa, OPEN_EXISTING, 0, NULL); |
| if (hNullFile == INVALID_HANDLE_VALUE) { |
| ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, NULL, |
| "Parent: Unable to create null stdout pipe for this service process.\n"); |
| } |
| else { |
| int fh; |
| FILE *fl; |
| fflush(stdout); |
| SetStdHandle(STD_OUTPUT_HANDLE, hNullFile); |
| fh = _open_osfhandle((long) STD_OUTPUT_HANDLE, |
| _O_WRONLY | _O_BINARY); |
| dup2(fh, STDOUT_FILENO); |
| fl = _fdopen(STDOUT_FILENO, "wcb"); |
| memcpy(stdout, fl, sizeof(FILE)); |
| } |
| |
| /* Grab it or lose it */ |
| globdat.name = argv[0]; |
| |
| ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, |
| "Hooked up the Service Error Event Logger."); |
| |
| /* Fold the "Start Parameters" in with the true executable argv[0], |
| * and insert a -n tag to pass the service name from the SCM's argv[0] |
| */ |
| newargv = (char**) malloc((argc + 3) * sizeof(char*)); |
| newargv[0] = ap_server_argv0; /* The true executable name */ |
| newargv[1] = "-n"; /* True service name follows (argv[0]) */ |
| memcpy (newargv + 2, argv, argc * sizeof(char*)); |
| newargv[argc + 2] = NULL; /* SCM doesn't null terminate the array */ |
| argv = newargv; |
| argc += 2; |
| |
| /* Use the name of the service as the error log marker */ |
| ap_server_argv0 = globdat.name; |
| |
| globdat.exit_status = globdat.main_fn( argc, argv ); |
| } |
| |
| /* Set the service description regardless of platform. |
| * We revert to set_service_description_string on NT/9x, the |
| * very long way so any Apache management program can grab the |
| * description. This would be bad on Win2000, since it wouldn't |
| * notify the service control manager of the name change. |
| */ |
| static void set_service_description_string(const char *description) |
| { |
| char szPath[MAX_PATH]; |
| HKEY hkey; |
| |
| /* Create/Find the Service key that Monitor Applications iterate */ |
| ap_snprintf(szPath, sizeof(szPath), |
| "SYSTEM\\CurrentControlSet\\Services\\%s", globdat.name); |
| ap_remove_spaces(szPath, szPath); |
| if (RegCreateKey(HKEY_LOCAL_MACHINE, szPath, &hkey) != ERROR_SUCCESS) { |
| return; |
| } |
| |
| /* Attempt to set the Description value for our service */ |
| RegSetValueEx(hkey, "Description", 0, REG_SZ, |
| (unsigned char *) description, |
| strlen(description) + 1); |
| RegCloseKey(hkey); |
| } |
| |
| |
| char *get_service_name(char *display_name) |
| { |
| /* Get the service's true name from the SCM on NT/2000, since it |
| * can be changed by the user on 2000, especially, from the |
| * service control panel. We can't trust the service name to |
| * match a space-collapsed display name. |
| */ |
| char service_name[MAX_PATH]; |
| if (isWindowsNT()) |
| { |
| SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); |
| DWORD namelen = sizeof(service_name); |
| if (scm) { |
| BOOL ok = GetServiceKeyName(scm, display_name, service_name, |
| &namelen); |
| CloseServiceHandle(scm); |
| if (ok) |
| return strdup(service_name); |
| } |
| } |
| ap_remove_spaces(service_name, display_name); |
| return strdup(service_name); |
| } |
| |
| |
| char *get_display_name(char *service_name) |
| { |
| /* Get the service's display name from the SCM on NT/2000, since it |
| * can be changed by the user on 2000, especially, from the |
| * service control panel. We can't trust the service name as |
| * provided by the user. |
| */ |
| if (isWindowsNT()) |
| { |
| char display_name[MAX_PATH]; |
| SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); |
| DWORD namelen = sizeof(display_name); |
| if (scm) { |
| BOOL ok = GetServiceDisplayName(scm, service_name, display_name, |
| &namelen); |
| CloseServiceHandle(scm); |
| if (ok) |
| return strdup(display_name); |
| } |
| } |
| return service_name; |
| } |
| |
| |
| /* ChangeServiceConfig2() prototype: |
| */ |
| typedef WINADVAPI BOOL (WINAPI *CSD_T)(SC_HANDLE, DWORD, LPCVOID); |
| |
| |
| /* Windows 2000 alone supports ChangeServiceConfig2 in order to |
| * register our server_version string... so we need some fixups |
| * to avoid binding to that function if we are on WinNT/9x. |
| * Fall back on set_service_description_string if we fail. |
| */ |
| void service_set_status(int status) |
| { |
| const char *full_description; |
| SC_HANDLE schSCManager; |
| CSD_T ChangeServiceDescription; |
| HANDLE hwin2000scm; |
| BOOL ret = 0; |
| |
| /* Nothing to do if we are a console |
| */ |
| if (!is_service) |
| return; |
| |
| ReportStatusToSCMgr(status, NO_ERROR, 3000); |
| |
| if (status != SERVICE_RUNNING) |
| return; |
| |
| /* Time to fix up the description, upon each successful restart |
| */ |
| full_description = ap_get_server_version(); |
| hwin2000scm = GetModuleHandle("ADVAPI32.DLL"); |
| if (!hwin2000scm) { |
| set_service_description_string(full_description); |
| return; |
| } |
| ChangeServiceDescription = (CSD_T) GetProcAddress(hwin2000scm, |
| "ChangeServiceConfig2A"); |
| if (!ChangeServiceDescription) { |
| set_service_description_string(full_description); |
| return; |
| } |
| schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); |
| if (schSCManager) { |
| SC_HANDLE schService = OpenService(schSCManager, globdat.name, |
| SERVICE_ALL_ACCESS); |
| if (schService) { |
| ret = ChangeServiceDescription(schService, |
| 1 /* SERVICE_CONFIG_DESCRIPTION */, |
| &full_description); |
| CloseServiceHandle(schService); |
| } |
| CloseServiceHandle(schSCManager); |
| } |
| if (!ret) |
| set_service_description_string(full_description); |
| } |
| |
| |
| // |
| // FUNCTION: service_ctrl |
| // |
| // PURPOSE: This function is called by the SCM whenever |
| // ControlService() is called on this service. |
| // |
| // PARAMETERS: |
| // dwCtrlCode - type of control requested |
| // |
| // RETURN VALUE: |
| // none |
| // |
| // COMMENTS: See the user-defined Handler() entry in the PSDK |
| // |
| VOID WINAPI service_ctrl(DWORD dwCtrlCode) |
| { |
| switch(dwCtrlCode) |
| { |
| // Stop the service. |
| // |
| case SERVICE_CONTROL_SHUTDOWN: |
| case SERVICE_CONTROL_STOP: |
| ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, |
| "Service Stop/Shutdown signaled, shutting down server."); |
| ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 15000); |
| ap_start_shutdown(); |
| break; |
| |
| case SERVICE_APACHE_RESTART: |
| ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, NULL, |
| "Service Restart signaled, shutting down server."); |
| ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 15000); |
| ap_start_restart(1); |
| break; |
| |
| // Update the service status. |
| // |
| case SERVICE_CONTROL_INTERROGATE: |
| ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, NO_ERROR, 0); |
| break; |
| |
| // invalid control code, ignored |
| // |
| default: |
| break; |
| } |
| } |
| |
| |
| int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint) |
| { |
| static int firstTime = 1; |
| static int checkPoint = 1; |
| int rv; |
| |
| if (firstTime) |
| { |
| firstTime = 0; |
| globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
| globdat.ssStatus.dwServiceSpecificExitCode = 0; |
| globdat.ssStatus.dwCheckPoint = 1; |
| } |
| |
| if (globdat.connected) |
| { |
| if ((currentState == SERVICE_START_PENDING) |
| || (currentState == SERVICE_STOP_PENDING)) |
| globdat.ssStatus.dwControlsAccepted = 0; |
| else |
| globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
| | SERVICE_ACCEPT_SHUTDOWN; |
| |
| globdat.ssStatus.dwCurrentState = currentState; |
| globdat.ssStatus.dwWin32ExitCode = exitCode; |
| |
| if ( ( currentState == SERVICE_RUNNING ) || |
| ( currentState == SERVICE_STOPPED ) ) |
| { |
| globdat.ssStatus.dwWaitHint = 0; |
| globdat.ssStatus.dwCheckPoint = 0; |
| } |
| else |
| { |
| if(waitHint) |
| globdat.ssStatus.dwWaitHint = waitHint; |
| globdat.ssStatus.dwCheckPoint = ++checkPoint; |
| } |
| |
| rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus); |
| } |
| return(1); |
| } |
| |
| void InstallService(pool *p, char *display_name, int argc, char **argv, int reconfig) |
| { |
| TCHAR szPath[MAX_PATH]; |
| TCHAR szQuotedPath[512]; |
| char *service_name; |
| int regargc = 0; |
| char default_depends[] = "Tcpip\0Afd\0"; |
| char *depends = default_depends; |
| size_t depends_len = sizeof(default_depends); |
| char **regargv = malloc((argc + 4) * sizeof(char*)); |
| char **newelem = regargv; |
| |
| regargc += 4; |
| *(newelem++) = "-d"; |
| *(newelem++) = ap_server_root; |
| *(newelem++) = "-f"; |
| *(newelem++) = ap_server_confname; |
| |
| while (++argv, --argc) { |
| if ((**argv == '-') && strchr("kndf", argv[0][1])) |
| --argc, ++argv; /* Skip already handled -k -n -d -f options */ |
| else if ((**argv == '-') && (argv[0][1] == 'W')) |
| { |
| /* Catch this service -W dependency |
| * the depends list is null seperated, double-null terminated |
| */ |
| char *service = get_service_name(*(argv + 1)); |
| size_t add_len = strlen(service) + 1; |
| char *more_depends = malloc(depends_len + add_len); |
| memcpy (more_depends, depends, depends_len - 1); |
| memcpy (more_depends + depends_len - 1, service, add_len); |
| depends_len += add_len; |
| depends = more_depends; |
| depends[depends_len - 1] = '\0'; |
| ++argv, --argc; |
| } |
| else if ((**argv != '-') || !strchr("iuw", argv[0][1])) |
| *(newelem++) = *argv, ++regargc; /* Ignoring -i -u -w options */ |
| } |
| |
| /* Remove spaces from display name to create service name */ |
| service_name = get_service_name(display_name); |
| |
| printf(reconfig ? "Reconfiguring the %s service\n" |
| : "Installing the %s service\n", |
| display_name); |
| |
| if (GetModuleFileName( NULL, szPath, 512 ) == 0) |
| { |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "GetModuleFileName failed"); |
| return; |
| } |
| |
| if (isWindowsNT()) |
| { |
| SC_HANDLE schService; |
| SC_HANDLE schSCManager; |
| |
| ap_snprintf(szQuotedPath, sizeof(szQuotedPath), "\"%s\" --ntservice", szPath); |
| |
| schSCManager = OpenSCManager( |
| NULL, // machine (local) |
| NULL, // database (default) |
| SC_MANAGER_ALL_ACCESS // access required |
| ); |
| if (!schSCManager) { |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "OpenSCManager failed"); |
| return; |
| } |
| |
| /* Added dependencies for the following: TCPIP, AFD |
| * AFD is the winsock handler, TCPIP is self evident |
| * |
| * RPCSS is the Remote Procedure Call (RPC) Locator |
| * required for DCOM communication. I am far from |
| * convinced we should toggle this, but be warned that |
| * future apache modules or ISAPI dll's may depend on it. |
| * Also UNC share users may need the networking service |
| * started (usually "LanmanWorkstation"). "ProtectedStorage" |
| * may be needed depending on how files and registry keys are |
| * stored. And W3SVC may be needed to wait until IIS has |
| * glommed and released 0.0.0.0:80 if the admin allocates |
| * two different IP's to Apache and IIS on the same port. |
| */ |
| if (reconfig) |
| { |
| schService = OpenService(schSCManager, service_name, |
| SERVICE_ALL_ACCESS); |
| if (!schService) |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "OpenService failed"); |
| else if (!ChangeServiceConfig( |
| schService, // Service handle |
| SERVICE_WIN32_OWN_PROCESS, // service type |
| SERVICE_AUTO_START, // start type |
| SERVICE_ERROR_NORMAL, // error control type |
| szQuotedPath, // service's binary |
| NULL, // no load ordering group |
| NULL, // no tag identifier |
| depends, // dependencies |
| NULL, // user account |
| NULL, // account password |
| display_name)) { // service display name |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "ChangeServiceConfig failed"); |
| /* !schService aborts configuration below */ |
| CloseServiceHandle(schService); |
| schService = NULL; |
| } |
| } |
| else /* !reconfig */ |
| { |
| schService = CreateService( |
| schSCManager, // SCManager database |
| service_name, // name of service |
| display_name, // name to display |
| SERVICE_ALL_ACCESS, // desired access |
| SERVICE_WIN32_OWN_PROCESS, // service type |
| SERVICE_AUTO_START, // start type |
| SERVICE_ERROR_NORMAL, // error control type |
| szQuotedPath, // service's binary |
| NULL, // no load ordering group |
| NULL, // no tag identifier |
| depends, // dependencies |
| NULL, // user account |
| NULL); // account password |
| if (!schService) |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "CreateService failed"); |
| } |
| if (schService) |
| CloseServiceHandle(schService); |
| |
| CloseServiceHandle(schSCManager); |
| |
| if (!schService) |
| return; |
| } |
| else /* !isWindowsNT() */ |
| { |
| HKEY hkey; |
| DWORD rv; |
| |
| ap_snprintf(szQuotedPath, sizeof(szQuotedPath), |
| "\"%s\" -k start -n %s", |
| szPath, service_name); |
| /* Create/Find the RunServices key */ |
| rv = RegCreateKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows" |
| "\\CurrentVersion\\RunServices", &hkey); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Could not create/open the RunServices registry key"); |
| return; |
| } |
| |
| /* Attempt to add the value for our service */ |
| rv = RegSetValueEx(hkey, service_name, 0, REG_SZ, |
| (unsigned char *)szQuotedPath, |
| strlen(szQuotedPath) + 1); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Unable to install service: " |
| "Could not add to RunServices Registry Key"); |
| RegCloseKey(hkey); |
| return; |
| } |
| |
| RegCloseKey(hkey); |
| |
| /* Create/Find the Service key for Monitor Applications to iterate */ |
| ap_snprintf(szPath, sizeof(szPath), |
| "SYSTEM\\CurrentControlSet\\Services\\%s", service_name); |
| rv = RegCreateKey(HKEY_LOCAL_MACHINE, szPath, &hkey); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Could not create/open the %s registry key", szPath); |
| return; |
| } |
| |
| /* Attempt to add the ImagePath value to identify it as Apache */ |
| rv = RegSetValueEx(hkey, "ImagePath", 0, REG_SZ, |
| (unsigned char *)szQuotedPath, |
| strlen(szQuotedPath) + 1); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Unable to install service: " |
| "Could not add ImagePath to %s Registry Key", |
| service_name); |
| RegCloseKey(hkey); |
| return; |
| } |
| |
| /* Attempt to add the DisplayName value for our service */ |
| rv = RegSetValueEx(hkey, "DisplayName", 0, REG_SZ, |
| (unsigned char *)display_name, |
| strlen(display_name) + 1); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Unable to install service: " |
| "Could not add DisplayName to %s Registry Key", |
| service_name); |
| RegCloseKey(hkey); |
| return; |
| } |
| |
| RegCloseKey(hkey); |
| } |
| |
| /* Both Platforms: Now store the args in the registry */ |
| if (ap_registry_set_service_args(p, regargc, regargv, service_name)) { |
| return; |
| } |
| |
| printf("The %s service has been %s successfully.\n", |
| display_name, reconfig ? "reconfigured" : "installed"); |
| } |
| |
| void RemoveService(char *display_name) |
| { |
| char *service_name; |
| BOOL success = FALSE; |
| |
| printf("Removing the %s service\n", display_name); |
| |
| /* Remove spaces from display name to create service name */ |
| service_name = get_service_name(display_name); |
| |
| if (isWindowsNT()) |
| { |
| SC_HANDLE schService; |
| SC_HANDLE schSCManager; |
| |
| schSCManager = OpenSCManager( |
| NULL, // machine (NULL == local) |
| NULL, // database (NULL == default) |
| SC_MANAGER_ALL_ACCESS // access required |
| ); |
| if (!schSCManager) { |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "OpenSCManager failed"); |
| return; |
| } |
| |
| schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS); |
| |
| if (schService == NULL) { |
| /* Could not open the service */ |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "OpenService failed"); |
| } |
| else { |
| /* try to stop the service */ |
| ap_stop_service(schService); |
| |
| // now remove the service |
| if (DeleteService(schService) == 0) |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "DeleteService failed"); |
| else |
| success = TRUE; |
| CloseServiceHandle(schService); |
| } |
| /* SCM removes registry parameters */ |
| CloseServiceHandle(schSCManager); |
| } |
| else /* !isWindowsNT() */ |
| { |
| HKEY hkey; |
| DWORD service_pid; |
| DWORD rv; |
| HWND hwnd; |
| |
| /* Locate the named window of class ApacheWin95ServiceMonitor |
| * from the active top level windows |
| */ |
| hwnd = FindWindow("ApacheWin95ServiceMonitor", service_name); |
| if (hwnd && GetWindowThreadProcessId(hwnd, &service_pid)) |
| { |
| int ticks = 120; |
| char prefix[20]; |
| ap_snprintf(prefix, sizeof(prefix), "ap%ld", (long)service_pid); |
| setup_signal_names(prefix); |
| ap_start_shutdown(); |
| while (--ticks) { |
| if (!IsWindow(hwnd)) |
| break; |
| Sleep(1000); |
| } |
| } |
| |
| /* Open the RunServices key */ |
| rv = RegOpenKey(HKEY_LOCAL_MACHINE, |
| "Software\\Microsoft\\Windows\\CurrentVersion\\RunServices", |
| &hkey); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Could not open the RunServices registry key."); |
| } |
| else { |
| /* Delete the registry value for this service */ |
| rv = RegDeleteValue(hkey, service_name); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Unable to remove service: " |
| "Could not delete the RunServices entry."); |
| } |
| else |
| success = TRUE; |
| } |
| RegCloseKey(hkey); |
| |
| /* Open the Services key */ |
| rv = RegOpenKey(HKEY_LOCAL_MACHINE, |
| "SYSTEM\\CurrentControlSet\\Services", &hkey); |
| if (rv != ERROR_SUCCESS) { |
| rv = RegDeleteValue(hkey, service_name); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Could not open the Services registry key."); |
| success = FALSE; |
| } |
| else { |
| /* Delete the registry key for this service */ |
| rv = RegDeleteKey(hkey, service_name); |
| if (rv != ERROR_SUCCESS) { |
| SetLastError(rv); |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "Unable to remove service: " |
| "Could not delete the Services registry key."); |
| success = FALSE; |
| } |
| } |
| RegCloseKey(hkey); |
| } |
| if (success) |
| printf("The %s service has been removed successfully.\n", |
| display_name); |
| } |
| |
| |
| BOOL isWindowsNT(void) |
| { |
| static BOOL once = FALSE; |
| static BOOL isNT = FALSE; |
| |
| if (!once) |
| { |
| OSVERSIONINFO osver; |
| osver.dwOSVersionInfoSize = sizeof(osver); |
| if (GetVersionEx(&osver)) |
| if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) |
| isNT = TRUE; |
| once = TRUE; |
| } |
| return isNT; |
| } |
| |
| |
| /* |
| * A hack to determine if we're running as a service without waiting for |
| * the SCM to fail. |
| */ |
| |
| BOOL isProcessService() |
| { |
| if (is_service != -1) |
| return is_service; |
| if (!isWindowsNT() || !AllocConsole()) { |
| /* Don't assume anything, just yet */ |
| return FALSE; |
| } |
| FreeConsole(); |
| is_service = 1; |
| return TRUE; |
| } |
| |
| /* Determine is service_name is a valid service |
| * |
| * TODO: be nice if we tested that it is an 'apache' service, no? |
| */ |
| |
| BOOL isValidService(char *display_name) { |
| char service_key[MAX_PATH]; |
| char *service_name; |
| HKEY hkey; |
| |
| /* Remove spaces from display name to create service name */ |
| strcpy(service_key, "System\\CurrentControlSet\\Services\\"); |
| service_name = get_service_name(display_name); |
| strcat(service_key, service_name); |
| |
| if (RegOpenKey(HKEY_LOCAL_MACHINE, service_key, &hkey) != ERROR_SUCCESS) { |
| return FALSE; |
| } |
| RegCloseKey(hkey); |
| return TRUE; |
| } |
| |
| |
| int send_signal_to_service(char *display_name, char *sig, |
| int argc, char **argv) |
| { |
| DWORD service_pid; |
| HANDLE hwnd; |
| SC_HANDLE schService; |
| SC_HANDLE schSCManager; |
| char *service_name; |
| int success = FALSE; |
| |
| enum { start, restart, stop, unknown } action; |
| static char *param[] = { "start", "restart", "shutdown" }; |
| static char *participle[] = { "starting", "restarting", "stopping" }; |
| static char *past[] = { "started", "restarted", "stopped" }; |
| |
| for (action = start; action < unknown; action++) |
| if (!strcasecmp(sig, param[action])) |
| break; |
| |
| if (action == unknown) { |
| printf("signal must be start, restart, or shutdown\n"); |
| return FALSE; |
| } |
| |
| service_name = get_service_name(display_name); |
| |
| if (isWindowsNT()) |
| { |
| schSCManager = OpenSCManager( |
| NULL, // machine (NULL == local) |
| NULL, // database (NULL == default) |
| SC_MANAGER_ALL_ACCESS // access required |
| ); |
| if (!schSCManager) { |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "OpenSCManager failed"); |
| return FALSE; |
| } |
| |
| schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS); |
| |
| if (schService == NULL) { |
| /* Could not open the service */ |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "OpenService failed"); |
| CloseServiceHandle(schSCManager); |
| return FALSE; |
| } |
| |
| if (!QueryServiceStatus(schService, &globdat.ssStatus)) { |
| ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL, |
| "QueryService failed"); |
| CloseServiceHandle(schService); |
| CloseServiceHandle(schSCManager); |
| } |
| } |
| else /* !isWindowsNT() */ |
| { |
| /* Locate the window named service_name of class ApacheWin95ServiceMonitor |
| * from the active top level windows |
| */ |
| hwnd = FindWindow("ApacheWin95ServiceMonitor", service_name); |
| if (hwnd && GetWindowThreadProcessId(hwnd, &service_pid)) |
| globdat.ssStatus.dwCurrentState = SERVICE_RUNNING; |
| else |
| globdat.ssStatus.dwCurrentState = SERVICE_STOPPED; |
| } |
| |
| if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED |
| && action == stop) { |
| printf("The %s service is not started.\n", display_name); |
| return FALSE; |
| } |
| else if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING |
| && action == start) { |
| printf("The %s service has already been started.\n", display_name); |
| strcpy(sig, ""); |
| return FALSE; |
| } |
| else |
| { |
| printf("The %s service is %s.\n", display_name, participle[action]); |
| |
| if (isWindowsNT()) |
| { |
| if (action == stop) |
| success = ap_stop_service(schService); |
| else if ((action == start) |
| || ((action == restart) |
| && (globdat.ssStatus.dwCurrentState |
| == SERVICE_STOPPED))) |
| { |
| /* start NT service needs service args */ |
| char **args = malloc(argc * sizeof(char*)); |
| int i, j; |
| for (i = 1, j = 0; i < argc; i++) { |
| if ((argv[i][0] == '-') && ((argv[i][1] == 'k') |
| || (argv[i][1] == 'n'))) |
| ++i; |
| else |
| args[j++] = argv[i]; |
| } |
| success = ap_start_service(schService, j, args); |
| } |
| else if (action == restart) |
| success = ap_restart_service(schService); |
| } |
| else /* !isWindowsNT()) */ |
| { |
| char prefix[20]; |
| ap_snprintf(prefix, sizeof(prefix), "ap%ld", (long)service_pid); |
| setup_signal_names(prefix); |
| |
| if (action == stop) { |
| int ticks = 60; |
| ap_start_shutdown(); |
| while (--ticks) |
| { |
| if (!IsWindow(hwnd)) { |
| success = TRUE; |
| break; |
| } |
| Sleep(1000); |
| } |
| } |
| else if (action == restart) |
| { |
| /* This gets a bit tricky... start and restart (of stopped service) |
| * will simply fall through and *THIS* process will fade into an |
| * invisible 'service' process, detaching from the user's console. |
| * We need to change the restart signal to "start", however, |
| * if the service was not -yet- running, and we do return FALSE |
| * to assure main() that we haven't done anything yet. |
| */ |
| if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) |
| { |
| printf("The %s service has %s.\n", display_name, |
| past[action]); |
| strcpy(sig, "start"); |
| return FALSE; |
| } |
| |
| ap_start_restart(1); |
| success = TRUE; |
| } |
| else /* action == start */ |
| { |
| printf("The %s service is %s.\n", display_name, |
| past[action]); |
| return FALSE; |
| } |
| } |
| |
| if( success ) |
| printf("The %s service has %s.\n", display_name, past[action]); |
| else |
| printf("Failed to %s the %s service.\n", sig, display_name); |
| } |
| |
| if (isWindowsNT()) { |
| CloseServiceHandle(schService); |
| CloseServiceHandle(schSCManager); |
| } |
| return success; |
| } |
| |
| int ap_stop_service(SC_HANDLE schService) |
| { |
| if (ControlService(schService, SERVICE_CONTROL_STOP, &globdat.ssStatus)) { |
| Sleep(1000); |
| while (QueryServiceStatus(schService, &globdat.ssStatus)) { |
| if (globdat.ssStatus.dwCurrentState == SERVICE_STOP_PENDING) |
| Sleep(1000); |
| else |
| break; |
| } |
| } |
| if (QueryServiceStatus(schService, &globdat.ssStatus)) |
| if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) |
| return TRUE; |
| return FALSE; |
| } |
| |
| int ap_start_service(SC_HANDLE schService, DWORD argc, char **argv) { |
| if (StartService(schService, argc, argv)) { |
| Sleep(1000); |
| while(QueryServiceStatus(schService, &globdat.ssStatus)) { |
| if(globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING) |
| Sleep(1000); |
| else |
| break; |
| } |
| } |
| if (QueryServiceStatus(schService, &globdat.ssStatus)) |
| if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING) |
| return TRUE; |
| return FALSE; |
| } |
| |
| int ap_restart_service(SC_HANDLE schService) |
| { |
| int ticks; |
| if (ControlService(schService, SERVICE_APACHE_RESTART, &globdat.ssStatus)) |
| { |
| ticks = 60; |
| while (globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING) |
| { |
| Sleep(1000); |
| if (!QueryServiceStatus(schService, &globdat.ssStatus)) |
| return FALSE; |
| if (!--ticks) |
| break; |
| } |
| } |
| if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING) |
| return TRUE; |
| return FALSE; |
| } |
| |
| #endif /* WIN32 */ |