Annotation of src/usr.bin/aucat/file.c, Revision 1.13
1.13 ! ratchov 1: /* $OpenBSD: file.c,v 1.12 2009/07/25 08:44:27 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: /*
18: * non-blocking file i/o module: each file can be read or written (or
19: * both). To achieve non-blocking io, we simply use the poll() syscall
20: * in an event loop. If a read() or write() syscall return EAGAIN
21: * (operation will block), then the file is marked as "for polling", else
22: * the file is not polled again.
23: *
1.12 ratchov 24: * the module also provides trivial timeout implementation,
25: * derived from:
26: *
27: * anoncvs@moule.caoua.org:/cvs/midish/timo.c rev 1.16
28: *
29: * A timeout is used to schedule the call of a routine (the callback)
30: * there is a global list of timeouts that is processed inside the
31: * event loop. Timeouts work as follows:
32: *
33: * first the timo structure must be initialized with timo_set()
34: *
35: * then the timeout is scheduled (only once) with timo_add()
36: *
37: * if the timeout expires, the call-back is called; then it can
38: * be scheduled again if needed. It's OK to reschedule it again
39: * from the callback
40: *
41: * the timeout can be aborted with timo_del(), it is OK to try to
42: * abort a timout that has expired
43: *
1.1 ratchov 44: */
1.12 ratchov 45:
1.1 ratchov 46: #include <sys/types.h>
47:
48: #include <err.h>
49: #include <errno.h>
50: #include <fcntl.h>
51: #include <poll.h>
52: #include <signal.h>
53: #include <stdio.h>
54: #include <stdlib.h>
55:
1.13 ! ratchov 56: #include "abuf.h"
! 57: #include "aproc.h"
1.1 ratchov 58: #include "conf.h"
59: #include "file.h"
60:
61: #define MAXFDS 100
62:
1.4 ratchov 63: extern struct fileops listen_ops, pipe_ops;
1.12 ratchov 64:
65: struct timeval file_tv;
1.1 ratchov 66: struct filelist file_list;
1.12 ratchov 67: struct timo *timo_queue;
68: unsigned timo_abstime;
69:
70: /*
71: * initialise a timeout structure, arguments are callback and argument
72: * that will be passed to the callback
73: */
74: void
75: timo_set(struct timo *o, void (*cb)(void *), void *arg)
76: {
77: o->cb = cb;
78: o->arg = arg;
79: o->set = 0;
80: }
81:
82: /*
83: * schedule the callback in 'delta' 24-th of microseconds. The timeout
84: * must not be already scheduled
85: */
86: void
87: timo_add(struct timo *o, unsigned delta)
88: {
89: struct timo **i;
90: unsigned val;
91: int diff;
92:
93: #ifdef DEBUG
94: if (o->set) {
95: fprintf(stderr, "timo_set: already set\n");
96: abort();
97: }
98: if (delta == 0) {
99: fprintf(stderr, "timo_set: zero timeout is evil\n");
100: abort();
101: }
102: #endif
103: val = timo_abstime + delta;
104: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
105: diff = (*i)->val - val;
106: if (diff > 0) {
107: break;
108: }
109: }
110: o->set = 1;
111: o->val = val;
112: o->next = *i;
113: *i = o;
114: }
115:
116: /*
117: * abort a scheduled timeout
118: */
119: void
120: timo_del(struct timo *o)
121: {
122: struct timo **i;
123:
124: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
125: if (*i == o) {
126: *i = o->next;
127: o->set = 0;
128: return;
129: }
130: }
131: DPRINTF("timo_del: not found\n");
132: }
133:
134: /*
135: * routine to be called by the timer when 'delta' 24-th of microsecond
136: * elapsed. This routine updates time referece used by timeouts and
137: * calls expired timeouts
138: */
139: void
140: timo_update(unsigned delta)
141: {
142: struct timo *to;
143: int diff;
144:
145: /*
146: * update time reference
147: */
148: timo_abstime += delta;
149:
150: /*
151: * remove from the queue and run expired timeouts
152: */
153: while (timo_queue != NULL) {
154: /*
155: * there is no overflow here because + and - are
156: * modulo 2^32, they are the same for both signed and
157: * unsigned integers
158: */
159: diff = timo_queue->val - timo_abstime;
160: if (diff > 0)
161: break;
162: to = timo_queue;
163: timo_queue = to->next;
164: to->set = 0;
165: to->cb(to->arg);
166: }
167: }
168:
169: /*
170: * initialize timeout queue
171: */
172: void
173: timo_init(void)
174: {
175: timo_queue = NULL;
176: timo_abstime = 0;
177: }
178:
179: /*
180: * destroy timeout queue
181: */
182: void
183: timo_done(void)
184: {
185: if (timo_queue != NULL) {
186: fprintf(stderr, "timo_done: timo_queue not empty!\n");
187: abort();
188: }
189: timo_queue = (struct timo *)0xdeadbeef;
190: }
1.1 ratchov 191:
1.3 ratchov 192: void
193: file_dprint(int n, struct file *f)
194: {
1.6 ratchov 195: #ifdef DEBUG
1.4 ratchov 196: if (debug_level < n)
197: return;
198: fprintf(stderr, "%s:%s <", f->ops->name, f->name);
199: if (f->state & FILE_ROK)
200: fprintf(stderr, "ROK");
201: if (f->state & FILE_WOK)
202: fprintf(stderr, "WOK");
203: if (f->state & FILE_EOF)
204: fprintf(stderr, "EOF");
205: if (f->state & FILE_HUP)
206: fprintf(stderr, "HUP");
207: fprintf(stderr, ">");
1.6 ratchov 208: #endif
1.3 ratchov 209: }
210:
1.1 ratchov 211: struct file *
1.4 ratchov 212: file_new(struct fileops *ops, char *name, unsigned nfds)
1.1 ratchov 213: {
214: struct file *f;
215:
216: LIST_FOREACH(f, &file_list, entry)
1.4 ratchov 217: nfds += f->ops->nfds(f);
1.11 ratchov 218: if (nfds > MAXFDS) {
219: DPRINTF("file_new: %s: too many polled files\n", name);
220: return NULL;
221: }
1.4 ratchov 222: f = malloc(ops->size);
1.1 ratchov 223: if (f == NULL)
1.4 ratchov 224: err(1, "file_new: %s", ops->name);
225: f->ops = ops;
1.1 ratchov 226: f->name = name;
227: f->state = 0;
228: f->rproc = NULL;
229: f->wproc = NULL;
230: LIST_INSERT_HEAD(&file_list, f, entry);
1.4 ratchov 231: DPRINTF("file_new: %s:%s\n", ops->name, f->name);
1.1 ratchov 232: return f;
233: }
234:
235: void
236: file_del(struct file *f)
237: {
1.3 ratchov 238: DPRINTF("file_del: ");
239: file_dprint(1, f);
1.12 ratchov 240: if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
1.4 ratchov 241: DPRINTF(": delayed\n");
242: f->state |= FILE_ZOMB;
243: return;
244: } else {
245: DPRINTF(": immediate\n");
246: LIST_REMOVE(f, entry);
247: f->ops->close(f);
248: free(f);
249: }
1.1 ratchov 250: }
251:
252: int
253: file_poll(void)
254: {
1.4 ratchov 255: nfds_t nfds, n;
256: short events, revents;
1.1 ratchov 257: struct pollfd pfds[MAXFDS];
258: struct file *f, *fnext;
1.2 ratchov 259: struct aproc *p;
1.12 ratchov 260: struct timeval tv;
261: long delta_usec;
262: int timo;
1.1 ratchov 263:
1.4 ratchov 264: /*
1.13 ! ratchov 265: * Fill the pfds[] array with files that are blocked on reading
! 266: * and/or writing, skipping those that are just waiting.
1.4 ratchov 267: */
268: DPRINTFN(4, "file_poll:");
1.1 ratchov 269: nfds = 0;
270: LIST_FOREACH(f, &file_list, entry) {
1.4 ratchov 271: events = 0;
272: if (f->rproc && !(f->state & FILE_ROK))
273: events |= POLLIN;
274: if (f->wproc && !(f->state & FILE_WOK))
275: events |= POLLOUT;
276: DPRINTFN(4, " %s(%x)", f->name, events);
277: n = f->ops->pollfd(f, pfds + nfds, events);
278: if (n == 0) {
1.1 ratchov 279: f->pfd = NULL;
280: continue;
281: }
1.4 ratchov 282: f->pfd = pfds + nfds;
283: nfds += n;
1.1 ratchov 284: }
1.4 ratchov 285: DPRINTFN(4, "\n");
1.12 ratchov 286: if (debug_level >= 4) {
287: DPRINTF("file_poll: pfds[] =");
288: for (n = 0; n < nfds; n++)
289: DPRINTF(" %x", pfds[n].events);
290: DPRINTF("\n");
1.1 ratchov 291: }
292: if (LIST_EMPTY(&file_list)) {
293: DPRINTF("file_poll: nothing to do...\n");
294: return 0;
295: }
1.9 ratchov 296: if (nfds > 0) {
1.12 ratchov 297: if (timo_queue) {
298: timo = (timo_queue->val - timo_abstime) / (2 * 1000);
299: if (timo == 0)
300: timo = 1;
301: } else
302: timo = -1;
303: if (poll(pfds, nfds, timo) < 0) {
1.9 ratchov 304: if (errno == EINTR)
305: return 1;
306: err(1, "file_poll: poll failed");
307: }
1.12 ratchov 308: gettimeofday(&tv, NULL);
309: delta_usec = 1000000L * (tv.tv_sec - file_tv.tv_sec);
310: delta_usec += tv.tv_usec - file_tv.tv_usec;
311: if (delta_usec > 0) {
312: file_tv = tv;
313: timo_update(delta_usec);
314: }
1.1 ratchov 315: }
1.4 ratchov 316: f = LIST_FIRST(&file_list);
317: while (f != LIST_END(&file_list)) {
318: if (f->pfd == NULL) {
319: f = LIST_NEXT(f, entry);
1.1 ratchov 320: continue;
1.4 ratchov 321: }
322: revents = f->ops->revents(f, f->pfd);
323: if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
324: revents &= ~POLLIN;
1.1 ratchov 325: f->state |= FILE_ROK;
326: DPRINTFN(3, "file_poll: %s rok\n", f->name);
1.12 ratchov 327: f->state |= FILE_RINUSE;
1.4 ratchov 328: for (;;) {
1.2 ratchov 329: p = f->rproc;
330: if (!p || !p->ops->in(p, NULL))
1.1 ratchov 331: break;
332: }
1.12 ratchov 333: f->state &= ~FILE_RINUSE;
1.1 ratchov 334: }
1.4 ratchov 335: if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
336: revents &= ~POLLOUT;
1.1 ratchov 337: f->state |= FILE_WOK;
338: DPRINTFN(3, "file_poll: %s wok\n", f->name);
1.12 ratchov 339: f->state |= FILE_WINUSE;
1.4 ratchov 340: for (;;) {
1.2 ratchov 341: p = f->wproc;
342: if (!p || !p->ops->out(p, NULL))
1.1 ratchov 343: break;
344: }
1.12 ratchov 345: f->state &= ~FILE_WINUSE;
1.7 ratchov 346: }
347: if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
348: DPRINTFN(2, "file_poll: %s: disconnected\n", f->name);
349: f->state |= (FILE_EOF | FILE_HUP);
1.1 ratchov 350: }
1.4 ratchov 351: if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
1.1 ratchov 352: DPRINTFN(2, "file_poll: %s: eof\n", f->name);
1.2 ratchov 353: p = f->rproc;
1.12 ratchov 354: if (p) {
355: f->state |= FILE_RINUSE;
1.2 ratchov 356: p->ops->eof(p, NULL);
1.12 ratchov 357: f->state &= ~FILE_RINUSE;
358: }
1.1 ratchov 359: f->state &= ~FILE_EOF;
360: }
1.4 ratchov 361: if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
1.1 ratchov 362: DPRINTFN(2, "file_poll: %s hup\n", f->name);
1.2 ratchov 363: p = f->wproc;
1.12 ratchov 364: if (p) {
365: f->state |= FILE_WINUSE;
1.2 ratchov 366: p->ops->hup(p, NULL);
1.12 ratchov 367: f->state &= ~FILE_WINUSE;
368: }
1.1 ratchov 369: f->state &= ~FILE_HUP;
370: }
371: fnext = LIST_NEXT(f, entry);
1.4 ratchov 372: if (f->state & FILE_ZOMB)
1.3 ratchov 373: file_del(f);
1.4 ratchov 374: f = fnext;
1.1 ratchov 375: }
376: if (LIST_EMPTY(&file_list)) {
377: DPRINTFN(2, "file_poll: terminated\n");
378: return 0;
379: }
380: return 1;
381: }
382:
383: void
1.4 ratchov 384: filelist_init(void)
1.1 ratchov 385: {
386: sigset_t set;
387:
388: sigemptyset(&set);
389: (void)sigaddset(&set, SIGPIPE);
390: if (sigprocmask(SIG_BLOCK, &set, NULL))
391: err(1, "sigprocmask");
392: LIST_INIT(&file_list);
1.12 ratchov 393: timo_init();
394: gettimeofday(&file_tv, NULL);
1.1 ratchov 395: }
396:
397: void
1.4 ratchov 398: filelist_done(void)
1.1 ratchov 399: {
400: struct file *f;
401:
1.12 ratchov 402: timo_done();
1.1 ratchov 403: if (!LIST_EMPTY(&file_list)) {
1.4 ratchov 404: fprintf(stderr, "filelist_done: list not empty:\n");
1.1 ratchov 405: LIST_FOREACH(f, &file_list, entry) {
1.4 ratchov 406: fprintf(stderr, "\t");
407: file_dprint(0, f);
408: fprintf(stderr, "\n");
1.1 ratchov 409: }
1.4 ratchov 410: abort();
1.1 ratchov 411: }
412: }
413:
1.4 ratchov 414: /*
1.13 ! ratchov 415: * Close all listening sockets.
1.4 ratchov 416: *
417: * XXX: remove this
418: */
419: void
420: filelist_unlisten(void)
1.1 ratchov 421: {
1.4 ratchov 422: struct file *f, *fnext;
1.10 ratchov 423:
1.4 ratchov 424: for (f = LIST_FIRST(&file_list); f != NULL; f = fnext) {
425: fnext = LIST_NEXT(f, entry);
426: if (f->ops == &listen_ops)
427: file_del(f);
1.1 ratchov 428: }
429: }
430:
1.4 ratchov 431: unsigned
432: file_read(struct file *file, unsigned char *data, unsigned count)
433: {
434: return file->ops->read(file, data, count);
435: }
1.1 ratchov 436:
437: unsigned
438: file_write(struct file *file, unsigned char *data, unsigned count)
439: {
1.4 ratchov 440: return file->ops->write(file, data, count);
1.1 ratchov 441: }
442:
443: void
444: file_eof(struct file *f)
445: {
1.4 ratchov 446: struct aproc *p;
447:
1.12 ratchov 448: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 449: DPRINTFN(2, "file_eof: %s: immediate\n", f->name);
450: p = f->rproc;
1.12 ratchov 451: if (p) {
452: f->state |= FILE_RINUSE;
1.4 ratchov 453: p->ops->eof(p, NULL);
1.12 ratchov 454: f->state &= ~FILE_RINUSE;
455: }
1.4 ratchov 456: if (f->state & FILE_ZOMB)
457: file_del(f);
458: } else {
459: DPRINTFN(2, "file_eof: %s: delayed\n", f->name);
460: f->state &= ~FILE_ROK;
461: f->state |= FILE_EOF;
462: }
1.1 ratchov 463: }
464:
465: void
466: file_hup(struct file *f)
467: {
1.4 ratchov 468: struct aproc *p;
469:
1.12 ratchov 470: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 471: DPRINTFN(2, "file_hup: %s immediate\n", f->name);
472: p = f->wproc;
1.12 ratchov 473: if (p) {
474: f->state |= FILE_WINUSE;
1.4 ratchov 475: p->ops->hup(p, NULL);
1.12 ratchov 476: f->state &= ~FILE_WINUSE;
477: }
1.4 ratchov 478: if (f->state & FILE_ZOMB)
479: file_del(f);
480: } else {
481: DPRINTFN(2, "file_hup: %s: delayed\n", f->name);
482: f->state &= ~FILE_WOK;
483: f->state |= FILE_HUP;
1.9 ratchov 484: }
485: }
486:
487: void
488: file_close(struct file *f)
489: {
490: struct aproc *p;
491:
1.12 ratchov 492: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.9 ratchov 493: DPRINTFN(2, "file_close: %s: immediate\n", f->name);
494: p = f->rproc;
1.12 ratchov 495: if (p) {
496: f->state |= FILE_RINUSE;
1.9 ratchov 497: p->ops->eof(p, NULL);
1.12 ratchov 498: f->state &= ~FILE_RINUSE;
499: }
1.9 ratchov 500: p = f->wproc;
1.12 ratchov 501: if (p) {
502: f->state |= FILE_WINUSE;
1.9 ratchov 503: p->ops->hup(p, NULL);
1.12 ratchov 504: f->state &= ~FILE_WINUSE;
505: }
1.9 ratchov 506: if (f->state & FILE_ZOMB)
507: file_del(f);
508: } else {
509: DPRINTFN(2, "file_close: %s: delayed\n", f->name);
510: f->state &= ~(FILE_ROK | FILE_WOK);
511: f->state |= (FILE_EOF | FILE_HUP);
1.4 ratchov 512: }
1.1 ratchov 513: }