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#include "httpd.h"
18#include "http_log.h"
19#include "mpm_winnt.h"
20#include "apr_strings.h"
21#include "apr_lib.h"
22#include "apr_portable.h"
23#include "ap_regkey.h"
24
25static const char *display_name  = NULL;
26static HANDLE stderr_thread = NULL;
27static HANDLE stderr_ready;
28
29static DWORD WINAPI service_stderr_thread(LPVOID hPipe)
30{
31    HANDLE hPipeRead = (HANDLE) hPipe;
32    HANDLE hEventSource;
33    char errbuf[256];
34    char *errmsg = errbuf;
35    const char *errarg[9];
36    DWORD errres;
37    ap_regkey_t *regkey;
38    apr_status_t rv;
39    apr_pool_t *p;
40
41    apr_pool_create_ex(&p, NULL, NULL, NULL);
42
43    errarg[0] = "The Apache service named";
44    errarg[1] = display_name;
45    errarg[2] = "reported the following error:\r\n>>>";
46    errarg[3] = errbuf;
47    errarg[4] = NULL;
48    errarg[5] = NULL;
49    errarg[6] = NULL;
50    errarg[7] = NULL;
51    errarg[8] = NULL;
52
53    /* What are we going to do in here, bail on the user?  not. */
54    if ((rv = ap_regkey_open(&regkey, AP_REGKEY_LOCAL_MACHINE,
55                             "SYSTEM\\CurrentControlSet\\Services\\"
56                             "EventLog\\Application\\Apache Service",
57                             APR_READ | APR_WRITE | APR_CREATE, p))
58            == APR_SUCCESS)
59    {
60        DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
61                       EVENTLOG_INFORMATION_TYPE;
62
63        /* The stock message file */
64        ap_regkey_value_set(regkey, "EventMessageFile",
65                            "%SystemRoot%\\System32\\netmsg.dll",
66                            AP_REGKEY_EXPAND, p);
67
68        ap_regkey_value_raw_set(regkey, "TypesSupported", &dwData,
69                                sizeof(dwData), REG_DWORD, p);
70        ap_regkey_close(regkey);
71    }
72
73    hEventSource = RegisterEventSourceW(NULL, L"Apache Service");
74
75    SetEvent(stderr_ready);
76
77    while (ReadFile(hPipeRead, errmsg, 1, &errres, NULL) && (errres == 1))
78    {
79        if ((errmsg > errbuf) || !apr_isspace(*errmsg))
80        {
81            ++errmsg;
82            if ((*(errmsg - 1) == '\n')
83                    || (errmsg >= errbuf + sizeof(errbuf) - 1))
84            {
85                while ((errmsg > errbuf) && apr_isspace(*(errmsg - 1))) {
86                    --errmsg;
87                }
88                *errmsg = '\0';
89
90                /* Generic message: '%1 %2 %3 %4 %5 %6 %7 %8 %9'
91                 * The event code in netmsg.dll is 3299
92                 */
93                ReportEvent(hEventSource, EVENTLOG_ERROR_TYPE, 0,
94                            3299, NULL, 9, 0, errarg, NULL);
95                errmsg = errbuf;
96            }
97        }
98    }
99
100    if ((errres = GetLastError()) != ERROR_BROKEN_PIPE) {
101        apr_snprintf(errbuf, sizeof(errbuf),
102                     "Win32 error %lu reading stderr pipe stream\r\n",
103                     GetLastError());
104
105        ReportEvent(hEventSource, EVENTLOG_ERROR_TYPE, 0,
106                    3299, NULL, 9, 0, errarg, NULL);
107    }
108
109    CloseHandle(hPipeRead);
110    DeregisterEventSource(hEventSource);
111    CloseHandle(stderr_thread);
112    stderr_thread = NULL;
113    apr_pool_destroy(p);
114    return 0;
115}
116
117
118void mpm_nt_eventlog_stderr_flush(void)
119{
120    HANDLE cleanup_thread = stderr_thread;
121
122    if (cleanup_thread) {
123        HANDLE hErr = GetStdHandle(STD_ERROR_HANDLE);
124        fclose(stderr);
125        CloseHandle(hErr);
126        WaitForSingleObject(cleanup_thread, 30000);
127        CloseHandle(cleanup_thread);
128    }
129}
130
131
132void mpm_nt_eventlog_stderr_open(const char *argv0, apr_pool_t *p)
133{
134    SECURITY_ATTRIBUTES sa;
135    HANDLE hPipeRead = NULL;
136    HANDLE hPipeWrite = NULL;
137    DWORD  threadid;
138    apr_file_t *eventlog_file;
139    apr_file_t *stderr_file;
140
141    display_name = argv0;
142
143    /* Create a pipe to send stderr messages to the system error log.
144     *
145     * _dup2() duplicates the write handle inheritable for us.
146     */
147    sa.nLength = sizeof(sa);
148    sa.lpSecurityDescriptor = NULL;
149    sa.bInheritHandle = FALSE;
150    CreatePipe(&hPipeRead, &hPipeWrite, NULL, 0);
151    ap_assert(hPipeRead && hPipeWrite);
152
153    stderr_ready = CreateEvent(NULL, FALSE, FALSE, NULL);
154    stderr_thread = CreateThread(NULL, 65536, service_stderr_thread,
155                                 (LPVOID)hPipeRead, stack_res_flag, &threadid);
156    ap_assert(stderr_ready && stderr_thread);
157
158    WaitForSingleObject(stderr_ready, INFINITE);
159
160    if ((apr_file_open_stderr(&stderr_file, p)
161             == APR_SUCCESS)
162     && (apr_os_file_put(&eventlog_file, &hPipeWrite, APR_WRITE, p)
163             == APR_SUCCESS))
164        apr_file_dup2(stderr_file, eventlog_file, p);
165
166    /* The code above _will_ corrupt the StdHandle...
167     * and we must do so anyways.  We set this up only
168     * after we initialized the posix stderr API.
169     */
170    ap_open_stderr_log(p);
171}
172