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