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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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...
26 #include "mpm_winnt.h"
27 #include "apr_strings.h"
29 #include "ap_regkey.h"
38 APLOG_USE_MODULE(mpm_winnt);
40 /* Todo; clear up statics */
41 static char *mpm_service_name = NULL;
42 static char *mpm_display_name = NULL;
44 typedef struct nt_service_ctx_t
46 HANDLE mpm_thread; /* primary thread handle of the apache server */
47 HANDLE service_thread; /* thread service/monitor handle */
48 DWORD service_thread_id;/* thread service/monitor ID */
49 HANDLE service_init; /* controller thread init mutex */
50 HANDLE service_term; /* NT service thread kill signal */
51 SERVICE_STATUS ssStatus;
52 SERVICE_STATUS_HANDLE hServiceStatus;
55 static nt_service_ctx_t globdat;
57 static int ReportStatusToSCMgr(int currentState, int waitHint,
58 nt_service_ctx_t *ctx);
60 /* exit() for Win32 is macro mapped (horrible, we agree) that allows us
61 * to catch the non-zero conditions and inform the console process that
62 * the application died, and hang on to the console a bit longer.
64 * The macro only maps for http_main.c and other sources that include
65 * the service.h header, so we best assume it's an error to exit from
68 * If ap_real_exit_code is reset to 0, it will not be set or trigger this
69 * behavior on exit. All service and child processes are expected to
70 * reset this flag to zero to avoid undesireable side effects.
72 AP_DECLARE_DATA int ap_real_exit_code = 1;
74 void hold_console_open_on_error(void)
81 char *msg = "Note the errors or messages above, "
82 "and press the <ESC> key to exit. ";
83 CONSOLE_SCREEN_BUFFER_INFO coninfo;
87 if (!ap_real_exit_code)
89 hConIn = GetStdHandle(STD_INPUT_HANDLE);
90 hConErr = GetStdHandle(STD_ERROR_HANDLE);
91 if ((hConIn == INVALID_HANDLE_VALUE) || (hConErr == INVALID_HANDLE_VALUE))
93 if (!WriteConsole(hConErr, msg, (DWORD)strlen(msg), &result, NULL) || !result)
95 if (!GetConsoleScreenBufferInfo(hConErr, &coninfo))
97 if (!SetConsoleMode(hConIn, ENABLE_MOUSE_INPUT | 0x80))
103 while (PeekConsoleInput(hConIn, &in, 1, &result) && result)
105 if (!ReadConsoleInput(hConIn, &in, 1, &result) || !result)
107 if ((in.EventType == KEY_EVENT) && in.Event.KeyEvent.bKeyDown
108 && (in.Event.KeyEvent.uChar.AsciiChar == 27))
110 if (in.EventType == MOUSE_EVENT
111 && (in.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK))
114 remains = ((start + 30) - time(NULL));
115 sprintf(count, "%d...",
116 (int)remains); /* 30 or less, so can't overflow int */
117 if (!SetConsoleCursorPosition(hConErr, coninfo.dwCursorPosition))
119 if (!WriteConsole(hConErr, count, (DWORD)strlen(count), &result, NULL)
123 while ((remains > 0) && WaitForSingleObject(hConIn, 1000) != WAIT_FAILED);
126 static BOOL CALLBACK console_control_handler(DWORD ctrl_type)
130 case CTRL_BREAK_EVENT:
131 fprintf(stderr, "Apache server restarting...\n");
132 ap_signal_parent(SIGNAL_PARENT_RESTART);
135 fprintf(stderr, "Apache server interrupted...\n");
136 /* for Interrupt signals, shut down the server.
137 * Tell the system we have dealt with the signal
138 * without waiting for Apache to terminate.
140 ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
143 case CTRL_CLOSE_EVENT:
144 case CTRL_LOGOFF_EVENT:
145 case CTRL_SHUTDOWN_EVENT:
146 /* for Terminate signals, shut down the server.
147 * Wait for Apache to terminate, but respond
148 * after a reasonable time to tell the system
149 * that we did attempt to shut ourself down.
151 fprintf(stderr, "Apache server shutdown initiated...\n");
152 ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
157 /* We should never get here, but this is (mostly) harmless */
162 static void stop_console_handler(void)
164 SetConsoleCtrlHandler(console_control_handler, FALSE);
168 void mpm_start_console_handler(void)
170 SetConsoleCtrlHandler(console_control_handler, TRUE);
171 atexit(stop_console_handler);
175 void mpm_start_child_console_handler(void)
181 /**********************************
182 WinNT service control management
183 **********************************/
185 static int ReportStatusToSCMgr(int currentState, int waitHint,
186 nt_service_ctx_t *ctx)
188 int rv = APR_SUCCESS;
190 if (ctx->hServiceStatus)
192 if (currentState == SERVICE_RUNNING) {
193 ctx->ssStatus.dwWaitHint = 0;
194 ctx->ssStatus.dwCheckPoint = 0;
195 ctx->ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
196 | SERVICE_ACCEPT_SHUTDOWN;
198 else if (currentState == SERVICE_STOPPED) {
199 ctx->ssStatus.dwWaitHint = 0;
200 ctx->ssStatus.dwCheckPoint = 0;
201 /* An unexpected exit? Better to error! */
202 if (ctx->ssStatus.dwCurrentState != SERVICE_STOP_PENDING
203 && !ctx->ssStatus.dwServiceSpecificExitCode)
204 ctx->ssStatus.dwServiceSpecificExitCode = 1;
205 if (ctx->ssStatus.dwServiceSpecificExitCode)
206 ctx->ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
209 ++ctx->ssStatus.dwCheckPoint;
210 ctx->ssStatus.dwControlsAccepted = 0;
212 ctx->ssStatus.dwWaitHint = waitHint;
215 ctx->ssStatus.dwCurrentState = currentState;
217 rv = SetServiceStatus(ctx->hServiceStatus, &ctx->ssStatus);
222 /* Note this works on Win2000 and later due to ChangeServiceConfig2
223 * Continue to test its existence, but at least drop the feature
224 * of revising service description tags prior to Win2000.
227 /* borrowed from mpm_winnt.c */
228 extern apr_pool_t *pconf;
230 static void set_service_description(void)
232 const char *full_description;
233 SC_HANDLE schSCManager;
235 /* Nothing to do if we are a console
237 if (!mpm_service_name)
240 /* Time to fix up the description, upon each successful restart
242 full_description = ap_get_server_description();
244 if ((ChangeServiceConfig2) &&
245 (schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)))
247 SC_HANDLE schService = OpenService(schSCManager, mpm_service_name,
248 SERVICE_CHANGE_CONFIG);
250 /* Cast is necessary, ChangeServiceConfig2 handles multiple
251 * object types, some volatile, some not.
254 ChangeServiceConfig2(schService,
255 1 /* SERVICE_CONFIG_DESCRIPTION */,
256 (LPVOID) &full_description);
257 CloseServiceHandle(schService);
259 CloseServiceHandle(schSCManager);
263 /* handle the SCM's ControlService() callbacks to our service */
265 static DWORD WINAPI service_nt_ctrl(DWORD dwCtrlCode, DWORD dwEventType,
266 LPVOID lpEventData, LPVOID lpContext)
268 nt_service_ctx_t *ctx = lpContext;
270 /* SHUTDOWN is offered before STOP, accept the first opportunity */
271 if ((dwCtrlCode == SERVICE_CONTROL_STOP)
272 || (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
274 ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
275 ReportStatusToSCMgr(SERVICE_STOP_PENDING, 30000, ctx);
278 if (dwCtrlCode == SERVICE_APACHE_RESTART)
280 ap_signal_parent(SIGNAL_PARENT_RESTART);
281 ReportStatusToSCMgr(SERVICE_START_PENDING, 30000, ctx);
284 if (dwCtrlCode == SERVICE_CONTROL_INTERROGATE) {
285 ReportStatusToSCMgr(globdat.ssStatus.dwCurrentState, 0, ctx);
289 return (ERROR_CALL_NOT_IMPLEMENTED);
293 /* service_nt_main_fn is outside of the call stack and outside of the
294 * primary server thread... so now we _really_ need a placeholder!
295 * The winnt_rewrite_args has created and shared mpm_new_argv with us.
297 extern apr_array_header_t *mpm_new_argv;
300 static void __stdcall service_nt_main_fn(DWORD argc, LPTSTR *argv)
303 nt_service_ctx_t *ctx = &globdat;
305 /* args and service names live in the same pool */
306 mpm_service_set_name(mpm_new_argv->pool, &ignored, argv[0]);
308 memset(&ctx->ssStatus, 0, sizeof(ctx->ssStatus));
309 ctx->ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
310 ctx->ssStatus.dwCurrentState = SERVICE_START_PENDING;
311 ctx->ssStatus.dwCheckPoint = 1;
314 if (!(ctx->hServiceStatus = RegisterServiceCtrlHandlerEx(argv[0], service_nt_ctrl, ctx)))
316 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(),
317 NULL, "Failure registering service handler");
321 /* Report status, no errors, and buy 3 more seconds */
322 ReportStatusToSCMgr(SERVICE_START_PENDING, 30000, ctx);
324 /* We need to append all the command arguments passed via StartService()
325 * to our running service... which just got here via the SCM...
326 * but we hvae no interest in argv[0] for the mpm_new_argv list.
332 mpm_new_argv->nalloc = mpm_new_argv->nelts + argc - 1;
333 cmb_data = malloc(mpm_new_argv->nalloc * sizeof(const char *));
335 /* mpm_new_argv remains first (of lower significance) */
336 memcpy (cmb_data, mpm_new_argv->elts,
337 mpm_new_argv->elt_size * mpm_new_argv->nelts);
339 /* Service args follow from StartService() invocation */
340 memcpy (cmb_data + mpm_new_argv->nelts, argv + 1,
341 mpm_new_argv->elt_size * (argc - 1));
343 /* The replacement arg list is complete */
344 mpm_new_argv->elts = (char *)cmb_data;
345 mpm_new_argv->nelts = mpm_new_argv->nalloc;
348 /* Let the main thread continue now... but hang on to the
349 * signal_monitor event so we can take further action
351 SetEvent(ctx->service_init);
353 WaitForSingleObject(ctx->service_term, INFINITE);
357 static DWORD WINAPI service_nt_dispatch_thread(LPVOID nada)
359 apr_status_t rv = APR_SUCCESS;
361 SERVICE_TABLE_ENTRY dispatchTable[] =
363 { "", service_nt_main_fn },
368 if (!StartServiceCtrlDispatcher(dispatchTable))
370 /* This is a genuine failure of the SCM. */
371 rv = apr_get_os_error();
372 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
373 "Error starting service control dispatcher");
380 /* The service configuration's is stored under the following trees:
382 * HKLM\System\CurrentControlSet\Services\[service name]
386 * \Parameters\ConfigArgs
390 apr_status_t mpm_service_set_name(apr_pool_t *p, const char **display_name,
391 const char *set_name)
393 char key_name[MAX_PATH];
397 /* ### Needs improvement, on Win2K the user can _easily_
398 * change the display name to a string that doesn't reflect
399 * the internal service name + whitespace!
401 mpm_service_name = apr_palloc(p, strlen(set_name) + 1);
402 apr_collapse_spaces((char*) mpm_service_name, set_name);
403 apr_snprintf(key_name, sizeof(key_name), SERVICECONFIG, mpm_service_name);
404 rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name, APR_READ, pconf);
405 if (rv == APR_SUCCESS) {
406 rv = ap_regkey_value_get(&mpm_display_name, key, "DisplayName", pconf);
407 ap_regkey_close(key);
409 if (rv != APR_SUCCESS) {
410 /* Take the given literal name if there is no service entry */
411 mpm_display_name = apr_pstrdup(p, set_name);
413 *display_name = mpm_display_name;
418 apr_status_t mpm_merge_service_args(apr_pool_t *p,
419 apr_array_header_t *args,
422 apr_array_header_t *svc_args = NULL;
423 char conf_key[MAX_PATH];
428 apr_snprintf(conf_key, sizeof(conf_key), SERVICEPARAMS, mpm_service_name);
429 rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, conf_key, APR_READ, p);
430 if (rv == APR_SUCCESS) {
431 rv = ap_regkey_value_array_get(&svc_args, key, "ConfigArgs", p);
432 ap_regkey_close(key);
434 if (rv != APR_SUCCESS) {
435 if (rv == ERROR_FILE_NOT_FOUND) {
436 ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL,
437 "No ConfigArgs registered for %s, perhaps "
438 "this service is not installed?",
446 if (!svc_args || svc_args->nelts == 0) {
447 return (APR_SUCCESS);
450 /* Now we have the mpm_service_name arg, and the mpm_runservice_nt()
451 * call appended the arguments passed by StartService(), so it's
452 * time to _prepend_ the default arguments for the server from
453 * the service's default arguments (all others override them)...
455 args->nalloc = args->nelts + svc_args->nelts;
456 cmb_data = malloc(args->nalloc * sizeof(const char *));
458 /* First three args (argv[0], -f, path) remain first */
459 memcpy(cmb_data, args->elts, args->elt_size * fixed_args);
461 /* Service args follow from service registry array */
462 memcpy(cmb_data + fixed_args, svc_args->elts,
463 svc_args->elt_size * svc_args->nelts);
465 /* Remaining new args follow */
466 memcpy(cmb_data + fixed_args + svc_args->nelts,
467 (const char **)args->elts + fixed_args,
468 args->elt_size * (args->nelts - fixed_args));
470 args->elts = (char *)cmb_data;
471 args->nelts = args->nalloc;
477 static void service_stopped(void)
479 /* Still have a thread & window to clean up, so signal now */
480 if (globdat.service_thread)
482 /* Stop logging to the event log */
483 mpm_nt_eventlog_stderr_flush();
485 /* Cause the service_nt_main_fn to complete */
486 ReleaseMutex(globdat.service_term);
488 ReportStatusToSCMgr(SERVICE_STOPPED, 0, &globdat);
490 WaitForSingleObject(globdat.service_thread, 5000);
491 CloseHandle(globdat.service_thread);
496 apr_status_t mpm_service_to_start(const char **display_name, apr_pool_t *p)
498 HANDLE hProc = GetCurrentProcess();
499 HANDLE hThread = GetCurrentThread();
502 /* Prevent holding open the (hidden) console */
503 ap_real_exit_code = 0;
505 /* GetCurrentThread returns a psuedo-handle, we need
506 * a real handle for another thread to wait upon.
508 if (!DuplicateHandle(hProc, hThread, hProc, &(globdat.mpm_thread),
509 0, FALSE, DUPLICATE_SAME_ACCESS)) {
510 return APR_ENOTHREAD;
513 globdat.service_init = CreateEvent(NULL, FALSE, FALSE, NULL);
514 globdat.service_term = CreateMutex(NULL, TRUE, NULL);
515 if (!globdat.service_init || !globdat.service_term) {
519 globdat.service_thread = CreateThread(NULL, 65536,
520 service_nt_dispatch_thread,
521 NULL, stack_res_flag,
522 &globdat.service_thread_id);
524 if (!globdat.service_thread) {
525 return APR_ENOTHREAD;
528 waitfor[0] = globdat.service_init;
529 waitfor[1] = globdat.service_thread;
531 /* Wait for controlling thread init or termination */
532 if (WaitForMultipleObjects(2, waitfor, FALSE, 10000) != WAIT_OBJECT_0) {
533 return APR_ENOTHREAD;
536 atexit(service_stopped);
537 *display_name = mpm_display_name;
542 apr_status_t mpm_service_started(void)
544 set_service_description();
545 ReportStatusToSCMgr(SERVICE_RUNNING, 0, &globdat);
550 void mpm_service_stopping(void)
552 ReportStatusToSCMgr(SERVICE_STOP_PENDING, 30000, &globdat);
556 apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
557 const char * const * argv, int reconfig)
559 char key_name[MAX_PATH];
560 char exe_path[MAX_PATH];
564 SC_HANDLE schService;
565 SC_HANDLE schSCManager;
567 fprintf(stderr,reconfig ? "Reconfiguring the %s service\n"
568 : "Installing the %s service\n", mpm_display_name);
571 if (GetModuleFileName(NULL, exe_path, sizeof(exe_path)) == 0)
573 apr_status_t rv = apr_get_os_error();
574 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
575 "GetModuleFileName failed");
579 schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
580 SC_MANAGER_CREATE_SERVICE);
582 rv = apr_get_os_error();
583 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
584 "Failed to open the WinNT service manager, perhaps "
585 "you forgot to log in as Adminstrator?");
589 launch_cmd = apr_psprintf(ptemp, "\"%s\" -k runservice", exe_path);
593 schService = OpenService(schSCManager, mpm_service_name,
594 SERVICE_CHANGE_CONFIG);
596 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR,
597 apr_get_os_error(), NULL,
598 "OpenService failed");
601 else if (!ChangeServiceConfig(schService,
602 SERVICE_WIN32_OWN_PROCESS,
604 SERVICE_ERROR_NORMAL,
605 launch_cmd, NULL, NULL,
606 "Tcpip\0Afd\0", NULL, NULL,
608 ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_ERR,
609 apr_get_os_error(), NULL,
610 "ChangeServiceConfig failed");
612 /* !schService aborts configuration below */
613 CloseServiceHandle(schService);
618 /* RPCSS is the Remote Procedure Call (RPC) Locator required
619 * for DCOM communication pipes. I am far from convinced we
620 * should add this to the default service dependencies, but
621 * be warned that future apache modules or ISAPI dll's may
625 schService = CreateService(schSCManager, /* SCManager database */
626 mpm_service_name, /* name of service */
627 mpm_display_name, /* name to display */
628 SERVICE_ALL_ACCESS, /* access required */
629 SERVICE_WIN32_OWN_PROCESS, /* service type */
630 SERVICE_AUTO_START, /* start type */
631 SERVICE_ERROR_NORMAL, /* error control type */
632 launch_cmd, /* service's binary */
633 NULL, /* no load svc group */
634 NULL, /* no tag identifier */
635 "Tcpip\0Afd\0", /* dependencies */
636 NULL, /* use SYSTEM account */
637 NULL); /* no password */
641 rv = apr_get_os_error();
642 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
643 "Failed to create WinNT Service Profile");
644 CloseServiceHandle(schSCManager);
649 CloseServiceHandle(schService);
650 CloseServiceHandle(schSCManager);
652 set_service_description();
654 /* Store the service ConfigArgs in the registry...
656 apr_snprintf(key_name, sizeof(key_name), SERVICEPARAMS, mpm_service_name);
657 rv = ap_regkey_open(&key, AP_REGKEY_LOCAL_MACHINE, key_name,
658 APR_READ | APR_WRITE | APR_CREATE, pconf);
659 if (rv == APR_SUCCESS) {
660 rv = ap_regkey_value_array_set(key, "ConfigArgs", argc, argv, pconf);
661 ap_regkey_close(key);
663 if (rv != APR_SUCCESS) {
664 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
665 "%s: Failed to store the ConfigArgs in the registry.",
669 fprintf(stderr,"The %s service is successfully installed.\n", mpm_display_name);
674 apr_status_t mpm_service_uninstall(void)
677 SC_HANDLE schService;
678 SC_HANDLE schSCManager;
680 fprintf(stderr,"Removing the %s service\n", mpm_display_name);
682 schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
685 rv = apr_get_os_error();
686 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
687 "Failed to open the WinNT service manager.");
692 schService = OpenService(schSCManager, mpm_service_name, DELETE);
695 rv = apr_get_os_error();
696 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
697 "%s: OpenService failed", mpm_display_name);
701 /* assure the service is stopped before continuing
703 * This may be out of order... we might not be able to be
704 * granted all access if the service is running anyway.
706 * And do we want to make it *this easy* for them
707 * to uninstall their service unintentionally?
709 /* ap_stop_service(schService);
712 if (DeleteService(schService) == 0) {
713 rv = apr_get_os_error();
714 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
715 "%s: Failed to delete the service.", mpm_display_name);
719 CloseServiceHandle(schService);
720 CloseServiceHandle(schSCManager);
722 fprintf(stderr,"The %s service has been removed successfully.\n", mpm_display_name);
727 /* signal_service_transition is a simple thunk to signal the service
728 * and monitor its successful transition. If the signal passed is 0,
729 * then the caller is assumed to already have performed some service
730 * operation to be monitored (such as StartService), and no actual
731 * ControlService signal is sent.
734 static int signal_service_transition(SC_HANDLE schService, DWORD signal, DWORD pending, DWORD complete)
736 if (signal && !ControlService(schService, signal, &globdat.ssStatus))
741 if (!QueryServiceStatus(schService, &globdat.ssStatus))
743 } while (globdat.ssStatus.dwCurrentState == pending);
745 return (globdat.ssStatus.dwCurrentState == complete);
749 apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc,
750 const char * const * argv)
754 SC_HANDLE schService;
755 SC_HANDLE schSCManager;
757 fprintf(stderr,"Starting the %s service\n", mpm_display_name);
759 schSCManager = OpenSCManager(NULL, NULL, /* local, default database */
762 rv = apr_get_os_error();
763 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
764 "Failed to open the WinNT service manager");
769 schService = OpenService(schSCManager, mpm_service_name,
770 SERVICE_START | SERVICE_QUERY_STATUS);
772 rv = apr_get_os_error();
773 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
774 "%s: Failed to open the service.", mpm_display_name);
775 CloseServiceHandle(schSCManager);
779 if (QueryServiceStatus(schService, &globdat.ssStatus)
780 && (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)) {
781 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL,
782 "Service %s is already started!", mpm_display_name);
783 CloseServiceHandle(schService);
784 CloseServiceHandle(schSCManager);
788 start_argv = malloc((argc + 1) * sizeof(const char **));
789 memcpy(start_argv, argv, argc * sizeof(const char **));
790 start_argv[argc] = NULL;
794 if (StartService(schService, argc, start_argv)
795 && signal_service_transition(schService, 0, /* test only */
796 SERVICE_START_PENDING,
799 if (rv != APR_SUCCESS)
800 rv = apr_get_os_error();
802 CloseServiceHandle(schService);
803 CloseServiceHandle(schSCManager);
805 if (rv == APR_SUCCESS)
806 fprintf(stderr,"The %s service is running.\n", mpm_display_name);
808 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
809 "%s: Failed to start the service process.",
816 /* signal is zero to stop, non-zero for restart */
818 void mpm_signal_service(apr_pool_t *ptemp, int signal)
821 SC_HANDLE schService;
822 SC_HANDLE schSCManager;
824 schSCManager = OpenSCManager(NULL, NULL, /* default machine & database */
828 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
829 "Failed to open the NT Service Manager");
834 schService = OpenService(schSCManager, mpm_service_name,
835 SERVICE_INTERROGATE | SERVICE_QUERY_STATUS |
836 SERVICE_USER_DEFINED_CONTROL |
837 SERVICE_START | SERVICE_STOP);
839 if (schService == NULL) {
840 /* Could not open the service */
841 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
842 "Failed to open the %s Service", mpm_display_name);
843 CloseServiceHandle(schSCManager);
847 if (!QueryServiceStatus(schService, &globdat.ssStatus)) {
848 ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, apr_get_os_error(), NULL,
849 "Query of Service %s failed", mpm_display_name);
850 CloseServiceHandle(schService);
851 CloseServiceHandle(schSCManager);
855 if (!signal && (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)) {
856 fprintf(stderr,"The %s service is not started.\n", mpm_display_name);
857 CloseServiceHandle(schService);
858 CloseServiceHandle(schSCManager);
862 fprintf(stderr,"The %s service is %s.\n", mpm_display_name,
863 signal ? "restarting" : "stopping");
866 success = signal_service_transition(schService,
867 SERVICE_CONTROL_STOP,
868 SERVICE_STOP_PENDING,
870 else if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED) {
871 mpm_service_start(ptemp, 0, NULL);
872 CloseServiceHandle(schService);
873 CloseServiceHandle(schSCManager);
877 success = signal_service_transition(schService,
878 SERVICE_APACHE_RESTART,
879 SERVICE_START_PENDING,
882 CloseServiceHandle(schService);
883 CloseServiceHandle(schSCManager);
886 fprintf(stderr,"The %s service has %s.\n", mpm_display_name,
887 signal ? "restarted" : "stopped");
889 fprintf(stderr,"Failed to %s the %s service.\n",
890 signal ? "restart" : "stop", mpm_display_name);