1: #include "network_backends.h"
2:
3: #ifdef USE_LINUX_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: #include <fcntl.h>
26:
27: /* on linux 2.4.29 + debian/ubuntu we have crashes if this is enabled */
28: #undef HAVE_POSIX_FADVISE
29:
30: int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
31: chunk *c;
32:
33: for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) {
34: int chunk_finished = 0;
35:
36: switch(c->type) {
37: case MEM_CHUNK: {
38: char * offset;
39: off_t toSend;
40: ssize_t r;
41:
42: size_t num_chunks, i;
43: struct iovec chunks[UIO_MAXIOV];
44: chunk *tc;
45: size_t num_bytes = 0;
46:
47: /* build writev list
48: *
49: * 1. limit: num_chunks < UIO_MAXIOV
50: * 2. limit: num_bytes < max_bytes
51: */
52: for (num_chunks = 0, tc = c;
53: tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV;
54: tc = tc->next, num_chunks++);
55:
56: for (tc = c, i = 0; i < num_chunks; tc = tc->next, i++) {
57: if (tc->mem->used == 0) {
58: chunks[i].iov_base = tc->mem->ptr;
59: chunks[i].iov_len = 0;
60: } else {
61: offset = tc->mem->ptr + tc->offset;
62: toSend = tc->mem->used - 1 - tc->offset;
63:
64: chunks[i].iov_base = offset;
65:
66: /* protect the return value of writev() */
67: if (toSend > max_bytes ||
68: (off_t) num_bytes + toSend > max_bytes) {
69: chunks[i].iov_len = max_bytes - num_bytes;
70:
71: num_chunks = i + 1;
72: break;
73: } else {
74: chunks[i].iov_len = toSend;
75: }
76:
77: num_bytes += toSend;
78: }
79: }
80:
81: if ((r = writev(fd, chunks, num_chunks)) < 0) {
82: switch (errno) {
83: case EAGAIN:
84: case EINTR:
85: r = 0;
86: break;
87: case EPIPE:
88: case ECONNRESET:
89: return -2;
90: default:
91: log_error_write(srv, __FILE__, __LINE__, "ssd",
92: "writev failed:", strerror(errno), fd);
93:
94: return -1;
95: }
96: }
97:
98: /* check which chunks have been written */
99: cq->bytes_out += r;
100: max_bytes -= r;
101:
102: for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) {
103: if (r >= (ssize_t)chunks[i].iov_len) {
104: /* written */
105: r -= chunks[i].iov_len;
106: tc->offset += chunks[i].iov_len;
107:
108: if (chunk_finished) {
109: /* skip the chunks from further touches */
110: c = c->next;
111: } else {
112: /* chunks_written + c = c->next is done in the for()*/
113: chunk_finished = 1;
114: }
115: } else {
116: /* partially written */
117:
118: tc->offset += r;
119: chunk_finished = 0;
120:
121: break;
122: }
123: }
124:
125: break;
126: }
127: case FILE_CHUNK: {
128: ssize_t r;
129: off_t offset;
130: off_t toSend;
131: stat_cache_entry *sce = NULL;
132:
133: offset = c->file.start + c->offset;
134: toSend = c->file.length - c->offset;
135: if (toSend > max_bytes) toSend = max_bytes;
136:
137: /* open file if not already opened */
138: if (-1 == c->file.fd) {
139: if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
140: log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
141:
142: return -1;
143: }
144: fd_close_on_exec(c->file.fd);
145: #ifdef HAVE_POSIX_FADVISE
146: /* tell the kernel that we want to stream the file */
147: if (-1 == posix_fadvise(c->file.fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {
148: if (ENOSYS != errno) {
149: log_error_write(srv, __FILE__, __LINE__, "ssd",
150: "posix_fadvise failed:", strerror(errno), c->file.fd);
151: }
152: }
153: #endif
154: }
155:
156: if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) {
157: switch (errno) {
158: case EAGAIN:
159: case EINTR:
160: /* ok, we can't send more, let's try later again */
161: r = 0;
162: break;
163: case EPIPE:
164: case ECONNRESET:
165: return -2;
166: default:
167: log_error_write(srv, __FILE__, __LINE__, "ssd",
168: "sendfile failed:", strerror(errno), fd);
169: return -1;
170: }
171: } else if (r == 0) {
172: int oerrno = errno;
173: /* We got an event to write but we wrote nothing
174: *
175: * - the file shrinked -> error
176: * - the remote side closed inbetween -> remote-close */
177:
178: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
179: /* file is gone ? */
180: return -1;
181: }
182:
183: if (offset > sce->st.st_size) {
184: /* file shrinked, close the connection */
185: errno = oerrno;
186:
187: return -1;
188: }
189:
190: errno = oerrno;
191: return -2;
192: }
193:
194: #ifdef HAVE_POSIX_FADVISE
195: #if 0
196: #define K * 1024
197: #define M * 1024 K
198: #define READ_AHEAD 4 M
199: /* check if we need a new chunk */
200: if ((c->offset & ~(READ_AHEAD - 1)) != ((c->offset + r) & ~(READ_AHEAD - 1))) {
201: /* tell the kernel that we want to stream the file */
202: if (-1 == posix_fadvise(c->file.fd, (c->offset + r) & ~(READ_AHEAD - 1), READ_AHEAD, POSIX_FADV_NOREUSE)) {
203: log_error_write(srv, __FILE__, __LINE__, "ssd",
204: "posix_fadvise failed:", strerror(errno), c->file.fd);
205: }
206: }
207: #endif
208: #endif
209:
210: c->offset += r;
211: cq->bytes_out += r;
212: max_bytes -= r;
213:
214: if (c->offset == c->file.length) {
215: chunk_finished = 1;
216:
217: /* chunk_free() / chunk_reset() will cleanup for us but it is a ok to be faster :) */
218:
219: if (c->file.fd != -1) {
220: close(c->file.fd);
221: c->file.fd = -1;
222: }
223: }
224:
225: break;
226: }
227: default:
228:
229: log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
230:
231: return -1;
232: }
233:
234: if (!chunk_finished) {
235: /* not finished yet */
236:
237: break;
238: }
239: }
240:
241: return 0;
242: }
243:
244: #endif
245: #if 0
246: network_linuxsendfile_init(void) {
247: p->write = network_linuxsendfile_write_chunkset;
248: }
249: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>