Annotation of embedaddon/lighttpd/src/network_freebsd_sendfile.c, revision 1.1.1.2

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: 
1.1.1.2 ! misho     154:                                fd_close_on_exec(c->file.fd);
1.1       misho     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>