Annotation of embedaddon/lighttpd/src/network_freebsd_sendfile.c, revision 1.1.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>