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