183f9f279532c54bffba137eb34a4cb70eb8cc81
[ashd.git] / lib / mtio.c
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
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <sys/socket.h>
29
30 #include <log.h>
31 #include <utils.h>
32 #include <mt.h>
33 #include <mtio.h>
34
35 static ssize_t mtread(void *cookie, void *buf, size_t len)
36 {
37     struct stdiofd *d = cookie;
38     int ev;
39     ssize_t ret;
40     
41     while(1) {
42         ret = read(d->fd, buf, len);
43         if((ret < 0) && (errno == EAGAIN)) {
44             ev = block(d->fd, EV_READ, d->timeout);
45             if(ev < 0) {
46                 /* If we just go on, we should get the real error. */
47                 continue;
48             } else if(ev == 0) {
49                 errno = ETIMEDOUT;
50                 return(-1);
51             } else {
52                 continue;
53             }
54         } else {
55             return(ret);
56         }
57     }
58 }
59
60 static ssize_t mtwrite(void *cookie, const void *buf, size_t len)
61 {
62     struct stdiofd *d = cookie;
63     int ev;
64     ssize_t ret;
65     
66     while(1) {
67         if(d->sock)
68             ret = send(d->fd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL);
69         else
70             ret = write(d->fd, buf, len);
71         if((ret < 0) && (errno == EAGAIN)) {
72             ev = block(d->fd, EV_WRITE, d->timeout);
73             if(ev < 0) {
74                 /* If we just go on, we should get the real error. */
75                 continue;
76             } else if(ev == 0) {
77                 errno = ETIMEDOUT;
78                 return(-1);
79             }
80         } else {
81             return(ret);
82         }
83     }
84 }
85
86 static int mtclose(void *cookie)
87 {
88     struct stdiofd *d = cookie;
89     
90     close(d->fd);
91     free(d);
92     return(0);
93 }
94
95 FILE *mtstdopen(int fd, int issock, int timeout, char *mode, struct stdiofd **infop)
96 {
97     struct stdiofd *d;
98     FILE *ret;
99     int r, w;
100     
101     if(!strcmp(mode, "r")) {
102         r = 1; w = 0;
103     } else if(!strcmp(mode, "w")) {
104         r = 0; w = 1;
105     } else if(!strcmp(mode, "r+")) {
106         r = w = 1;
107     } else {
108         return(NULL);
109     }
110     omalloc(d);
111     d->fd = fd;
112     d->sock = issock;
113     d->timeout = timeout;
114     if(!(ret = funstdio(d, r?mtread:NULL, w?mtwrite:NULL, NULL, mtclose))) {
115         free(d);
116         return(NULL);
117     }
118     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
119     if(infop)
120         *infop = d;
121     return(ret);
122 }
123
124 struct bufio *mtbioopen(int fd, int issock, int timeout, char *mode, struct stdiofd **infop)
125 {
126     static struct bufioops ops = {
127         .read = mtread, .write = mtwrite, .close = mtclose,
128     };
129     struct stdiofd *d;
130     struct bufio *ret;
131     
132     if(!strcmp(mode, "r")) {
133     } else if(!strcmp(mode, "w")) {
134     } else if(!strcmp(mode, "r+")) {
135     } else {
136         return(NULL);
137     }
138     omalloc(d);
139     d->fd = fd;
140     d->sock = issock;
141     d->timeout = timeout;
142     if(!(ret = bioopen(d, &ops))) {
143         free(d);
144         return(NULL);
145     }
146     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
147     if(infop)
148         *infop = d;
149     return(ret);
150 }
151
152 struct pipe {
153     struct charbuf data;
154     size_t bufmax;
155     int closed;
156     struct muth *r, *w;
157 };
158
159 static void freepipe(struct pipe *p)
160 {
161     buffree(p->data);
162     free(p);
163 }
164
165 static ssize_t piperead(void *pdata, void *buf, size_t len)
166 {
167     struct pipe *p = pdata;
168     ssize_t ret;
169     
170     while(p->data.d == 0) {
171         if(p->closed & 2)
172             return(0);
173         if(p->r) {
174             errno = EBUSY;
175             return(-1);
176         }
177         p->r = current;
178         yield();
179         p->r = NULL;
180     }
181     ret = min(len, p->data.d);
182     memcpy(buf, p->data.b, ret);
183     memmove(p->data.b, p->data.b + ret, p->data.d -= ret);
184     if(p->w)
185         resume(p->w, 0);
186     return(ret);
187 }
188
189 static int piperclose(void *pdata)
190 {
191     struct pipe *p = pdata;
192     
193     if(p->closed & 2) {
194         freepipe(p);
195     } else {
196         p->closed |= 1;
197         if(p->w)
198             resume(p->w, 0);
199     }
200     return(0);
201 }
202
203 static ssize_t pipewrite(void *pdata, const void *buf, size_t len)
204 {
205     struct pipe *p = pdata;
206     ssize_t ret;
207     
208     if(p->closed & 1) {
209         errno = EPIPE;
210         return(-1);
211     }
212     while(p->data.d >= p->bufmax) {
213         if(p->w) {
214             errno = EBUSY;
215             return(-1);
216         }
217         p->w = current;
218         yield();
219         p->w = NULL;
220         if(p->closed & 1) {
221             errno = EPIPE;
222             return(-1);
223         }
224     }
225     ret = min(len, p->bufmax - p->data.d);
226     sizebuf(p->data, p->data.d + ret);
227     memcpy(p->data.b + p->data.d, buf, ret);
228     p->data.d += ret;
229     if(p->r)
230         resume(p->r, 0);
231     return(ret);
232 }
233
234 static int pipewclose(void *pdata)
235 {
236     struct pipe *p = pdata;
237     
238     if(p->closed & 1) {
239         freepipe(p);
240     } else {
241         p->closed |= 2;
242         if(p->r)
243             resume(p->r, 0);
244     }
245     return(0);
246 }
247
248 void mtiopipe(FILE **read, FILE **write)
249 {
250     struct pipe *p;
251     
252     omalloc(p);
253     p->bufmax = 4096;
254     *read = funstdio(p, piperead, NULL, NULL, piperclose);
255     *write = funstdio(p, NULL, pipewrite, NULL, pipewclose);
256 }