Annotation of src/usr.bin/aucat/file.c, Revision 1.31
1.31 ! ratchov 1: /* $OpenBSD$ */
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: *
1.26 ratchov 27: * anoncvs@moule.caoua.org:/midish
1.19 ratchov 28: *
1.26 ratchov 29: * midish/timo.c rev 1.18
30: * midish/mdep.c rev 1.71
1.12 ratchov 31: *
32: * A timeout is used to schedule the call of a routine (the callback)
33: * there is a global list of timeouts that is processed inside the
34: * event loop. Timeouts work as follows:
35: *
36: * first the timo structure must be initialized with timo_set()
37: *
38: * then the timeout is scheduled (only once) with timo_add()
39: *
40: * if the timeout expires, the call-back is called; then it can
41: * be scheduled again if needed. It's OK to reschedule it again
42: * from the callback
43: *
44: * the timeout can be aborted with timo_del(), it is OK to try to
45: * abort a timout that has expired
46: *
1.1 ratchov 47: */
1.12 ratchov 48:
1.22 ratchov 49: #include <sys/time.h>
1.1 ratchov 50: #include <sys/types.h>
51:
52: #include <err.h>
53: #include <errno.h>
54: #include <fcntl.h>
55: #include <poll.h>
56: #include <signal.h>
57: #include <stdio.h>
58: #include <stdlib.h>
1.22 ratchov 59: #include <time.h>
1.1 ratchov 60:
1.13 ratchov 61: #include "abuf.h"
62: #include "aproc.h"
1.1 ratchov 63: #include "conf.h"
64: #include "file.h"
1.15 ratchov 65: #ifdef DEBUG
66: #include "dbg.h"
67: #endif
1.1 ratchov 68:
69: #define MAXFDS 100
1.19 ratchov 70: #define TIMER_USEC 10000
1.31 ! ratchov 71:
! 72: void timo_update(unsigned int);
! 73: void timo_init(void);
! 74: void timo_done(void);
! 75: void file_sigalrm(int);
1.1 ratchov 76:
1.19 ratchov 77: struct timespec file_ts;
1.1 ratchov 78: struct filelist file_list;
1.12 ratchov 79: struct timo *timo_queue;
1.30 ratchov 80: unsigned int timo_abstime;
1.29 ratchov 81: int file_slowaccept = 0;
1.27 ratchov 82: #ifdef DEBUG
83: long long file_wtime, file_utime;
84: #endif
1.12 ratchov 85:
86: /*
87: * initialise a timeout structure, arguments are callback and argument
88: * that will be passed to the callback
89: */
90: void
91: timo_set(struct timo *o, void (*cb)(void *), void *arg)
92: {
93: o->cb = cb;
94: o->arg = arg;
95: o->set = 0;
96: }
97:
98: /*
99: * schedule the callback in 'delta' 24-th of microseconds. The timeout
100: * must not be already scheduled
101: */
102: void
1.30 ratchov 103: timo_add(struct timo *o, unsigned int delta)
1.12 ratchov 104: {
105: struct timo **i;
1.30 ratchov 106: unsigned int val;
1.12 ratchov 107: int diff;
108:
1.15 ratchov 109: #ifdef DEBUG
110: if (o->set) {
1.19 ratchov 111: dbg_puts("timo_add: already set\n");
1.15 ratchov 112: dbg_panic();
113: }
114: if (delta == 0) {
1.19 ratchov 115: dbg_puts("timo_add: zero timeout is evil\n");
1.15 ratchov 116: dbg_panic();
117: }
118: #endif
1.12 ratchov 119: val = timo_abstime + delta;
120: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
121: diff = (*i)->val - val;
122: if (diff > 0) {
123: break;
124: }
125: }
126: o->set = 1;
127: o->val = val;
128: o->next = *i;
129: *i = o;
130: }
131:
132: /*
133: * abort a scheduled timeout
134: */
135: void
136: timo_del(struct timo *o)
137: {
138: struct timo **i;
139:
140: for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
141: if (*i == o) {
142: *i = o->next;
143: o->set = 0;
144: return;
145: }
146: }
1.15 ratchov 147: #ifdef DEBUG
148: if (debug_level >= 4)
149: dbg_puts("timo_del: not found\n");
150: #endif
1.12 ratchov 151: }
152:
153: /*
154: * routine to be called by the timer when 'delta' 24-th of microsecond
155: * elapsed. This routine updates time referece used by timeouts and
156: * calls expired timeouts
157: */
158: void
1.30 ratchov 159: timo_update(unsigned int delta)
1.12 ratchov 160: {
161: struct timo *to;
162: int diff;
163:
164: /*
165: * update time reference
166: */
167: timo_abstime += delta;
168:
169: /*
170: * remove from the queue and run expired timeouts
171: */
172: while (timo_queue != NULL) {
173: /*
174: * there is no overflow here because + and - are
175: * modulo 2^32, they are the same for both signed and
176: * unsigned integers
177: */
178: diff = timo_queue->val - timo_abstime;
179: if (diff > 0)
180: break;
181: to = timo_queue;
182: timo_queue = to->next;
183: to->set = 0;
184: to->cb(to->arg);
185: }
186: }
187:
188: /*
189: * initialize timeout queue
190: */
191: void
192: timo_init(void)
193: {
194: timo_queue = NULL;
195: timo_abstime = 0;
196: }
197:
198: /*
199: * destroy timeout queue
200: */
201: void
202: timo_done(void)
203: {
1.15 ratchov 204: #ifdef DEBUG
205: if (timo_queue != NULL) {
206: dbg_puts("timo_done: timo_queue not empty!\n");
207: dbg_panic();
208: }
209: #endif
1.12 ratchov 210: timo_queue = (struct timo *)0xdeadbeef;
211: }
1.1 ratchov 212:
1.15 ratchov 213: #ifdef DEBUG
214: void
215: file_dbg(struct file *f)
216: {
217: dbg_puts(f->ops->name);
218: dbg_puts("(");
219: dbg_puts(f->name);
220: dbg_puts("|");
221: if (f->state & FILE_ROK)
222: dbg_puts("r");
223: if (f->state & FILE_RINUSE)
224: dbg_puts("R");
225: if (f->state & FILE_WOK)
226: dbg_puts("w");
227: if (f->state & FILE_WINUSE)
228: dbg_puts("W");
229: if (f->state & FILE_EOF)
230: dbg_puts("e");
231: if (f->state & FILE_HUP)
232: dbg_puts("h");
233: if (f->state & FILE_ZOMB)
234: dbg_puts("Z");
235: dbg_puts(")");
236: }
237: #endif
1.3 ratchov 238:
1.1 ratchov 239: struct file *
1.30 ratchov 240: file_new(struct fileops *ops, char *name, unsigned int nfds)
1.1 ratchov 241: {
242: struct file *f;
243:
244: LIST_FOREACH(f, &file_list, entry)
1.4 ratchov 245: nfds += f->ops->nfds(f);
1.11 ratchov 246: if (nfds > MAXFDS) {
1.15 ratchov 247: #ifdef DEBUG
248: if (debug_level >= 1) {
249: dbg_puts(name);
250: dbg_puts(": too many polled files\n");
251: }
252: #endif
1.11 ratchov 253: return NULL;
254: }
1.4 ratchov 255: f = malloc(ops->size);
1.1 ratchov 256: if (f == NULL)
1.4 ratchov 257: err(1, "file_new: %s", ops->name);
258: f->ops = ops;
1.1 ratchov 259: f->name = name;
260: f->state = 0;
1.23 ratchov 261: #ifdef DEBUG
1.21 ratchov 262: f->cycles = 0;
1.23 ratchov 263: #endif
1.1 ratchov 264: f->rproc = NULL;
265: f->wproc = NULL;
266: LIST_INSERT_HEAD(&file_list, f, entry);
1.15 ratchov 267: #ifdef DEBUG
268: if (debug_level >= 3) {
269: file_dbg(f);
270: dbg_puts(": created\n");
271: }
272: #endif
1.1 ratchov 273: return f;
274: }
275:
276: void
277: file_del(struct file *f)
278: {
1.15 ratchov 279: #ifdef DEBUG
280: if (debug_level >= 3) {
281: file_dbg(f);
282: dbg_puts(": terminating...\n");
283: }
284: #endif
1.12 ratchov 285: if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
1.4 ratchov 286: f->state |= FILE_ZOMB;
287: } else {
288: LIST_REMOVE(f, entry);
1.15 ratchov 289: #ifdef DEBUG
290: if (debug_level >= 3) {
291: file_dbg(f);
292: dbg_puts(": destroyed\n");
293: }
294: #endif
1.4 ratchov 295: f->ops->close(f);
296: free(f);
297: }
1.1 ratchov 298: }
299:
300: int
301: file_poll(void)
302: {
1.4 ratchov 303: nfds_t nfds, n;
304: short events, revents;
1.1 ratchov 305: struct pollfd pfds[MAXFDS];
306: struct file *f, *fnext;
1.2 ratchov 307: struct aproc *p;
1.19 ratchov 308: struct timespec ts;
1.27 ratchov 309: #ifdef DEBUG
310: struct timespec sleepts;
311: #endif
1.26 ratchov 312: long long delta_nsec;
313: int res;
1.1 ratchov 314:
1.25 ratchov 315: if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
1.19 ratchov 316: #ifdef DEBUG
317: if (debug_level >= 3)
318: dbg_puts("nothing to do...\n");
319: #endif
320: return 0;
321: }
1.4 ratchov 322: /*
1.13 ratchov 323: * Fill the pfds[] array with files that are blocked on reading
324: * and/or writing, skipping those that are just waiting.
1.4 ratchov 325: */
1.15 ratchov 326: #ifdef DEBUG
327: dbg_flush();
328: if (debug_level >= 4)
329: dbg_puts("poll:");
330: #endif
1.1 ratchov 331: nfds = 0;
332: LIST_FOREACH(f, &file_list, entry) {
1.4 ratchov 333: events = 0;
334: if (f->rproc && !(f->state & FILE_ROK))
335: events |= POLLIN;
336: if (f->wproc && !(f->state & FILE_WOK))
337: events |= POLLOUT;
1.15 ratchov 338: #ifdef DEBUG
339: if (debug_level >= 4) {
340: dbg_puts(" ");
341: file_dbg(f);
342: }
343: #endif
1.4 ratchov 344: n = f->ops->pollfd(f, pfds + nfds, events);
345: if (n == 0) {
1.1 ratchov 346: f->pfd = NULL;
347: continue;
348: }
1.4 ratchov 349: f->pfd = pfds + nfds;
350: nfds += n;
1.1 ratchov 351: }
1.15 ratchov 352: #ifdef DEBUG
353: if (debug_level >= 4) {
354: dbg_puts("\npfds[] =");
355: for (n = 0; n < nfds; n++) {
356: dbg_puts(" ");
357: dbg_putx(pfds[n].events);
358: }
359: dbg_puts("\n");
360: }
361: #endif
1.27 ratchov 362: #ifdef DEBUG
363: clock_gettime(CLOCK_MONOTONIC, &sleepts);
364: file_utime += 1000000000LL * (sleepts.tv_sec - file_ts.tv_sec);
365: file_utime += sleepts.tv_nsec - file_ts.tv_nsec;
366: #endif
1.26 ratchov 367: res = poll(pfds, nfds, -1);
368: if (res < 0 && errno != EINTR)
369: err(1, "poll");
370: clock_gettime(CLOCK_MONOTONIC, &ts);
1.27 ratchov 371: #ifdef DEBUG
372: file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
373: file_wtime += ts.tv_nsec - sleepts.tv_nsec;
374: #endif
1.26 ratchov 375: delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
376: delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
377: #ifdef DEBUG
1.28 ratchov 378: if (delta_nsec < 0)
1.27 ratchov 379: dbg_puts("file_poll: negative time interval\n");
380: #endif
381: file_ts = ts;
1.28 ratchov 382: if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
1.27 ratchov 383: timo_update(delta_nsec / 1000);
384: else {
385: #ifdef DEBUG
1.28 ratchov 386: if (debug_level >= 1)
387: dbg_puts("ignored huge clock delta\n");
1.26 ratchov 388: #endif
1.1 ratchov 389: }
1.26 ratchov 390: if (res <= 0)
391: return 1;
392:
1.4 ratchov 393: f = LIST_FIRST(&file_list);
1.22 ratchov 394: while (f != NULL) {
1.4 ratchov 395: if (f->pfd == NULL) {
396: f = LIST_NEXT(f, entry);
1.1 ratchov 397: continue;
1.4 ratchov 398: }
399: revents = f->ops->revents(f, f->pfd);
1.20 ratchov 400: #ifdef DEBUG
401: if (revents) {
402: f->cycles++;
403: if (f->cycles > FILE_MAXCYCLES) {
404: file_dbg(f);
405: dbg_puts(": busy loop, disconnecting\n");
406: revents = POLLHUP;
407: }
408: }
409: #endif
1.4 ratchov 410: if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
411: revents &= ~POLLIN;
1.15 ratchov 412: #ifdef DEBUG
413: if (debug_level >= 4) {
414: file_dbg(f);
415: dbg_puts(": rok\n");
416: }
417: #endif
1.1 ratchov 418: f->state |= FILE_ROK;
1.12 ratchov 419: f->state |= FILE_RINUSE;
1.4 ratchov 420: for (;;) {
1.2 ratchov 421: p = f->rproc;
1.14 ratchov 422: if (!p)
423: break;
1.15 ratchov 424: #ifdef DEBUG
425: if (debug_level >= 4) {
426: aproc_dbg(p);
427: dbg_puts(": in\n");
428: }
429: #endif
1.14 ratchov 430: if (!p->ops->in(p, NULL))
1.1 ratchov 431: break;
432: }
1.12 ratchov 433: f->state &= ~FILE_RINUSE;
1.1 ratchov 434: }
1.4 ratchov 435: if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
436: revents &= ~POLLOUT;
1.15 ratchov 437: #ifdef DEBUG
438: if (debug_level >= 4) {
439: file_dbg(f);
440: dbg_puts(": wok\n");
441: }
442: #endif
1.1 ratchov 443: f->state |= FILE_WOK;
1.12 ratchov 444: f->state |= FILE_WINUSE;
1.4 ratchov 445: for (;;) {
1.2 ratchov 446: p = f->wproc;
1.14 ratchov 447: if (!p)
448: break;
1.15 ratchov 449: #ifdef DEBUG
450: if (debug_level >= 4) {
451: aproc_dbg(p);
452: dbg_puts(": out\n");
453: }
454: #endif
1.14 ratchov 455: if (!p->ops->out(p, NULL))
1.1 ratchov 456: break;
457: }
1.12 ratchov 458: f->state &= ~FILE_WINUSE;
1.7 ratchov 459: }
460: if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
1.15 ratchov 461: #ifdef DEBUG
462: if (debug_level >= 3) {
463: file_dbg(f);
464: dbg_puts(": disconnected\n");
465: }
466: #endif
1.7 ratchov 467: f->state |= (FILE_EOF | FILE_HUP);
1.1 ratchov 468: }
1.4 ratchov 469: if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
1.15 ratchov 470: #ifdef DEBUG
471: if (debug_level >= 3) {
472: file_dbg(f);
473: dbg_puts(": eof\n");
474: }
475: #endif
1.2 ratchov 476: p = f->rproc;
1.12 ratchov 477: if (p) {
478: f->state |= FILE_RINUSE;
1.15 ratchov 479: #ifdef DEBUG
480: if (debug_level >= 3) {
481: aproc_dbg(p);
482: dbg_puts(": eof\n");
483: }
484: #endif
1.2 ratchov 485: p->ops->eof(p, NULL);
1.12 ratchov 486: f->state &= ~FILE_RINUSE;
487: }
1.1 ratchov 488: f->state &= ~FILE_EOF;
489: }
1.4 ratchov 490: if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
1.15 ratchov 491: #ifdef DEBUG
492: if (debug_level >= 3) {
493: file_dbg(f);
494: dbg_puts(": hup\n");
495: }
496: #endif
1.2 ratchov 497: p = f->wproc;
1.12 ratchov 498: if (p) {
499: f->state |= FILE_WINUSE;
1.15 ratchov 500: #ifdef DEBUG
501: if (debug_level >= 3) {
502: aproc_dbg(p);
503: dbg_puts(": hup\n");
504: }
505: #endif
1.2 ratchov 506: p->ops->hup(p, NULL);
1.12 ratchov 507: f->state &= ~FILE_WINUSE;
508: }
1.1 ratchov 509: f->state &= ~FILE_HUP;
510: }
511: fnext = LIST_NEXT(f, entry);
1.4 ratchov 512: if (f->state & FILE_ZOMB)
1.3 ratchov 513: file_del(f);
1.4 ratchov 514: f = fnext;
1.1 ratchov 515: }
1.25 ratchov 516: if (LIST_EMPTY(&file_list) && timo_queue == NULL) {
1.15 ratchov 517: #ifdef DEBUG
518: if (debug_level >= 3)
519: dbg_puts("no files anymore...\n");
520: #endif
1.1 ratchov 521: return 0;
522: }
523: return 1;
524: }
525:
1.19 ratchov 526: /*
527: * handler for SIGALRM, invoked periodically
528: */
529: void
530: file_sigalrm(int i)
531: {
532: /* nothing to do, we only want poll() to return EINTR */
533: }
534:
535:
1.1 ratchov 536: void
1.4 ratchov 537: filelist_init(void)
1.1 ratchov 538: {
1.19 ratchov 539: static struct sigaction sa;
540: struct itimerval it;
1.1 ratchov 541: sigset_t set;
542:
543: sigemptyset(&set);
544: (void)sigaddset(&set, SIGPIPE);
545: if (sigprocmask(SIG_BLOCK, &set, NULL))
546: err(1, "sigprocmask");
547: LIST_INIT(&file_list);
1.19 ratchov 548: if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
549: perror("clock_gettime");
550: exit(1);
551: }
552: sa.sa_flags = SA_RESTART;
553: sa.sa_handler = file_sigalrm;
554: sigfillset(&sa.sa_mask);
555: if (sigaction(SIGALRM, &sa, NULL) < 0) {
556: perror("sigaction");
557: exit(1);
558: }
559: it.it_interval.tv_sec = 0;
560: it.it_interval.tv_usec = TIMER_USEC;
561: it.it_value.tv_sec = 0;
562: it.it_value.tv_usec = TIMER_USEC;
563: if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
564: perror("setitimer");
565: exit(1);
566: }
1.12 ratchov 567: timo_init();
1.15 ratchov 568: #ifdef DEBUG
569: dbg_sync = 0;
570: #endif
1.1 ratchov 571: }
572:
573: void
1.4 ratchov 574: filelist_done(void)
1.1 ratchov 575: {
1.19 ratchov 576: struct itimerval it;
1.15 ratchov 577: #ifdef DEBUG
578: struct file *f;
579:
580: if (!LIST_EMPTY(&file_list)) {
581: LIST_FOREACH(f, &file_list, entry) {
582: file_dbg(f);
1.19 ratchov 583: dbg_puts(" not closed\n");
1.15 ratchov 584: }
585: dbg_panic();
586: }
587: dbg_sync = 1;
588: dbg_flush();
589: #endif
1.24 okan 590: timerclear(&it.it_value);
591: timerclear(&it.it_interval);
1.19 ratchov 592: if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
593: perror("setitimer");
594: exit(1);
595: }
1.12 ratchov 596: timo_done();
1.1 ratchov 597: }
598:
1.30 ratchov 599: unsigned int
600: file_read(struct file *f, unsigned char *data, unsigned int count)
1.4 ratchov 601: {
1.30 ratchov 602: unsigned int n;
1.15 ratchov 603: #ifdef DEBUG
1.19 ratchov 604: struct timespec ts0, ts1;
605: long us;
1.15 ratchov 606:
607: if (!(f->state & FILE_ROK)) {
608: file_dbg(f);
609: dbg_puts(": read: bad state\n");
610: dbg_panic();
611: }
1.19 ratchov 612: clock_gettime(CLOCK_MONOTONIC, &ts0);
1.15 ratchov 613: #endif
1.14 ratchov 614: n = f->ops->read(f, data, count);
1.15 ratchov 615: #ifdef DEBUG
1.20 ratchov 616: if (n > 0)
617: f->cycles = 0;
1.19 ratchov 618: clock_gettime(CLOCK_MONOTONIC, &ts1);
619: us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
620: us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
621: if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
1.15 ratchov 622: dbg_puts(f->name);
623: dbg_puts(": read ");
624: dbg_putu(n);
625: dbg_puts(" bytes in ");
626: dbg_putu(us);
627: dbg_puts("us\n");
628: }
629: #endif
1.14 ratchov 630: return n;
1.4 ratchov 631: }
1.1 ratchov 632:
1.30 ratchov 633: unsigned int
634: file_write(struct file *f, unsigned char *data, unsigned int count)
1.1 ratchov 635: {
1.30 ratchov 636: unsigned int n;
1.15 ratchov 637: #ifdef DEBUG
1.19 ratchov 638: struct timespec ts0, ts1;
639: long us;
1.15 ratchov 640:
641: if (!(f->state & FILE_WOK)) {
642: file_dbg(f);
643: dbg_puts(": write: bad state\n");
644: dbg_panic();
645: }
1.19 ratchov 646: clock_gettime(CLOCK_MONOTONIC, &ts0);
1.15 ratchov 647: #endif
1.14 ratchov 648: n = f->ops->write(f, data, count);
1.15 ratchov 649: #ifdef DEBUG
1.20 ratchov 650: if (n > 0)
651: f->cycles = 0;
1.19 ratchov 652: clock_gettime(CLOCK_MONOTONIC, &ts1);
653: us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
654: us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
655: if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
1.15 ratchov 656: dbg_puts(f->name);
657: dbg_puts(": wrote ");
658: dbg_putu(n);
659: dbg_puts(" bytes in ");
660: dbg_putu(us);
661: dbg_puts("us\n");
662: }
663: #endif
1.14 ratchov 664: return n;
1.1 ratchov 665: }
666:
667: void
668: file_eof(struct file *f)
669: {
1.4 ratchov 670: struct aproc *p;
671:
1.15 ratchov 672: #ifdef DEBUG
673: if (debug_level >= 3) {
674: file_dbg(f);
675: dbg_puts(": eof requested\n");
676: }
677: #endif
1.12 ratchov 678: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 679: p = f->rproc;
1.12 ratchov 680: if (p) {
681: f->state |= FILE_RINUSE;
1.15 ratchov 682: #ifdef DEBUG
683: if (debug_level >= 3) {
684: aproc_dbg(p);
685: dbg_puts(": eof\n");
686: }
687: #endif
1.4 ratchov 688: p->ops->eof(p, NULL);
1.12 ratchov 689: f->state &= ~FILE_RINUSE;
690: }
1.4 ratchov 691: if (f->state & FILE_ZOMB)
692: file_del(f);
693: } else {
694: f->state &= ~FILE_ROK;
695: f->state |= FILE_EOF;
696: }
1.1 ratchov 697: }
698:
699: void
700: file_hup(struct file *f)
701: {
1.4 ratchov 702: struct aproc *p;
703:
1.15 ratchov 704: #ifdef DEBUG
705: if (debug_level >= 3) {
706: file_dbg(f);
707: dbg_puts(": hup requested\n");
708: }
709: #endif
1.12 ratchov 710: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 711: p = f->wproc;
1.12 ratchov 712: if (p) {
713: f->state |= FILE_WINUSE;
1.15 ratchov 714: #ifdef DEBUG
715: if (debug_level >= 3) {
716: aproc_dbg(p);
717: dbg_puts(": hup\n");
718: }
719: #endif
1.4 ratchov 720: p->ops->hup(p, NULL);
1.12 ratchov 721: f->state &= ~FILE_WINUSE;
722: }
1.4 ratchov 723: if (f->state & FILE_ZOMB)
724: file_del(f);
725: } else {
726: f->state &= ~FILE_WOK;
727: f->state |= FILE_HUP;
1.9 ratchov 728: }
729: }
730:
731: void
732: file_close(struct file *f)
733: {
734: struct aproc *p;
735:
1.15 ratchov 736: #ifdef DEBUG
737: if (debug_level >= 3) {
738: file_dbg(f);
739: dbg_puts(": closing\n");
740: }
741: #endif
1.18 ratchov 742: if (f->wproc == NULL && f->rproc == NULL)
743: f->state |= FILE_ZOMB;
1.12 ratchov 744: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.9 ratchov 745: p = f->rproc;
1.12 ratchov 746: if (p) {
747: f->state |= FILE_RINUSE;
1.15 ratchov 748: #ifdef DEBUG
749: if (debug_level >= 3) {
750: aproc_dbg(p);
751: dbg_puts(": eof\n");
752: }
753: #endif
1.9 ratchov 754: p->ops->eof(p, NULL);
1.12 ratchov 755: f->state &= ~FILE_RINUSE;
756: }
1.9 ratchov 757: p = f->wproc;
1.12 ratchov 758: if (p) {
759: f->state |= FILE_WINUSE;
1.15 ratchov 760: #ifdef DEBUG
761: if (debug_level >= 3) {
762: aproc_dbg(p);
763: dbg_puts(": hup\n");
764: }
765: #endif
1.9 ratchov 766: p->ops->hup(p, NULL);
1.12 ratchov 767: f->state &= ~FILE_WINUSE;
768: }
1.9 ratchov 769: if (f->state & FILE_ZOMB)
770: file_del(f);
771: } else {
772: f->state &= ~(FILE_ROK | FILE_WOK);
773: f->state |= (FILE_EOF | FILE_HUP);
1.4 ratchov 774: }
1.1 ratchov 775: }