1 /* 2 * socket.c - low-level socket operations 3 */ 4 5 #ifdef HAVE_CONFIG_H 6 # include <config.h> 7 #endif 8 9 #include <stdio.h> 10 11 #include "ntp.h" 12 #include "ntp_io.h" 13 #include "ntp_net.h" 14 #include "ntp_debug.h" 15 16 /* 17 * Windows C runtime ioctl() can't deal properly with sockets, 18 * map to ioctlsocket for this source file. 19 */ 20 #ifdef SYS_WINNT 21 #define ioctl(fd, opt, val) ioctlsocket(fd, opt, (u_long *)(val)) 22 #endif 23 24 /* 25 * on Unix systems the stdio library typically 26 * makes use of file descriptors in the lower 27 * integer range. stdio usually will make use 28 * of the file descriptors in the range of 29 * [0..FOPEN_MAX) 30 * in order to keep this range clean, for socket 31 * file descriptors we attempt to move them above 32 * FOPEN_MAX. This is not as easy as it sounds as 33 * FOPEN_MAX changes from implementation to implementation 34 * and may exceed to current file decriptor limits. 35 * We are using following strategy: 36 * - keep a current socket fd boundary initialized with 37 * max(0, min(GETDTABLESIZE() - FD_CHUNK, FOPEN_MAX)) 38 * - attempt to move the descriptor to the boundary or 39 * above. 40 * - if that fails and boundary > 0 set boundary 41 * to min(0, socket_fd_boundary - FD_CHUNK) 42 * -> retry 43 * if failure and boundary == 0 return old fd 44 * - on success close old fd return new fd 45 * 46 * effects: 47 * - fds will be moved above the socket fd boundary 48 * if at all possible. 49 * - the socket boundary will be reduced until 50 * allocation is possible or 0 is reached - at this 51 * point the algrithm will be disabled 52 */ 53 SOCKET 54 move_fd( 55 SOCKET fd 56 ) 57 { 58 #if !defined(SYS_WINNT) && defined(F_DUPFD) 59 #ifndef FD_CHUNK 60 #define FD_CHUNK 10 61 #endif 62 #ifndef FOPEN_MAX 63 #define FOPEN_MAX 20 64 #endif 65 /* 66 * number of fds we would like to have for 67 * stdio FILE* available. 68 * we can pick a "low" number as our use of 69 * FILE* is limited to log files and temporarily 70 * to data and config files. Except for log files 71 * we don't keep the other FILE* open beyond the 72 * scope of the function that opened it. 73 */ 74 #ifndef FD_PREFERRED_SOCKBOUNDARY 75 #define FD_PREFERRED_SOCKBOUNDARY 48 76 #endif 77 78 static SOCKET socket_boundary = -1; 79 SOCKET newfd; 80 81 REQUIRE((int)fd >= 0); 82 83 /* 84 * check whether boundary has be set up 85 * already 86 */ 87 if (socket_boundary == -1) { 88 socket_boundary = max(0, min(GETDTABLESIZE() - FD_CHUNK, 89 min(FOPEN_MAX, FD_PREFERRED_SOCKBOUNDARY))); 90 TRACE(1, ("move_fd: estimated max descriptors: %d, " 91 "initial socket boundary: %d\n", 92 GETDTABLESIZE(), socket_boundary)); 93 } 94 95 /* 96 * Leave a space for stdio to work in. potentially moving the 97 * socket_boundary lower until allocation succeeds. 98 */ 99 do { 100 if (fd >= 0 && fd < socket_boundary) { 101 /* inside reserved range: attempt to move fd */ 102 newfd = fcntl(fd, F_DUPFD, socket_boundary); 103 104 if (newfd != -1) { 105 /* success: drop the old one - return the new one */ 106 close(fd); 107 return newfd; 108 } 109 } else { 110 /* outside reserved range: no work - return the original one */ 111 return fd; 112 } 113 socket_boundary = max(0, socket_boundary - FD_CHUNK); 114 TRACE(1, ("move_fd: selecting new socket boundary: %d\n", 115 socket_boundary)); 116 } while (socket_boundary > 0); 117 #else 118 ENSURE((int)fd >= 0); 119 #endif /* !defined(SYS_WINNT) && defined(F_DUPFD) */ 120 return fd; 121 } 122 123 124 /* 125 * make_socket_nonblocking() - set up descriptor to be non blocking 126 */ 127 void 128 make_socket_nonblocking( 129 SOCKET fd 130 ) 131 { 132 /* 133 * set non-blocking, 134 */ 135 136 #ifdef USE_FIONBIO 137 /* in vxWorks we use FIONBIO, but the others are defined for old 138 * systems, so all hell breaks loose if we leave them defined 139 */ 140 #undef O_NONBLOCK 141 #undef FNDELAY 142 #undef O_NDELAY 143 #endif 144 145 #if defined(O_NONBLOCK) /* POSIX */ 146 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { 147 msyslog(LOG_ERR, 148 "fcntl(O_NONBLOCK) fails on fd #%d: %m", fd); 149 exit(1); 150 } 151 #elif defined(FNDELAY) 152 if (fcntl(fd, F_SETFL, FNDELAY) < 0) { 153 msyslog(LOG_ERR, "fcntl(FNDELAY) fails on fd #%d: %m", 154 fd); 155 exit(1); 156 } 157 #elif defined(O_NDELAY) /* generally the same as FNDELAY */ 158 if (fcntl(fd, F_SETFL, O_NDELAY) < 0) { 159 msyslog(LOG_ERR, "fcntl(O_NDELAY) fails on fd #%d: %m", 160 fd); 161 exit(1); 162 } 163 #elif defined(FIONBIO) 164 { 165 int on = 1; 166 167 if (ioctl(fd, FIONBIO, &on) < 0) { 168 msyslog(LOG_ERR, 169 "ioctl(FIONBIO) fails on fd #%d: %m", 170 fd); 171 exit(1); 172 } 173 } 174 #elif defined(FIOSNBIO) 175 if (ioctl(fd, FIOSNBIO, &on) < 0) { 176 msyslog(LOG_ERR, 177 "ioctl(FIOSNBIO) fails on fd #%d: %m", fd); 178 exit(1); 179 } 180 #else 181 # include "Bletch: Need non-blocking I/O!" 182 #endif 183 } 184 185 #if 0 186 187 /* The following subroutines should probably be moved here */ 188 189 static SOCKET 190 open_socket( 191 sockaddr_u * addr, 192 int bcast, 193 int turn_off_reuse, 194 endpt * interf 195 ) 196 void 197 sendpkt( 198 sockaddr_u * dest, 199 struct interface * ep, 200 int ttl, 201 struct pkt * pkt, 202 int len 203 ) 204 205 static inline int 206 read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts) 207 208 static inline int 209 read_network_packet( 210 SOCKET fd, 211 struct interface * itf, 212 l_fp ts 213 ) 214 215 void 216 kill_asyncio(int startfd) 217 218 #endif /* 0 */ 219