Added the etc direction to the distribution.
[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
19#include <stdlib.h>
e1cdf02e
FT
20#include <stdio.h>
21#include <fcntl.h>
83723896
FT
22#include <unistd.h>
23#include <string.h>
24#include <time.h>
25#include <errno.h>
26#include <sys/select.h>
e1cdf02e 27#include <sys/socket.h>
83723896
FT
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32#include <log.h>
33#include <utils.h>
34#include <mt.h>
35#include <mtio.h>
36
37struct blocker {
38 struct blocker *n, *p;
39 int fd;
40 int ev;
41 time_t to;
42 struct muth *th;
43};
44
45static struct blocker *blockers;
46
47int block(int fd, int ev, time_t to)
48{
49 struct blocker *bl;
50 int rv;
51
52 omalloc(bl);
53 bl->fd = fd;
54 bl->ev = ev;
55 if(to > 0)
56 bl->to = time(NULL) + to;
57 bl->th = current;
58 bl->n = blockers;
59 if(blockers)
60 blockers->p = bl;
61 blockers = bl;
62 rv = yield();
63 if(bl->n)
64 bl->n->p = bl->p;
65 if(bl->p)
66 bl->p->n = bl->n;
67 if(bl == blockers)
68 blockers = bl->n;
96fc434a 69 free(bl);
83723896
FT
70 return(rv);
71}
72
73void ioloop(void)
74{
75 int ret;
76 fd_set rfds, wfds, efds;
77 struct blocker *bl, *nbl;
78 struct timeval toval;
79 time_t now, timeout;
80 int maxfd;
81 int ev;
82
83 while(blockers != NULL) {
84 FD_ZERO(&rfds);
85 FD_ZERO(&wfds);
86 FD_ZERO(&efds);
87 maxfd = 0;
88 now = time(NULL);
89 timeout = 0;
90 for(bl = blockers; bl; bl = bl->n) {
91 if(bl->ev & EV_READ)
92 FD_SET(bl->fd, &rfds);
93 if(bl->ev & EV_WRITE)
94 FD_SET(bl->fd, &wfds);
95 FD_SET(bl->fd, &efds);
96 if(bl->fd > maxfd)
97 maxfd = bl->fd;
98 if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to)))
99 timeout = bl->to;
100 }
101 toval.tv_sec = timeout - now;
102 toval.tv_usec = 0;
103 ret = select(maxfd + 1, &rfds, &wfds, &efds, timeout?(&toval):NULL);
104 if(ret < 0) {
105 if(errno != EINTR) {
106 flog(LOG_CRIT, "ioloop: select errored out: %s", strerror(errno));
107 /* To avoid CPU hogging in case it's bad, which it
108 * probably is. */
109 sleep(1);
110 }
111 }
112 now = time(NULL);
113 for(bl = blockers; bl; bl = nbl) {
114 nbl = bl->n;
115 ev = 0;
116 if(FD_ISSET(bl->fd, &rfds))
117 ev |= EV_READ;
118 if(FD_ISSET(bl->fd, &wfds))
119 ev |= EV_WRITE;
120 if(FD_ISSET(bl->fd, &efds))
121 ev = -1;
3dc69b45 122 if((ev < 0) || (ev & bl->ev))
83723896
FT
123 resume(bl->th, ev);
124 else if((bl->to != 0) && (bl->to <= now))
125 resume(bl->th, 0);
126 }
127 }
128}
e1cdf02e
FT
129
130struct stdiofd {
131 int fd;
132 int sock;
133 int timeout;
134};
135
136static ssize_t mtread(void *cookie, char *buf, size_t len)
137{
138 struct stdiofd *d = cookie;
139 int ev;
140 ssize_t ret;
141
142 while(1) {
143 ret = read(d->fd, buf, len);
144 if((ret < 0) && (errno == EAGAIN)) {
145 ev = block(d->fd, EV_READ, d->timeout);
146 if(ev < 0) {
147 /* If we just go on, we should get the real error. */
148 continue;
149 } else if(ev == 0) {
150 errno = ETIMEDOUT;
151 return(-1);
152 } else {
153 continue;
154 }
155 } else {
156 return(ret);
157 }
158 }
159}
160
161static ssize_t mtwrite(void *cookie, const char *buf, size_t len)
162{
163 struct stdiofd *d = cookie;
164 int ev;
9dc25d3d 165 size_t off;
e1cdf02e
FT
166 ssize_t ret;
167
9dc25d3d
FT
168 off = 0;
169 while(off < len) {
e1cdf02e 170 if(d->sock)
9dc25d3d 171 ret = send(d->fd, buf + off, len - off, MSG_DONTWAIT | MSG_NOSIGNAL);
e1cdf02e 172 else
9dc25d3d
FT
173 ret = write(d->fd, buf + off, len - off);
174 if(ret < 0) {
175 if(errno == EAGAIN) {
176 ev = block(d->fd, EV_WRITE, d->timeout);
177 if(ev < 0) {
178 /* If we just go on, we should get the real error. */
179 continue;
180 } else if(ev == 0) {
181 errno = ETIMEDOUT;
182 return(off);
183 } else {
184 continue;
185 }
e1cdf02e 186 } else {
9dc25d3d 187 return(off);
e1cdf02e
FT
188 }
189 } else {
9dc25d3d 190 off += ret;
e1cdf02e
FT
191 }
192 }
9dc25d3d 193 return(off);
e1cdf02e
FT
194}
195
196static int mtclose(void *cookie)
197{
198 struct stdiofd *d = cookie;
199
200 close(d->fd);
201 free(d);
202 return(0);
203}
204
205static cookie_io_functions_t iofuns = {
206 .read = mtread,
207 .write = mtwrite,
208 .close = mtclose,
209};
210
211FILE *mtstdopen(int fd, int issock, int timeout, char *mode)
212{
213 struct stdiofd *d;
214 FILE *ret;
215
216 omalloc(d);
217 d->fd = fd;
218 d->sock = issock;
219 d->timeout = timeout;
220 ret = fopencookie(d, mode, iofuns);
221 if(!ret)
222 free(d);
223 else
224 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
225 return(ret);
226}