File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / network_freebsd_sendfile.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    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: 				fd_close_on_exec(c->file.fd);
  155: 			}
  156: 
  157: 			r = 0;
  158: 
  159: 			/* FreeBSD sendfile() */
  160: 			if (-1 == sendfile(c->file.fd, fd, offset, toSend, NULL, &r, 0)) {
  161: 				switch(errno) {
  162: 				case EAGAIN:
  163: 				case EINTR:
  164: 					/* for EAGAIN/EINTR r still contains the sent bytes */
  165: 					break; /* try again later */
  166: 				case EPIPE:
  167: 				case ENOTCONN:
  168: 					return -2;
  169: 				default:
  170: 					log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno);
  171: 					return -1;
  172: 				}
  173: 			} else if (r == 0) {
  174: 				/* We got an event to write but we wrote nothing
  175: 				 *
  176: 				 * - the file shrinked -> error
  177: 				 * - the remote side closed inbetween -> remote-close */
  178: 
  179: 				if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
  180: 					/* file is gone ? */
  181: 					return -1;
  182: 				}
  183: 
  184: 				if (offset >= sce->st.st_size) {
  185: 					/* file shrinked, close the connection */
  186: 					return -1;
  187: 				}
  188: 
  189: 				return -2;
  190: 			}
  191: 
  192: 			c->offset += r;
  193: 			cq->bytes_out += r;
  194: 			max_bytes -= r;
  195: 
  196: 			if (c->offset == c->file.length) {
  197: 				chunk_finished = 1;
  198: 			}
  199: 
  200: 			break;
  201: 		}
  202: 		default:
  203: 
  204: 			log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
  205: 
  206: 			return -1;
  207: 		}
  208: 
  209: 		if (!chunk_finished) {
  210: 			/* not finished yet */
  211: 
  212: 			break;
  213: 		}
  214: 	}
  215: 
  216: 	return 0;
  217: }
  218: 
  219: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>