~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to server/mpm/winnt/service.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 ap_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 ap_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 (!ap_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, (DWORD)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, (DWORD)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
    ap_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
}