1 #include "network_backends.h"
2 
3 #ifdef USE_SOLARIS_SENDFILEV
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 <limits.h>
26 
27 #ifndef UIO_MAXIOV
28 # define UIO_MAXIOV IOV_MAX
29 #endif
30 
31 /**
32  * a very simple sendfilev() interface for solaris which can be optimised a lot more
33  * as solaris sendfilev() supports 'sending everythin in one syscall()'
34  *
35  * If you want such an interface and need the performance, just give me an account on
36  * a solaris box.
37  *   - [email protected]
38  */
39 
40 
network_write_chunkqueue_solarissendfilev(server * srv,connection * con,int fd,chunkqueue * cq,off_t max_bytes)41 int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {
42 	chunk *c;
43 
44 	for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) {
45 		int chunk_finished = 0;
46 
47 		switch(c->type) {
48 		case MEM_CHUNK: {
49 			char * offset;
50 			off_t toSend;
51 			ssize_t r;
52 
53 			size_t num_chunks, i;
54 			struct iovec chunks[UIO_MAXIOV];
55 			chunk *tc;
56 
57 			size_t num_bytes = 0;
58 
59 			/* we can't send more then SSIZE_MAX bytes in one chunk */
60 
61 			/* build writev list
62 			 *
63 			 * 1. limit: num_chunks < UIO_MAXIOV
64 			 * 2. limit: num_bytes < SSIZE_MAX
65 			 */
66 			for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next);
67 
68 			for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) {
69 				if (tc->mem->used == 0) {
70 					chunks[i].iov_base = tc->mem->ptr;
71 					chunks[i].iov_len  = 0;
72 				} else {
73 					offset = tc->mem->ptr + tc->offset;
74 					toSend = tc->mem->used - 1 - tc->offset;
75 
76 					chunks[i].iov_base = offset;
77 
78 					/* protect the return value of writev() */
79 					if (toSend > max_bytes ||
80 					    (off_t) num_bytes + toSend > max_bytes) {
81 						chunks[i].iov_len = max_bytes - num_bytes;
82 
83 						num_chunks = i + 1;
84 						break;
85 					} else {
86 						chunks[i].iov_len = toSend;
87 					}
88 
89 					num_bytes += toSend;
90 				}
91 			}
92 
93 			if ((r = writev(fd, chunks, num_chunks)) < 0) {
94 				switch (errno) {
95 				case EAGAIN:
96 				case EINTR:
97 					r = 0;
98 					break;
99 				case EPIPE:
100 				case ECONNRESET:
101 					return -2;
102 				default:
103 					log_error_write(srv, __FILE__, __LINE__, "ssd",
104 							"writev failed:", strerror(errno), fd);
105 
106 					return -1;
107 				}
108 			}
109 
110 			/* check which chunks have been written */
111 			cq->bytes_out += r;
112 
113 			for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) {
114 				if (r >= (ssize_t)chunks[i].iov_len) {
115 					/* written */
116 					r -= chunks[i].iov_len;
117 					tc->offset += chunks[i].iov_len;
118 
119 					if (chunk_finished) {
120 						/* skip the chunks from further touches */
121 						c = c->next;
122 					} else {
123 						/* chunks_written + c = c->next is done in the for()*/
124 						chunk_finished = 1;
125 					}
126 				} else {
127 					/* partially written */
128 
129 					tc->offset += r;
130 					chunk_finished = 0;
131 
132 					break;
133 				}
134 			}
135 
136 			break;
137 		}
138 		case FILE_CHUNK: {
139 			ssize_t r;
140 			off_t offset, toSend;
141 			size_t written;
142 			sendfilevec_t fvec;
143 			stat_cache_entry *sce = NULL;
144 			int ifd;
145 
146 			if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
147 				log_error_write(srv, __FILE__, __LINE__, "sb",
148 						strerror(errno), c->file.name);
149 				return -1;
150 			}
151 
152 			offset = c->file.start + c->offset;
153 			toSend = c->file.length - c->offset;
154 			if (toSend > max_bytes) toSend = max_bytes;
155 
156 			if (offset > sce->st.st_size) {
157 				log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name);
158 
159 				return -1;
160 			}
161 
162 			if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) {
163 				log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
164 
165 				return -1;
166 			}
167 
168 			fvec.sfv_fd = ifd;
169 			fvec.sfv_flag = 0;
170 			fvec.sfv_off = offset;
171 			fvec.sfv_len = toSend;
172 
173 			/* Solaris sendfilev() */
174 			if (-1 == (r = sendfilev(fd, &fvec, 1, &written))) {
175 				if (errno != EAGAIN) {
176 					log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno);
177 
178 					close(ifd);
179 					return -1;
180 				}
181 
182 				r = 0;
183 			}
184 
185 			close(ifd);
186 			c->offset += written;
187 			cq->bytes_out += written;
188 			max_bytes -= written;
189 
190 			if (c->offset == c->file.length) {
191 				chunk_finished = 1;
192 			}
193 
194 			break;
195 		}
196 		default:
197 
198 			log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");
199 
200 			return -1;
201 		}
202 
203 		if (!chunk_finished) {
204 			/* not finished yet */
205 
206 			break;
207 		}
208 	}
209 
210 	return 0;
211 }
212 
213 #endif
214