Annotation of src/usr.bin/aucat/file.c, Revision 1.18
1.18 ! ratchov 1: /* $OpenBSD: file.c,v 1.17 2010/04/03 17:59:17 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"
1.15 ratchov 60: #ifdef DEBUG
61: #include "dbg.h"
62: #endif
1.1 ratchov 63:
64: #define MAXFDS 100
65:
1.12 ratchov 66: struct timeval file_tv;
1.1 ratchov 67: struct filelist file_list;
1.12 ratchov 68: struct timo *timo_queue;
69: unsigned timo_abstime;
70:
71: /*
72: * initialise a timeout structure, arguments are callback and argument
73: * that will be passed to the callback
74: */
75: void
76: timo_set(struct timo *o, void (*cb)(void *), void *arg)
77: {
78: o->cb = cb;
79: o->arg = arg;
80: o->set = 0;
81: }
82:
83: /*
84: * schedule the callback in 'delta' 24-th of microseconds. The timeout
85: * must not be already scheduled
86: */
87: void
88: timo_add(struct timo *o, unsigned delta)
89: {
90: struct timo **i;
91: unsigned val;
92: int diff;
93:
1.15 ratchov 94: #ifdef DEBUG
95: if (o->set) {
96: dbg_puts("timo_set: already set\n");
97: dbg_panic();
98: }
99: if (delta == 0) {
100: dbg_puts("timo_set: zero timeout is evil\n");
101: dbg_panic();
102: }
103: #endif
1.12 ratchov 104: val = timo_abstime + delta;
105: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
106: diff = (*i)->val - val;
107: if (diff > 0) {
108: break;
109: }
110: }
111: o->set = 1;
112: o->val = val;
113: o->next = *i;
114: *i = o;
115: }
116:
117: /*
118: * abort a scheduled timeout
119: */
120: void
121: timo_del(struct timo *o)
122: {
123: struct timo **i;
124:
125: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
126: if (*i == o) {
127: *i = o->next;
128: o->set = 0;
129: return;
130: }
131: }
1.15 ratchov 132: #ifdef DEBUG
133: if (debug_level >= 4)
134: dbg_puts("timo_del: not found\n");
135: #endif
1.12 ratchov 136: }
137:
138: /*
139: * routine to be called by the timer when 'delta' 24-th of microsecond
140: * elapsed. This routine updates time referece used by timeouts and
141: * calls expired timeouts
142: */
143: void
144: timo_update(unsigned delta)
145: {
146: struct timo *to;
147: int diff;
148:
149: /*
150: * update time reference
151: */
152: timo_abstime += delta;
153:
154: /*
155: * remove from the queue and run expired timeouts
156: */
157: while (timo_queue != NULL) {
158: /*
159: * there is no overflow here because + and - are
160: * modulo 2^32, they are the same for both signed and
161: * unsigned integers
162: */
163: diff = timo_queue->val - timo_abstime;
164: if (diff > 0)
165: break;
166: to = timo_queue;
167: timo_queue = to->next;
168: to->set = 0;
169: to->cb(to->arg);
170: }
171: }
172:
173: /*
174: * initialize timeout queue
175: */
176: void
177: timo_init(void)
178: {
179: timo_queue = NULL;
180: timo_abstime = 0;
181: }
182:
183: /*
184: * destroy timeout queue
185: */
186: void
187: timo_done(void)
188: {
1.15 ratchov 189: #ifdef DEBUG
190: if (timo_queue != NULL) {
191: dbg_puts("timo_done: timo_queue not empty!\n");
192: dbg_panic();
193: }
194: #endif
1.12 ratchov 195: timo_queue = (struct timo *)0xdeadbeef;
196: }
1.1 ratchov 197:
1.15 ratchov 198: #ifdef DEBUG
199: void
200: file_dbg(struct file *f)
201: {
202: dbg_puts(f->ops->name);
203: dbg_puts("(");
204: dbg_puts(f->name);
205: dbg_puts("|");
206: if (f->state & FILE_ROK)
207: dbg_puts("r");
208: if (f->state & FILE_RINUSE)
209: dbg_puts("R");
210: if (f->state & FILE_WOK)
211: dbg_puts("w");
212: if (f->state & FILE_WINUSE)
213: dbg_puts("W");
214: if (f->state & FILE_EOF)
215: dbg_puts("e");
216: if (f->state & FILE_HUP)
217: dbg_puts("h");
218: if (f->state & FILE_ZOMB)
219: dbg_puts("Z");
220: dbg_puts(")");
221: }
222: #endif
1.3 ratchov 223:
1.1 ratchov 224: struct file *
1.4 ratchov 225: file_new(struct fileops *ops, char *name, unsigned nfds)
1.1 ratchov 226: {
227: struct file *f;
228:
229: LIST_FOREACH(f, &file_list, entry)
1.4 ratchov 230: nfds += f->ops->nfds(f);
1.11 ratchov 231: if (nfds > MAXFDS) {
1.15 ratchov 232: #ifdef DEBUG
233: if (debug_level >= 1) {
234: dbg_puts(name);
235: dbg_puts(": too many polled files\n");
236: }
237: #endif
1.11 ratchov 238: return NULL;
239: }
1.4 ratchov 240: f = malloc(ops->size);
1.1 ratchov 241: if (f == NULL)
1.4 ratchov 242: err(1, "file_new: %s", ops->name);
243: f->ops = ops;
1.1 ratchov 244: f->name = name;
245: f->state = 0;
246: f->rproc = NULL;
247: f->wproc = NULL;
248: LIST_INSERT_HEAD(&file_list, f, entry);
1.15 ratchov 249: #ifdef DEBUG
250: if (debug_level >= 3) {
251: file_dbg(f);
252: dbg_puts(": created\n");
253: }
254: #endif
1.1 ratchov 255: return f;
256: }
257:
258: void
259: file_del(struct file *f)
260: {
1.15 ratchov 261: #ifdef DEBUG
262: if (debug_level >= 3) {
263: file_dbg(f);
264: dbg_puts(": terminating...\n");
265: }
266: #endif
1.12 ratchov 267: if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
1.4 ratchov 268: f->state |= FILE_ZOMB;
269: } else {
270: LIST_REMOVE(f, entry);
1.15 ratchov 271: #ifdef DEBUG
272: if (debug_level >= 3) {
273: file_dbg(f);
274: dbg_puts(": destroyed\n");
275: }
276: #endif
1.4 ratchov 277: f->ops->close(f);
278: free(f);
279: }
1.1 ratchov 280: }
281:
282: int
283: file_poll(void)
284: {
1.4 ratchov 285: nfds_t nfds, n;
286: short events, revents;
1.1 ratchov 287: struct pollfd pfds[MAXFDS];
288: struct file *f, *fnext;
1.2 ratchov 289: struct aproc *p;
1.12 ratchov 290: struct timeval tv;
291: long delta_usec;
292: int timo;
1.1 ratchov 293:
1.4 ratchov 294: /*
1.13 ratchov 295: * Fill the pfds[] array with files that are blocked on reading
296: * and/or writing, skipping those that are just waiting.
1.4 ratchov 297: */
1.15 ratchov 298: #ifdef DEBUG
299: dbg_flush();
300: if (debug_level >= 4)
301: dbg_puts("poll:");
302: #endif
1.1 ratchov 303: nfds = 0;
304: LIST_FOREACH(f, &file_list, entry) {
1.4 ratchov 305: events = 0;
306: if (f->rproc && !(f->state & FILE_ROK))
307: events |= POLLIN;
308: if (f->wproc && !(f->state & FILE_WOK))
309: events |= POLLOUT;
1.15 ratchov 310: #ifdef DEBUG
311: if (debug_level >= 4) {
312: dbg_puts(" ");
313: file_dbg(f);
314: }
315: #endif
1.4 ratchov 316: n = f->ops->pollfd(f, pfds + nfds, events);
317: if (n == 0) {
1.1 ratchov 318: f->pfd = NULL;
319: continue;
320: }
1.4 ratchov 321: f->pfd = pfds + nfds;
322: nfds += n;
1.1 ratchov 323: }
1.15 ratchov 324: #ifdef DEBUG
325: if (debug_level >= 4) {
326: dbg_puts("\npfds[] =");
327: for (n = 0; n < nfds; n++) {
328: dbg_puts(" ");
329: dbg_putx(pfds[n].events);
330: }
331: dbg_puts("\n");
332: }
333: #endif
1.1 ratchov 334: if (LIST_EMPTY(&file_list)) {
1.15 ratchov 335: #ifdef DEBUG
336: if (debug_level >= 3)
337: dbg_puts("nothing to do...\n");
338: #endif
1.1 ratchov 339: return 0;
340: }
1.9 ratchov 341: if (nfds > 0) {
1.12 ratchov 342: if (timo_queue) {
343: timo = (timo_queue->val - timo_abstime) / (2 * 1000);
344: if (timo == 0)
345: timo = 1;
346: } else
347: timo = -1;
348: if (poll(pfds, nfds, timo) < 0) {
1.9 ratchov 349: if (errno == EINTR)
350: return 1;
351: err(1, "file_poll: poll failed");
352: }
1.12 ratchov 353: gettimeofday(&tv, NULL);
354: delta_usec = 1000000L * (tv.tv_sec - file_tv.tv_sec);
355: delta_usec += tv.tv_usec - file_tv.tv_usec;
356: if (delta_usec > 0) {
357: file_tv = tv;
358: timo_update(delta_usec);
359: }
1.1 ratchov 360: }
1.4 ratchov 361: f = LIST_FIRST(&file_list);
362: while (f != LIST_END(&file_list)) {
363: if (f->pfd == NULL) {
364: f = LIST_NEXT(f, entry);
1.1 ratchov 365: continue;
1.4 ratchov 366: }
367: revents = f->ops->revents(f, f->pfd);
368: if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
369: revents &= ~POLLIN;
1.15 ratchov 370: #ifdef DEBUG
371: if (debug_level >= 4) {
372: file_dbg(f);
373: dbg_puts(": rok\n");
374: }
375: #endif
1.1 ratchov 376: f->state |= FILE_ROK;
1.12 ratchov 377: f->state |= FILE_RINUSE;
1.4 ratchov 378: for (;;) {
1.2 ratchov 379: p = f->rproc;
1.14 ratchov 380: if (!p)
381: break;
1.15 ratchov 382: #ifdef DEBUG
383: if (debug_level >= 4) {
384: aproc_dbg(p);
385: dbg_puts(": in\n");
386: }
387: #endif
1.14 ratchov 388: if (!p->ops->in(p, NULL))
1.1 ratchov 389: break;
390: }
1.12 ratchov 391: f->state &= ~FILE_RINUSE;
1.1 ratchov 392: }
1.4 ratchov 393: if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
394: revents &= ~POLLOUT;
1.15 ratchov 395: #ifdef DEBUG
396: if (debug_level >= 4) {
397: file_dbg(f);
398: dbg_puts(": wok\n");
399: }
400: #endif
1.1 ratchov 401: f->state |= FILE_WOK;
1.12 ratchov 402: f->state |= FILE_WINUSE;
1.4 ratchov 403: for (;;) {
1.2 ratchov 404: p = f->wproc;
1.14 ratchov 405: if (!p)
406: break;
1.15 ratchov 407: #ifdef DEBUG
408: if (debug_level >= 4) {
409: aproc_dbg(p);
410: dbg_puts(": out\n");
411: }
412: #endif
1.14 ratchov 413: if (!p->ops->out(p, NULL))
1.1 ratchov 414: break;
415: }
1.12 ratchov 416: f->state &= ~FILE_WINUSE;
1.7 ratchov 417: }
418: if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
1.15 ratchov 419: #ifdef DEBUG
420: if (debug_level >= 3) {
421: file_dbg(f);
422: dbg_puts(": disconnected\n");
423: }
424: #endif
1.7 ratchov 425: f->state |= (FILE_EOF | FILE_HUP);
1.1 ratchov 426: }
1.4 ratchov 427: if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
1.15 ratchov 428: #ifdef DEBUG
429: if (debug_level >= 3) {
430: file_dbg(f);
431: dbg_puts(": eof\n");
432: }
433: #endif
1.2 ratchov 434: p = f->rproc;
1.12 ratchov 435: if (p) {
436: f->state |= FILE_RINUSE;
1.15 ratchov 437: #ifdef DEBUG
438: if (debug_level >= 3) {
439: aproc_dbg(p);
440: dbg_puts(": eof\n");
441: }
442: #endif
1.2 ratchov 443: p->ops->eof(p, NULL);
1.12 ratchov 444: f->state &= ~FILE_RINUSE;
445: }
1.1 ratchov 446: f->state &= ~FILE_EOF;
447: }
1.4 ratchov 448: if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
1.15 ratchov 449: #ifdef DEBUG
450: if (debug_level >= 3) {
451: file_dbg(f);
452: dbg_puts(": hup\n");
453: }
454: #endif
1.2 ratchov 455: p = f->wproc;
1.12 ratchov 456: if (p) {
457: f->state |= FILE_WINUSE;
1.15 ratchov 458: #ifdef DEBUG
459: if (debug_level >= 3) {
460: aproc_dbg(p);
461: dbg_puts(": hup\n");
462: }
463: #endif
1.2 ratchov 464: p->ops->hup(p, NULL);
1.12 ratchov 465: f->state &= ~FILE_WINUSE;
466: }
1.1 ratchov 467: f->state &= ~FILE_HUP;
468: }
469: fnext = LIST_NEXT(f, entry);
1.4 ratchov 470: if (f->state & FILE_ZOMB)
1.3 ratchov 471: file_del(f);
1.4 ratchov 472: f = fnext;
1.1 ratchov 473: }
474: if (LIST_EMPTY(&file_list)) {
1.15 ratchov 475: #ifdef DEBUG
476: if (debug_level >= 3)
477: dbg_puts("no files anymore...\n");
478: #endif
1.1 ratchov 479: return 0;
480: }
481: return 1;
482: }
483:
484: void
1.4 ratchov 485: filelist_init(void)
1.1 ratchov 486: {
487: sigset_t set;
488:
489: sigemptyset(&set);
490: (void)sigaddset(&set, SIGPIPE);
491: if (sigprocmask(SIG_BLOCK, &set, NULL))
492: err(1, "sigprocmask");
493: LIST_INIT(&file_list);
1.12 ratchov 494: timo_init();
495: gettimeofday(&file_tv, NULL);
1.15 ratchov 496: #ifdef DEBUG
497: dbg_sync = 0;
498: #endif
1.1 ratchov 499: }
500:
501: void
1.4 ratchov 502: filelist_done(void)
1.1 ratchov 503: {
1.15 ratchov 504: #ifdef DEBUG
505: struct file *f;
506:
507: if (!LIST_EMPTY(&file_list)) {
508: LIST_FOREACH(f, &file_list, entry) {
509: dbg_puts("\t");
510: file_dbg(f);
511: dbg_puts("\n");
512: }
513: dbg_panic();
514: }
515: dbg_sync = 1;
516: dbg_flush();
517: #endif
1.12 ratchov 518: timo_done();
1.1 ratchov 519: }
520:
1.4 ratchov 521: unsigned
1.14 ratchov 522: file_read(struct file *f, unsigned char *data, unsigned count)
1.4 ratchov 523: {
1.14 ratchov 524: unsigned n;
1.15 ratchov 525: #ifdef DEBUG
526: struct timeval tv0, tv1, dtv;
527: unsigned us;
528:
529: gettimeofday(&tv0, NULL);
530: if (!(f->state & FILE_ROK)) {
531: file_dbg(f);
532: dbg_puts(": read: bad state\n");
533: dbg_panic();
534: }
535: #endif
1.14 ratchov 536: n = f->ops->read(f, data, count);
1.15 ratchov 537: #ifdef DEBUG
538: gettimeofday(&tv1, NULL);
539: timersub(&tv1, &tv0, &dtv);
540: us = dtv.tv_sec * 1000000 + dtv.tv_usec;
541: if (debug_level >= 4 || (debug_level >= 1 && us >= 5000)) {
542: dbg_puts(f->name);
543: dbg_puts(": read ");
544: dbg_putu(n);
545: dbg_puts(" bytes in ");
546: dbg_putu(us);
547: dbg_puts("us\n");
548: }
549: #endif
1.14 ratchov 550: return n;
1.4 ratchov 551: }
1.1 ratchov 552:
553: unsigned
1.14 ratchov 554: file_write(struct file *f, unsigned char *data, unsigned count)
1.1 ratchov 555: {
1.14 ratchov 556: unsigned n;
1.15 ratchov 557: #ifdef DEBUG
558: struct timeval tv0, tv1, dtv;
559: unsigned us;
560:
561: if (!(f->state & FILE_WOK)) {
562: file_dbg(f);
563: dbg_puts(": write: bad state\n");
564: dbg_panic();
565: }
566: gettimeofday(&tv0, NULL);
567: #endif
1.14 ratchov 568: n = f->ops->write(f, data, count);
1.15 ratchov 569: #ifdef DEBUG
570: gettimeofday(&tv1, NULL);
571: timersub(&tv1, &tv0, &dtv);
572: us = dtv.tv_sec * 1000000 + dtv.tv_usec;
573: if (debug_level >= 4 || (debug_level >= 1 && us >= 5000)) {
574: dbg_puts(f->name);
575: dbg_puts(": wrote ");
576: dbg_putu(n);
577: dbg_puts(" bytes in ");
578: dbg_putu(us);
579: dbg_puts("us\n");
580: }
581: #endif
1.14 ratchov 582: return n;
1.1 ratchov 583: }
584:
585: void
586: file_eof(struct file *f)
587: {
1.4 ratchov 588: struct aproc *p;
589:
1.15 ratchov 590: #ifdef DEBUG
591: if (debug_level >= 3) {
592: file_dbg(f);
593: dbg_puts(": eof requested\n");
594: }
595: #endif
1.12 ratchov 596: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 597: p = f->rproc;
1.12 ratchov 598: if (p) {
599: f->state |= FILE_RINUSE;
1.15 ratchov 600: #ifdef DEBUG
601: if (debug_level >= 3) {
602: aproc_dbg(p);
603: dbg_puts(": eof\n");
604: }
605: #endif
1.4 ratchov 606: p->ops->eof(p, NULL);
1.12 ratchov 607: f->state &= ~FILE_RINUSE;
608: }
1.4 ratchov 609: if (f->state & FILE_ZOMB)
610: file_del(f);
611: } else {
612: f->state &= ~FILE_ROK;
613: f->state |= FILE_EOF;
614: }
1.1 ratchov 615: }
616:
617: void
618: file_hup(struct file *f)
619: {
1.4 ratchov 620: struct aproc *p;
621:
1.15 ratchov 622: #ifdef DEBUG
623: if (debug_level >= 3) {
624: file_dbg(f);
625: dbg_puts(": hup requested\n");
626: }
627: #endif
1.12 ratchov 628: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 629: p = f->wproc;
1.12 ratchov 630: if (p) {
631: f->state |= FILE_WINUSE;
1.15 ratchov 632: #ifdef DEBUG
633: if (debug_level >= 3) {
634: aproc_dbg(p);
635: dbg_puts(": hup\n");
636: }
637: #endif
1.4 ratchov 638: p->ops->hup(p, NULL);
1.12 ratchov 639: f->state &= ~FILE_WINUSE;
640: }
1.4 ratchov 641: if (f->state & FILE_ZOMB)
642: file_del(f);
643: } else {
644: f->state &= ~FILE_WOK;
645: f->state |= FILE_HUP;
1.9 ratchov 646: }
647: }
648:
649: void
650: file_close(struct file *f)
651: {
652: struct aproc *p;
653:
1.15 ratchov 654: #ifdef DEBUG
655: if (debug_level >= 3) {
656: file_dbg(f);
657: dbg_puts(": closing\n");
658: }
659: #endif
1.18 ! ratchov 660: if (f->wproc == NULL && f->rproc == NULL)
! 661: f->state |= FILE_ZOMB;
1.12 ratchov 662: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.9 ratchov 663: p = f->rproc;
1.12 ratchov 664: if (p) {
665: f->state |= FILE_RINUSE;
1.15 ratchov 666: #ifdef DEBUG
667: if (debug_level >= 3) {
668: aproc_dbg(p);
669: dbg_puts(": eof\n");
670: }
671: #endif
1.9 ratchov 672: p->ops->eof(p, NULL);
1.12 ratchov 673: f->state &= ~FILE_RINUSE;
674: }
1.9 ratchov 675: p = f->wproc;
1.12 ratchov 676: if (p) {
677: f->state |= FILE_WINUSE;
1.15 ratchov 678: #ifdef DEBUG
679: if (debug_level >= 3) {
680: aproc_dbg(p);
681: dbg_puts(": hup\n");
682: }
683: #endif
1.9 ratchov 684: p->ops->hup(p, NULL);
1.12 ratchov 685: f->state &= ~FILE_WINUSE;
686: }
1.9 ratchov 687: if (f->state & FILE_ZOMB)
688: file_del(f);
689: } else {
690: f->state &= ~(FILE_ROK | FILE_WOK);
691: f->state |= (FILE_EOF | FILE_HUP);
1.4 ratchov 692: }
1.1 ratchov 693: }