]> granicus.if.org Git - apache/blob - server/mpm/winnt/child.c
Update the copyright year in all .c, .h and .xml files
[apache] / server / mpm / winnt / child.c
1 /* Copyright 2002-2006 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * 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 #ifdef WIN32
18
19 #define CORE_PRIVATE
20 #include "httpd.h"
21 #include "http_main.h"
22 #include "http_log.h"
23 #include "http_config.h"  /* for read_config */
24 #include "http_core.h"    /* for get_remote_host */
25 #include "http_connection.h"
26 #include "apr_portable.h"
27 #include "apr_thread_proc.h"
28 #include "apr_getopt.h"
29 #include "apr_strings.h"
30 #include "apr_lib.h"
31 #include "apr_shm.h"
32 #include "apr_thread_mutex.h"
33 #include "ap_mpm.h"
34 #include "ap_config.h"
35 #include "ap_listen.h"
36 #include "mpm_default.h"
37 #include "mpm_winnt.h"
38 #include "mpm_common.h"
39 #include <malloc.h>
40 #include "apr_atomic.h"
41
42 /* shared with mpm_winnt.c */
43 extern DWORD my_pid;
44
45 /* used by parent to signal the child to start and exit */
46 /* shared with mpm_winnt.c, but should be private to child.c */
47 apr_proc_mutex_t *start_mutex;
48 HANDLE exit_event;
49
50 /* child_main() should never need to modify is_graceful!?! */
51 extern int volatile is_graceful;
52
53 /* Queue for managing the passing of COMP_CONTEXTs between
54  * the accept and worker threads.
55  */
56 static apr_pool_t *pchild;
57 static int shutdown_in_progress = 0;
58 static int workers_may_exit = 0;
59 static unsigned int g_blocked_threads = 0;
60 static HANDLE max_requests_per_child_event;
61
62 static apr_thread_mutex_t  *child_lock;
63 static apr_thread_mutex_t  *qlock;
64 static PCOMP_CONTEXT qhead = NULL;
65 static PCOMP_CONTEXT qtail = NULL;
66 static int num_completion_contexts = 0;
67 static int max_num_completion_contexts = 0;
68 static HANDLE ThreadDispatchIOCP = NULL;
69 static HANDLE qwait_event = NULL;
70
71
72 void mpm_recycle_completion_context(PCOMP_CONTEXT context)
73 {
74     /* Recycle the completion context.
75      * - clear the ptrans pool
76      * - put the context on the queue to be consumed by the accept thread
77      * Note:
78      * context->accept_socket may be in a disconnected but reusable
79      * state so -don't- close it.
80      */
81     if (context) {
82         apr_pool_clear(context->ptrans);
83         context->next = NULL;
84         ResetEvent(context->Overlapped.hEvent);
85         apr_thread_mutex_lock(qlock);
86         if (qtail) {
87             qtail->next = context;
88         } else {
89             qhead = context;
90             SetEvent(qwait_event);
91         }
92         qtail = context;
93         apr_thread_mutex_unlock(qlock);
94     }
95 }
96
97 PCOMP_CONTEXT mpm_get_completion_context(void)
98 {
99     apr_status_t rv;
100     PCOMP_CONTEXT context = NULL;
101
102     while (1) {
103         /* Grab a context off the queue */
104         apr_thread_mutex_lock(qlock);
105         if (qhead) {
106             context = qhead;
107             qhead = qhead->next;
108             if (!qhead)
109                 qtail = NULL;
110         } else {
111             ResetEvent(qwait_event);
112         }
113         apr_thread_mutex_unlock(qlock);
114
115         if (!context) {
116             /* We failed to grab a context off the queue, consider allocating
117              * a new one out of the child pool. There may be up to
118              * (ap_threads_per_child + num_listeners) contexts in the system
119              * at once.
120              */
121             if (num_completion_contexts >= max_num_completion_contexts) {
122                 /* All workers are busy, need to wait for one */
123                 static int reported = 0;
124                 if (!reported) {
125                     ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf,
126                                  "Server ran out of threads to serve requests. Consider "
127                                  "raising the ThreadsPerChild setting");
128                     reported = 1;
129                 }
130
131                 /* Wait for a worker to free a context. Once per second, give
132                  * the caller a chance to check for shutdown. If the wait
133                  * succeeds, get the context off the queue. It must be available,
134                  * since there's only one consumer.
135                  */
136                 rv = WaitForSingleObject(qwait_event, 1000);
137                 if (rv == WAIT_OBJECT_0)
138                     continue;
139                 else /* Hopefully, WAIT_TIMEOUT */
140                     return NULL;
141             } else {
142                 /* Allocate another context.
143                  * Note:
144                  * Multiple failures in the next two steps will cause the pchild pool
145                  * to 'leak' storage. I don't think this is worth fixing...
146                  */
147                 apr_allocator_t *allocator;
148
149                 apr_thread_mutex_lock(child_lock);
150                 context = (PCOMP_CONTEXT) apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
151
152                 context->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
153                 if (context->Overlapped.hEvent == NULL) {
154                     /* Hopefully this is a temporary condition ... */
155                     ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_os_error(), ap_server_conf,
156                                  "mpm_get_completion_context: CreateEvent failed.");
157
158                     apr_thread_mutex_unlock(child_lock);
159                     return NULL;
160                 }
161
162                 /* Create the tranaction pool */
163                 apr_allocator_create(&allocator);
164                 apr_allocator_max_free_set(allocator, ap_max_mem_free);
165                 rv = apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
166                 if (rv != APR_SUCCESS) {
167                     ap_log_error(APLOG_MARK,APLOG_WARNING, rv, ap_server_conf,
168                                  "mpm_get_completion_context: Failed to create the transaction pool.");
169                     CloseHandle(context->Overlapped.hEvent);
170
171                     apr_thread_mutex_unlock(child_lock);
172                     return NULL;
173                 }
174                 apr_allocator_owner_set(allocator, context->ptrans);
175                 apr_pool_tag(context->ptrans, "transaction");
176
177                 context->accept_socket = INVALID_SOCKET;
178                 context->ba = apr_bucket_alloc_create(pchild);
179                 apr_atomic_inc32(&num_completion_contexts);
180
181                 apr_thread_mutex_unlock(child_lock);
182                 break;
183             }
184         } else {
185             /* Got a context from the queue */
186             break;
187         }
188     }
189
190     return context;
191 }
192
193 apr_status_t mpm_post_completion_context(PCOMP_CONTEXT context,
194                                          io_state_e state)
195 {
196     LPOVERLAPPED pOverlapped;
197     if (context)
198         pOverlapped = &context->Overlapped;
199     else
200         pOverlapped = NULL;
201
202     PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, state, pOverlapped);
203     return APR_SUCCESS;
204 }
205
206
207 /*
208  * find_ready_listener()
209  * Only used by Win9* and should go away when the win9*_accept() function is
210  * reimplemented using apr_poll().
211  */
212 static ap_listen_rec *head_listener;
213
214 static APR_INLINE ap_listen_rec *find_ready_listener(fd_set * main_fds)
215 {
216     ap_listen_rec *lr;
217     SOCKET nsd;
218
219     lr = head_listener;
220     do {
221         apr_os_sock_get(&nsd, lr->sd);
222         if (FD_ISSET(nsd, main_fds)) {
223             head_listener = lr->next;
224             if (!head_listener) {
225                 head_listener = ap_listeners;
226             }
227             return lr;
228         }
229         lr = lr->next;
230         if (!lr) {
231             lr = ap_listeners;
232         }
233     } while (lr != head_listener);
234     return NULL;
235 }
236
237
238 /* Windows 9x specific code...
239  * Accept processing for on Windows 95/98 uses a producer/consumer queue
240  * model. A single thread accepts connections and queues the accepted socket
241  * to the accept queue for consumption by a pool of worker threads.
242  *
243  * win9x_accept()
244  *    The accept threads runs this function, which accepts connections off
245  *    the network and calls add_job() to queue jobs to the accept_queue.
246  * add_job()/remove_job()
247  *    Add or remove an accepted socket from the list of sockets
248  *    connected to clients. allowed_globals.jobmutex protects
249  *    against multiple concurrent access to the linked list of jobs.
250  * win9x_get_connection()
251  *    Calls remove_job() to pull a job from the accept queue. All the worker
252  *    threads block on remove_job.
253  */
254
255 typedef struct joblist_s {
256     struct joblist_s *next;
257     SOCKET sock;
258 } joblist;
259
260 typedef struct globals_s {
261     HANDLE jobsemaphore;
262     joblist *jobhead;
263     joblist *jobtail;
264     apr_thread_mutex_t *jobmutex;
265     int jobcount;
266 } globals;
267
268 globals allowed_globals = {NULL, NULL, NULL, NULL, 0};
269
270 #define MAX_SELECT_ERRORS 100
271
272
273 static void add_job(SOCKET sock)
274 {
275     joblist *new_job;
276
277     new_job = (joblist *) malloc(sizeof(joblist));
278     if (new_job == NULL) {
279         ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
280                      "Ouch!  Out of memory in add_job()!");
281         return;
282     }
283     new_job->next = NULL;
284     new_job->sock = sock;
285
286     apr_thread_mutex_lock(allowed_globals.jobmutex);
287
288     if (allowed_globals.jobtail != NULL)
289         allowed_globals.jobtail->next = new_job;
290     allowed_globals.jobtail = new_job;
291     if (!allowed_globals.jobhead)
292         allowed_globals.jobhead = new_job;
293     allowed_globals.jobcount++;
294     ReleaseSemaphore(allowed_globals.jobsemaphore, 1, NULL);
295
296     apr_thread_mutex_unlock(allowed_globals.jobmutex);
297 }
298
299
300 static SOCKET remove_job(void)
301 {
302     joblist *job;
303     SOCKET sock;
304
305     WaitForSingleObject(allowed_globals.jobsemaphore, INFINITE);
306     apr_thread_mutex_lock(allowed_globals.jobmutex);
307
308     if (shutdown_in_progress && !allowed_globals.jobhead) {
309         apr_thread_mutex_unlock(allowed_globals.jobmutex);
310         return (INVALID_SOCKET);
311     }
312     job = allowed_globals.jobhead;
313     ap_assert(job);
314     allowed_globals.jobhead = job->next;
315     if (allowed_globals.jobhead == NULL)
316         allowed_globals.jobtail = NULL;
317     apr_thread_mutex_unlock(allowed_globals.jobmutex);
318     sock = job->sock;
319     free(job);
320
321     return (sock);
322 }
323
324
325 static unsigned int __stdcall win9x_accept(void * dummy)
326 {
327     struct timeval tv;
328     fd_set main_fds;
329     int wait_time = 1;
330     SOCKET csd;
331     SOCKET nsd = INVALID_SOCKET;
332     int count_select_errors = 0;
333     int rc;
334     int clen;
335     ap_listen_rec *lr;
336     struct fd_set listenfds;
337 #if APR_HAVE_IPV6
338     struct sockaddr_in6 sa_client;
339 #else
340     struct sockaddr_in sa_client;
341 #endif
342
343     /* Setup the listeners
344      * ToDo: Use apr_poll()
345      */
346     FD_ZERO(&listenfds);
347     for (lr = ap_listeners; lr; lr = lr->next) {
348         if (lr->sd != NULL) {
349             apr_os_sock_get(&nsd, lr->sd);
350             FD_SET(nsd, &listenfds);
351             ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
352                          "Child %d: Listening on port %d.", my_pid, lr->bind_addr->port);
353         }
354     }
355
356     head_listener = ap_listeners;
357
358     while (!shutdown_in_progress) {
359         tv.tv_sec = wait_time;
360         tv.tv_usec = 0;
361         memcpy(&main_fds, &listenfds, sizeof(fd_set));
362
363         /* First parameter of select() is ignored on Windows */
364         rc = select(0, &main_fds, NULL, NULL, &tv);
365
366         if (rc == 0 || (rc == SOCKET_ERROR && APR_STATUS_IS_EINTR(apr_get_netos_error()))) {
367             count_select_errors = 0;    /* reset count of errors */
368             continue;
369         }
370         else if (rc == SOCKET_ERROR) {
371             /* A "real" error occurred, log it and increment the count of
372              * select errors. This count is used to ensure we don't go into
373              * a busy loop of continuous errors.
374              */
375             ap_log_error(APLOG_MARK, APLOG_INFO, apr_get_netos_error(), ap_server_conf,
376                          "select failed with error %d", apr_get_netos_error());
377             count_select_errors++;
378             if (count_select_errors > MAX_SELECT_ERRORS) {
379                 shutdown_in_progress = 1;
380                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
381                              "Too many errors in select loop. Child process exiting.");
382                 break;
383             }
384         } else {
385             ap_listen_rec *lr;
386
387             lr = find_ready_listener(&main_fds);
388             if (lr != NULL) {
389                 /* fetch the native socket descriptor */
390                 apr_os_sock_get(&nsd, lr->sd);
391             }
392         }
393
394         do {
395             clen = sizeof(sa_client);
396             csd = accept(nsd, (struct sockaddr *) &sa_client, &clen);
397         } while (csd < 0 && APR_STATUS_IS_EINTR(apr_get_netos_error()));
398
399         if (csd < 0) {
400             if (APR_STATUS_IS_ECONNABORTED(apr_get_netos_error())) {
401                 ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(), ap_server_conf,
402                             "accept: (client socket)");
403             }
404         }
405         else {
406             add_job(csd);
407         }
408     }
409     SetEvent(exit_event);
410     return 0;
411 }
412
413
414 static PCOMP_CONTEXT win9x_get_connection(PCOMP_CONTEXT context)
415 {
416     apr_os_sock_info_t sockinfo;
417     int len, salen;
418 #if APR_HAVE_IPV6
419     salen = sizeof(struct sockaddr_in6);
420 #else
421     salen = sizeof(struct sockaddr_in);
422 #endif
423
424
425     if (context == NULL) {
426         /* allocate the completion context and the transaction pool */
427         apr_allocator_t *allocator;
428         apr_thread_mutex_lock(child_lock);
429         context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT));
430         apr_allocator_create(&allocator);
431         apr_allocator_max_free_set(allocator, ap_max_mem_free);
432         apr_pool_create_ex(&context->ptrans, pchild, NULL, allocator);
433         apr_allocator_owner_set(allocator, context->ptrans);
434         apr_pool_tag(context->ptrans, "transaction");
435         context->ba = apr_bucket_alloc_create(pchild);
436         apr_thread_mutex_unlock(child_lock);
437     }
438
439     while (1) {
440         apr_pool_clear(context->ptrans);
441         context->accept_socket = remove_job();
442         if (context->accept_socket == INVALID_SOCKET) {
443             return NULL;
444         }
445         len = salen;
446         context->sa_server = apr_palloc(context->ptrans, len);
447         if (getsockname(context->accept_socket,
448                         context->sa_server, &len)== SOCKET_ERROR) {
449             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
450                          "getsockname failed");
451             continue;
452         }
453         len = salen;
454         context->sa_client = apr_palloc(context->ptrans, len);
455         if ((getpeername(context->accept_socket,
456                          context->sa_client, &len)) == SOCKET_ERROR) {
457             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
458                          "getpeername failed");
459             memset(&context->sa_client, '\0', sizeof(context->sa_client));
460         }
461         sockinfo.os_sock = &context->accept_socket;
462         sockinfo.local   = context->sa_server;
463         sockinfo.remote  = context->sa_client;
464         sockinfo.family  = context->sa_server->sa_family;
465         sockinfo.type    = SOCK_STREAM;
466         apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
467
468         return context;
469     }
470 }
471
472
473 /* Windows NT/2000 specific code...
474  * Accept processing for on Windows NT uses a producer/consumer queue
475  * model. An accept thread accepts connections off the network then issues
476  * PostQueuedCompletionStatus() to awake a thread blocked on the ThreadDispatch
477  * IOCompletionPort.
478  *
479  * winnt_accept()
480  *    One or more accept threads run in this function, each of which accepts
481  *    connections off the network and calls PostQueuedCompletionStatus() to
482  *    queue an io completion packet to the ThreadDispatch IOCompletionPort.
483  * winnt_get_connection()
484  *    Worker threads block on the ThreadDispatch IOCompletionPort awaiting
485  *    connections to service.
486  */
487 #define MAX_ACCEPTEX_ERR_COUNT 100
488 static unsigned int __stdcall winnt_accept(void *lr_)
489 {
490     ap_listen_rec *lr = (ap_listen_rec *)lr_;
491     apr_os_sock_info_t sockinfo;
492     PCOMP_CONTEXT context = NULL;
493     DWORD BytesRead;
494     SOCKET nlsd;
495     int rv, err_count = 0;
496 #if APR_HAVE_IPV6
497     SOCKADDR_STORAGE ss_listen;
498     int namelen = sizeof(ss_listen);
499 #endif
500
501     apr_os_sock_get(&nlsd, lr->sd);
502
503 #if APR_HAVE_IPV6
504     if (getsockname(nlsd, (struct sockaddr *)&ss_listen, &namelen) == SOCKET_ERROR) {
505         ap_log_error(APLOG_MARK,APLOG_ERR, apr_get_netos_error(), ap_server_conf,
506                     "winnt_accept: getsockname error on listening socket, is IPv6 available?");
507         return 1;
508    }
509 #endif
510
511     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
512                  "Child %d: Starting thread to listen on port %d.", my_pid, lr->bind_addr->port);
513     while (!shutdown_in_progress) {
514         if (!context) {
515             context = mpm_get_completion_context();
516             if (!context) {
517                 /* Temporary resource constraint? */
518                 Sleep(0);
519                 continue;
520             }
521         }
522
523         /* Create and initialize the accept socket */
524 #if APR_HAVE_IPV6
525         if (context->accept_socket == INVALID_SOCKET) {
526             context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
527             context->socket_family = ss_listen.ss_family;
528         }
529         else if (context->socket_family != ss_listen.ss_family) {
530             closesocket(context->accept_socket);
531             context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM, IPPROTO_TCP);
532             context->socket_family = ss_listen.ss_family;
533         }
534
535         if (context->accept_socket == INVALID_SOCKET) {
536             ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
537                          "winnt_accept: Failed to allocate an accept socket. "
538                          "Temporary resource constraint? Try again.");
539             Sleep(100);
540             continue;
541         }
542 #else
543         if (context->accept_socket == INVALID_SOCKET) {
544             context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
545             if (context->accept_socket == INVALID_SOCKET) {
546                 /* Another temporary condition? */
547                 ap_log_error(APLOG_MARK,APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
548                              "winnt_accept: Failed to allocate an accept socket. "
549                              "Temporary resource constraint? Try again.");
550                 Sleep(100);
551                 continue;
552             }
553         }
554 #endif
555         /* AcceptEx on the completion context. The completion context will be
556          * signaled when a connection is accepted.
557          */
558         if (!AcceptEx(nlsd, context->accept_socket,
559                       context->buff,
560                       0,
561                       PADDED_ADDR_SIZE,
562                       PADDED_ADDR_SIZE,
563                       &BytesRead,
564                       &context->Overlapped)) {
565             rv = apr_get_netos_error();
566             if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
567                 (rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
568                 /* We can get here when:
569                  * 1) the client disconnects early
570                  * 2) TransmitFile does not properly recycle the accept socket (typically
571                  *    because the client disconnected)
572                  * 3) there is VPN or Firewall software installed with buggy AcceptEx implementation
573                  * 4) the webserver is using a dynamic address that has changed
574                  */
575                 ++err_count;
576                 closesocket(context->accept_socket);
577                 context->accept_socket = INVALID_SOCKET;
578                 if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
579                     ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
580                                  "Child %d: Encountered too many errors accepting client connections. "
581                                  "Possible causes: dynamic address renewal, or incompatible VPN or firewall software. "
582                                  "Try using the Win32DisableAcceptEx directive.", my_pid);
583                     err_count = 0;
584                 }
585                 continue;
586             }
587             else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
588                      (rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
589                 ++err_count;
590                 if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
591                     ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
592                                  "Child %d: Encountered too many errors accepting client connections. "
593                                  "Possible causes: Unknown. "
594                                  "Try using the Win32DisableAcceptEx directive.", my_pid);
595                     err_count = 0;
596                 }
597                 closesocket(context->accept_socket);
598                 context->accept_socket = INVALID_SOCKET;
599                 continue;
600             }
601             err_count = 0;
602
603             /* Wait for pending i/o.
604              * Wake up once per second to check for shutdown .
605              * XXX: We should be waiting on exit_event instead of polling
606              */
607             while (1) {
608                 rv = WaitForSingleObject(context->Overlapped.hEvent, 1000);
609                 if (rv == WAIT_OBJECT_0) {
610                     if (context->accept_socket == INVALID_SOCKET) {
611                         /* socket already closed */
612                         break;
613                     }
614                     if (!GetOverlappedResult((HANDLE)context->accept_socket,
615                                              &context->Overlapped,
616                                              &BytesRead, FALSE)) {
617                         ap_log_error(APLOG_MARK, APLOG_WARNING,
618                                      apr_get_os_error(), ap_server_conf,
619                              "winnt_accept: Asynchronous AcceptEx failed.");
620                         closesocket(context->accept_socket);
621                         context->accept_socket = INVALID_SOCKET;
622                     }
623                     break;
624                 }
625                 /* WAIT_TIMEOUT */
626                 if (shutdown_in_progress) {
627                     closesocket(context->accept_socket);
628                     context->accept_socket = INVALID_SOCKET;
629                     break;
630                 }
631             }
632             if (context->accept_socket == INVALID_SOCKET) {
633                 continue;
634             }
635         }
636         err_count = 0;
637         /* Inherit the listen socket settings. Required for
638          * shutdown() to work
639          */
640         if (setsockopt(context->accept_socket, SOL_SOCKET,
641                        SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
642                        sizeof(nlsd))) {
643             ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf,
644                          "setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
645             /* Not a failure condition. Keep running. */
646         }
647
648         /* Get the local & remote address */
649         GetAcceptExSockaddrs(context->buff,
650                              0,
651                              PADDED_ADDR_SIZE,
652                              PADDED_ADDR_SIZE,
653                              &context->sa_server,
654                              &context->sa_server_len,
655                              &context->sa_client,
656                              &context->sa_client_len);
657
658         sockinfo.os_sock = &context->accept_socket;
659         sockinfo.local   = context->sa_server;
660         sockinfo.remote  = context->sa_client;
661         sockinfo.family  = context->sa_server->sa_family;
662         sockinfo.type    = SOCK_STREAM;
663         apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
664
665         /* When a connection is received, send an io completion notification to
666          * the ThreadDispatchIOCP. This function could be replaced by
667          * mpm_post_completion_context(), but why do an extra function call...
668          */
669         PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_CONNECTION_ACCEPTED,
670                                    &context->Overlapped);
671         context = NULL;
672     }
673     if (!shutdown_in_progress) {
674         /* Yow, hit an irrecoverable error! Tell the child to die. */
675         SetEvent(exit_event);
676     }
677     ap_log_error(APLOG_MARK, APLOG_INFO, APR_SUCCESS, ap_server_conf,
678                  "Child %d: Accept thread exiting.", my_pid);
679     return 0;
680 }
681
682
683 static PCOMP_CONTEXT winnt_get_connection(PCOMP_CONTEXT context)
684 {
685     int rc;
686     DWORD BytesRead;
687     LPOVERLAPPED pol;
688 #ifdef _WIN64
689     ULONG_PTR CompKey;
690 #else
691     DWORD CompKey;
692 #endif
693
694     mpm_recycle_completion_context(context);
695
696     apr_atomic_inc32(&g_blocked_threads);
697     while (1) {
698         if (workers_may_exit) {
699             apr_atomic_dec32(&g_blocked_threads);
700             return NULL;
701         }
702         rc = GetQueuedCompletionStatus(ThreadDispatchIOCP, &BytesRead, &CompKey,
703                                        &pol, INFINITE);
704         if (!rc) {
705             rc = apr_get_os_error();
706             ap_log_error(APLOG_MARK,APLOG_DEBUG, rc, ap_server_conf,
707                              "Child %d: GetQueuedComplationStatus returned %d", my_pid, rc);
708             continue;
709         }
710
711         switch (CompKey) {
712         case IOCP_CONNECTION_ACCEPTED:
713             context = CONTAINING_RECORD(pol, COMP_CONTEXT, Overlapped);
714             break;
715         case IOCP_SHUTDOWN:
716             apr_atomic_dec32(&g_blocked_threads);
717             return NULL;
718         default:
719             apr_atomic_dec32(&g_blocked_threads);
720             return NULL;
721         }
722         break;
723     }
724     apr_atomic_dec32(&g_blocked_threads);
725
726     return context;
727 }
728
729
730 /*
731  * worker_main()
732  * Main entry point for the worker threads. Worker threads block in
733  * win*_get_connection() awaiting a connection to service.
734  */
735 static unsigned int __stdcall worker_main(void *thread_num_val)
736 {
737     static int requests_this_child = 0;
738     PCOMP_CONTEXT context = NULL;
739     int thread_num = (int)thread_num_val;
740     ap_sb_handle_t *sbh;
741
742     while (1) {
743         conn_rec *c;
744         apr_int32_t disconnected;
745
746         ap_update_child_status_from_indexes(0, thread_num, SERVER_READY, NULL);
747
748         /* Grab a connection off the network */
749         if (use_acceptex) {
750             context = winnt_get_connection(context);
751         }
752         else {
753             context = win9x_get_connection(context);
754         }
755
756         if (!context) {
757             /* Time for the thread to exit */
758             break;
759         }
760
761         /* Have we hit MaxRequestPerChild connections? */
762         if (ap_max_requests_per_child) {
763             requests_this_child++;
764             if (requests_this_child > ap_max_requests_per_child) {
765                 SetEvent(max_requests_per_child_event);
766             }
767         }
768
769         ap_create_sb_handle(&sbh, context->ptrans, 0, thread_num);
770         c = ap_run_create_connection(context->ptrans, ap_server_conf,
771                                      context->sock, thread_num, sbh,
772                                      context->ba);
773
774         if (c) {
775             ap_process_connection(c, context->sock);
776             apr_socket_opt_get(context->sock, APR_SO_DISCONNECTED,
777                                &disconnected);
778             if (!disconnected) {
779                 context->accept_socket = INVALID_SOCKET;
780                 ap_lingering_close(c);
781             }
782             else if (!use_acceptex) {
783                 /* If the socket is disconnected but we are not using acceptex,
784                  * we cannot reuse the socket. Disconnected sockets are removed
785                  * from the apr_socket_t struct by apr_sendfile() to prevent the
786                  * socket descriptor from being inadvertently closed by a call
787                  * to apr_socket_close(), so close it directly.
788                  */
789                 closesocket(context->accept_socket);
790                 context->accept_socket = INVALID_SOCKET;
791             }
792         }
793         else {
794             /* ap_run_create_connection closes the socket on failure */
795             context->accept_socket = INVALID_SOCKET;
796         }
797     }
798
799     ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD,
800                                         (request_rec *) NULL);
801
802     return 0;
803 }
804
805
806 static void cleanup_thread(HANDLE *handles, int *thread_cnt, int thread_to_clean)
807 {
808     int i;
809
810     CloseHandle(handles[thread_to_clean]);
811     for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
812         handles[i] = handles[i + 1];
813     (*thread_cnt)--;
814 }
815
816
817 /*
818  * child_main()
819  * Entry point for the main control thread for the child process.
820  * This thread creates the accept thread, worker threads and
821  * monitors the child process for maintenance and shutdown
822  * events.
823  */
824 static void create_listener_thread()
825 {
826     int tid;
827     int num_listeners = 0;
828     if (!use_acceptex) {
829         _beginthreadex(NULL, 0, win9x_accept,
830                        NULL, 0, &tid);
831     } else {
832         /* Start an accept thread per listener
833          * XXX: Why would we have a NULL sd in our listeners?
834          */
835         ap_listen_rec *lr;
836
837         /* Number of completion_contexts allowed in the system is
838          * (ap_threads_per_child + num_listeners). We need the additional
839          * completion contexts to prevent server hangs when ThreadsPerChild
840          * is configured to something less than or equal to the number
841          * of listeners. This is not a usual case, but people have
842          * encountered it.
843          * */
844         for (lr = ap_listeners; lr ; lr = lr->next) {
845             num_listeners++;
846         }
847         max_num_completion_contexts = ap_threads_per_child + num_listeners;
848
849         /* Now start a thread per listener */
850         for (lr = ap_listeners; lr; lr = lr->next) {
851             if (lr->sd != NULL) {
852                 _beginthreadex(NULL, 1000, winnt_accept,
853                                (void *) lr, 0, &tid);
854             }
855         }
856     }
857 }
858
859
860 void child_main(apr_pool_t *pconf)
861 {
862     apr_status_t status;
863     apr_hash_t *ht;
864     ap_listen_rec *lr;
865     HANDLE child_events[2];
866     int threads_created = 0;
867     int listener_started = 0;
868     int tid;
869     HANDLE *child_handles;
870     int rv;
871     time_t end_time;
872     int i;
873     int cld;
874
875     apr_pool_create(&pchild, pconf);
876     apr_pool_tag(pchild, "pchild");
877
878     ap_run_child_init(pchild, ap_server_conf);
879     ht = apr_hash_make(pchild);
880
881     /* Initialize the child_events */
882     max_requests_per_child_event = CreateEvent(NULL, TRUE, FALSE, NULL);
883     if (!max_requests_per_child_event) {
884         ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
885                      "Child %d: Failed to create a max_requests event.", my_pid);
886         exit(APEXIT_CHILDINIT);
887     }
888     child_events[0] = exit_event;
889     child_events[1] = max_requests_per_child_event;
890
891     allowed_globals.jobsemaphore = CreateSemaphore(NULL, 0, 1000000, NULL);
892     apr_thread_mutex_create(&allowed_globals.jobmutex,
893                             APR_THREAD_MUTEX_DEFAULT, pchild);
894
895     /*
896      * Wait until we have permission to start accepting connections.
897      * start_mutex is used to ensure that only one child ever
898      * goes into the listen/accept loop at once.
899      */
900     status = apr_proc_mutex_lock(start_mutex);
901     if (status != APR_SUCCESS) {
902         ap_log_error(APLOG_MARK,APLOG_ERR, status, ap_server_conf,
903                      "Child %d: Failed to acquire the start_mutex. Process will exit.", my_pid);
904         exit(APEXIT_CHILDINIT);
905     }
906     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
907                  "Child %d: Acquired the start mutex.", my_pid);
908
909     /*
910      * Create the worker thread dispatch IOCompletionPort
911      * on Windows NT/2000
912      */
913     if (use_acceptex) {
914         /* Create the worker thread dispatch IOCP */
915         ThreadDispatchIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
916                                                     NULL,
917                                                     0,
918                                                     0); /* CONCURRENT ACTIVE THREADS */
919         apr_thread_mutex_create(&qlock, APR_THREAD_MUTEX_DEFAULT, pchild);
920         qwait_event = CreateEvent(NULL, TRUE, FALSE, NULL);
921         if (!qwait_event) {
922             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
923                          "Child %d: Failed to create a qwait event.", my_pid);
924             exit(APEXIT_CHILDINIT);
925         }
926     }
927
928     /*
929      * Create the pool of worker threads
930      */
931     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
932                  "Child %d: Starting %d worker threads.", my_pid, ap_threads_per_child);
933     child_handles = (HANDLE) apr_pcalloc(pchild, ap_threads_per_child * sizeof(HANDLE));
934     apr_thread_mutex_create(&child_lock, APR_THREAD_MUTEX_DEFAULT, pchild);
935
936     while (1) {
937         for (i = 0; i < ap_threads_per_child; i++) {
938             int *score_idx;
939             int status = ap_scoreboard_image->servers[0][i].status;
940             if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
941                 continue;
942             }
943             ap_update_child_status_from_indexes(0, i, SERVER_STARTING, NULL);
944             child_handles[i] = (HANDLE) _beginthreadex(NULL, (unsigned)ap_thread_stacksize,
945                                                        worker_main, (void *) i, 0, &tid);
946             if (child_handles[i] == 0) {
947                 ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
948                              "Child %d: _beginthreadex failed. Unable to create all worker threads. "
949                              "Created %d of the %d threads requested with the ThreadsPerChild configuration directive.",
950                              my_pid, threads_created, ap_threads_per_child);
951                 ap_signal_parent(SIGNAL_PARENT_SHUTDOWN);
952                 goto shutdown;
953             }
954             threads_created++;
955             /* Save the score board index in ht keyed to the thread handle. We need this
956              * when cleaning up threads down below...
957              */
958             apr_thread_mutex_lock(child_lock);
959             score_idx = apr_pcalloc(pchild, sizeof(int));
960             *score_idx = i;
961             apr_hash_set(ht, &child_handles[i], sizeof(HANDLE), score_idx);
962             apr_thread_mutex_unlock(child_lock);
963         }
964         /* Start the listener only when workers are available */
965         if (!listener_started && threads_created) {
966             create_listener_thread();
967             listener_started = 1;
968             winnt_mpm_state = AP_MPMQ_RUNNING;
969         }
970         if (threads_created == ap_threads_per_child) {
971             break;
972         }
973         /* Check to see if the child has been told to exit */
974         if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) {
975             break;
976         }
977         /* wait for previous generation to clean up an entry in the scoreboard */
978         apr_sleep(1 * APR_USEC_PER_SEC);
979     }
980
981     /* Wait for one of three events:
982      * exit_event:
983      *    The exit_event is signaled by the parent process to notify
984      *    the child that it is time to exit.
985      *
986      * max_requests_per_child_event:
987      *    This event is signaled by the worker threads to indicate that
988      *    the process has handled MaxRequestsPerChild connections.
989      *
990      * TIMEOUT:
991      *    To do periodic maintenance on the server (check for thread exits,
992      *    number of completion contexts, etc.)
993      *
994      * XXX: thread exits *aren't* being checked.
995      *
996      * XXX: other_child - we need the process handles to the other children
997      *      in order to map them to apr_proc_other_child_read (which is not
998      *      named well, it's more like a_p_o_c_died.)
999      *
1000      * XXX: however - if we get a_p_o_c handle inheritance working, and
1001      *      the parent process creates other children and passes the pipes
1002      *      to our worker processes, then we have no business doing such
1003      *      things in the child_main loop, but should happen in master_main.
1004      */
1005     while (1) {
1006 #if !APR_HAS_OTHER_CHILD
1007         rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, INFINITE);
1008         cld = rv - WAIT_OBJECT_0;
1009 #else
1010         rv = WaitForMultipleObjects(2, (HANDLE *) child_events, FALSE, 1000);
1011         cld = rv - WAIT_OBJECT_0;
1012         if (rv == WAIT_TIMEOUT) {
1013             apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
1014         }
1015         else
1016 #endif
1017             if (rv == WAIT_FAILED) {
1018             /* Something serious is wrong */
1019             ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf,
1020                          "Child %d: WAIT_FAILED -- shutting down server", my_pid);
1021             break;
1022         }
1023         else if (cld == 0) {
1024             /* Exit event was signaled */
1025             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1026                          "Child %d: Exit event signaled. Child process is ending.", my_pid);
1027             break;
1028         }
1029         else {
1030             /* MaxRequestsPerChild event set by the worker threads.
1031              * Signal the parent to restart
1032              */
1033             ap_log_error(APLOG_MARK, APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1034                          "Child %d: Process exiting because it reached "
1035                          "MaxRequestsPerChild. Signaling the parent to "
1036                          "restart a new child process.", my_pid);
1037             ap_signal_parent(SIGNAL_PARENT_RESTART);
1038             break;
1039         }
1040     }
1041
1042     /*
1043      * Time to shutdown the child process
1044      */
1045
1046  shutdown:
1047
1048     winnt_mpm_state = AP_MPMQ_STOPPING;
1049     /* Setting is_graceful will cause threads handling keep-alive connections
1050      * to close the connection after handling the current request.
1051      */
1052     is_graceful = 1;
1053
1054     /* Close the listening sockets. Note, we must close the listeners
1055      * before closing any accept sockets pending in AcceptEx to prevent
1056      * memory leaks in the kernel.
1057      */
1058     for (lr = ap_listeners; lr ; lr = lr->next) {
1059         apr_socket_close(lr->sd);
1060     }
1061
1062     /* Shutdown listener threads and pending AcceptEx socksts
1063      * but allow the worker threads to continue consuming from
1064      * the queue of accepted connections.
1065      */
1066     shutdown_in_progress = 1;
1067
1068     Sleep(1000);
1069
1070     /* Tell the worker threads to exit */
1071     workers_may_exit = 1;
1072
1073     /* Release the start_mutex to let the new process (in the restart
1074      * scenario) a chance to begin accepting and servicing requests
1075      */
1076     rv = apr_proc_mutex_unlock(start_mutex);
1077     if (rv == APR_SUCCESS) {
1078         ap_log_error(APLOG_MARK,APLOG_NOTICE, rv, ap_server_conf,
1079                      "Child %d: Released the start mutex", my_pid);
1080     }
1081     else {
1082         ap_log_error(APLOG_MARK,APLOG_ERR, rv, ap_server_conf,
1083                      "Child %d: Failure releasing the start mutex", my_pid);
1084     }
1085
1086     /* Shutdown the worker threads */
1087     if (!use_acceptex) {
1088         for (i = 0; i < threads_created; i++) {
1089             add_job(INVALID_SOCKET);
1090         }
1091     }
1092     else { /* Windows NT/2000 */
1093         /* Post worker threads blocked on the ThreadDispatch IOCompletion port */
1094         while (g_blocked_threads > 0) {
1095             ap_log_error(APLOG_MARK,APLOG_INFO, APR_SUCCESS, ap_server_conf,
1096                          "Child %d: %d threads blocked on the completion port", my_pid, g_blocked_threads);
1097             for (i=g_blocked_threads; i > 0; i--) {
1098                 PostQueuedCompletionStatus(ThreadDispatchIOCP, 0, IOCP_SHUTDOWN, NULL);
1099             }
1100             Sleep(1000);
1101         }
1102         /* Empty the accept queue of completion contexts */
1103         apr_thread_mutex_lock(qlock);
1104         while (qhead) {
1105             CloseHandle(qhead->Overlapped.hEvent);
1106             closesocket(qhead->accept_socket);
1107             qhead = qhead->next;
1108         }
1109         apr_thread_mutex_unlock(qlock);
1110     }
1111
1112     /* Give busy worker threads a chance to service their connections */
1113     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1114                  "Child %d: Waiting for %d worker threads to exit.", my_pid, threads_created);
1115     end_time = time(NULL) + 180;
1116     while (threads_created) {
1117         rv = wait_for_many_objects(threads_created, child_handles, (DWORD)(end_time - time(NULL)));
1118         if (rv != WAIT_TIMEOUT) {
1119             rv = rv - WAIT_OBJECT_0;
1120             ap_assert((rv >= 0) && (rv < threads_created));
1121             cleanup_thread(child_handles, &threads_created, rv);
1122             continue;
1123         }
1124         break;
1125     }
1126
1127     /* Kill remaining threads off the hard way */
1128     if (threads_created) {
1129         ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1130                      "Child %d: Terminating %d threads that failed to exit.",
1131                      my_pid, threads_created);
1132     }
1133     for (i = 0; i < threads_created; i++) {
1134         int *score_idx;
1135         TerminateThread(child_handles[i], 1);
1136         CloseHandle(child_handles[i]);
1137         /* Reset the scoreboard entry for the thread we just whacked */
1138         score_idx = apr_hash_get(ht, &child_handles[i], sizeof(HANDLE));
1139         ap_update_child_status_from_indexes(0, *score_idx, SERVER_DEAD, NULL);
1140     }
1141     ap_log_error(APLOG_MARK,APLOG_NOTICE, APR_SUCCESS, ap_server_conf,
1142                  "Child %d: All worker threads have exited.", my_pid);
1143
1144     CloseHandle(allowed_globals.jobsemaphore);
1145     apr_thread_mutex_destroy(allowed_globals.jobmutex);
1146     apr_thread_mutex_destroy(child_lock);
1147
1148     if (use_acceptex) {
1149         apr_thread_mutex_destroy(qlock);
1150         CloseHandle(qwait_event);
1151     }
1152
1153     apr_pool_destroy(pchild);
1154     CloseHandle(exit_event);
1155 }
1156
1157 #endif /* def WIN32 */