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