Merge branch 'master' into timeheap
[ashd.git] / lib / mtio.c
CommitLineData
83723896
FT
1/*
2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
945d02f5
FT
19#ifdef HAVE_CONFIG_H
20#include <config.h>
21#endif
83723896 22#include <stdlib.h>
e1cdf02e 23#include <stdio.h>
0b7964e9 24#include <unistd.h>
e1cdf02e 25#include <fcntl.h>
83723896 26#include <string.h>
83723896 27#include <errno.h>
e1cdf02e 28#include <sys/socket.h>
83723896 29
83723896
FT
30#include <log.h>
31#include <utils.h>
32#include <mt.h>
33#include <mtio.h>
2931529e 34#include <bufio.h>
83723896 35
927cffe0
FT
36static ssize_t mtrecv(struct stdiofd *d, void *buf, size_t len)
37{
38 struct msghdr msg;
39 char cbuf[512];
40 struct cmsghdr *cmsg;
41 struct iovec bufvec;
42 socklen_t clen;
43 ssize_t ret;
44 int i, *fds;
45
46 msg = (struct msghdr){};
47 msg.msg_iov = &bufvec;
48 msg.msg_iovlen = 1;
49 bufvec.iov_base = buf;
50 bufvec.iov_len = len;
51 msg.msg_control = cbuf;
52 msg.msg_controllen = sizeof(cbuf);
53 if((ret = recvmsg(d->fd, &msg, MSG_DONTWAIT)) < 0)
54 return(ret);
55 for(cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
56 if((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) {
57 fds = (int *)CMSG_DATA(cmsg);
58 clen = (cmsg->cmsg_len - ((char *)fds - (char *)cmsg)) / sizeof(*fds);
59 for(i = 0; i < clen; i++) {
60 if(d->rights < 0)
61 d->rights = fds[i];
62 else
63 close(fds[i]);
64 }
65 }
66 }
67 return(ret);
68}
69
2b8eb6df 70static ssize_t mtread(void *cookie, void *buf, size_t len)
e1cdf02e
FT
71{
72 struct stdiofd *d = cookie;
73 int ev;
74 ssize_t ret;
75
76 while(1) {
927cffe0
FT
77 if(d->sock)
78 ret = mtrecv(d, buf, len);
79 else
80 ret = read(d->fd, buf, len);
e1cdf02e
FT
81 if((ret < 0) && (errno == EAGAIN)) {
82 ev = block(d->fd, EV_READ, d->timeout);
83 if(ev < 0) {
84 /* If we just go on, we should get the real error. */
85 continue;
86 } else if(ev == 0) {
87 errno = ETIMEDOUT;
88 return(-1);
89 } else {
90 continue;
91 }
92 } else {
93 return(ret);
94 }
95 }
96}
97
927cffe0
FT
98static ssize_t mtsend(struct stdiofd *d, const void *buf, size_t len)
99{
100 struct msghdr msg;
101 struct cmsghdr *cmsg;
102 char cbuf[CMSG_SPACE(sizeof(int))];
103 struct iovec bufvec;
104 ssize_t ret;
105 int cr;
106
107 msg = (struct msghdr){};
108 msg.msg_iov = &bufvec;
109 msg.msg_iovlen = 1;
110 bufvec.iov_base = (void *)buf;
111 bufvec.iov_len = len;
112 cr = -1;
113 if(d->sendrights >= 0) {
114 msg.msg_control = cbuf;
115 msg.msg_controllen = sizeof(cbuf);
116 cmsg = CMSG_FIRSTHDR(&msg);
117 cmsg->cmsg_level = SOL_SOCKET;
118 cmsg->cmsg_type = SCM_RIGHTS;
119 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
120 *((int *)CMSG_DATA(cmsg)) = d->sendrights;
121 cr = d->sendrights;
122 d->sendrights = -1;
123 msg.msg_controllen = cmsg->cmsg_len;
124 }
125 ret = sendmsg(d->fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL);
126 if(cr >= 0)
127 close(cr);
128 return(ret);
129}
130
2b8eb6df 131static ssize_t mtwrite(void *cookie, const void *buf, size_t len)
e1cdf02e
FT
132{
133 struct stdiofd *d = cookie;
134 int ev;
135 ssize_t ret;
136
c3424008 137 while(1) {
e1cdf02e 138 if(d->sock)
927cffe0 139 ret = mtsend(d, buf, len);
e1cdf02e 140 else
c3424008
FT
141 ret = write(d->fd, buf, len);
142 if((ret < 0) && (errno == EAGAIN)) {
143 ev = block(d->fd, EV_WRITE, d->timeout);
144 if(ev < 0) {
145 /* If we just go on, we should get the real error. */
146 continue;
147 } else if(ev == 0) {
148 errno = ETIMEDOUT;
149 return(-1);
e1cdf02e
FT
150 }
151 } else {
c3424008 152 return(ret);
e1cdf02e
FT
153 }
154 }
155}
156
157static int mtclose(void *cookie)
158{
159 struct stdiofd *d = cookie;
160
161 close(d->fd);
927cffe0
FT
162 if(d->rights >= 0)
163 close(d->rights);
164 if(d->sendrights >= 0)
165 close(d->sendrights);
e1cdf02e
FT
166 free(d);
167 return(0);
168}
169
b71ad67f 170FILE *mtstdopen(int fd, int issock, int timeout, char *mode, struct stdiofd **infop)
2a619a21
FT
171{
172 struct stdiofd *d;
173 FILE *ret;
174 int r, w;
175
176 if(!strcmp(mode, "r")) {
177 r = 1; w = 0;
178 } else if(!strcmp(mode, "w")) {
179 r = 0; w = 1;
180 } else if(!strcmp(mode, "r+")) {
181 r = w = 1;
182 } else {
183 return(NULL);
184 }
185 omalloc(d);
186 d->fd = fd;
187 d->sock = issock;
188 d->timeout = timeout;
927cffe0 189 d->rights = d->sendrights = -1;
b71ad67f 190 if(!(ret = funstdio(d, r?mtread:NULL, w?mtwrite:NULL, NULL, mtclose))) {
2a619a21 191 free(d);
b71ad67f
FT
192 return(NULL);
193 }
194 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
195 if(infop)
196 *infop = d;
2a619a21
FT
197 return(ret);
198}
d8aea4cf 199
122c2462
FT
200struct bufio *mtbioopen(int fd, int issock, int timeout, char *mode, struct stdiofd **infop)
201{
202 static struct bufioops ops = {
203 .read = mtread, .write = mtwrite, .close = mtclose,
204 };
205 struct stdiofd *d;
206 struct bufio *ret;
207
208 if(!strcmp(mode, "r")) {
209 } else if(!strcmp(mode, "w")) {
210 } else if(!strcmp(mode, "r+")) {
211 } else {
212 return(NULL);
213 }
214 omalloc(d);
215 d->fd = fd;
216 d->sock = issock;
217 d->timeout = timeout;
927cffe0 218 d->rights = d->sendrights = -1;
122c2462
FT
219 if(!(ret = bioopen(d, &ops))) {
220 free(d);
221 return(NULL);
222 }
223 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
224 if(infop)
225 *infop = d;
226 return(ret);
227}
228
d8aea4cf
FT
229struct pipe {
230 struct charbuf data;
231 size_t bufmax;
232 int closed;
233 struct muth *r, *w;
234};
235
236static void freepipe(struct pipe *p)
237{
238 buffree(p->data);
239 free(p);
240}
241
242static ssize_t piperead(void *pdata, void *buf, size_t len)
243{
244 struct pipe *p = pdata;
245 ssize_t ret;
246
247 while(p->data.d == 0) {
248 if(p->closed & 2)
249 return(0);
250 if(p->r) {
251 errno = EBUSY;
252 return(-1);
253 }
254 p->r = current;
255 yield();
256 p->r = NULL;
257 }
258 ret = min(len, p->data.d);
259 memcpy(buf, p->data.b, ret);
260 memmove(p->data.b, p->data.b + ret, p->data.d -= ret);
261 if(p->w)
262 resume(p->w, 0);
263 return(ret);
264}
265
266static int piperclose(void *pdata)
267{
268 struct pipe *p = pdata;
269
270 if(p->closed & 2) {
271 freepipe(p);
272 } else {
273 p->closed |= 1;
274 if(p->w)
275 resume(p->w, 0);
276 }
277 return(0);
278}
279
280static ssize_t pipewrite(void *pdata, const void *buf, size_t len)
281{
282 struct pipe *p = pdata;
c3424008 283 ssize_t ret;
d8aea4cf
FT
284
285 if(p->closed & 1) {
286 errno = EPIPE;
287 return(-1);
288 }
c3424008
FT
289 while(p->data.d >= p->bufmax) {
290 if(p->w) {
291 errno = EBUSY;
292 return(-1);
293 }
294 p->w = current;
295 yield();
296 p->w = NULL;
297 if(p->closed & 1) {
298 errno = EPIPE;
299 return(-1);
d8aea4cf 300 }
d8aea4cf 301 }
c3424008
FT
302 ret = min(len, p->bufmax - p->data.d);
303 sizebuf(p->data, p->data.d + ret);
304 memcpy(p->data.b + p->data.d, buf, ret);
305 p->data.d += ret;
306 if(p->r)
307 resume(p->r, 0);
308 return(ret);
d8aea4cf
FT
309}
310
311static int pipewclose(void *pdata)
312{
313 struct pipe *p = pdata;
314
315 if(p->closed & 1) {
316 freepipe(p);
317 } else {
318 p->closed |= 2;
319 if(p->r)
320 resume(p->r, 0);
321 }
322 return(0);
323}
324
325void mtiopipe(FILE **read, FILE **write)
326{
327 struct pipe *p;
328
329 omalloc(p);
330 p->bufmax = 4096;
331 *read = funstdio(p, piperead, NULL, NULL, piperclose);
332 *write = funstdio(p, NULL, pipewrite, NULL, pipewclose);
333}