lib: Transfer the responsibility of fopencookie bugs to the generic implementation.
[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>
34
e1cdf02e
FT
35struct stdiofd {
36 int fd;
37 int sock;
38 int timeout;
39};
40
2b8eb6df 41static ssize_t mtread(void *cookie, void *buf, size_t len)
e1cdf02e
FT
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
2b8eb6df 66static ssize_t mtwrite(void *cookie, const void *buf, size_t len)
e1cdf02e
FT
67{
68 struct stdiofd *d = cookie;
69 int ev;
70 ssize_t ret;
71
c3424008 72 while(1) {
e1cdf02e 73 if(d->sock)
c3424008 74 ret = send(d->fd, buf, len, MSG_DONTWAIT | MSG_NOSIGNAL);
e1cdf02e 75 else
c3424008
FT
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);
e1cdf02e
FT
85 }
86 } else {
c3424008 87 return(ret);
e1cdf02e
FT
88 }
89 }
90}
91
92static int mtclose(void *cookie)
93{
94 struct stdiofd *d = cookie;
95
96 close(d->fd);
97 free(d);
98 return(0);
99}
100
2a619a21
FT
101FILE *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;
2b8eb6df 120 ret = funstdio(d, r?mtread:NULL, w?mtwrite:NULL, NULL, mtclose);
2a619a21
FT
121 if(!ret)
122 free(d);
123 else
124 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
125 return(ret);
126}
d8aea4cf
FT
127
128struct pipe {
129 struct charbuf data;
130 size_t bufmax;
131 int closed;
132 struct muth *r, *w;
133};
134
135static void freepipe(struct pipe *p)
136{
137 buffree(p->data);
138 free(p);
139}
140
141static 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
165static 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
179static ssize_t pipewrite(void *pdata, const void *buf, size_t len)
180{
181 struct pipe *p = pdata;
c3424008 182 ssize_t ret;
d8aea4cf
FT
183
184 if(p->closed & 1) {
185 errno = EPIPE;
186 return(-1);
187 }
c3424008
FT
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);
d8aea4cf 199 }
d8aea4cf 200 }
c3424008
FT
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);
d8aea4cf
FT
208}
209
210static 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
224void 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}