lib: Allow reception of mtstdopen info structure.
[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 pipe {
125     struct charbuf data;
126     size_t bufmax;
127     int closed;
128     struct muth *r, *w;
129 };
130
131 static void freepipe(struct pipe *p)
132 {
133     buffree(p->data);
134     free(p);
135 }
136
137 static ssize_t piperead(void *pdata, void *buf, size_t len)
138 {
139     struct pipe *p = pdata;
140     ssize_t ret;
141     
142     while(p->data.d == 0) {
143         if(p->closed & 2)
144             return(0);
145         if(p->r) {
146             errno = EBUSY;
147             return(-1);
148         }
149         p->r = current;
150         yield();
151         p->r = NULL;
152     }
153     ret = min(len, p->data.d);
154     memcpy(buf, p->data.b, ret);
155     memmove(p->data.b, p->data.b + ret, p->data.d -= ret);
156     if(p->w)
157         resume(p->w, 0);
158     return(ret);
159 }
160
161 static int piperclose(void *pdata)
162 {
163     struct pipe *p = pdata;
164     
165     if(p->closed & 2) {
166         freepipe(p);
167     } else {
168         p->closed |= 1;
169         if(p->w)
170             resume(p->w, 0);
171     }
172     return(0);
173 }
174
175 static ssize_t pipewrite(void *pdata, const void *buf, size_t len)
176 {
177     struct pipe *p = pdata;
178     ssize_t ret;
179     
180     if(p->closed & 1) {
181         errno = EPIPE;
182         return(-1);
183     }
184     while(p->data.d >= p->bufmax) {
185         if(p->w) {
186             errno = EBUSY;
187             return(-1);
188         }
189         p->w = current;
190         yield();
191         p->w = NULL;
192         if(p->closed & 1) {
193             errno = EPIPE;
194             return(-1);
195         }
196     }
197     ret = min(len, p->bufmax - p->data.d);
198     sizebuf(p->data, p->data.d + ret);
199     memcpy(p->data.b + p->data.d, buf, ret);
200     p->data.d += ret;
201     if(p->r)
202         resume(p->r, 0);
203     return(ret);
204 }
205
206 static int pipewclose(void *pdata)
207 {
208     struct pipe *p = pdata;
209     
210     if(p->closed & 1) {
211         freepipe(p);
212     } else {
213         p->closed |= 2;
214         if(p->r)
215             resume(p->r, 0);
216     }
217     return(0);
218 }
219
220 void mtiopipe(FILE **read, FILE **write)
221 {
222     struct pipe *p;
223     
224     omalloc(p);
225     p->bufmax = 4096;
226     *read = funstdio(p, piperead, NULL, NULL, piperclose);
227     *write = funstdio(p, NULL, pipewrite, NULL, pipewclose);
228 }