socket.c revision 290001
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 */
53SOCKET
54move_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 */
127void
128make_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
189static SOCKET
190open_socket(
191	sockaddr_u *	addr,
192	int		bcast,
193	int		turn_off_reuse,
194	endpt *		interf
195	)
196void
197sendpkt(
198	sockaddr_u *		dest,
199	struct interface *	ep,
200	int			ttl,
201	struct pkt *		pkt,
202	int			len
203	)
204
205static inline int
206read_refclock_packet(SOCKET fd, struct refclockio *rp, l_fp ts)
207
208static inline int
209read_network_packet(
210	SOCKET			fd,
211	struct interface *	itf,
212	l_fp			ts
213	)
214
215void
216kill_asyncio(int startfd)
217
218#endif /* 0 */
219