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