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