Annotation of embedaddon/lighttpd/src/network_freebsd_sendfile.c, revision 1.1
1.1 ! misho 1: #include "network_backends.h"
! 2:
! 3: #ifdef USE_FREEBSD_SENDFILE
! 4:
! 5: #include "network.h"
! 6: #include "fdevent.h"
! 7: #include "log.h"
! 8: #include "stat_cache.h"
! 9:
! 10: #include <sys/types.h>
! 11: #include <sys/socket.h>
! 12: #include <sys/stat.h>
! 13: #include <sys/time.h>
! 14: #include <sys/resource.h>
! 15:
! 16: #include <netinet/in.h>
! 17: #include <netinet/tcp.h>
! 18:
! 19: #include <errno.h>
! 20: #include <fcntl.h>
! 21: #include <unistd.h>
! 22: #include <netdb.h>
! 23: #include <string.h>
! 24: #include <stdlib.h>
! 25:
! 26:
! 27: #ifndef UIO_MAXIOV
! 28: # if defined(__FreeBSD__) || defined(__DragonFly__)
! 29: /* FreeBSD 4.7, 4.9 defined it in sys/uio.h only if _KERNEL is specified */
! 30: # define UIO_MAXIOV 1024
! 31: # endif
! 32: #endif
! 33:
! 34: int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
! 35: chunk *c;
! 36:
! 37: for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) {
! 38: int chunk_finished = 0;
! 39:
! 40: switch(c->type) {
! 41: case MEM_CHUNK: {
! 42: char * offset;
! 43: off_t toSend;
! 44: ssize_t r;
! 45:
! 46: size_t num_chunks, i;
! 47: struct iovec chunks[UIO_MAXIOV];
! 48: chunk *tc;
! 49: size_t num_bytes = 0;
! 50:
! 51: /* build writev list
! 52: *
! 53: * 1. limit: num_chunks < UIO_MAXIOV
! 54: * 2. limit: num_bytes < max_bytes
! 55: */
! 56: for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next);
! 57:
! 58: for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) {
! 59: if (tc->mem->used == 0) {
! 60: chunks[i].iov_base = tc->mem->ptr;
! 61: chunks[i].iov_len = 0;
! 62: } else {
! 63: offset = tc->mem->ptr + tc->offset;
! 64: toSend = tc->mem->used - 1 - tc->offset;
! 65:
! 66: chunks[i].iov_base = offset;
! 67:
! 68: /* protect the return value of writev() */
! 69: if (toSend > max_bytes ||
! 70: (off_t) num_bytes + toSend > max_bytes) {
! 71: chunks[i].iov_len = max_bytes - num_bytes;
! 72:
! 73: num_chunks = i + 1;
! 74: break;
! 75: } else {
! 76: chunks[i].iov_len = toSend;
! 77: }
! 78:
! 79: num_bytes += toSend;
! 80: }
! 81: }
! 82:
! 83: if ((r = writev(fd, chunks, num_chunks)) < 0) {
! 84: switch (errno) {
! 85: case EAGAIN:
! 86: case EINTR:
! 87: r = 0;
! 88: break;
! 89: case ENOTCONN:
! 90: case EPIPE:
! 91: case ECONNRESET:
! 92: return -2;
! 93: default:
! 94: log_error_write(srv, __FILE__, __LINE__, "ssd",
! 95: "writev failed:", strerror(errno), fd);
! 96:
! 97: return -1;
! 98: }
! 99:
! 100: r = 0;
! 101: }
! 102:
! 103: /* check which chunks have been written */
! 104: cq->bytes_out += r;
! 105: max_bytes -= r;
! 106:
! 107: for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) {
! 108: if (r >= (ssize_t)chunks[i].iov_len) {
! 109: /* written */
! 110: r -= chunks[i].iov_len;
! 111: tc->offset += chunks[i].iov_len;
! 112:
! 113: if (chunk_finished) {
! 114: /* skip the chunks from further touches */
! 115: c = c->next;
! 116: } else {
! 117: /* chunks_written + c = c->next is done in the for()*/
! 118: chunk_finished = 1;
! 119: }
! 120: } else {
! 121: /* partially written */
! 122:
! 123: tc->offset += r;
! 124: chunk_finished = 0;
! 125:
! 126: break;
! 127: }
! 128: }
! 129:
! 130: break;
! 131: }
! 132: case FILE_CHUNK: {
! 133: off_t offset, r;
! 134: off_t toSend;
! 135: stat_cache_entry *sce = NULL;
! 136:
! 137: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
! 138: log_error_write(srv, __FILE__, __LINE__, "sb",
! 139: strerror(errno), c->file.name);
! 140: return -1;
! 141: }
! 142:
! 143: offset = c->file.start + c->offset;
! 144: toSend = c->file.length - c->offset;
! 145: if (toSend > max_bytes) toSend = max_bytes;
! 146:
! 147: if (-1 == c->file.fd) {
! 148: if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
! 149: log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
! 150:
! 151: return -1;
! 152: }
! 153:
! 154: #ifdef FD_CLOEXEC
! 155: fcntl(c->file.fd, F_SETFD, FD_CLOEXEC);
! 156: #endif
! 157: }
! 158:
! 159: r = 0;
! 160:
! 161: /* FreeBSD sendfile() */
! 162: if (-1 == sendfile(c->file.fd, fd, offset, toSend, NULL, &r, 0)) {
! 163: switch(errno) {
! 164: case EAGAIN:
! 165: case EINTR:
! 166: /* for EAGAIN/EINTR r still contains the sent bytes */
! 167: break; /* try again later */
! 168: case EPIPE:
! 169: case ENOTCONN:
! 170: return -2;
! 171: default:
! 172: log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno);
! 173: return -1;
! 174: }
! 175: } else if (r == 0) {
! 176: /* We got an event to write but we wrote nothing
! 177: *
! 178: * - the file shrinked -> error
! 179: * - the remote side closed inbetween -> remote-close */
! 180:
! 181: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
! 182: /* file is gone ? */
! 183: return -1;
! 184: }
! 185:
! 186: if (offset >= sce->st.st_size) {
! 187: /* file shrinked, close the connection */
! 188: return -1;
! 189: }
! 190:
! 191: return -2;
! 192: }
! 193:
! 194: c->offset += r;
! 195: cq->bytes_out += r;
! 196: max_bytes -= r;
! 197:
! 198: if (c->offset == c->file.length) {
! 199: chunk_finished = 1;
! 200: }
! 201:
! 202: break;
! 203: }
! 204: default:
! 205:
! 206: log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
! 207:
! 208: return -1;
! 209: }
! 210:
! 211: if (!chunk_finished) {
! 212: /* not finished yet */
! 213:
! 214: break;
! 215: }
! 216: }
! 217:
! 218: return 0;
! 219: }
! 220:
! 221: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>