/* * close_all_beyond() * * Close all file descriptors after the given keep_fd, which is the * highest fd to keep open. See * * http://stackoverflow.com/questions/899038/getting-the-highest-allocated-file-descriptor */ void close_all_beyond( int keep_fd ) { # ifdef HAVE_CLOSEFROM closefrom(keep_fd + 1); # elif defined(F_CLOSEM) /* * From 'Writing Reliable AIX Daemons,' SG24-4946-00, * by Eric Agar (saves us from doing 32767 system * calls) */ if (fcntl(keep_fd + 1, F_CLOSEM, 0) == -1) msyslog(LOG_ERR, "F_CLOSEM(%d): %m", keep_fd + 1); # else /* !HAVE_CLOSEFROM && !F_CLOSEM follows */ int fd; int max_fd; /* includes POSIX case */ max_fd = GETDTABLESIZE(); for (fd = keep_fd + 1; fd < max_fd; fd++) close(fd); # endif /* !HAVE_CLOSEFROM && !F_CLOSEM */ }
/* * on Unix systems the stdio library typically * makes use of file descriptors in the lower * integer range. stdio usually will make use * of the file descriptors in the range of * [0..FOPEN_MAX) * in order to keep this range clean, for socket * file descriptors we attempt to move them above * FOPEN_MAX. This is not as easy as it sounds as * FOPEN_MAX changes from implementation to implementation * and may exceed to current file decriptor limits. * We are using following strategy: * - keep a current socket fd boundary initialized with * max(0, min(GETDTABLESIZE() - FD_CHUNK, FOPEN_MAX)) * - attempt to move the descriptor to the boundary or * above. * - if that fails and boundary > 0 set boundary * to min(0, socket_fd_boundary - FD_CHUNK) * -> retry * if failure and boundary == 0 return old fd * - on success close old fd return new fd * * effects: * - fds will be moved above the socket fd boundary * if at all possible. * - the socket boundary will be reduced until * allocation is possible or 0 is reached - at this * point the algrithm will be disabled */ SOCKET move_fd( SOCKET fd ) { #if !defined(SYS_WINNT) && defined(F_DUPFD) #ifndef FD_CHUNK #define FD_CHUNK 10 #endif #ifndef FOPEN_MAX #define FOPEN_MAX 20 #endif /* * number of fds we would like to have for * stdio FILE* available. * we can pick a "low" number as our use of * FILE* is limited to log files and temporarily * to data and config files. Except for log files * we don't keep the other FILE* open beyond the * scope of the function that opened it. */ #ifndef FD_PREFERRED_SOCKBOUNDARY #define FD_PREFERRED_SOCKBOUNDARY 48 #endif static SOCKET socket_boundary = -1; SOCKET newfd; NTP_REQUIRE((int)fd >= 0); /* * check whether boundary has be set up * already */ if (socket_boundary == -1) { socket_boundary = max(0, min(GETDTABLESIZE() - FD_CHUNK, min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY))); TRACE(1, ("move_fd: estimated max descriptors: %d, " "initial socket boundary: %d\n", GETDTABLESIZE(), socket_boundary)); } /* * Leave a space for stdio to work in. potentially moving the * socket_boundary lower until allocation succeeds. */ do { if (fd >= 0 && fd < socket_boundary) { /* inside reserved range: attempt to move fd */ newfd = fcntl(fd, F_DUPFD, socket_boundary); if (newfd != -1) { /* success: drop the old one - return the new one */ close(fd); return newfd; } } else { /* outside reserved range: no work - return the original one */ return fd; } socket_boundary = max(0, socket_boundary - FD_CHUNK); TRACE(1, ("move_fd: selecting new socket boundary: %d\n", socket_boundary)); } while (socket_boundary > 0); #else NTP_REQUIRE((int)fd >= 0); #endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */ return fd; }