1 #include "bsdtar_platform.h"
2
3 #include <sys/types.h>
4 #include <sys/socket.h>
5
6 #include <netinet/in.h>
7 #include <netinet/tcp.h>
8
9 #include <errno.h>
10
11 #include "warnp.h"
12
13 #include "tsnetwork_internal.h"
14
15 /**
16 * XXX Portability
17 * XXX These functions serve two purposes:
18 * XXX 1. To avoid wasting bandwidth, by ensuring that multiple small writes
19 * XXX over a socket are aggregated into a single TCP/IP packet.
20 * XXX 2. To avoid severe performance issues which would otherwise result
21 * XXX from nagling, by allowing data to be "pushed" out once there are no
22 * XXX more writes queued.
23 * XXX
24 * XXX POSIX defines TCP_NODELAY for purpose #2, although it does not require
25 * XXX that implementations obey it; BSD and Linux respectively define
26 * XXX TCP_NOPUSH and TCP_CORK for purpose #1. On OS X, TCP_NOPUSH is
27 * XXX defined, but seems to be broken; we use autoconf to detect OS X and
28 * XXX define BROKEN_TCP_NOPUSH. On Cygwin, TCP_NOPUSH is defined, but
29 * XXX using it produces a ENOPROTOOPT error; we define BROKEN_TCP_NOPUSH
30 * XXX in this case, too. On Minix, TCP_NODELAY fails with ENOSYS; since
31 * XXX corking occurs for performance reasons only, we ignore this errno.
32 */
33
34 /* Macro to simplify setting options. */
35 #define setopt(fd, opt, value, err0) do { \
36 int val; \
37 \
38 val = value; \
39 if (setsockopt(fd, IPPROTO_TCP, opt, &val, sizeof(int))) { \
40 if ((errno != ETIMEDOUT) && \
41 (errno != ECONNRESET) && \
42 (errno != ENOSYS)) { \
43 warnp("setsockopt(%s, %d)", #opt, val); \
44 goto err0; \
45 } \
46 } \
47 } while (0);
48
49 /**
50 * network_cork(fd):
51 * Clear the TCP_NODELAY socket option, and set TCP_CORK or TCP_NOPUSH if
52 * either is defined.
53 */
54 int
network_cork(int fd)55 network_cork(int fd)
56 {
57
58 /* Clear TCP_NODELAY. */
59 setopt(fd, TCP_NODELAY, 0, err0);
60
61 /* Set TCP_CORK or TCP_NOPUSH as appropriate. */
62 #ifdef TCP_CORK
63 setopt(fd, TCP_CORK, 1, err0);
64 #else
65 #ifdef TCP_NOPUSH
66 #ifndef BROKEN_TCP_NOPUSH
67 setopt(fd, TCP_NOPUSH, 1, err0);
68 #endif
69 #endif
70 #endif
71
72 /* Success! */
73 return (0);
74
75 err0:
76 /* Failure! */
77 return (-1);
78 }
79
80 /**
81 * network_uncork(fd):
82 * Set the TCP_NODELAY socket option, and clear TCP_CORK or TCP_NOPUSH if
83 * either is defined.
84 */
85 int
network_uncork(int fd)86 network_uncork(int fd)
87 {
88
89 /* Clear TCP_CORK or TCP_NOPUSH as appropriate. */
90 #ifdef TCP_CORK
91 setopt(fd, TCP_CORK, 0, err0);
92 #else
93 #ifdef TCP_NOPUSH
94 #ifndef BROKEN_TCP_NOPUSH
95 setopt(fd, TCP_NOPUSH, 0, err0);
96 #endif
97 #endif
98 #endif
99
100 /* Set TCP_NODELAY. */
101 /*
102 * For compatibility with Linux 2.4, this must be done after we
103 * clear TCP_CORK; otherwise it will throw an EINVAL back at us.
104 */
105 setopt(fd, TCP_NODELAY, 1, err0);
106
107 /* Success! */
108 return (0);
109
110 err0:
111 /* Failure! */
112 return (-1);
113 }
114