1 /*-
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1996-1999 by Internet Software Consortium
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /* ev_streams.c - implement asynch stream file IO for the eventlib
21 * vix 04mar96 [initial]
22 */
23
24 #if !defined(LINT) && !defined(CODECENTER)
25 static const char rcsid[] = "$Id: ev_streams.c,v 1.5 2005/04/27 04:56:36 sra Exp $";
26 #endif
27 #include "port_before.h"
28 #ifndef _LIBC
29 #include "fd_setsize.h"
30 #endif
31
32 #include <sys/types.h>
33 #include <sys/uio.h>
34
35 #include <errno.h>
36
37 #include <isc/eventlib.h>
38 #ifndef _LIBC
39 #include <isc/assertions.h>
40 #endif
41 #include "eventlib_p.h"
42
43 #include "port_after.h"
44
45 #ifndef _LIBC
46 static int copyvec(evStream *str, const struct iovec *iov, int iocnt);
47 static void consume(evStream *str, size_t bytes);
48 static void done(evContext opaqueCtx, evStream *str);
49 static void writable(evContext opaqueCtx, void *uap, int fd, int evmask);
50 static void readable(evContext opaqueCtx, void *uap, int fd, int evmask);
51 #endif
52
53 struct iovec
evConsIovec(void * buf,size_t cnt)54 evConsIovec(void *buf, size_t cnt) {
55 struct iovec ret;
56
57 memset(&ret, 0xf5, sizeof ret);
58 ret.iov_base = buf;
59 ret.iov_len = cnt;
60 return (ret);
61 }
62
63 #ifndef _LIBC
64 int
evWrite(evContext opaqueCtx,int fd,const struct iovec * iov,int iocnt,evStreamFunc func,void * uap,evStreamID * id)65 evWrite(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt,
66 evStreamFunc func, void *uap, evStreamID *id)
67 {
68 evContext_p *ctx = opaqueCtx.opaque;
69 evStream *new;
70 int save;
71
72 OKNEW(new);
73 new->func = func;
74 new->uap = uap;
75 new->fd = fd;
76 new->flags = 0;
77 if (evSelectFD(opaqueCtx, fd, EV_WRITE, writable, new, &new->file) < 0)
78 goto free;
79 if (copyvec(new, iov, iocnt) < 0)
80 goto free;
81 new->prevDone = NULL;
82 new->nextDone = NULL;
83 if (ctx->streams != NULL)
84 ctx->streams->prev = new;
85 new->prev = NULL;
86 new->next = ctx->streams;
87 ctx->streams = new;
88 if (id != NULL)
89 id->opaque = new;
90 return (0);
91 free:
92 save = errno;
93 FREE(new);
94 errno = save;
95 return (-1);
96 }
97
98 int
evRead(evContext opaqueCtx,int fd,const struct iovec * iov,int iocnt,evStreamFunc func,void * uap,evStreamID * id)99 evRead(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt,
100 evStreamFunc func, void *uap, evStreamID *id)
101 {
102 evContext_p *ctx = opaqueCtx.opaque;
103 evStream *new;
104 int save;
105
106 OKNEW(new);
107 new->func = func;
108 new->uap = uap;
109 new->fd = fd;
110 new->flags = 0;
111 if (evSelectFD(opaqueCtx, fd, EV_READ, readable, new, &new->file) < 0)
112 goto free;
113 if (copyvec(new, iov, iocnt) < 0)
114 goto free;
115 new->prevDone = NULL;
116 new->nextDone = NULL;
117 if (ctx->streams != NULL)
118 ctx->streams->prev = new;
119 new->prev = NULL;
120 new->next = ctx->streams;
121 ctx->streams = new;
122 if (id)
123 id->opaque = new;
124 return (0);
125 free:
126 save = errno;
127 FREE(new);
128 errno = save;
129 return (-1);
130 }
131
132 int
evTimeRW(evContext opaqueCtx,evStreamID id,evTimerID timer)133 evTimeRW(evContext opaqueCtx, evStreamID id, evTimerID timer) /*ARGSUSED*/ {
134 evStream *str = id.opaque;
135
136 UNUSED(opaqueCtx);
137
138 str->timer = timer;
139 str->flags |= EV_STR_TIMEROK;
140 return (0);
141 }
142
143 int
evUntimeRW(evContext opaqueCtx,evStreamID id)144 evUntimeRW(evContext opaqueCtx, evStreamID id) /*ARGSUSED*/ {
145 evStream *str = id.opaque;
146
147 UNUSED(opaqueCtx);
148
149 str->flags &= ~EV_STR_TIMEROK;
150 return (0);
151 }
152
153 int
evCancelRW(evContext opaqueCtx,evStreamID id)154 evCancelRW(evContext opaqueCtx, evStreamID id) {
155 evContext_p *ctx = opaqueCtx.opaque;
156 evStream *old = id.opaque;
157
158 /*
159 * The streams list is doubly threaded. First, there's ctx->streams
160 * that's used by evDestroy() to find and cancel all streams. Second,
161 * there's ctx->strDone (head) and ctx->strLast (tail) which thread
162 * through the potentially smaller number of "IO completed" streams,
163 * used in evGetNext() to avoid scanning the entire list.
164 */
165
166 /* Unlink from ctx->streams. */
167 if (old->prev != NULL)
168 old->prev->next = old->next;
169 else
170 ctx->streams = old->next;
171 if (old->next != NULL)
172 old->next->prev = old->prev;
173
174 /*
175 * If 'old' is on the ctx->strDone list, remove it. Update
176 * ctx->strLast if necessary.
177 */
178 if (old->prevDone == NULL && old->nextDone == NULL) {
179 /*
180 * Either 'old' is the only item on the done list, or it's
181 * not on the done list. If the former, then we unlink it
182 * from the list. If the latter, we leave the list alone.
183 */
184 if (ctx->strDone == old) {
185 ctx->strDone = NULL;
186 ctx->strLast = NULL;
187 }
188 } else {
189 if (old->prevDone != NULL)
190 old->prevDone->nextDone = old->nextDone;
191 else
192 ctx->strDone = old->nextDone;
193 if (old->nextDone != NULL)
194 old->nextDone->prevDone = old->prevDone;
195 else
196 ctx->strLast = old->prevDone;
197 }
198
199 /* Deallocate the stream. */
200 if (old->file.opaque)
201 evDeselectFD(opaqueCtx, old->file);
202 memput(old->iovOrig, sizeof (struct iovec) * old->iovOrigCount);
203 FREE(old);
204 return (0);
205 }
206
207 /* Copy a scatter/gather vector and initialize a stream handler's IO. */
208 static int
copyvec(evStream * str,const struct iovec * iov,int iocnt)209 copyvec(evStream *str, const struct iovec *iov, int iocnt) {
210 int i;
211
212 str->iovOrig = (struct iovec *)memget(sizeof(struct iovec) * iocnt);
213 if (str->iovOrig == NULL) {
214 errno = ENOMEM;
215 return (-1);
216 }
217 str->ioTotal = 0;
218 for (i = 0; i < iocnt; i++) {
219 str->iovOrig[i] = iov[i];
220 str->ioTotal += iov[i].iov_len;
221 }
222 str->iovOrigCount = iocnt;
223 str->iovCur = str->iovOrig;
224 str->iovCurCount = str->iovOrigCount;
225 str->ioDone = 0;
226 return (0);
227 }
228
229 /* Pull off or truncate lead iovec(s). */
230 static void
consume(evStream * str,size_t bytes)231 consume(evStream *str, size_t bytes) {
232 while (bytes > 0U) {
233 if (bytes < (size_t)str->iovCur->iov_len) {
234 str->iovCur->iov_len -= bytes;
235 str->iovCur->iov_base = (void *)
236 ((u_char *)str->iovCur->iov_base + bytes);
237 str->ioDone += bytes;
238 bytes = 0;
239 } else {
240 bytes -= str->iovCur->iov_len;
241 str->ioDone += str->iovCur->iov_len;
242 str->iovCur++;
243 str->iovCurCount--;
244 }
245 }
246 }
247
248 /* Add a stream to Done list and deselect the FD. */
249 static void
done(evContext opaqueCtx,evStream * str)250 done(evContext opaqueCtx, evStream *str) {
251 evContext_p *ctx = opaqueCtx.opaque;
252
253 if (ctx->strLast != NULL) {
254 str->prevDone = ctx->strLast;
255 ctx->strLast->nextDone = str;
256 ctx->strLast = str;
257 } else {
258 INSIST(ctx->strDone == NULL);
259 ctx->strDone = ctx->strLast = str;
260 }
261 evDeselectFD(opaqueCtx, str->file);
262 str->file.opaque = NULL;
263 /* evDrop() will call evCancelRW() on us. */
264 }
265
266 /* Dribble out some bytes on the stream. (Called by evDispatch().) */
267 static void
writable(evContext opaqueCtx,void * uap,int fd,int evmask)268 writable(evContext opaqueCtx, void *uap, int fd, int evmask) {
269 evStream *str = uap;
270 int bytes;
271
272 UNUSED(evmask);
273
274 bytes = writev(fd, str->iovCur, str->iovCurCount);
275 if (bytes > 0) {
276 if ((str->flags & EV_STR_TIMEROK) != 0)
277 evTouchIdleTimer(opaqueCtx, str->timer);
278 consume(str, bytes);
279 } else {
280 if (bytes < 0 && errno != EINTR) {
281 str->ioDone = -1;
282 str->ioErrno = errno;
283 }
284 }
285 if (str->ioDone == -1 || str->ioDone == str->ioTotal)
286 done(opaqueCtx, str);
287 }
288
289 /* Scoop up some bytes from the stream. (Called by evDispatch().) */
290 static void
readable(evContext opaqueCtx,void * uap,int fd,int evmask)291 readable(evContext opaqueCtx, void *uap, int fd, int evmask) {
292 evStream *str = uap;
293 int bytes;
294
295 UNUSED(evmask);
296
297 bytes = readv(fd, str->iovCur, str->iovCurCount);
298 if (bytes > 0) {
299 if ((str->flags & EV_STR_TIMEROK) != 0)
300 evTouchIdleTimer(opaqueCtx, str->timer);
301 consume(str, bytes);
302 } else {
303 if (bytes == 0)
304 str->ioDone = 0;
305 else {
306 if (errno != EINTR) {
307 str->ioDone = -1;
308 str->ioErrno = errno;
309 }
310 }
311 }
312 if (str->ioDone <= 0 || str->ioDone == str->ioTotal)
313 done(opaqueCtx, str);
314 }
315 #endif
316
317 /*! \file */
318