Main Page | Modules | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages | Examples

service.c

Go to the documentation of this file.
00001 /* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
00002  * applicable.
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 /* This module ALONE requires the window message API from user.h 
00018  * and the default APR include of windows.h will omit it, so
00019  * preload the API symbols now...
00020  */
00021 
00022 #define CORE_PRIVATE 
00023 #define _WINUSER_
00024 
00025 #include "httpd.h"
00026 #include "http_log.h"
00027 #include "mpm_winnt.h"
00028 #include "apr_strings.h"
00029 #include "apr_lib.h"
00030 #include "ap_regkey.h"
00031 
00032 #ifdef NOUSER
00033 #undef NOUSER
00034 #endif
00035 #undef _WINUSER_
00036 #include <winuser.h>
00037 
00038 static char *mpm_service_name = NULL;
00039 static char *mpm_display_name = NULL;
00040 
00041 static struct
00042 {
00043     HANDLE mpm_thread;       /* primary thread handle of the apache server */
00044     HANDLE service_thread;   /* thread service/monitor handle */
00045     DWORD  service_thread_id;/* thread service/monitor ID */
00046     HANDLE service_init;     /* controller thread init mutex */
00047     HANDLE service_term;     /* NT service thread kill signal */
00048     SERVICE_STATUS ssStatus;
00049     SERVICE_STATUS_HANDLE hServiceStatus;
00050 } globdat;
00051 
00052 static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint);
00053 
00054 
00055 #define PRODREGKEY "SOFTWARE\\" AP_SERVER_BASEVENDOR "\\" \
00056                    AP_SERVER_BASEPRODUCT "\\" AP_SERVER_BASEREVISION
00057 
00058 /*
00059  * Get the server root from the registry into 'dir' which is
00060  * size bytes long. Returns 0 if the server root was found
00061  * or if the serverroot key does not exist (in which case
00062  * dir will contain an empty string), or -1 if there was
00063  * an error getting the key.
00064  */
00065 apr_status_t ap_registry_get_server_root(apr_pool_t *p, char **buf)
00066 {
00067     apr_status_t rv;
00068     ap_regkey_t *key;
00069 
00070     if ((rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, PRODREGKEY, 
00071                              APR_READ, p)) == APR_SUCCESS) {
00072         rv = ap_regkey_value_get(buf, key, "ServerRoot", p);
00073         ap_regkey_close(key);
00074         if (rv == APR_SUCCESS) 
00075             return rv;
00076     }
00077 
00078     if ((rv = ap_regkey_open(&key, AP_REGKEY_CURRENT_USER, PRODREGKEY, 
00079                              APR_READ, p)) == APR_SUCCESS) {
00080         rv = ap_regkey_value_get(buf, key, "ServerRoot", p);
00081         ap_regkey_close(key);
00082         if (rv == APR_SUCCESS) 
00083             return rv;
00084     }
00085 
00086     *buf = NULL;
00087     return rv;
00088 }
00089 
00090 
00091 /* The service configuration's is stored under the following trees:
00092  *
00093  * HKLM\System\CurrentControlSet\Services\[service name]
00094  *
00095  *     \DisplayName
00096  *     \ImagePath
00097  *     \Parameters\ConfigArgs
00098  *
00099  * For Win9x, the launch service command is stored under:
00100  *
00101  * HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices\[service name]
00102  */
00103 
00104 
00105 /* exit() for Win32 is macro mapped (horrible, we agree) that allows us 
00106  * to catch the non-zero conditions and inform the console process that
00107  * the application died, and hang on to the console a bit longer.
00108  *
00109  * The macro only maps for http_main.c and other sources that include
00110  * the service.h header, so we best assume it's an error to exit from
00111  * _any_ other module.
00112  *
00113  * If real_exit_code is reset to 0, it will not be set or trigger this
00114  * behavior on exit.  All service and child processes are expected to
00115  * reset this flag to zero to avoid undesireable side effects.
00116  */
00117 AP_DECLARE_DATA int real_exit_code = 1;
00118 
00119 void hold_console_open_on_error(void)
00120 {
00121     HANDLE hConIn;
00122     HANDLE hConErr;
00123     DWORD result;
00124     time_t start;
00125     time_t remains;
00126     char *msg = "Note the errors or messages above, "
00127                 "and press the <ESC> key to exit.  ";
00128     CONSOLE_SCREEN_BUFFER_INFO coninfo;
00129     INPUT_RECORD in;
00130     char count[16];
00131     
00132     if (!real_exit_code)
00133         return;
00134     hConIn = GetStdHandle(STD_INPUT_HANDLE);
00135     hConErr = GetStdHandle(STD_ERROR_HANDLE);
00136     if ((hConIn == INVALID_HANDLE_VALUE) || (hConErr == INVALID_HANDLE_VALUE))
00137         return;
00138     if (!WriteConsole(hConErr, msg, strlen(msg), &result, NULL) || !result)
00139         return;
00140     if (!GetConsoleScreenBufferInfo(hConErr, &coninfo))
00141         return;
00142     if (!SetConsoleMode(hConIn, ENABLE_MOUSE_INPUT | 0x80))
00143         return;
00144         
00145     start = time(NULL);
00146     do
00147     {
00148         while (PeekConsoleInput(hConIn, &in, 1, &result) && result)
00149         {
00150             if (!ReadConsoleInput(hConIn, &in, 1, &result) || !result)
00151                 return;
00152             if ((in.EventType == KEY_EVENT) && in.Event.KeyEvent.bKeyDown 
00153                     && (in.Event.KeyEvent.uChar.AsciiChar == 27))
00154                 return;
00155             if (in.EventType == MOUSE_EVENT 
00156                     && (in.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK))
00157                 return;
00158         }
00159         remains = ((start + 30) - time(NULL)); 
00160         sprintf (count, "%d...", remains);
00161         if (!SetConsoleCursorPosition(hConErr, coninfo.dwCursorPosition))
00162             return;
00163         if (!WriteConsole(hConErr, count, strlen(count), &result, NULL) 
00164                 || !result)
00165             return;
00166     }
00167     while ((remains > 0) && WaitForSingleObject(hConIn, 1000) != WAIT_FAILED);
00168 }
00169 
00170 static BOOL  die_on_logoff = FALSE;
00171 
00172 static LRESULT CALLBACK monitor_service_9x_proc(HWND hWnd, UINT msg, 
00173                                                 WPARAM wParam, LPARAM lParam)
00174 {
00175 /* This is the WndProc procedure for our invisible window.
00176  * When the user shuts down the system, this window is sent
00177  * a signal WM_ENDSESSION. We clean up by signaling Apache
00178  * to shut down, and idle until Apache's primary thread quits.
00179  */
00180     if ((msg == WM_ENDSESSION) 
00181             && (die_on_logoff || (lParam != ENDSESSION_LOGOFF)))
00182     {
00183         ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
00184         if (wParam)
00185             /* Don't leave this message until we are dead! */
00186             WaitForSingleObject(globdat.mpm_thread, 30000);
00187         return 0;
00188     }
00189     return (DefWindowProc(hWnd, msg, wParam, lParam));
00190 }
00191 
00192 static DWORD WINAPI monitor_service_9x_thread(void *service_name)
00193 {
00194     /* When running as a service under Windows 9x, there is no console
00195      * window present, and no ConsoleCtrlHandler to call when the system 
00196      * is shutdown.  If the WatchWindow thread is created with a NULL
00197      * service_name argument, then the ...SystemMonitor window class is
00198      * used to create the "Apache" window to watch for logoff and shutdown.
00199      * If the service_name is provided, the ...ServiceMonitor window class
00200      * is used to create the window named by the service_name argument,
00201      * and the logoff message is ignored.
00202      */
00203     WNDCLASS wc;
00204     HWND hwndMain;
00205     MSG msg;
00206     
00207     wc.style         = CS_GLOBALCLASS;
00208     wc.lpfnWndProc   = monitor_service_9x_proc; 
00209     wc.cbClsExtra    = 0;
00210     wc.cbWndExtra    = 0; 
00211     wc.hInstance     = NULL;
00212     wc.hIcon         = NULL;
00213     wc.hCursor       = NULL;
00214     wc.hbrBackground = NULL;
00215     wc.lpszMenuName  = NULL;
00216     if (service_name)
00217         wc.lpszClassName = "ApacheWin95ServiceMonitor";
00218     else
00219         wc.lpszClassName = "ApacheWin95SystemMonitor";
00220  
00221     die_on_logoff = service_name ? FALSE : TRUE;
00222 
00223     if (!RegisterClass(&wc)) 
00224     {
00225         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), 
00226                      NULL, "Could not register window class for WatchWindow");
00227         globdat.service_thread_id = 0;
00228         return 0;
00229     }
00230     
00231     /* Create an invisible window */
00232     hwndMain = CreateWindow(wc.lpszClassName, 
00233                             service_name ? (char *) service_name : "Apache",
00234                             WS_OVERLAPPEDWINDOW & ~WS_VISIBLE, 
00235                             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
00236                             CW_USEDEFAULT, NULL, NULL, NULL, NULL);
00237                             
00238     if (!hwndMain)
00239     {
00240         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), 
00241                      NULL, "Could not create WatchWindow");
00242         globdat.service_thread_id = 0;
00243         return 0;
00244     }
00245 
00246     /* If we succeed, eliminate the console window.
00247      * Signal the parent we are all set up, and
00248      * watch the message queue while the window lives.
00249      */
00250     FreeConsole();
00251     SetEvent(globdat.service_init);
00252 
00253     while (GetMessage(&msg, NULL, 0, 0)) 
00254     {
00255         if (msg.message == WM_CLOSE)
00256             DestroyWindow(hwndMain); 
00257         else {
00258             TranslateMessage(&msg);
00259             DispatchMessage(&msg);
00260         }
00261     }
00262     globdat.service_thread_id = 0;
00263     return 0;
00264 }
00265 
00266 
00267 static BOOL CALLBACK console_control_handler(DWORD ctrl_type)
00268 {
00269     switch (ctrl_type)
00270     {
00271         case CTRL_BREAK_EVENT:
00272             fprintf(stderr, "Apache server restarting...\n");
00273             ap_signal_parent(SIGNAL_PARENT_RESTART);
00274             return TRUE;
00275         case CTRL_C_EVENT:
00276             fprintf(stderr, "Apache server interrupted...\n");
00277             /* for Interrupt signals, shut down the server.
00278              * Tell the system we have dealt with the signal
00279              * without waiting for Apache to terminate.
00280              */
00281             ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
00282             return TRUE;
00283 
00284         case CTRL_CLOSE_EVENT:
00285         case CTRL_LOGOFF_EVENT:
00286         case CTRL_SHUTDOWN_EVENT:
00287             /* for Terminate signals, shut down the server.
00288              * Wait for Apache to terminate, but respond
00289              * after a reasonable time to tell the system
00290              * that we did attempt to shut ourself down.
00291              * THESE EVENTS WILL NOT OCCUR UNDER WIN9x!
00292              */
00293             fprintf(stderr, "Apache server shutdown initiated...\n");
00294             ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
00295             Sleep(30000);
00296             return TRUE;
00297     }
00298  
00299     /* We should never get here, but this is (mostly) harmless */
00300     return FALSE;
00301 }
00302 
00303 
00304 static void stop_console_handler(void)
00305 {
00306     SetConsoleCtrlHandler(console_control_handler, FALSE);
00307 }
00308 
00309 
00310 void mpm_start_console_handler(void)
00311 {
00312     SetConsoleCtrlHandler(console_control_handler, TRUE);
00313     atexit(stop_console_handler);
00314 }
00315 
00316 
00317 /* Special situation - children of services need to mind their
00318  * P's & Q's and wait quietly, ignoring the mean OS signaling
00319  * shutdown and other horrors, to kill them gracefully...
00320  */
00321 
00322 static BOOL CALLBACK child_control_handler(DWORD ctrl_type)
00323 {
00324     switch (ctrl_type)
00325     {
00326         case CTRL_C_EVENT:
00327         case CTRL_BREAK_EVENT:
00328             /* for Interrupt signals, ignore them.
00329              * The system will also signal the parent process,
00330              * which will terminate Apache.
00331              */
00332             return TRUE;
00333 
00334         case CTRL_CLOSE_EVENT:
00335         case CTRL_LOGOFF_EVENT:
00336         case CTRL_SHUTDOWN_EVENT:
00337             /* for Shutdown signals, ignore them, but...             .
00338              * The system will also signal the parent process,
00339              * which will terminate Apache, so we need to wait.
00340              */
00341             Sleep(30000);
00342             return TRUE;
00343     }
00344  
00345     /* We should never get here, but this is (mostly) harmless */
00346     return FALSE;
00347 }
00348 
00349 
00350 static void stop_child_console_handler(void)
00351 {
00352     SetConsoleCtrlHandler(child_control_handler, FALSE);
00353 }
00354 
00355 
00356 void mpm_start_child_console_handler(void)
00357 {
00358     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
00359         FreeConsole();
00360     }
00361     else
00362     {
00363         SetConsoleCtrlHandler(child_control_handler, TRUE);
00364         atexit(stop_child_console_handler);
00365     }
00366 }
00367 
00368 
00369 /**********************************
00370   WinNT service control management
00371  **********************************/
00372 
00373 static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
00374 {
00375     static int checkPoint = 1;
00376     int rv = APR_SUCCESS;
00377     
00378     if (globdat.hServiceStatus)
00379     {
00380         if (currentState == SERVICE_RUNNING) {
00381             globdat.ssStatus.dwWaitHint = 0;
00382             globdat.ssStatus.dwCheckPoint = 0;
00383             globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
00384         }
00385         else if (currentState == SERVICE_STOPPED) {
00386             globdat.ssStatus.dwWaitHint = 0;
00387             globdat.ssStatus.dwCheckPoint = 0;
00388             if (!exitCode && globdat.ssStatus.dwCurrentState 
00389                                            != SERVICE_STOP_PENDING) {
00390                 /* An unexpected exit?  Better to error! */
00391                 exitCode = 1;
00392             }
00393             if (exitCode) {
00394                 globdat.ssStatus.dwWin32ExitCode =ERROR_SERVICE_SPECIFIC_ERROR;
00395                 globdat.ssStatus.dwServiceSpecificExitCode = exitCode;
00396             }
00397         }
00398         else {
00399             globdat.ssStatus.dwCheckPoint = ++checkPoint;
00400             globdat.ssStatus.dwControlsAccepted = 0;
00401             if(waitHint)
00402                 globdat.ssStatus.dwWaitHint = waitHint;
00403         }
00404 
00405         globdat.ssStatus.dwCurrentState = currentState;
00406         
00407         rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus);
00408     }
00409     return(rv);
00410 }
00411 
00412 /* Set the service description regardless of platform.
00413  * We revert to set_service_description on NT/9x, the
00414  * very long way so any Apache management program can grab the
00415  * description.  This would be bad on Win2000, since it wouldn't
00416  * notify the service control manager of the name change.
00417  */
00418 
00419 /* borrowed from mpm_winnt.c */
00420 extern apr_pool_t *pconf;
00421 
00422 /* Windows 2000 alone supports ChangeServiceConfig2 in order to
00423  * register our server_version string... so we need some fixups
00424  * to avoid binding to that function if we are on WinNT/9x.
00425  */
00426 static void set_service_description(void)
00427 {
00428     const char *full_description;
00429     SC_HANDLE schSCManager;
00430     BOOL ret = 0;
00431 
00432     /* Nothing to do if we are a console
00433      */
00434     if (!mpm_service_name)
00435         return;
00436 
00437     /* Time to fix up the description, upon each successful restart
00438      */
00439     full_description = ap_get_server_version();
00440 
00441     if ((osver.dwPlatformId == VER_PLATFORM_WIN32_NT) 
00442           && (osver.dwMajorVersion > 4) 
00443           && (ChangeServiceConfig2)
00444           && (schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)))
00445     {    
00446         SC_HANDLE schService = OpenService(schSCManager, mpm_service_name,
00447                                            SERVICE_CHANGE_CONFIG);
00448         if (schService) {
00449             /* Cast is necessary, ChangeServiceConfig2 handles multiple
00450              * object types, some volatile, some not.
00451              */
00452             /* ###: utf-ize */
00453             if (ChangeServiceConfig2(schService,
00454                                      1 /* SERVICE_CONFIG_DESCRIPTION */,
00455                                      (LPVOID) &full_description)) {
00456                 full_description = NULL;
00457             }
00458             CloseServiceHandle(schService);
00459         }
00460         CloseServiceHandle(schSCManager);
00461     }
00462 
00463     if (full_description) 
00464     {
00465         char szPath[MAX_PATH];
00466         ap_regkey_t *svckey;
00467         apr_status_t rv;
00468 
00469         /* Find the Service key that Monitor Applications iterate */
00470         apr_snprintf(szPath, sizeof(szPath), 
00471                      "SYSTEM\\CurrentControlSet\\Services\\%s", 
00472                      mpm_service_name);
00473         rv = ap_regkey_open(&svckey, AP_REGKEY_LOCAL_MACHINE, szPath,
00474                             APR_READ | APR_WRITE, pconf);
00475         if (rv != APR_SUCCESS) {
00476             return;
00477         }
00478         /* Attempt to set the Description value for our service */
00479         ap_regkey_value_set(svckey, "Description", full_description, 0, pconf);
00480         ap_regkey_close(svckey);
00481     }
00482 }
00483 
00484 /* handle the SCM's ControlService() callbacks to our service */
00485 
00486 static VOID WINAPI service_nt_ctrl(DWORD dwCtrlCode)
00487 {
00488     if (dwCtrlCode == SERVICE_CONTROL_STOP)
00489     {
00490         ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
00491         ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 30000);
00492         return;
00493     }
00494     if (dwCtrlCode == SERVICE_APACHE_RESTART)
00495     {
00496         ap_signal_parent(SIGNAL_PARENT_RESTART);
00497         ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 30000);
00498         return;
00499     }
00500     
00501     ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, NO_ERROR, 0);            
00502 }
00503 
00504 
00505 /* service_nt_main_fn is outside of the call stack and outside of the
00506  * primary server thread... so now we _really_ need a placeholder!
00507  * The winnt_rewrite_args has created and shared mpm_new_argv with us.
00508  */
00509 extern apr_array_header_t *mpm_new_argv;
00510 
00511 /* ###: utf-ize */
00512 static void __stdcall service_nt_main_fn(DWORD argc, LPTSTR *argv)
00513 {
00514     const char *ignored;
00515 
00516     /* args and service names live in the same pool */
00517     mpm_service_set_name(mpm_new_argv->pool, &ignored, argv[0]);
00518 
00519     memset(&globdat.ssStatus, 0, sizeof(globdat.ssStatus));
00520     globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
00521     globdat.ssStatus.dwCurrentState = SERVICE_START_PENDING;
00522     globdat.ssStatus.dwCheckPoint = 1;
00523 
00524     /* ###: utf-ize */
00525     if (!(globdat.hServiceStatus = RegisterServiceCtrlHandler(argv[0], service_nt_ctrl)))
00526     {
00527         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), 
00528                      NULL, "Failure registering service handler");
00529         return;
00530     }
00531 
00532     /* Report status, no errors, and buy 3 more seconds */
00533     ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 30000);
00534 
00535     /* We need to append all the command arguments passed via StartService() 
00536      * to our running service... which just got here via the SCM...
00537      * but we hvae no interest in argv[0] for the mpm_new_argv list.
00538      */
00539     if (argc > 1) 
00540     {
00541         char **cmb_data;
00542 
00543         mpm_new_argv->nalloc = mpm_new_argv->nelts + argc - 1;
00544         cmb_data = malloc(mpm_new_argv->nalloc * sizeof(const char *));
00545 
00546         /* mpm_new_argv remains first (of lower significance) */
00547         memcpy (cmb_data, mpm_new_argv->elts, 
00548                 mpm_new_argv->elt_size * mpm_new_argv->nelts);
00549         
00550         /* Service args follow from StartService() invocation */
00551         memcpy (cmb_data + mpm_new_argv->nelts, argv + 1, 
00552                 mpm_new_argv->elt_size * (argc - 1));
00553         
00554         /* The replacement arg list is complete */
00555         mpm_new_argv->elts = (char *)cmb_data;
00556         mpm_new_argv->nelts = mpm_new_argv->nalloc;
00557     }
00558 
00559     /* Let the main thread continue now... but hang on to the
00560      * signal_monitor event so we can take further action
00561      */
00562     SetEvent(globdat.service_init);
00563 
00564     WaitForSingleObject(globdat.service_term, INFINITE);
00565 }
00566 
00567 
00568 DWORD WINAPI service_nt_dispatch_thread(LPVOID nada)
00569 {
00570     apr_status_t rv = APR_SUCCESS;
00571 
00572     SERVICE_TABLE_ENTRY dispatchTable[] =
00573     {
00574         { "", service_nt_main_fn },
00575         { NULL, NULL }
00576     };
00577 
00578     /* ###: utf-ize */
00579     if (!StartServiceCtrlDispatcher(dispatchTable))
00580     {
00581         /* This is a genuine failure of the SCM. */
00582         rv = apr_get_os_error();
00583         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
00584                      "Error starting service control dispatcher");
00585     }
00586 
00587     return (rv);
00588 }
00589 
00590 
00591 apr_status_t mpm_service_set_name(apr_pool_t *p, const char **display_name, 
00592                                   const char *set_name)
00593 {
00594     char key_name[MAX_PATH];
00595     ap_regkey_t *key;
00596     apr_status_t rv;
00597 
00598     /* ### Needs improvement, on Win2K the user can _easily_ 
00599      * change the display name to a string that doesn't reflect 
00600      * the internal service name + whitespace!
00601      */
00602     mpm_service_name = apr_palloc(p, strlen(set_name) + 1);
00603     apr_collapse_spaces((char*) mpm_service_name, set_name);
00604     apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
00605     rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name, APR_READ, pconf);
00606     if (rv == APR_SUCCESS) {
00607         rv = ap_regkey_value_get(&mpm_display_name, key, "DisplayName", pconf);
00608         ap_regkey_close(key);
00609     }
00610     if (rv != APR_SUCCESS) {
00611         /* Take the given literal name if there is no service entry */
00612         mpm_display_name = apr_pstrdup(p, set_name);
00613     } 
00614     *display_name = mpm_display_name;
00615     return rv;
00616 }
00617 
00618 
00619 apr_status_t mpm_merge_service_args(apr_pool_t *p, 
00620                                    apr_array_header_t *args, 
00621                                    int fixed_args)
00622 {
00623     apr_array_header_t *svc_args = NULL;
00624     char conf_key[MAX_PATH];
00625     char **cmb_data;
00626     apr_status_t rv;
00627     ap_regkey_t *key;
00628 
00629     apr_snprintf(conf_key, sizeof(conf_key), SERVICEPARAMS, mpm_service_name);
00630     rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, conf_key, APR_READ, p);
00631     if (rv == APR_SUCCESS) {
00632         rv = ap_regkey_value_array_get(&svc_args, key, "ConfigArgs", p);
00633         ap_regkey_close(key);
00634     }
00635     if (rv != APR_SUCCESS) {
00636         if (rv == ERROR_FILE_NOT_FOUND) {
00637             ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
00638                          "No ConfigArgs registered for %s, perhaps "
00639                          "this service is not installed?", 
00640                          mpm_service_name);
00641             return APR_SUCCESS;
00642         }
00643         else
00644             return (rv);        
00645     }
00646 
00647     if (!svc_args || svc_args->nelts == 0) {
00648         return (APR_SUCCESS);
00649     }
00650 
00651     /* Now we have the mpm_service_name arg, and the mpm_runservice_nt()
00652      * call appended the arguments passed by StartService(), so it's  
00653      * time to _prepend_ the default arguments for the server from 
00654      * the service's default arguments (all others override them)...
00655      */
00656     args->nalloc = args->nelts + svc_args->nelts;
00657     cmb_data = malloc(args->nalloc * sizeof(const char *));
00658 
00659     /* First three args (argv[0], -f, path) remain first */
00660     memcpy(cmb_data, args->elts, args->elt_size * fixed_args);
00661     
00662     /* Service args follow from service registry array */
00663     memcpy(cmb_data + fixed_args, svc_args->elts, 
00664            svc_args->elt_size * svc_args->nelts);
00665     
00666     /* Remaining new args follow  */
00667     memcpy(cmb_data + fixed_args + svc_args->nelts,
00668            (const char **)args->elts + fixed_args, 
00669            args->elt_size * (args->nelts - fixed_args));
00670     
00671     args->elts = (char *)cmb_data;
00672     args->nelts = args->nalloc;
00673 
00674     return APR_SUCCESS;
00675 }
00676 
00677 
00678 void service_stopped(void)
00679 {
00680     /* Still have a thread & window to clean up, so signal now */
00681     if (globdat.service_thread)
00682     {
00683         if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
00684         {
00685             /* Stop logging to the event log */
00686             mpm_nt_eventlog_stderr_flush();
00687 
00688             /* Cause the service_nt_main_fn to complete */
00689             ReleaseMutex(globdat.service_term);
00690 
00691             ReportStatusToSCMgr(SERVICE_STOPPED, // service state
00692                                 NO_ERROR,        // exit code
00693                                 0);              // wait hint
00694         }
00695         else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
00696         {
00697             RegisterServiceProcess(0, 0);
00698             PostThreadMessage(globdat.service_thread_id, WM_CLOSE, 0, 0);
00699         }
00700 
00701         WaitForSingleObject(globdat.service_thread, 5000);
00702         CloseHandle(globdat.service_thread);
00703     }
00704 }
00705 
00706 
00707 apr_status_t mpm_service_to_start(const char **display_name, apr_pool_t *p)
00708 {
00709     HANDLE hProc = GetCurrentProcess();
00710     HANDLE hThread = GetCurrentThread();
00711     HANDLE waitfor[2];
00712 
00713     /* Prevent holding open the (hidden) console */
00714     real_exit_code = 0;
00715 
00716      /* GetCurrentThread returns a psuedo-handle, we need
00717       * a real handle for another thread to wait upon.
00718       */
00719     if (!DuplicateHandle(hProc, hThread, hProc, &(globdat.mpm_thread),
00720                          0, FALSE, DUPLICATE_SAME_ACCESS)) {
00721         return APR_ENOTHREAD;
00722     }
00723     
00724     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
00725     {
00726         globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
00727         globdat.service_term = CreateMutex(NULL, TRUE, NULL);
00728         if (!globdat.service_init || !globdat.service_term) {
00729              return APR_EGENERAL;
00730         }
00731 
00732         globdat.service_thread = CreateThread(NULL, 0, service_nt_dispatch_thread, 
00733                                               NULL, 0, &globdat.service_thread_id);
00734     }
00735     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
00736     {
00737         if (!RegisterServiceProcess(0, 1)) 
00738             return GetLastError();
00739 
00740         globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
00741         if (!globdat.service_init) {
00742             return APR_EGENERAL;
00743         }
00744 
00745         globdat.service_thread = CreateThread(NULL, 0, monitor_service_9x_thread, 
00746                                               (LPVOID) mpm_service_name, 0,
00747                                               &globdat.service_thread_id);
00748     }
00749 
00750     if (!globdat.service_thread) {
00751         return APR_ENOTHREAD;
00752     }
00753 
00754     waitfor[0] = globdat.service_init;
00755     waitfor[1] = globdat.service_thread;
00756 
00757     /* Wait for controlling thread init or termination */
00758     if (WaitForMultipleObjects(2, waitfor, FALSE, 10000) != WAIT_OBJECT_0) {
00759         return APR_ENOTHREAD;
00760     }
00761 
00762     atexit(service_stopped);
00763     *display_name = mpm_display_name; 
00764     return APR_SUCCESS;
00765 }
00766 
00767 
00768 apr_status_t mpm_service_started(void)
00769 {
00770     set_service_description();
00771     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
00772     {
00773         ReportStatusToSCMgr(SERVICE_RUNNING,    // service state
00774                             NO_ERROR,           // exit code
00775                             0);                 // wait hint
00776     }
00777     return APR_SUCCESS;
00778 }
00779 
00780 
00781 void mpm_service_stopping(void)
00782 {
00783     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
00784         ReportStatusToSCMgr(SERVICE_STOP_PENDING, // service state
00785                             NO_ERROR,             // exit code
00786                             30000);               // wait hint
00787 }
00788 
00789 
00790 apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc, 
00791                                  const char * const * argv, int reconfig)
00792 {
00793     char key_name[MAX_PATH];
00794     char exe_path[MAX_PATH];
00795     char *launch_cmd;
00796     ap_regkey_t *key;
00797     apr_status_t rv;
00798     
00799     fprintf(stderr,reconfig ? "Reconfiguring the %s service\n"
00800                    : "Installing the %s service\n", mpm_display_name);
00801 
00802     /* ###: utf-ize */
00803     if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
00804     {
00805         apr_status_t rv = apr_get_os_error();
00806         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
00807                      "GetModuleFileName failed");
00808         return rv;
00809     }
00810 
00811     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
00812     {
00813         SC_HANDLE   schService;
00814         SC_HANDLE   schSCManager;
00815 
00816         schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
00817                                      SC_MANAGER_CREATE_SERVICE);
00818         if (!schSCManager) {
00819             rv = apr_get_os_error();
00820             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
00821                          "Failed to open the WinNT service manager");
00822             return (rv);
00823         }
00824 
00825         launch_cmd = apr_psprintf(ptemp, "\"%s\" -k runservice", exe_path);
00826 
00827         if (reconfig) {
00828             /* ###: utf-ize */
00829             schService = OpenService(schSCManager, mpm_service_name, 
00830                                      SERVICE_CHANGE_CONFIG);
00831             if (!schService) {
00832                 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR, 
00833                              apr_get_os_error(), NULL,
00834                              "OpenService failed");
00835             }
00836             /* ###: utf-ize */
00837             else if (!ChangeServiceConfig(schService, 
00838                                           SERVICE_WIN32_OWN_PROCESS,
00839                                           SERVICE_AUTO_START,
00840                                           SERVICE_ERROR_NORMAL,
00841                                           launch_cmd, NULL, NULL, 
00842                                           "Tcpip\0Afd\0", NULL, NULL,
00843                                           mpm_display_name)) {
00844                 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR, 
00845                              apr_get_os_error(), NULL,
00846                              "ChangeServiceConfig failed");
00847                 /* !schService aborts configuration below */
00848                 CloseServiceHandle(schService);
00849                 schService = NULL;
00850             }
00851         }
00852         else {
00853             /* RPCSS is the Remote Procedure Call (RPC) Locator required 
00854              * for DCOM communication pipes.  I am far from convinced we 
00855              * should add this to the default service dependencies, but 
00856              * be warned that future apache modules or ISAPI dll's may 
00857              * depend on it.
00858              */
00859             /* ###: utf-ize */
00860             schService = CreateService(schSCManager,         // SCManager database
00861                                    mpm_service_name,     // name of service
00862                                    mpm_display_name,     // name to display
00863                                    SERVICE_ALL_ACCESS,   // access required
00864                                    SERVICE_WIN32_OWN_PROCESS,  // service type
00865                                    SERVICE_AUTO_START,   // start type
00866                                    SERVICE_ERROR_NORMAL, // error control type
00867                                    launch_cmd,           // service's binary
00868                                    NULL,                 // no load svc group
00869                                    NULL,                 // no tag identifier
00870                                    "Tcpip\0Afd\0",       // dependencies
00871                                    NULL,                 // use SYSTEM account
00872                                    NULL);                // no password
00873 
00874             if (!schService) 
00875             {
00876                 rv = apr_get_os_error();
00877                 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
00878                              "Failed to create WinNT Service Profile");
00879                 CloseServiceHandle(schSCManager);
00880                 return (rv);
00881             }
00882         }
00883         
00884         CloseServiceHandle(schService);
00885         CloseServiceHandle(schSCManager);
00886     }
00887     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
00888     {
00889         /* Store the launch command in the registry */
00890         launch_cmd = apr_psprintf(ptemp, "\"%s\" -n %s -k runservice", 
00891                                  exe_path, mpm_service_name);
00892         rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, SERVICECONFIG9X, 
00893                             APR_READ | APR_WRITE | APR_CREATE, pconf);
00894         if (rv == APR_SUCCESS) {
00895             rv = ap_regkey_value_set(key, mpm_service_name, 
00896                                      launch_cmd, 0, pconf);
00897             ap_regkey_close(key);
00898         }
00899         if (rv != APR_SUCCESS) {
00900             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
00901                          "%s: Failed to add the RunServices registry entry.", 
00902                          mpm_display_name);
00903             return (rv);
00904         }
00905 
00906         apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
00907         rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name, 
00908                             APR_READ | APR_WRITE | APR_CREATE, pconf);
00909         if (rv != APR_SUCCESS) {
00910             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
00911                          "%s: Failed to create the registry service key.", 
00912                          mpm_display_name);
00913             return (rv);
00914         }
00915         rv = ap_regkey_value_set(key, "ImagePath", launch_cmd, 0, pconf);
00916         if (rv != APR_SUCCESS) {
00917             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
00918                          "%s: Failed to store ImagePath in the registry.", 
00919                          mpm_display_name);
00920             ap_regkey_close(key);
00921             return (rv);
00922         }
00923         rv = ap_regkey_value_set(key, "DisplayName", 
00924                                  mpm_display_name, 0, pconf);
00925         ap_regkey_close(key);
00926         if (rv != APR_SUCCESS) {
00927             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
00928                          "%s: Failed to store DisplayName in the registry.", 
00929                          mpm_display_name);
00930             return (rv);
00931         }
00932     }
00933 
00934     set_service_description();
00935 
00936     /* For both WinNT & Win9x store the service ConfigArgs in the registry...
00937      */
00938     apr_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, mpm_service_name);
00939     rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name, 
00940                         APR_READ | APR_WRITE | APR_CREATE, pconf);
00941     if (rv == APR_SUCCESS) {
00942         rv = ap_regkey_value_array_set(key, "ConfigArgs", argc, argv, pconf);
00943         ap_regkey_close(key);
00944     }
00945     if (rv != APR_SUCCESS) {
00946         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
00947                      "%s: Failed to store the ConfigArgs in the registry.", 
00948                      mpm_display_name);
00949         return (rv);
00950     }
00951     fprintf(stderr,"The %s service is successfully installed.\n", mpm_display_name);
00952     return APR_SUCCESS;
00953 }
00954 
00955 
00956 apr_status_t mpm_service_uninstall(void)
00957 {
00958     char key_name[MAX_PATH];
00959     apr_status_t rv;
00960 
00961     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
00962     {
00963         SC_HANDLE schService;
00964         SC_HANDLE schSCManager;
00965 
00966         fprintf(stderr,"Removing the %s service\n", mpm_display_name);
00967 
00968         schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
00969                                      SC_MANAGER_CONNECT);
00970         if (!schSCManager) {
00971             rv = apr_get_os_error();
00972             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
00973                          "Failed to open the WinNT service manager.");
00974             return (rv);
00975         }
00976         
00977         /* ###: utf-ize */
00978         schService = OpenService(schSCManager, mpm_service_name, DELETE);
00979 
00980         if (!schService) {
00981            rv = apr_get_os_error();
00982            ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
00983                         "%s: OpenService failed", mpm_display_name);
00984            return (rv);
00985         }
00986         
00987         /* assure the service is stopped before continuing
00988          *
00989          * This may be out of order... we might not be able to be
00990          * granted all access if the service is running anyway.
00991          *
00992          * And do we want to make it *this easy* for them
00993          * to uninstall their service unintentionally?
00994          */
00995         // ap_stop_service(schService);
00996 
00997         if (DeleteService(schService) == 0) {
00998             rv = apr_get_os_error();
00999             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
01000                          "%s: Failed to delete the service.", mpm_display_name);
01001             return (rv);
01002         }
01003         
01004         CloseServiceHandle(schService);        
01005         CloseServiceHandle(schSCManager);
01006     }
01007     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
01008     {
01009         apr_status_t rv2, rv3;
01010         ap_regkey_t *key;
01011         fprintf(stderr,"Removing the %s service\n", mpm_display_name);
01012 
01013         /* TODO: assure the service is stopped before continuing */
01014 
01015         rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, SERVICECONFIG9X, 
01016                             APR_READ | APR_WRITE | APR_CREATE, pconf);
01017         if (rv == APR_SUCCESS) {
01018             rv = ap_regkey_value_remove(key, mpm_service_name, pconf);
01019             ap_regkey_close(key);
01020         }
01021         if (rv != APR_SUCCESS) {
01022             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
01023                          "%s: Failed to remove the RunServices registry "
01024                          "entry.", mpm_display_name);
01025         }
01026         
01027         /* we blast Services/us, not just the Services/us/Parameters branch */
01028         apr_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, mpm_service_name);
01029         rv2 = ap_regkey_remove(AP_REGKEY_LOCAL_MACHINE, key_name, pconf);
01030         apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
01031         rv3 = ap_regkey_remove(AP_REGKEY_LOCAL_MACHINE, key_name, pconf);
01032         rv2 = (rv2 != APR_SUCCESS) ? rv2 : rv3;
01033         if (rv2 != APR_SUCCESS) {
01034             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv2, NULL,
01035                          "%s: Failed to remove the service config from the "
01036                          "registry.", mpm_display_name);
01037         }
01038         rv = (rv != APR_SUCCESS) ? rv : rv2;
01039         if (rv != APR_SUCCESS)
01040             return rv;
01041     }
01042     fprintf(stderr,"The %s service has been removed successfully.\n", mpm_display_name);
01043     return APR_SUCCESS;
01044 }
01045 
01046 
01047 /* signal_service_transition is a simple thunk to signal the service
01048  * and monitor its successful transition.  If the signal passed is 0,
01049  * then the caller is assumed to already have performed some service 
01050  * operation to be monitored (such as StartService), and no actual
01051  * ControlService signal is sent.
01052  */
01053 
01054 static int signal_service_transition(SC_HANDLE schService, DWORD signal, DWORD pending, DWORD complete)
01055 {
01056     if (signal && !ControlService(schService, signal, &globdat.ssStatus)) 
01057         return FALSE;
01058     
01059     do {
01060         Sleep(1000);    
01061         if (!QueryServiceStatus(schService, &globdat.ssStatus))
01062             return FALSE;
01063     } while (globdat.ssStatus.dwCurrentState == pending);
01064         
01065     return (globdat.ssStatus.dwCurrentState == complete);
01066 }
01067 
01068 
01069 apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc, 
01070                                const char * const * argv)
01071 {
01072     apr_status_t rv;
01073     
01074     fprintf(stderr,"Starting the %s service\n", mpm_display_name);
01075 
01076     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
01077     {
01078         char **start_argv;
01079         SC_HANDLE   schService;
01080         SC_HANDLE   schSCManager;
01081 
01082         schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
01083                                      SC_MANAGER_CONNECT);
01084         if (!schSCManager) {
01085             rv = apr_get_os_error();
01086             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
01087                          "Failed to open the WinNT service manager");
01088             return (rv);
01089         }
01090 
01091         /* ###: utf-ize */
01092         schService = OpenService(schSCManager, mpm_service_name, 
01093                                  SERVICE_START | SERVICE_QUERY_STATUS);
01094         if (!schService) {
01095             rv = apr_get_os_error();
01096             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
01097                          "%s: Failed to open the service.", mpm_display_name);
01098             CloseServiceHandle(schSCManager);
01099             return (rv);
01100         }
01101 
01102         if (QueryServiceStatus(schService, &globdat.ssStatus)
01103             && (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)) {
01104             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL,
01105                          "Service %s is already started!", mpm_display_name);
01106             CloseServiceHandle(schService);
01107             CloseServiceHandle(schSCManager);
01108             return 0;
01109         }
01110         
01111         start_argv = malloc((argc + 1) * sizeof(const char **));
01112         memcpy(start_argv, argv, argc * sizeof(const char **));
01113         start_argv[argc] = NULL;
01114 
01115         rv = APR_EINIT;
01116         /* ###: utf-ize */
01117         if (StartService(schService, argc, start_argv)
01118             && signal_service_transition(schService, 0, /* test only */
01119                                          SERVICE_START_PENDING, 
01120                                          SERVICE_RUNNING))
01121                 rv = APR_SUCCESS;
01122 
01123         if (rv != APR_SUCCESS)
01124             rv = apr_get_os_error();
01125         
01126         CloseServiceHandle(schService);
01127         CloseServiceHandle(schSCManager);
01128     }
01129     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
01130     {
01131         STARTUPINFO si;           /* Filled in prior to call to CreateProcess */
01132         PROCESS_INFORMATION pi;   /* filled in on call to CreateProcess */
01133         char exe_path[MAX_PATH];
01134         char exe_cmd[MAX_PATH * 4];
01135         char *next_arg;
01136         int i;
01137 
01138         /* Locate the active top level window named service_name
01139          * provided the class is ApacheWin95ServiceMonitor
01140          */
01141         if (FindWindow("ApacheWin95ServiceMonitor", mpm_service_name)) {
01142             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL,
01143                          "Service %s is already started!", mpm_display_name);
01144             return 0;
01145         }
01146 
01147         /* This may not appear intuitive, but Win9x will not allow a process
01148          * to detach from the console without releasing the entire console.
01149          * Ergo, we must spawn a new process for the service to get back our
01150          * console window.
01151          * The config is pre-flighted, so there should be no danger of failure.
01152          */
01153         
01154         if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
01155         {
01156             apr_status_t rv = apr_get_os_error();
01157             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
01158                          "GetModuleFileName failed");
01159             return rv;
01160         }
01161         
01162         apr_snprintf(exe_cmd, sizeof(exe_cmd), 
01163                      "\"%s\" -n %s -k runservice", 
01164                      exe_path, mpm_service_name);  
01165         next_arg = strchr(exe_cmd, '\0');
01166         for (i = 0; i < argc; ++i) {
01167             apr_snprintf(next_arg, sizeof(exe_cmd) - (next_arg - exe_cmd), 
01168                          " \"%s\"", argv[i]);
01169             next_arg = strchr(exe_cmd, '\0');
01170         }
01171         
01172         memset(&si, 0, sizeof(si));
01173         memset(&pi, 0, sizeof(pi));
01174         si.cb = sizeof(si);
01175         si.dwFlags     = STARTF_USESHOWWINDOW;
01176         si.wShowWindow = SW_HIDE;   /* This might be redundant */
01177         
01178         rv = APR_EINIT;
01179         if (CreateProcess(NULL, exe_cmd, NULL, NULL, FALSE, 
01180                            DETACHED_PROCESS, /* Creation flags */
01181                            NULL, NULL, &si, &pi)) 
01182         {
01183             DWORD code;
01184             while (GetExitCodeProcess(pi.hProcess, &code) == STILL_ACTIVE) {
01185                 if (FindWindow("ApacheWin95ServiceMonitor", mpm_service_name)) {
01186                     rv = APR_SUCCESS;
01187                     break;
01188                 }
01189                 Sleep (1000);
01190             }
01191         }
01192         
01193         if (rv != APR_SUCCESS)
01194             rv = apr_get_os_error();
01195         
01196         CloseHandle(pi.hProcess);
01197         CloseHandle(pi.hThread);
01198     }    
01199 
01200     if (rv == APR_SUCCESS)
01201         fprintf(stderr,"The %s service is running.\n", mpm_display_name);
01202     else
01203         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
01204                      "%s: Failed to start the service process.",
01205                      mpm_display_name);
01206         
01207     return rv;
01208 }
01209 
01210 
01211 /* signal is zero to stop, non-zero for restart */
01212 
01213 void mpm_signal_service(apr_pool_t *ptemp, int signal)
01214 {
01215     int success = FALSE;
01216     
01217     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) 
01218     {
01219         SC_HANDLE   schService;
01220         SC_HANDLE   schSCManager;
01221 
01222         schSCManager = OpenSCManager(NULL, NULL, // default machine & database
01223                                      SC_MANAGER_CONNECT);
01224         
01225         if (!schSCManager) {
01226             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
01227                          "Failed to open the NT Service Manager");
01228             return;
01229         }
01230 
01231         /* ###: utf-ize */
01232         schService = OpenService(schSCManager, mpm_service_name, 
01233                                  SERVICE_INTERROGATE | SERVICE_QUERY_STATUS | 
01234                                  SERVICE_USER_DEFINED_CONTROL |
01235                                  SERVICE_START | SERVICE_STOP);
01236 
01237         if (schService == NULL) {
01238             /* Could not open the service */
01239             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
01240                          "Failed to open the %s Service", mpm_display_name);
01241             CloseServiceHandle(schSCManager);
01242             return;
01243         }
01244         
01245         if (!QueryServiceStatus(schService, &globdat.ssStatus)) {
01246             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
01247                          "Query of Service %s failed", mpm_display_name);
01248             CloseServiceHandle(schService);
01249             CloseServiceHandle(schSCManager);
01250             return;
01251         }
01252 
01253         if (!signal && (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)) {
01254             fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
01255             CloseServiceHandle(schService);
01256             CloseServiceHandle(schSCManager);
01257             return;
01258         }
01259         
01260         fprintf(stderr,"The %s service is %s.\n", mpm_display_name, 
01261                signal ? "restarting" : "stopping");
01262 
01263         if (!signal)
01264             success = signal_service_transition(schService, 
01265                                                 SERVICE_CONTROL_STOP, 
01266                                                 SERVICE_STOP_PENDING, 
01267                                                 SERVICE_STOPPED);
01268         else if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
01269             mpm_service_start(ptemp, 0, NULL);
01270             CloseServiceHandle(schService);
01271             CloseServiceHandle(schSCManager);
01272             return;
01273         }
01274         else
01275             success = signal_service_transition(schService, 
01276                                                 SERVICE_APACHE_RESTART, 
01277                                                 SERVICE_START_PENDING, 
01278                                                 SERVICE_RUNNING);
01279 
01280         CloseServiceHandle(schService);
01281         CloseServiceHandle(schSCManager);
01282     }
01283     else /* !isWindowsNT() */
01284     {
01285         DWORD       service_pid;
01286         HANDLE      hwnd;
01287         char prefix[20];
01288         /* Locate the active top level window named service_name
01289          * provided the class is ApacheWin95ServiceMonitor
01290          */
01291         hwnd = FindWindow("ApacheWin95ServiceMonitor", mpm_service_name);
01292         if (hwnd && GetWindowThreadProcessId(hwnd, &service_pid))
01293             globdat.ssStatus.dwCurrentState = SERVICE_RUNNING;
01294         else
01295         {
01296             globdat.ssStatus.dwCurrentState = SERVICE_STOPPED;
01297             if (!signal) {
01298                 fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
01299                 return;
01300             }
01301         }
01302 
01303         fprintf(stderr,"The %s service is %s.\n", mpm_display_name, 
01304                signal ? "restarting" : "stopping");
01305 
01306         apr_snprintf(prefix, sizeof(prefix), "ap%ld", (long)service_pid);
01307         setup_signal_names(prefix);
01308 
01309         if (!signal) 
01310         {
01311             int ticks = 60;
01312             ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
01313             while (--ticks)
01314             {
01315                 if (!IsWindow(hwnd)) {
01316                     success = TRUE;
01317                     break;
01318                 }
01319                 Sleep(1000);
01320             }
01321         }
01322         else /* !stop */
01323         {   
01324             /* TODO: Aught to add a little test to the restart logic, and
01325              * store the restart counter in the window's user dword.
01326              * Then we can hang on and report a successful restart.  But
01327              * that's a project for another day.
01328              */
01329             if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
01330                 mpm_service_start(ptemp, 0, NULL);
01331                 return;
01332             }
01333             else {
01334                 success = TRUE;
01335                 ap_signal_parent(SIGNAL_PARENT_RESTART);
01336             }
01337         }
01338     }
01339 
01340     if (success)
01341         fprintf(stderr,"The %s service has %s.\n", mpm_display_name, 
01342                signal ? "restarted" : "stopped");
01343     else
01344         fprintf(stderr,"Failed to %s the %s service.\n", 
01345                signal ? "restart" : "stop", mpm_display_name);
01346 }