Return to network_freebsd_sendfile.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src |
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