dgaudet 98/02/14 04:17:30
Modified: src CHANGES src/main http_main.c Log: Deal gracefully with unexpected accept() and select() errors in the child_main() loop. This was built based on data from a bunch of PRs, and data from the new-httpd archives, nh.9603 and nh.9701 in particular. I've actually been running with the extra errno tests for linux for the past 6 or 7 months. Without them there is a LOT of spam in the error log on a linux box, compared to none on a solaris box on the same segment. PR: 1747, 1107, 588, 1787 Revision Changes Path 1.631 +7 -0 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.630 retrieving revision 1.631 diff -u -r1.630 -r1.631 --- CHANGES 1998/02/14 10:54:39 1.630 +++ CHANGES 1998/02/14 12:17:26 1.631 @@ -1,5 +1,12 @@ Changes with Apache 1.3b6 + *) Various errors from select() and accept() in child_main() would + result in an infinite loop. It seems these two tickle kernel + or library bugs occasionally, and result in log spammage and + a generally bad scene. Now the child exits immediately, + which seems to be a good workaround. + [Dean Gaudet] PR#1747, 1107, 588, 1787 + *) Cleaned up some race conditions in unix child_main during initialization. [Dean Gaudet] 1.289 +62 -15 apache-1.3/src/main/http_main.c Index: http_main.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_main.c,v retrieving revision 1.288 retrieving revision 1.289 diff -u -r1.288 -r1.289 --- http_main.c 1998/02/14 10:54:41 1.288 +++ http_main.c 1998/02/14 12:17:28 1.289 @@ -3032,14 +3032,14 @@ srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL); if (srv < 0 && errno != EINTR) { -#ifdef LINUX - if (errno == EFAULT) { - aplog_error(APLOG_MARK, APLOG_ERR, server_conf, - "select: (listen) fatal, child exiting"); - clean_child_exit(1); - } -#endif + /* Single Unix documents select as returning errnos + * EBADF, EINTR, and EINVAL... and in none of those + * cases does it make sense to continue. In fact + * on Linux 2.0.x we seem to end up with EFAULT + * occasionally, and we'd loop forever due to it. + */ aplog_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)"); + clean_child_exit(1); } if (srv <= 0) @@ -3075,15 +3075,62 @@ break; /* We have a socket ready for reading */ else { -#if defined(EPROTO) && defined(ECONNABORTED) - if ((errno != EPROTO) && (errno != ECONNABORTED)) -#elif defined(EPROTO) - if (errno != EPROTO) -#elif defined(ECONNABORTED) - if (errno != ECONNABORTED) + /* Our old behaviour here was to continue after accept() + * errors. But this leads us into lots of troubles + * because most of the errors are quite fatal. For + * example, EMFILE can be caused by slow descriptor + * leaks (say in a 3rd party module, or libc). It's + * foolish for us to continue after an EMFILE. We also + * seem to tickle kernel bugs on some platforms which + * lead to never-ending loops here. So it seems best + * to just exit in most cases. + */ + switch (errno) { +#ifdef EPROTO + /* EPROTO on certain older kernels really means + * ECONNABORTED, so we need to ignore it for them. + * See discussion in new-httpd archives nh.9701 + * search for EPROTO. + * + * Also see nh.9603, search for EPROTO: + * There is potentially a bug in Solaris 2.x x<6, + * and other boxes that implement tcp sockets in + * userland (i.e. on top of STREAMS). On these + * systems, EPROTO can actually result in a fatal + * loop. But we don't deal with that. + */ + case EPROTO: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + /* Linux generates the rest of these, other tcp + * stacks (i.e. bsd) tend to hide them behind + * getsockopt() interfaces. They occur when + * the net goes sour or the client disconnects + * after the three-way handshake has been done + * in the kernel but before userland has picked + * up the socket. + */ +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: #endif - aplog_error(APLOG_MARK, APLOG_ERR, server_conf, - "accept: (client socket)"); +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif +#ifdef ENETUNREACH + case ENETUNREACH: +#endif + break; + + default: + aplog_error(APLOG_MARK, APLOG_ERR, server_conf, + "accept: (client socket)"); + clean_child_exit(1); + } } /* go around again, safe to die */