1*0Sigor@sysoev.ru 
2*0Sigor@sysoev.ru /*
3*0Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
4*0Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
5*0Sigor@sysoev.ru  */
6*0Sigor@sysoev.ru 
7*0Sigor@sysoev.ru #include <nxt_main.h>
8*0Sigor@sysoev.ru 
9*0Sigor@sysoev.ru 
10*0Sigor@sysoev.ru /*
11*0Sigor@sysoev.ru  * sendfile() has been introduced in Linux 2.2.
12*0Sigor@sysoev.ru  * It supported 32-bit offsets only.
13*0Sigor@sysoev.ru  *
14*0Sigor@sysoev.ru  * Linux 2.4.21 has introduced sendfile64().  However, even on 64-bit
15*0Sigor@sysoev.ru  * platforms it returns EINVAL if the count argument is more than 2G-1 bytes.
16*0Sigor@sysoev.ru  * In Linux 2.6.17 sendfile() has been internally changed to splice()
17*0Sigor@sysoev.ru  * and this limitation has gone.
18*0Sigor@sysoev.ru  */
19*0Sigor@sysoev.ru 
20*0Sigor@sysoev.ru #ifdef NXT_TEST_BUILD_LINUX_SENDFILE
21*0Sigor@sysoev.ru 
22*0Sigor@sysoev.ru #define MSG_NOSIGNAL      0x4000
23*0Sigor@sysoev.ru #define MSG_MORE          0x8000
24*0Sigor@sysoev.ru 
25*0Sigor@sysoev.ru ssize_t nxt_linux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
26*0Sigor@sysoev.ru     size_t limit);
27*0Sigor@sysoev.ru 
28*0Sigor@sysoev.ru static ssize_t nxt_sys_sendfile(int out_fd, int in_fd, off_t *offset,
29*0Sigor@sysoev.ru     size_t count)
30*0Sigor@sysoev.ru {
31*0Sigor@sysoev.ru     return -1;
32*0Sigor@sysoev.ru }
33*0Sigor@sysoev.ru 
34*0Sigor@sysoev.ru #else
35*0Sigor@sysoev.ru #define nxt_sys_sendfile  sendfile
36*0Sigor@sysoev.ru #endif
37*0Sigor@sysoev.ru 
38*0Sigor@sysoev.ru 
39*0Sigor@sysoev.ru static ssize_t nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size,
40*0Sigor@sysoev.ru     nxt_uint_t flags);
41*0Sigor@sysoev.ru static ssize_t nxt_linux_sendmsg(nxt_event_conn_t *c,
42*0Sigor@sysoev.ru     nxt_sendbuf_coalesce_t *sb, nxt_uint_t niov, nxt_uint_t flags);
43*0Sigor@sysoev.ru 
44*0Sigor@sysoev.ru 
45*0Sigor@sysoev.ru ssize_t
46*0Sigor@sysoev.ru nxt_linux_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
47*0Sigor@sysoev.ru     size_t limit)
48*0Sigor@sysoev.ru {
49*0Sigor@sysoev.ru     size_t                  size;
50*0Sigor@sysoev.ru     ssize_t                 n;
51*0Sigor@sysoev.ru     nxt_buf_t               *fb;
52*0Sigor@sysoev.ru     nxt_err_t               err;
53*0Sigor@sysoev.ru     nxt_off_t               offset;
54*0Sigor@sysoev.ru     nxt_uint_t              niov, flags;
55*0Sigor@sysoev.ru     struct iovec            iov[NXT_IOBUF_MAX];
56*0Sigor@sysoev.ru     nxt_sendbuf_coalesce_t  sb;
57*0Sigor@sysoev.ru 
58*0Sigor@sysoev.ru     sb.buf = b;
59*0Sigor@sysoev.ru     sb.iobuf = iov;
60*0Sigor@sysoev.ru     sb.nmax = NXT_IOBUF_MAX;
61*0Sigor@sysoev.ru     sb.sync = 0;
62*0Sigor@sysoev.ru     sb.size = 0;
63*0Sigor@sysoev.ru     sb.limit = limit;
64*0Sigor@sysoev.ru 
65*0Sigor@sysoev.ru     niov = nxt_sendbuf_mem_coalesce(&sb);
66*0Sigor@sysoev.ru 
67*0Sigor@sysoev.ru     if (niov == 0 && sb.sync) {
68*0Sigor@sysoev.ru         return 0;
69*0Sigor@sysoev.ru     }
70*0Sigor@sysoev.ru 
71*0Sigor@sysoev.ru     fb = (sb.buf != NULL && nxt_buf_is_file(sb.buf)) ? sb.buf : NULL;
72*0Sigor@sysoev.ru 
73*0Sigor@sysoev.ru     if (niov != 0) {
74*0Sigor@sysoev.ru 
75*0Sigor@sysoev.ru         flags = MSG_NOSIGNAL;
76*0Sigor@sysoev.ru 
77*0Sigor@sysoev.ru         if (fb != NULL) {
78*0Sigor@sysoev.ru             /*
79*0Sigor@sysoev.ru              * The Linux-specific MSG_MORE flag is cheaper
80*0Sigor@sysoev.ru              * than additional setsockopt(TCP_CORK) syscall.
81*0Sigor@sysoev.ru              */
82*0Sigor@sysoev.ru             flags |= MSG_MORE;
83*0Sigor@sysoev.ru         }
84*0Sigor@sysoev.ru 
85*0Sigor@sysoev.ru         if (niov == 1) {
86*0Sigor@sysoev.ru             /*
87*0Sigor@sysoev.ru              * Disposal of surplus kernel msghdr
88*0Sigor@sysoev.ru              * and iovec copy-in operations.
89*0Sigor@sysoev.ru              */
90*0Sigor@sysoev.ru             return nxt_linux_send(c, iov->iov_base, iov->iov_len, flags);
91*0Sigor@sysoev.ru         }
92*0Sigor@sysoev.ru 
93*0Sigor@sysoev.ru         return nxt_linux_sendmsg(c, &sb, niov, flags);
94*0Sigor@sysoev.ru     }
95*0Sigor@sysoev.ru 
96*0Sigor@sysoev.ru     size = nxt_sendbuf_file_coalesce(&sb);
97*0Sigor@sysoev.ru 
98*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "sendfile(%d, %FD, @%O, %uz)",
99*0Sigor@sysoev.ru                   c->socket.fd, fb->file->fd, fb->file_pos, size);
100*0Sigor@sysoev.ru 
101*0Sigor@sysoev.ru     offset = fb->file_pos;
102*0Sigor@sysoev.ru 
103*0Sigor@sysoev.ru     n = nxt_sys_sendfile(c->socket.fd, fb->file->fd, &offset, size);
104*0Sigor@sysoev.ru 
105*0Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
106*0Sigor@sysoev.ru 
107*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "sendfile(): %d", n);
108*0Sigor@sysoev.ru 
109*0Sigor@sysoev.ru     if (n == -1) {
110*0Sigor@sysoev.ru         switch (err) {
111*0Sigor@sysoev.ru 
112*0Sigor@sysoev.ru         case NXT_EAGAIN:
113*0Sigor@sysoev.ru             c->socket.write_ready = 0;
114*0Sigor@sysoev.ru             break;
115*0Sigor@sysoev.ru 
116*0Sigor@sysoev.ru         case NXT_EINTR:
117*0Sigor@sysoev.ru             break;
118*0Sigor@sysoev.ru 
119*0Sigor@sysoev.ru         default:
120*0Sigor@sysoev.ru             c->socket.error = err;
121*0Sigor@sysoev.ru             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
122*0Sigor@sysoev.ru                           c->socket.log,
123*0Sigor@sysoev.ru                           "sendfile(%d, %FD, %O, %uz) failed %E \"%FN\"",
124*0Sigor@sysoev.ru                           c->socket.fd, fb->file->fd, fb->file_pos, size,
125*0Sigor@sysoev.ru                           err, fb->file->name);
126*0Sigor@sysoev.ru 
127*0Sigor@sysoev.ru             return NXT_ERROR;
128*0Sigor@sysoev.ru         }
129*0Sigor@sysoev.ru 
130*0Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "sendfile() %E", err);
131*0Sigor@sysoev.ru 
132*0Sigor@sysoev.ru         return 0;
133*0Sigor@sysoev.ru     }
134*0Sigor@sysoev.ru 
135*0Sigor@sysoev.ru     if (n < (ssize_t) size) {
136*0Sigor@sysoev.ru         c->socket.write_ready = 0;
137*0Sigor@sysoev.ru     }
138*0Sigor@sysoev.ru 
139*0Sigor@sysoev.ru     return n;
140*0Sigor@sysoev.ru }
141*0Sigor@sysoev.ru 
142*0Sigor@sysoev.ru 
143*0Sigor@sysoev.ru static ssize_t
144*0Sigor@sysoev.ru nxt_linux_send(nxt_event_conn_t *c, void *buf, size_t size, nxt_uint_t flags)
145*0Sigor@sysoev.ru {
146*0Sigor@sysoev.ru     ssize_t    n;
147*0Sigor@sysoev.ru     nxt_err_t  err;
148*0Sigor@sysoev.ru 
149*0Sigor@sysoev.ru     n = send(c->socket.fd, buf, size, flags);
150*0Sigor@sysoev.ru 
151*0Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
152*0Sigor@sysoev.ru 
153*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "send(%d, %p, %uz, 0x%uXi): %z",
154*0Sigor@sysoev.ru                   c->socket.fd, buf, size, flags, n);
155*0Sigor@sysoev.ru 
156*0Sigor@sysoev.ru     if (n == -1) {
157*0Sigor@sysoev.ru         switch (err) {
158*0Sigor@sysoev.ru 
159*0Sigor@sysoev.ru         case NXT_EAGAIN:
160*0Sigor@sysoev.ru             c->socket.write_ready = 0;
161*0Sigor@sysoev.ru             break;
162*0Sigor@sysoev.ru 
163*0Sigor@sysoev.ru         case NXT_EINTR:
164*0Sigor@sysoev.ru             break;
165*0Sigor@sysoev.ru 
166*0Sigor@sysoev.ru         default:
167*0Sigor@sysoev.ru             c->socket.error = err;
168*0Sigor@sysoev.ru             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
169*0Sigor@sysoev.ru                           c->socket.log, "send(%d, %p, %uz, 0x%uXi) failed %E",
170*0Sigor@sysoev.ru                           c->socket.fd, buf, size, flags, err);
171*0Sigor@sysoev.ru 
172*0Sigor@sysoev.ru             return NXT_ERROR;
173*0Sigor@sysoev.ru         }
174*0Sigor@sysoev.ru 
175*0Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "send() %E", err);
176*0Sigor@sysoev.ru 
177*0Sigor@sysoev.ru         return 0;
178*0Sigor@sysoev.ru     }
179*0Sigor@sysoev.ru 
180*0Sigor@sysoev.ru     if (n < (ssize_t) size) {
181*0Sigor@sysoev.ru         c->socket.write_ready = 0;
182*0Sigor@sysoev.ru     }
183*0Sigor@sysoev.ru 
184*0Sigor@sysoev.ru     return n;
185*0Sigor@sysoev.ru }
186*0Sigor@sysoev.ru 
187*0Sigor@sysoev.ru 
188*0Sigor@sysoev.ru static ssize_t
189*0Sigor@sysoev.ru nxt_linux_sendmsg(nxt_event_conn_t *c, nxt_sendbuf_coalesce_t *sb,
190*0Sigor@sysoev.ru     nxt_uint_t niov, nxt_uint_t flags)
191*0Sigor@sysoev.ru {
192*0Sigor@sysoev.ru     ssize_t        n;
193*0Sigor@sysoev.ru     nxt_err_t      err;
194*0Sigor@sysoev.ru     struct msghdr  msg;
195*0Sigor@sysoev.ru 
196*0Sigor@sysoev.ru     msg.msg_name = NULL;
197*0Sigor@sysoev.ru     msg.msg_namelen = 0;
198*0Sigor@sysoev.ru     msg.msg_iov = sb->iobuf;
199*0Sigor@sysoev.ru     msg.msg_iovlen = niov;
200*0Sigor@sysoev.ru     msg.msg_control = NULL;
201*0Sigor@sysoev.ru     msg.msg_controllen = 0;
202*0Sigor@sysoev.ru     msg.msg_flags = 0;
203*0Sigor@sysoev.ru 
204*0Sigor@sysoev.ru     n = sendmsg(c->socket.fd, &msg, flags);
205*0Sigor@sysoev.ru 
206*0Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
207*0Sigor@sysoev.ru 
208*0Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "sendmsg(%d, %ui, 0x%uXi): %d",
209*0Sigor@sysoev.ru                   c->socket.fd, niov, flags, n);
210*0Sigor@sysoev.ru 
211*0Sigor@sysoev.ru     if (n == -1) {
212*0Sigor@sysoev.ru         switch (err) {
213*0Sigor@sysoev.ru 
214*0Sigor@sysoev.ru         case NXT_EAGAIN:
215*0Sigor@sysoev.ru             c->socket.write_ready = 0;
216*0Sigor@sysoev.ru             break;
217*0Sigor@sysoev.ru 
218*0Sigor@sysoev.ru         case NXT_EINTR:
219*0Sigor@sysoev.ru             break;
220*0Sigor@sysoev.ru 
221*0Sigor@sysoev.ru         default:
222*0Sigor@sysoev.ru             c->socket.error = err;
223*0Sigor@sysoev.ru             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
224*0Sigor@sysoev.ru                           c->socket.log, "sendmsg(%d, %ui, 0x%uXi) failed %E",
225*0Sigor@sysoev.ru                           c->socket.fd, niov, flags, err);
226*0Sigor@sysoev.ru 
227*0Sigor@sysoev.ru             return NXT_ERROR;
228*0Sigor@sysoev.ru         }
229*0Sigor@sysoev.ru 
230*0Sigor@sysoev.ru         nxt_log_debug(c->socket.log, "sendmsg() %E", err);
231*0Sigor@sysoev.ru 
232*0Sigor@sysoev.ru         return 0;
233*0Sigor@sysoev.ru     }
234*0Sigor@sysoev.ru 
235*0Sigor@sysoev.ru     if (n < (ssize_t) sb->size) {
236*0Sigor@sysoev.ru         c->socket.write_ready = 0;
237*0Sigor@sysoev.ru     }
238*0Sigor@sysoev.ru 
239*0Sigor@sysoev.ru     return n;
240*0Sigor@sysoev.ru }
241