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