]> granicus.if.org Git - apache/blob - server/mpm/winnt/service.c
WIN64: fix some windows specific 64bit warnings
[apache] / server / mpm / winnt / service.c
1 /* Copyright 1999-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /* This module ALONE requires the window message API from user.h 
17  * and the default APR include of windows.h will omit it, so
18  * preload the API symbols now...
19  */
20
21 #define CORE_PRIVATE 
22 #define _WINUSER_
23
24 #include "httpd.h"
25 #include "http_log.h"
26 #include "mpm_winnt.h"
27 #include "apr_strings.h"
28 #include "apr_lib.h"
29 #include "ap_regkey.h"
30
31 #ifdef NOUSER
32 #undef NOUSER
33 #endif
34 #undef _WINUSER_
35 #include <winuser.h>
36
37 static char *mpm_service_name = NULL;
38 static char *mpm_display_name = NULL;
39
40 static struct
41 {
42     HANDLE mpm_thread;       /* primary thread handle of the apache server */
43     HANDLE service_thread;   /* thread service/monitor handle */
44     DWORD  service_thread_id;/* thread service/monitor ID */
45     HANDLE service_init;     /* controller thread init mutex */
46     HANDLE service_term;     /* NT service thread kill signal */
47     SERVICE_STATUS ssStatus;
48     SERVICE_STATUS_HANDLE hServiceStatus;
49 } globdat;
50
51 static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint);
52
53
54 #define PRODREGKEY "SOFTWARE\\" AP_SERVER_BASEVENDOR "\\" \
55                    AP_SERVER_BASEPRODUCT "\\" AP_SERVER_BASEREVISION
56
57 /*
58  * Get the server root from the registry into 'dir' which is
59  * size bytes long. Returns 0 if the server root was found
60  * or if the serverroot key does not exist (in which case
61  * dir will contain an empty string), or -1 if there was
62  * an error getting the key.
63  */
64 apr_status_t ap_registry_get_server_root(apr_pool_t *p, char **buf)
65 {
66     apr_status_t rv;
67     ap_regkey_t *key;
68
69     if ((rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, PRODREGKEY, 
70                              APR_READ, p)) == APR_SUCCESS) {
71         rv = ap_regkey_value_get(buf, key, "ServerRoot", p);
72         ap_regkey_close(key);
73         if (rv == APR_SUCCESS) 
74             return rv;
75     }
76
77     if ((rv = ap_regkey_open(&key, AP_REGKEY_CURRENT_USER, PRODREGKEY, 
78                              APR_READ, p)) == APR_SUCCESS) {
79         rv = ap_regkey_value_get(buf, key, "ServerRoot", p);
80         ap_regkey_close(key);
81         if (rv == APR_SUCCESS) 
82             return rv;
83     }
84
85     *buf = NULL;
86     return rv;
87 }
88
89
90 /* The service configuration's is stored under the following trees:
91  *
92  * HKLM\System\CurrentControlSet\Services\[service name]
93  *
94  *     \DisplayName
95  *     \ImagePath
96  *     \Parameters\ConfigArgs
97  *
98  * For Win9x, the launch service command is stored under:
99  *
100  * HKLM\Software\Microsoft\Windows\CurrentVersion\RunServices\[service name]
101  */
102
103
104 /* exit() for Win32 is macro mapped (horrible, we agree) that allows us 
105  * to catch the non-zero conditions and inform the console process that
106  * the application died, and hang on to the console a bit longer.
107  *
108  * The macro only maps for http_main.c and other sources that include
109  * the service.h header, so we best assume it's an error to exit from
110  * _any_ other module.
111  *
112  * If real_exit_code is reset to 0, it will not be set or trigger this
113  * behavior on exit.  All service and child processes are expected to
114  * reset this flag to zero to avoid undesireable side effects.
115  */
116 AP_DECLARE_DATA int real_exit_code = 1;
117
118 void hold_console_open_on_error(void)
119 {
120     HANDLE hConIn;
121     HANDLE hConErr;
122     DWORD result;
123     time_t start;
124     time_t remains;
125     char *msg = "Note the errors or messages above, "
126                 "and press the <ESC> key to exit.  ";
127     CONSOLE_SCREEN_BUFFER_INFO coninfo;
128     INPUT_RECORD in;
129     char count[16];
130     
131     if (!real_exit_code)
132         return;
133     hConIn = GetStdHandle(STD_INPUT_HANDLE);
134     hConErr = GetStdHandle(STD_ERROR_HANDLE);
135     if ((hConIn == INVALID_HANDLE_VALUE) || (hConErr == INVALID_HANDLE_VALUE))
136         return;
137     if (!WriteConsole(hConErr, msg, (int)strlen(msg), &result, NULL) || !result)
138         return;
139     if (!GetConsoleScreenBufferInfo(hConErr, &coninfo))
140         return;
141     if (!SetConsoleMode(hConIn, ENABLE_MOUSE_INPUT | 0x80))
142         return;
143         
144     start = time(NULL);
145     do
146     {
147         while (PeekConsoleInput(hConIn, &in, 1, &result) && result)
148         {
149             if (!ReadConsoleInput(hConIn, &in, 1, &result) || !result)
150                 return;
151             if ((in.EventType == KEY_EVENT) && in.Event.KeyEvent.bKeyDown 
152                     && (in.Event.KeyEvent.uChar.AsciiChar == 27))
153                 return;
154             if (in.EventType == MOUSE_EVENT 
155                     && (in.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK))
156                 return;
157         }
158         remains = ((start + 30) - time(NULL)); 
159         sprintf (count, "%d...", remains);
160         if (!SetConsoleCursorPosition(hConErr, coninfo.dwCursorPosition))
161             return;
162         if (!WriteConsole(hConErr, count, (int)strlen(count), &result, NULL) 
163                 || !result)
164             return;
165     }
166     while ((remains > 0) && WaitForSingleObject(hConIn, 1000) != WAIT_FAILED);
167 }
168
169 static BOOL  die_on_logoff = FALSE;
170
171 static LRESULT CALLBACK monitor_service_9x_proc(HWND hWnd, UINT msg, 
172                                                 WPARAM wParam, LPARAM lParam)
173 {
174 /* This is the WndProc procedure for our invisible window.
175  * When the user shuts down the system, this window is sent
176  * a signal WM_ENDSESSION. We clean up by signaling Apache
177  * to shut down, and idle until Apache's primary thread quits.
178  */
179     if ((msg == WM_ENDSESSION) 
180             && (die_on_logoff || (lParam != ENDSESSION_LOGOFF)))
181     {
182         ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
183         if (wParam)
184             /* Don't leave this message until we are dead! */
185             WaitForSingleObject(globdat.mpm_thread, 30000);
186         return 0;
187     }
188     return (DefWindowProc(hWnd, msg, wParam, lParam));
189 }
190
191 static DWORD WINAPI monitor_service_9x_thread(void *service_name)
192 {
193     /* When running as a service under Windows 9x, there is no console
194      * window present, and no ConsoleCtrlHandler to call when the system 
195      * is shutdown.  If the WatchWindow thread is created with a NULL
196      * service_name argument, then the ...SystemMonitor window class is
197      * used to create the "Apache" window to watch for logoff and shutdown.
198      * If the service_name is provided, the ...ServiceMonitor window class
199      * is used to create the window named by the service_name argument,
200      * and the logoff message is ignored.
201      */
202     WNDCLASS wc;
203     HWND hwndMain;
204     MSG msg;
205     
206     wc.style         = CS_GLOBALCLASS;
207     wc.lpfnWndProc   = monitor_service_9x_proc; 
208     wc.cbClsExtra    = 0;
209     wc.cbWndExtra    = 0; 
210     wc.hInstance     = NULL;
211     wc.hIcon         = NULL;
212     wc.hCursor       = NULL;
213     wc.hbrBackground = NULL;
214     wc.lpszMenuName  = NULL;
215     if (service_name)
216         wc.lpszClassName = "ApacheWin95ServiceMonitor";
217     else
218         wc.lpszClassName = "ApacheWin95SystemMonitor";
219  
220     die_on_logoff = service_name ? FALSE : TRUE;
221
222     if (!RegisterClass(&wc)) 
223     {
224         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), 
225                      NULL, "Could not register window class for WatchWindow");
226         globdat.service_thread_id = 0;
227         return 0;
228     }
229     
230     /* Create an invisible window */
231     hwndMain = CreateWindow(wc.lpszClassName, 
232                             service_name ? (char *) service_name : "Apache",
233                             WS_OVERLAPPEDWINDOW & ~WS_VISIBLE, 
234                             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
235                             CW_USEDEFAULT, NULL, NULL, NULL, NULL);
236                             
237     if (!hwndMain)
238     {
239         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), 
240                      NULL, "Could not create WatchWindow");
241         globdat.service_thread_id = 0;
242         return 0;
243     }
244
245     /* If we succeed, eliminate the console window.
246      * Signal the parent we are all set up, and
247      * watch the message queue while the window lives.
248      */
249     FreeConsole();
250     SetEvent(globdat.service_init);
251
252     while (GetMessage(&msg, NULL, 0, 0)) 
253     {
254         if (msg.message == WM_CLOSE)
255             DestroyWindow(hwndMain); 
256         else {
257             TranslateMessage(&msg);
258             DispatchMessage(&msg);
259         }
260     }
261     globdat.service_thread_id = 0;
262     return 0;
263 }
264
265
266 static BOOL CALLBACK console_control_handler(DWORD ctrl_type)
267 {
268     switch (ctrl_type)
269     {
270         case CTRL_BREAK_EVENT:
271             fprintf(stderr, "Apache server restarting...\n");
272             ap_signal_parent(SIGNAL_PARENT_RESTART);
273             return TRUE;
274         case CTRL_C_EVENT:
275             fprintf(stderr, "Apache server interrupted...\n");
276             /* for Interrupt signals, shut down the server.
277              * Tell the system we have dealt with the signal
278              * without waiting for Apache to terminate.
279              */
280             ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
281             return TRUE;
282
283         case CTRL_CLOSE_EVENT:
284         case CTRL_LOGOFF_EVENT:
285         case CTRL_SHUTDOWN_EVENT:
286             /* for Terminate signals, shut down the server.
287              * Wait for Apache to terminate, but respond
288              * after a reasonable time to tell the system
289              * that we did attempt to shut ourself down.
290              * THESE EVENTS WILL NOT OCCUR UNDER WIN9x!
291              */
292             fprintf(stderr, "Apache server shutdown initiated...\n");
293             ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
294             Sleep(30000);
295             return TRUE;
296     }
297  
298     /* We should never get here, but this is (mostly) harmless */
299     return FALSE;
300 }
301
302
303 static void stop_console_handler(void)
304 {
305     SetConsoleCtrlHandler(console_control_handler, FALSE);
306 }
307
308
309 void mpm_start_console_handler(void)
310 {
311     SetConsoleCtrlHandler(console_control_handler, TRUE);
312     atexit(stop_console_handler);
313 }
314
315
316 /* Special situation - children of services need to mind their
317  * P's & Q's and wait quietly, ignoring the mean OS signaling
318  * shutdown and other horrors, to kill them gracefully...
319  */
320
321 static BOOL CALLBACK child_control_handler(DWORD ctrl_type)
322 {
323     switch (ctrl_type)
324     {
325         case CTRL_C_EVENT:
326         case CTRL_BREAK_EVENT:
327             /* for Interrupt signals, ignore them.
328              * The system will also signal the parent process,
329              * which will terminate Apache.
330              */
331             return TRUE;
332
333         case CTRL_CLOSE_EVENT:
334         case CTRL_LOGOFF_EVENT:
335         case CTRL_SHUTDOWN_EVENT:
336             /* for Shutdown signals, ignore them, but...             .
337              * The system will also signal the parent process,
338              * which will terminate Apache, so we need to wait.
339              */
340             Sleep(30000);
341             return TRUE;
342     }
343  
344     /* We should never get here, but this is (mostly) harmless */
345     return FALSE;
346 }
347
348
349 static void stop_child_console_handler(void)
350 {
351     SetConsoleCtrlHandler(child_control_handler, FALSE);
352 }
353
354
355 void mpm_start_child_console_handler(void)
356 {
357     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) {
358         FreeConsole();
359     }
360     else
361     {
362         SetConsoleCtrlHandler(child_control_handler, TRUE);
363         atexit(stop_child_console_handler);
364     }
365 }
366
367
368 /**********************************
369   WinNT service control management
370  **********************************/
371
372 static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
373 {
374     static int checkPoint = 1;
375     int rv = APR_SUCCESS;
376     
377     if (globdat.hServiceStatus)
378     {
379         if (currentState == SERVICE_RUNNING) {
380             globdat.ssStatus.dwWaitHint = 0;
381             globdat.ssStatus.dwCheckPoint = 0;
382             globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
383         }
384         else if (currentState == SERVICE_STOPPED) {
385             globdat.ssStatus.dwWaitHint = 0;
386             globdat.ssStatus.dwCheckPoint = 0;
387             if (!exitCode && globdat.ssStatus.dwCurrentState 
388                                            != SERVICE_STOP_PENDING) {
389                 /* An unexpected exit?  Better to error! */
390                 exitCode = 1;
391             }
392             if (exitCode) {
393                 globdat.ssStatus.dwWin32ExitCode =ERROR_SERVICE_SPECIFIC_ERROR;
394                 globdat.ssStatus.dwServiceSpecificExitCode = exitCode;
395             }
396         }
397         else {
398             globdat.ssStatus.dwCheckPoint = ++checkPoint;
399             globdat.ssStatus.dwControlsAccepted = 0;
400             if(waitHint)
401                 globdat.ssStatus.dwWaitHint = waitHint;
402         }
403
404         globdat.ssStatus.dwCurrentState = currentState;
405         
406         rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus);
407     }
408     return(rv);
409 }
410
411 /* Set the service description regardless of platform.
412  * We revert to set_service_description on NT/9x, the
413  * very long way so any Apache management program can grab the
414  * description.  This would be bad on Win2000, since it wouldn't
415  * notify the service control manager of the name change.
416  */
417
418 /* borrowed from mpm_winnt.c */
419 extern apr_pool_t *pconf;
420
421 /* Windows 2000 alone supports ChangeServiceConfig2 in order to
422  * register our server_version string... so we need some fixups
423  * to avoid binding to that function if we are on WinNT/9x.
424  */
425 static void set_service_description(void)
426 {
427     const char *full_description;
428     SC_HANDLE schSCManager;
429     BOOL ret = 0;
430
431     /* Nothing to do if we are a console
432      */
433     if (!mpm_service_name)
434         return;
435
436     /* Time to fix up the description, upon each successful restart
437      */
438     full_description = ap_get_server_version();
439
440     if ((osver.dwPlatformId == VER_PLATFORM_WIN32_NT) 
441           && (osver.dwMajorVersion > 4) 
442           && (ChangeServiceConfig2)
443           && (schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)))
444     {    
445         SC_HANDLE schService = OpenService(schSCManager, mpm_service_name,
446                                            SERVICE_CHANGE_CONFIG);
447         if (schService) {
448             /* Cast is necessary, ChangeServiceConfig2 handles multiple
449              * object types, some volatile, some not.
450              */
451             /* ###: utf-ize */
452             if (ChangeServiceConfig2(schService,
453                                      1 /* SERVICE_CONFIG_DESCRIPTION */,
454                                      (LPVOID) &full_description)) {
455                 full_description = NULL;
456             }
457             CloseServiceHandle(schService);
458         }
459         CloseServiceHandle(schSCManager);
460     }
461
462     if (full_description) 
463     {
464         char szPath[MAX_PATH];
465         ap_regkey_t *svckey;
466         apr_status_t rv;
467
468         /* Find the Service key that Monitor Applications iterate */
469         apr_snprintf(szPath, sizeof(szPath), 
470                      "SYSTEM\\CurrentControlSet\\Services\\%s", 
471                      mpm_service_name);
472         rv = ap_regkey_open(&svckey, AP_REGKEY_LOCAL_MACHINE, szPath,
473                             APR_READ | APR_WRITE, pconf);
474         if (rv != APR_SUCCESS) {
475             return;
476         }
477         /* Attempt to set the Description value for our service */
478         ap_regkey_value_set(svckey, "Description", full_description, 0, pconf);
479         ap_regkey_close(svckey);
480     }
481 }
482
483 /* handle the SCM's ControlService() callbacks to our service */
484
485 static VOID WINAPI service_nt_ctrl(DWORD dwCtrlCode)
486 {
487     if (dwCtrlCode == SERVICE_CONTROL_STOP)
488     {
489         ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
490         ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 30000);
491         return;
492     }
493     if (dwCtrlCode == SERVICE_APACHE_RESTART)
494     {
495         ap_signal_parent(SIGNAL_PARENT_RESTART);
496         ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 30000);
497         return;
498     }
499     
500     ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, NO_ERROR, 0);            
501 }
502
503
504 /* service_nt_main_fn is outside of the call stack and outside of the
505  * primary server thread... so now we _really_ need a placeholder!
506  * The winnt_rewrite_args has created and shared mpm_new_argv with us.
507  */
508 extern apr_array_header_t *mpm_new_argv;
509
510 /* ###: utf-ize */
511 static void __stdcall service_nt_main_fn(DWORD argc, LPTSTR *argv)
512 {
513     const char *ignored;
514
515     /* args and service names live in the same pool */
516     mpm_service_set_name(mpm_new_argv->pool, &ignored, argv[0]);
517
518     memset(&globdat.ssStatus, 0, sizeof(globdat.ssStatus));
519     globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
520     globdat.ssStatus.dwCurrentState = SERVICE_START_PENDING;
521     globdat.ssStatus.dwCheckPoint = 1;
522
523     /* ###: utf-ize */
524     if (!(globdat.hServiceStatus = RegisterServiceCtrlHandler(argv[0], service_nt_ctrl)))
525     {
526         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), 
527                      NULL, "Failure registering service handler");
528         return;
529     }
530
531     /* Report status, no errors, and buy 3 more seconds */
532     ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 30000);
533
534     /* We need to append all the command arguments passed via StartService() 
535      * to our running service... which just got here via the SCM...
536      * but we hvae no interest in argv[0] for the mpm_new_argv list.
537      */
538     if (argc > 1) 
539     {
540         char **cmb_data;
541
542         mpm_new_argv->nalloc = mpm_new_argv->nelts + argc - 1;
543         cmb_data = malloc(mpm_new_argv->nalloc * sizeof(const char *));
544
545         /* mpm_new_argv remains first (of lower significance) */
546         memcpy (cmb_data, mpm_new_argv->elts, 
547                 mpm_new_argv->elt_size * mpm_new_argv->nelts);
548         
549         /* Service args follow from StartService() invocation */
550         memcpy (cmb_data + mpm_new_argv->nelts, argv + 1, 
551                 mpm_new_argv->elt_size * (argc - 1));
552         
553         /* The replacement arg list is complete */
554         mpm_new_argv->elts = (char *)cmb_data;
555         mpm_new_argv->nelts = mpm_new_argv->nalloc;
556     }
557
558     /* Let the main thread continue now... but hang on to the
559      * signal_monitor event so we can take further action
560      */
561     SetEvent(globdat.service_init);
562
563     WaitForSingleObject(globdat.service_term, INFINITE);
564 }
565
566
567 DWORD WINAPI service_nt_dispatch_thread(LPVOID nada)
568 {
569     apr_status_t rv = APR_SUCCESS;
570
571     SERVICE_TABLE_ENTRY dispatchTable[] =
572     {
573         { "", service_nt_main_fn },
574         { NULL, NULL }
575     };
576
577     /* ###: utf-ize */
578     if (!StartServiceCtrlDispatcher(dispatchTable))
579     {
580         /* This is a genuine failure of the SCM. */
581         rv = apr_get_os_error();
582         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
583                      "Error starting service control dispatcher");
584     }
585
586     return (rv);
587 }
588
589
590 apr_status_t mpm_service_set_name(apr_pool_t *p, const char **display_name, 
591                                   const char *set_name)
592 {
593     char key_name[MAX_PATH];
594     ap_regkey_t *key;
595     apr_status_t rv;
596
597     /* ### Needs improvement, on Win2K the user can _easily_ 
598      * change the display name to a string that doesn't reflect 
599      * the internal service name + whitespace!
600      */
601     mpm_service_name = apr_palloc(p, strlen(set_name) + 1);
602     apr_collapse_spaces((char*) mpm_service_name, set_name);
603     apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
604     rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name, APR_READ, pconf);
605     if (rv == APR_SUCCESS) {
606         rv = ap_regkey_value_get(&mpm_display_name, key, "DisplayName", pconf);
607         ap_regkey_close(key);
608     }
609     if (rv != APR_SUCCESS) {
610         /* Take the given literal name if there is no service entry */
611         mpm_display_name = apr_pstrdup(p, set_name);
612     } 
613     *display_name = mpm_display_name;
614     return rv;
615 }
616
617
618 apr_status_t mpm_merge_service_args(apr_pool_t *p, 
619                                    apr_array_header_t *args, 
620                                    int fixed_args)
621 {
622     apr_array_header_t *svc_args = NULL;
623     char conf_key[MAX_PATH];
624     char **cmb_data;
625     apr_status_t rv;
626     ap_regkey_t *key;
627
628     apr_snprintf(conf_key, sizeof(conf_key), SERVICEPARAMS, mpm_service_name);
629     rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, conf_key, APR_READ, p);
630     if (rv == APR_SUCCESS) {
631         rv = ap_regkey_value_array_get(&svc_args, key, "ConfigArgs", p);
632         ap_regkey_close(key);
633     }
634     if (rv != APR_SUCCESS) {
635         if (rv == ERROR_FILE_NOT_FOUND) {
636             ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
637                          "No ConfigArgs registered for %s, perhaps "
638                          "this service is not installed?", 
639                          mpm_service_name);
640             return APR_SUCCESS;
641         }
642         else
643             return (rv);        
644     }
645
646     if (!svc_args || svc_args->nelts == 0) {
647         return (APR_SUCCESS);
648     }
649
650     /* Now we have the mpm_service_name arg, and the mpm_runservice_nt()
651      * call appended the arguments passed by StartService(), so it's  
652      * time to _prepend_ the default arguments for the server from 
653      * the service's default arguments (all others override them)...
654      */
655     args->nalloc = args->nelts + svc_args->nelts;
656     cmb_data = malloc(args->nalloc * sizeof(const char *));
657
658     /* First three args (argv[0], -f, path) remain first */
659     memcpy(cmb_data, args->elts, args->elt_size * fixed_args);
660     
661     /* Service args follow from service registry array */
662     memcpy(cmb_data + fixed_args, svc_args->elts, 
663            svc_args->elt_size * svc_args->nelts);
664     
665     /* Remaining new args follow  */
666     memcpy(cmb_data + fixed_args + svc_args->nelts,
667            (const char **)args->elts + fixed_args, 
668            args->elt_size * (args->nelts - fixed_args));
669     
670     args->elts = (char *)cmb_data;
671     args->nelts = args->nalloc;
672
673     return APR_SUCCESS;
674 }
675
676
677 void service_stopped(void)
678 {
679     /* Still have a thread & window to clean up, so signal now */
680     if (globdat.service_thread)
681     {
682         if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
683         {
684             /* Stop logging to the event log */
685             mpm_nt_eventlog_stderr_flush();
686
687             /* Cause the service_nt_main_fn to complete */
688             ReleaseMutex(globdat.service_term);
689
690             ReportStatusToSCMgr(SERVICE_STOPPED, // service state
691                                 NO_ERROR,        // exit code
692                                 0);              // wait hint
693         }
694         else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
695         {
696             RegisterServiceProcess(0, 0);
697             PostThreadMessage(globdat.service_thread_id, WM_CLOSE, 0, 0);
698         }
699
700         WaitForSingleObject(globdat.service_thread, 5000);
701         CloseHandle(globdat.service_thread);
702     }
703 }
704
705
706 apr_status_t mpm_service_to_start(const char **display_name, apr_pool_t *p)
707 {
708     HANDLE hProc = GetCurrentProcess();
709     HANDLE hThread = GetCurrentThread();
710     HANDLE waitfor[2];
711
712     /* Prevent holding open the (hidden) console */
713     real_exit_code = 0;
714
715      /* GetCurrentThread returns a psuedo-handle, we need
716       * a real handle for another thread to wait upon.
717       */
718     if (!DuplicateHandle(hProc, hThread, hProc, &(globdat.mpm_thread),
719                          0, FALSE, DUPLICATE_SAME_ACCESS)) {
720         return APR_ENOTHREAD;
721     }
722     
723     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
724     {
725         globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
726         globdat.service_term = CreateMutex(NULL, TRUE, NULL);
727         if (!globdat.service_init || !globdat.service_term) {
728              return APR_EGENERAL;
729         }
730
731         globdat.service_thread = CreateThread(NULL, 0, service_nt_dispatch_thread, 
732                                               NULL, 0, &globdat.service_thread_id);
733     }
734     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
735     {
736         if (!RegisterServiceProcess(0, 1)) 
737             return GetLastError();
738
739         globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
740         if (!globdat.service_init) {
741             return APR_EGENERAL;
742         }
743
744         globdat.service_thread = CreateThread(NULL, 0, monitor_service_9x_thread, 
745                                               (LPVOID) mpm_service_name, 0,
746                                               &globdat.service_thread_id);
747     }
748
749     if (!globdat.service_thread) {
750         return APR_ENOTHREAD;
751     }
752
753     waitfor[0] = globdat.service_init;
754     waitfor[1] = globdat.service_thread;
755
756     /* Wait for controlling thread init or termination */
757     if (WaitForMultipleObjects(2, waitfor, FALSE, 10000) != WAIT_OBJECT_0) {
758         return APR_ENOTHREAD;
759     }
760
761     atexit(service_stopped);
762     *display_name = mpm_display_name; 
763     return APR_SUCCESS;
764 }
765
766
767 apr_status_t mpm_service_started(void)
768 {
769     set_service_description();
770     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
771     {
772         ReportStatusToSCMgr(SERVICE_RUNNING,    // service state
773                             NO_ERROR,           // exit code
774                             0);                 // wait hint
775     }
776     return APR_SUCCESS;
777 }
778
779
780 void mpm_service_stopping(void)
781 {
782     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
783         ReportStatusToSCMgr(SERVICE_STOP_PENDING, // service state
784                             NO_ERROR,             // exit code
785                             30000);               // wait hint
786 }
787
788
789 apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc, 
790                                  const char * const * argv, int reconfig)
791 {
792     char key_name[MAX_PATH];
793     char exe_path[MAX_PATH];
794     char *launch_cmd;
795     ap_regkey_t *key;
796     apr_status_t rv;
797     
798     fprintf(stderr,reconfig ? "Reconfiguring the %s service\n"
799                    : "Installing the %s service\n", mpm_display_name);
800
801     /* ###: utf-ize */
802     if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
803     {
804         apr_status_t rv = apr_get_os_error();
805         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
806                      "GetModuleFileName failed");
807         return rv;
808     }
809
810     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
811     {
812         SC_HANDLE   schService;
813         SC_HANDLE   schSCManager;
814
815         schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
816                                      SC_MANAGER_CREATE_SERVICE);
817         if (!schSCManager) {
818             rv = apr_get_os_error();
819             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
820                          "Failed to open the WinNT service manager");
821             return (rv);
822         }
823
824         launch_cmd = apr_psprintf(ptemp, "\"%s\" -k runservice", exe_path);
825
826         if (reconfig) {
827             /* ###: utf-ize */
828             schService = OpenService(schSCManager, mpm_service_name, 
829                                      SERVICE_CHANGE_CONFIG);
830             if (!schService) {
831                 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR, 
832                              apr_get_os_error(), NULL,
833                              "OpenService failed");
834             }
835             /* ###: utf-ize */
836             else if (!ChangeServiceConfig(schService, 
837                                           SERVICE_WIN32_OWN_PROCESS,
838                                           SERVICE_AUTO_START,
839                                           SERVICE_ERROR_NORMAL,
840                                           launch_cmd, NULL, NULL, 
841                                           "Tcpip\0Afd\0", NULL, NULL,
842                                           mpm_display_name)) {
843                 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR, 
844                              apr_get_os_error(), NULL,
845                              "ChangeServiceConfig failed");
846                 /* !schService aborts configuration below */
847                 CloseServiceHandle(schService);
848                 schService = NULL;
849             }
850         }
851         else {
852             /* RPCSS is the Remote Procedure Call (RPC) Locator required 
853              * for DCOM communication pipes.  I am far from convinced we 
854              * should add this to the default service dependencies, but 
855              * be warned that future apache modules or ISAPI dll's may 
856              * depend on it.
857              */
858             /* ###: utf-ize */
859             schService = CreateService(schSCManager,         // SCManager database
860                                    mpm_service_name,     // name of service
861                                    mpm_display_name,     // name to display
862                                    SERVICE_ALL_ACCESS,   // access required
863                                    SERVICE_WIN32_OWN_PROCESS,  // service type
864                                    SERVICE_AUTO_START,   // start type
865                                    SERVICE_ERROR_NORMAL, // error control type
866                                    launch_cmd,           // service's binary
867                                    NULL,                 // no load svc group
868                                    NULL,                 // no tag identifier
869                                    "Tcpip\0Afd\0",       // dependencies
870                                    NULL,                 // use SYSTEM account
871                                    NULL);                // no password
872
873             if (!schService) 
874             {
875                 rv = apr_get_os_error();
876                 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
877                              "Failed to create WinNT Service Profile");
878                 CloseServiceHandle(schSCManager);
879                 return (rv);
880             }
881         }
882         
883         CloseServiceHandle(schService);
884         CloseServiceHandle(schSCManager);
885     }
886     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
887     {
888         /* Store the launch command in the registry */
889         launch_cmd = apr_psprintf(ptemp, "\"%s\" -n %s -k runservice", 
890                                  exe_path, mpm_service_name);
891         rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, SERVICECONFIG9X, 
892                             APR_READ | APR_WRITE | APR_CREATE, pconf);
893         if (rv == APR_SUCCESS) {
894             rv = ap_regkey_value_set(key, mpm_service_name, 
895                                      launch_cmd, 0, pconf);
896             ap_regkey_close(key);
897         }
898         if (rv != APR_SUCCESS) {
899             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
900                          "%s: Failed to add the RunServices registry entry.", 
901                          mpm_display_name);
902             return (rv);
903         }
904
905         apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
906         rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name, 
907                             APR_READ | APR_WRITE | APR_CREATE, pconf);
908         if (rv != APR_SUCCESS) {
909             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
910                          "%s: Failed to create the registry service key.", 
911                          mpm_display_name);
912             return (rv);
913         }
914         rv = ap_regkey_value_set(key, "ImagePath", launch_cmd, 0, pconf);
915         if (rv != APR_SUCCESS) {
916             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
917                          "%s: Failed to store ImagePath in the registry.", 
918                          mpm_display_name);
919             ap_regkey_close(key);
920             return (rv);
921         }
922         rv = ap_regkey_value_set(key, "DisplayName", 
923                                  mpm_display_name, 0, pconf);
924         ap_regkey_close(key);
925         if (rv != APR_SUCCESS) {
926             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
927                          "%s: Failed to store DisplayName in the registry.", 
928                          mpm_display_name);
929             return (rv);
930         }
931     }
932
933     set_service_description();
934
935     /* For both WinNT & Win9x store the service ConfigArgs in the registry...
936      */
937     apr_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, mpm_service_name);
938     rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name, 
939                         APR_READ | APR_WRITE | APR_CREATE, pconf);
940     if (rv == APR_SUCCESS) {
941         rv = ap_regkey_value_array_set(key, "ConfigArgs", argc, argv, pconf);
942         ap_regkey_close(key);
943     }
944     if (rv != APR_SUCCESS) {
945         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL, 
946                      "%s: Failed to store the ConfigArgs in the registry.", 
947                      mpm_display_name);
948         return (rv);
949     }
950     fprintf(stderr,"The %s service is successfully installed.\n", mpm_display_name);
951     return APR_SUCCESS;
952 }
953
954
955 apr_status_t mpm_service_uninstall(void)
956 {
957     char key_name[MAX_PATH];
958     apr_status_t rv;
959
960     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
961     {
962         SC_HANDLE schService;
963         SC_HANDLE schSCManager;
964
965         fprintf(stderr,"Removing the %s service\n", mpm_display_name);
966
967         schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
968                                      SC_MANAGER_CONNECT);
969         if (!schSCManager) {
970             rv = apr_get_os_error();
971             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
972                          "Failed to open the WinNT service manager.");
973             return (rv);
974         }
975         
976         /* ###: utf-ize */
977         schService = OpenService(schSCManager, mpm_service_name, DELETE);
978
979         if (!schService) {
980            rv = apr_get_os_error();
981            ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
982                         "%s: OpenService failed", mpm_display_name);
983            return (rv);
984         }
985         
986         /* assure the service is stopped before continuing
987          *
988          * This may be out of order... we might not be able to be
989          * granted all access if the service is running anyway.
990          *
991          * And do we want to make it *this easy* for them
992          * to uninstall their service unintentionally?
993          */
994         // ap_stop_service(schService);
995
996         if (DeleteService(schService) == 0) {
997             rv = apr_get_os_error();
998             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
999                          "%s: Failed to delete the service.", mpm_display_name);
1000             return (rv);
1001         }
1002         
1003         CloseServiceHandle(schService);        
1004         CloseServiceHandle(schSCManager);
1005     }
1006     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
1007     {
1008         apr_status_t rv2, rv3;
1009         ap_regkey_t *key;
1010         fprintf(stderr,"Removing the %s service\n", mpm_display_name);
1011
1012         /* TODO: assure the service is stopped before continuing */
1013
1014         rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, SERVICECONFIG9X, 
1015                             APR_READ | APR_WRITE | APR_CREATE, pconf);
1016         if (rv == APR_SUCCESS) {
1017             rv = ap_regkey_value_remove(key, mpm_service_name, pconf);
1018             ap_regkey_close(key);
1019         }
1020         if (rv != APR_SUCCESS) {
1021             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
1022                          "%s: Failed to remove the RunServices registry "
1023                          "entry.", mpm_display_name);
1024         }
1025         
1026         /* we blast Services/us, not just the Services/us/Parameters branch */
1027         apr_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, mpm_service_name);
1028         rv2 = ap_regkey_remove(AP_REGKEY_LOCAL_MACHINE, key_name, pconf);
1029         apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
1030         rv3 = ap_regkey_remove(AP_REGKEY_LOCAL_MACHINE, key_name, pconf);
1031         rv2 = (rv2 != APR_SUCCESS) ? rv2 : rv3;
1032         if (rv2 != APR_SUCCESS) {
1033             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv2, NULL,
1034                          "%s: Failed to remove the service config from the "
1035                          "registry.", mpm_display_name);
1036         }
1037         rv = (rv != APR_SUCCESS) ? rv : rv2;
1038         if (rv != APR_SUCCESS)
1039             return rv;
1040     }
1041     fprintf(stderr,"The %s service has been removed successfully.\n", mpm_display_name);
1042     return APR_SUCCESS;
1043 }
1044
1045
1046 /* signal_service_transition is a simple thunk to signal the service
1047  * and monitor its successful transition.  If the signal passed is 0,
1048  * then the caller is assumed to already have performed some service 
1049  * operation to be monitored (such as StartService), and no actual
1050  * ControlService signal is sent.
1051  */
1052
1053 static int signal_service_transition(SC_HANDLE schService, DWORD signal, DWORD pending, DWORD complete)
1054 {
1055     if (signal && !ControlService(schService, signal, &globdat.ssStatus)) 
1056         return FALSE;
1057     
1058     do {
1059         Sleep(1000);    
1060         if (!QueryServiceStatus(schService, &globdat.ssStatus))
1061             return FALSE;
1062     } while (globdat.ssStatus.dwCurrentState == pending);
1063         
1064     return (globdat.ssStatus.dwCurrentState == complete);
1065 }
1066
1067
1068 apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc, 
1069                                const char * const * argv)
1070 {
1071     apr_status_t rv;
1072     
1073     fprintf(stderr,"Starting the %s service\n", mpm_display_name);
1074
1075     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT)
1076     {
1077         char **start_argv;
1078         SC_HANDLE   schService;
1079         SC_HANDLE   schSCManager;
1080
1081         schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
1082                                      SC_MANAGER_CONNECT);
1083         if (!schSCManager) {
1084             rv = apr_get_os_error();
1085             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
1086                          "Failed to open the WinNT service manager");
1087             return (rv);
1088         }
1089
1090         /* ###: utf-ize */
1091         schService = OpenService(schSCManager, mpm_service_name, 
1092                                  SERVICE_START | SERVICE_QUERY_STATUS);
1093         if (!schService) {
1094             rv = apr_get_os_error();
1095             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
1096                          "%s: Failed to open the service.", mpm_display_name);
1097             CloseServiceHandle(schSCManager);
1098             return (rv);
1099         }
1100
1101         if (QueryServiceStatus(schService, &globdat.ssStatus)
1102             && (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)) {
1103             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL,
1104                          "Service %s is already started!", mpm_display_name);
1105             CloseServiceHandle(schService);
1106             CloseServiceHandle(schSCManager);
1107             return 0;
1108         }
1109         
1110         start_argv = malloc((argc + 1) * sizeof(const char **));
1111         memcpy(start_argv, argv, argc * sizeof(const char **));
1112         start_argv[argc] = NULL;
1113
1114         rv = APR_EINIT;
1115         /* ###: utf-ize */
1116         if (StartService(schService, argc, start_argv)
1117             && signal_service_transition(schService, 0, /* test only */
1118                                          SERVICE_START_PENDING, 
1119                                          SERVICE_RUNNING))
1120                 rv = APR_SUCCESS;
1121
1122         if (rv != APR_SUCCESS)
1123             rv = apr_get_os_error();
1124         
1125         CloseServiceHandle(schService);
1126         CloseServiceHandle(schSCManager);
1127     }
1128     else /* osver.dwPlatformId != VER_PLATFORM_WIN32_NT */
1129     {
1130         STARTUPINFO si;           /* Filled in prior to call to CreateProcess */
1131         PROCESS_INFORMATION pi;   /* filled in on call to CreateProcess */
1132         char exe_path[MAX_PATH];
1133         char exe_cmd[MAX_PATH * 4];
1134         char *next_arg;
1135         int i;
1136
1137         /* Locate the active top level window named service_name
1138          * provided the class is ApacheWin95ServiceMonitor
1139          */
1140         if (FindWindow("ApacheWin95ServiceMonitor", mpm_service_name)) {
1141             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL,
1142                          "Service %s is already started!", mpm_display_name);
1143             return 0;
1144         }
1145
1146         /* This may not appear intuitive, but Win9x will not allow a process
1147          * to detach from the console without releasing the entire console.
1148          * Ergo, we must spawn a new process for the service to get back our
1149          * console window.
1150          * The config is pre-flighted, so there should be no danger of failure.
1151          */
1152         
1153         if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
1154         {
1155             apr_status_t rv = apr_get_os_error();
1156             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
1157                          "GetModuleFileName failed");
1158             return rv;
1159         }
1160         
1161         apr_snprintf(exe_cmd, sizeof(exe_cmd), 
1162                      "\"%s\" -n %s -k runservice", 
1163                      exe_path, mpm_service_name);  
1164         next_arg = strchr(exe_cmd, '\0');
1165         for (i = 0; i < argc; ++i) {
1166             apr_snprintf(next_arg, sizeof(exe_cmd) - (next_arg - exe_cmd), 
1167                          " \"%s\"", argv[i]);
1168             next_arg = strchr(exe_cmd, '\0');
1169         }
1170         
1171         memset(&si, 0, sizeof(si));
1172         memset(&pi, 0, sizeof(pi));
1173         si.cb = sizeof(si);
1174         si.dwFlags     = STARTF_USESHOWWINDOW;
1175         si.wShowWindow = SW_HIDE;   /* This might be redundant */
1176         
1177         rv = APR_EINIT;
1178         if (CreateProcess(NULL, exe_cmd, NULL, NULL, FALSE, 
1179                            DETACHED_PROCESS, /* Creation flags */
1180                            NULL, NULL, &si, &pi)) 
1181         {
1182             DWORD code;
1183             while (GetExitCodeProcess(pi.hProcess, &code) == STILL_ACTIVE) {
1184                 if (FindWindow("ApacheWin95ServiceMonitor", mpm_service_name)) {
1185                     rv = APR_SUCCESS;
1186                     break;
1187                 }
1188                 Sleep (1000);
1189             }
1190         }
1191         
1192         if (rv != APR_SUCCESS)
1193             rv = apr_get_os_error();
1194         
1195         CloseHandle(pi.hProcess);
1196         CloseHandle(pi.hThread);
1197     }    
1198
1199     if (rv == APR_SUCCESS)
1200         fprintf(stderr,"The %s service is running.\n", mpm_display_name);
1201     else
1202         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
1203                      "%s: Failed to start the service process.",
1204                      mpm_display_name);
1205         
1206     return rv;
1207 }
1208
1209
1210 /* signal is zero to stop, non-zero for restart */
1211
1212 void mpm_signal_service(apr_pool_t *ptemp, int signal)
1213 {
1214     int success = FALSE;
1215     
1216     if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) 
1217     {
1218         SC_HANDLE   schService;
1219         SC_HANDLE   schSCManager;
1220
1221         schSCManager = OpenSCManager(NULL, NULL, // default machine & database
1222                                      SC_MANAGER_CONNECT);
1223         
1224         if (!schSCManager) {
1225             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
1226                          "Failed to open the NT Service Manager");
1227             return;
1228         }
1229
1230         /* ###: utf-ize */
1231         schService = OpenService(schSCManager, mpm_service_name, 
1232                                  SERVICE_INTERROGATE | SERVICE_QUERY_STATUS | 
1233                                  SERVICE_USER_DEFINED_CONTROL |
1234                                  SERVICE_START | SERVICE_STOP);
1235
1236         if (schService == NULL) {
1237             /* Could not open the service */
1238             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
1239                          "Failed to open the %s Service", mpm_display_name);
1240             CloseServiceHandle(schSCManager);
1241             return;
1242         }
1243         
1244         if (!QueryServiceStatus(schService, &globdat.ssStatus)) {
1245             ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
1246                          "Query of Service %s failed", mpm_display_name);
1247             CloseServiceHandle(schService);
1248             CloseServiceHandle(schSCManager);
1249             return;
1250         }
1251
1252         if (!signal && (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)) {
1253             fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
1254             CloseServiceHandle(schService);
1255             CloseServiceHandle(schSCManager);
1256             return;
1257         }
1258         
1259         fprintf(stderr,"The %s service is %s.\n", mpm_display_name, 
1260                signal ? "restarting" : "stopping");
1261
1262         if (!signal)
1263             success = signal_service_transition(schService, 
1264                                                 SERVICE_CONTROL_STOP, 
1265                                                 SERVICE_STOP_PENDING, 
1266                                                 SERVICE_STOPPED);
1267         else if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
1268             mpm_service_start(ptemp, 0, NULL);
1269             CloseServiceHandle(schService);
1270             CloseServiceHandle(schSCManager);
1271             return;
1272         }
1273         else
1274             success = signal_service_transition(schService, 
1275                                                 SERVICE_APACHE_RESTART, 
1276                                                 SERVICE_START_PENDING, 
1277                                                 SERVICE_RUNNING);
1278
1279         CloseServiceHandle(schService);
1280         CloseServiceHandle(schSCManager);
1281     }
1282     else /* !isWindowsNT() */
1283     {
1284         DWORD       service_pid;
1285         HANDLE      hwnd;
1286         char prefix[20];
1287         /* Locate the active top level window named service_name
1288          * provided the class is ApacheWin95ServiceMonitor
1289          */
1290         hwnd = FindWindow("ApacheWin95ServiceMonitor", mpm_service_name);
1291         if (hwnd && GetWindowThreadProcessId(hwnd, &service_pid))
1292             globdat.ssStatus.dwCurrentState = SERVICE_RUNNING;
1293         else
1294         {
1295             globdat.ssStatus.dwCurrentState = SERVICE_STOPPED;
1296             if (!signal) {
1297                 fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
1298                 return;
1299             }
1300         }
1301
1302         fprintf(stderr,"The %s service is %s.\n", mpm_display_name, 
1303                signal ? "restarting" : "stopping");
1304
1305         apr_snprintf(prefix, sizeof(prefix), "ap%ld", (long)service_pid);
1306         setup_signal_names(prefix);
1307
1308         if (!signal) 
1309         {
1310             int ticks = 60;
1311             ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
1312             while (--ticks)
1313             {
1314                 if (!IsWindow(hwnd)) {
1315                     success = TRUE;
1316                     break;
1317                 }
1318                 Sleep(1000);
1319             }
1320         }
1321         else /* !stop */
1322         {   
1323             /* TODO: Aught to add a little test to the restart logic, and
1324              * store the restart counter in the window's user dword.
1325              * Then we can hang on and report a successful restart.  But
1326              * that's a project for another day.
1327              */
1328             if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
1329                 mpm_service_start(ptemp, 0, NULL);
1330                 return;
1331             }
1332             else {
1333                 success = TRUE;
1334                 ap_signal_parent(SIGNAL_PARENT_RESTART);
1335             }
1336         }
1337     }
1338
1339     if (success)
1340         fprintf(stderr,"The %s service has %s.\n", mpm_display_name, 
1341                signal ? "restarted" : "stopped");
1342     else
1343         fprintf(stderr,"Failed to %s the %s service.\n", 
1344                signal ? "restart" : "stop", mpm_display_name);
1345 }