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