10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru /*
110Sigor@sysoev.ru  * sendfile() has been introduced in FreeBSD 3.1,
120Sigor@sysoev.ru  * however, early implementation had various bugs.
130Sigor@sysoev.ru  * This code supports FreeBSD 5.0 implementation.
140Sigor@sysoev.ru  */
150Sigor@sysoev.ru 
160Sigor@sysoev.ru #ifdef NXT_TEST_BUILD_FREEBSD_SENDFILE
170Sigor@sysoev.ru 
180Sigor@sysoev.ru ssize_t nxt_freebsd_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
190Sigor@sysoev.ru     size_t limit);
200Sigor@sysoev.ru 
210Sigor@sysoev.ru static int nxt_sys_sendfile(int fd, int s, off_t offset, size_t nbytes,
220Sigor@sysoev.ru     struct sf_hdtr *hdtr, off_t *sbytes, int flags)
230Sigor@sysoev.ru {
240Sigor@sysoev.ru     return -1;
250Sigor@sysoev.ru }
260Sigor@sysoev.ru 
270Sigor@sysoev.ru #else
280Sigor@sysoev.ru #define nxt_sys_sendfile  sendfile
290Sigor@sysoev.ru #endif
300Sigor@sysoev.ru 
310Sigor@sysoev.ru 
320Sigor@sysoev.ru ssize_t
330Sigor@sysoev.ru nxt_freebsd_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
340Sigor@sysoev.ru     size_t limit)
350Sigor@sysoev.ru {
360Sigor@sysoev.ru     size_t                  file_size;
370Sigor@sysoev.ru     ssize_t                 n;
380Sigor@sysoev.ru     nxt_buf_t               *fb;
390Sigor@sysoev.ru     nxt_err_t               err;
400Sigor@sysoev.ru     nxt_off_t               sent;
410Sigor@sysoev.ru     nxt_uint_t              nhd, ntr;
420Sigor@sysoev.ru     struct iovec            hd[NXT_IOBUF_MAX], tr[NXT_IOBUF_MAX];
430Sigor@sysoev.ru     struct sf_hdtr          hdtr, *ht;
440Sigor@sysoev.ru     nxt_sendbuf_coalesce_t  sb;
450Sigor@sysoev.ru 
460Sigor@sysoev.ru     sb.buf = b;
470Sigor@sysoev.ru     sb.iobuf = hd;
480Sigor@sysoev.ru     sb.nmax = NXT_IOBUF_MAX;
490Sigor@sysoev.ru     sb.sync = 0;
500Sigor@sysoev.ru     sb.size = 0;
510Sigor@sysoev.ru     sb.limit = limit;
520Sigor@sysoev.ru 
531Sigor@sysoev.ru     nhd = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);
540Sigor@sysoev.ru 
550Sigor@sysoev.ru     if (nhd == 0 && sb.sync) {
560Sigor@sysoev.ru         return 0;
570Sigor@sysoev.ru     }
580Sigor@sysoev.ru 
590Sigor@sysoev.ru     if (sb.buf == NULL || !nxt_buf_is_file(sb.buf)) {
600Sigor@sysoev.ru         return nxt_event_conn_io_writev(c, hd, nhd);
610Sigor@sysoev.ru     }
620Sigor@sysoev.ru 
630Sigor@sysoev.ru     fb = sb.buf;
640Sigor@sysoev.ru 
650Sigor@sysoev.ru     file_size = nxt_sendbuf_file_coalesce(&sb);
660Sigor@sysoev.ru 
670Sigor@sysoev.ru     if (file_size == 0) {
680Sigor@sysoev.ru         return nxt_event_conn_io_writev(c, hd, nhd);
690Sigor@sysoev.ru     }
700Sigor@sysoev.ru 
710Sigor@sysoev.ru     sb.iobuf = tr;
720Sigor@sysoev.ru 
731Sigor@sysoev.ru     ntr = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);
740Sigor@sysoev.ru 
750Sigor@sysoev.ru     /*
760Sigor@sysoev.ru      * Disposal of surplus kernel operations
770Sigor@sysoev.ru      * if there are no headers or trailers.
780Sigor@sysoev.ru      */
790Sigor@sysoev.ru 
800Sigor@sysoev.ru     ht = NULL;
810Sigor@sysoev.ru     nxt_memzero(&hdtr, sizeof(struct sf_hdtr));
820Sigor@sysoev.ru 
830Sigor@sysoev.ru     if (nhd != 0) {
840Sigor@sysoev.ru         ht = &hdtr;
850Sigor@sysoev.ru         hdtr.headers = hd;
860Sigor@sysoev.ru         hdtr.hdr_cnt = nhd;
870Sigor@sysoev.ru     }
880Sigor@sysoev.ru 
890Sigor@sysoev.ru     if (ntr != 0) {
900Sigor@sysoev.ru         ht = &hdtr;
910Sigor@sysoev.ru         hdtr.trailers = tr;
920Sigor@sysoev.ru         hdtr.trl_cnt = ntr;
930Sigor@sysoev.ru     }
940Sigor@sysoev.ru 
95*13Sigor@sysoev.ru     nxt_debug(c->socket.task, "sendfile(%FD, %d, @%O, %uz) hd:%ui tr:%ui",
960Sigor@sysoev.ru                   fb->file->fd, c->socket.fd, fb->file_pos, file_size,
970Sigor@sysoev.ru                   nhd, ntr);
980Sigor@sysoev.ru 
990Sigor@sysoev.ru     sent = 0;
1000Sigor@sysoev.ru     n = nxt_sys_sendfile(fb->file->fd, c->socket.fd, fb->file_pos,
1010Sigor@sysoev.ru                          file_size, ht, &sent, 0);
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru     err = (n == -1) ? nxt_errno : 0;
1040Sigor@sysoev.ru 
105*13Sigor@sysoev.ru     nxt_debug(c->socket.task, "sendfile(): %d sent:%O", n, sent);
1060Sigor@sysoev.ru 
1070Sigor@sysoev.ru     if (n == -1) {
1080Sigor@sysoev.ru         switch (err) {
1090Sigor@sysoev.ru 
1100Sigor@sysoev.ru         case NXT_EAGAIN:
1110Sigor@sysoev.ru             c->socket.write_ready = 0;
1120Sigor@sysoev.ru             break;
1130Sigor@sysoev.ru 
1140Sigor@sysoev.ru         case NXT_EINTR:
1150Sigor@sysoev.ru             break;
1160Sigor@sysoev.ru 
1170Sigor@sysoev.ru         default:
1180Sigor@sysoev.ru             c->socket.error = err;
119*13Sigor@sysoev.ru             nxt_log(c->socket.task, nxt_socket_error_level(err),
120*13Sigor@sysoev.ru                 "sendfile(%FD, %d, %O, %uz) failed %E \"%FN\" hd:%ui tr:%ui",
121*13Sigor@sysoev.ru                 fb->file->fd, c->socket.fd, fb->file_pos, file_size, err,
122*13Sigor@sysoev.ru                 fb->file->name, nhd, ntr);
1230Sigor@sysoev.ru 
1240Sigor@sysoev.ru             return NXT_ERROR;
1250Sigor@sysoev.ru         }
1260Sigor@sysoev.ru 
127*13Sigor@sysoev.ru         nxt_debug(c->socket.task, "sendfile() %E", err);
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru         return sent;
1300Sigor@sysoev.ru 
1310Sigor@sysoev.ru     } else if (sent == 0) {
132*13Sigor@sysoev.ru         nxt_log(c->socket.task, NXT_LOG_ERR,
133*13Sigor@sysoev.ru                 "file \"%FN\" was truncated while sendfile()", fb->file->name);
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru         return NXT_ERROR;
1360Sigor@sysoev.ru     }
1370Sigor@sysoev.ru 
1380Sigor@sysoev.ru     if (sent < (nxt_off_t) sb.size) {
1390Sigor@sysoev.ru         c->socket.write_ready = 0;
1400Sigor@sysoev.ru     }
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru     return sent;
1430Sigor@sysoev.ru }
144