Annotation of src/usr.bin/aucat/file.c, Revision 1.21
1.21 ! ratchov 1: /* $OpenBSD: file.c,v 1.20 2010/07/06 20:06:35 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;
1.21 ! ratchov 250: f->cycles = 0;
1.1 ratchov 251: f->rproc = NULL;
252: f->wproc = NULL;
253: LIST_INSERT_HEAD(&file_list, f, entry);
1.15 ratchov 254: #ifdef DEBUG
255: if (debug_level >= 3) {
256: file_dbg(f);
257: dbg_puts(": created\n");
258: }
259: #endif
1.1 ratchov 260: return f;
261: }
262:
263: void
264: file_del(struct file *f)
265: {
1.15 ratchov 266: #ifdef DEBUG
267: if (debug_level >= 3) {
268: file_dbg(f);
269: dbg_puts(": terminating...\n");
270: }
271: #endif
1.12 ratchov 272: if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
1.4 ratchov 273: f->state |= FILE_ZOMB;
274: } else {
275: LIST_REMOVE(f, entry);
1.15 ratchov 276: #ifdef DEBUG
277: if (debug_level >= 3) {
278: file_dbg(f);
279: dbg_puts(": destroyed\n");
280: }
281: #endif
1.4 ratchov 282: f->ops->close(f);
283: free(f);
284: }
1.1 ratchov 285: }
286:
287: int
288: file_poll(void)
289: {
1.4 ratchov 290: nfds_t nfds, n;
291: short events, revents;
1.1 ratchov 292: struct pollfd pfds[MAXFDS];
293: struct file *f, *fnext;
1.2 ratchov 294: struct aproc *p;
1.19 ratchov 295: struct timespec ts;
296: long delta_nsec;
1.1 ratchov 297:
1.19 ratchov 298: if (LIST_EMPTY(&file_list)) {
299: #ifdef DEBUG
300: if (debug_level >= 3)
301: dbg_puts("nothing to do...\n");
302: #endif
303: return 0;
304: }
1.4 ratchov 305: /*
1.13 ratchov 306: * Fill the pfds[] array with files that are blocked on reading
307: * and/or writing, skipping those that are just waiting.
1.4 ratchov 308: */
1.15 ratchov 309: #ifdef DEBUG
310: dbg_flush();
311: if (debug_level >= 4)
312: dbg_puts("poll:");
313: #endif
1.1 ratchov 314: nfds = 0;
315: LIST_FOREACH(f, &file_list, entry) {
1.4 ratchov 316: events = 0;
317: if (f->rproc && !(f->state & FILE_ROK))
318: events |= POLLIN;
319: if (f->wproc && !(f->state & FILE_WOK))
320: events |= POLLOUT;
1.15 ratchov 321: #ifdef DEBUG
322: if (debug_level >= 4) {
323: dbg_puts(" ");
324: file_dbg(f);
325: }
326: #endif
1.4 ratchov 327: n = f->ops->pollfd(f, pfds + nfds, events);
328: if (n == 0) {
1.1 ratchov 329: f->pfd = NULL;
330: continue;
331: }
1.4 ratchov 332: f->pfd = pfds + nfds;
333: nfds += n;
1.1 ratchov 334: }
1.15 ratchov 335: #ifdef DEBUG
336: if (debug_level >= 4) {
337: dbg_puts("\npfds[] =");
338: for (n = 0; n < nfds; n++) {
339: dbg_puts(" ");
340: dbg_putx(pfds[n].events);
341: }
342: dbg_puts("\n");
343: }
344: #endif
1.9 ratchov 345: if (nfds > 0) {
1.19 ratchov 346: if (poll(pfds, nfds, -1) < 0) {
1.9 ratchov 347: if (errno == EINTR)
348: return 1;
349: err(1, "file_poll: poll failed");
350: }
1.19 ratchov 351: clock_gettime(CLOCK_MONOTONIC, &ts);
352: delta_nsec = 1000000000L * (ts.tv_sec - file_ts.tv_sec);
353: delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
354: if (delta_nsec > 0) {
355: file_ts = ts;
356: timo_update(delta_nsec / 1000);
1.12 ratchov 357: }
1.1 ratchov 358: }
1.4 ratchov 359: f = LIST_FIRST(&file_list);
360: while (f != LIST_END(&file_list)) {
361: if (f->pfd == NULL) {
362: f = LIST_NEXT(f, entry);
1.1 ratchov 363: continue;
1.4 ratchov 364: }
365: revents = f->ops->revents(f, f->pfd);
1.20 ratchov 366: #ifdef DEBUG
367: if (revents) {
368: f->cycles++;
369: if (f->cycles > FILE_MAXCYCLES) {
370: file_dbg(f);
371: dbg_puts(": busy loop, disconnecting\n");
372: revents = POLLHUP;
373: }
374: }
375: #endif
1.4 ratchov 376: if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
377: revents &= ~POLLIN;
1.15 ratchov 378: #ifdef DEBUG
379: if (debug_level >= 4) {
380: file_dbg(f);
381: dbg_puts(": rok\n");
382: }
383: #endif
1.1 ratchov 384: f->state |= FILE_ROK;
1.12 ratchov 385: f->state |= FILE_RINUSE;
1.4 ratchov 386: for (;;) {
1.2 ratchov 387: p = f->rproc;
1.14 ratchov 388: if (!p)
389: break;
1.15 ratchov 390: #ifdef DEBUG
391: if (debug_level >= 4) {
392: aproc_dbg(p);
393: dbg_puts(": in\n");
394: }
395: #endif
1.14 ratchov 396: if (!p->ops->in(p, NULL))
1.1 ratchov 397: break;
398: }
1.12 ratchov 399: f->state &= ~FILE_RINUSE;
1.1 ratchov 400: }
1.4 ratchov 401: if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
402: revents &= ~POLLOUT;
1.15 ratchov 403: #ifdef DEBUG
404: if (debug_level >= 4) {
405: file_dbg(f);
406: dbg_puts(": wok\n");
407: }
408: #endif
1.1 ratchov 409: f->state |= FILE_WOK;
1.12 ratchov 410: f->state |= FILE_WINUSE;
1.4 ratchov 411: for (;;) {
1.2 ratchov 412: p = f->wproc;
1.14 ratchov 413: if (!p)
414: break;
1.15 ratchov 415: #ifdef DEBUG
416: if (debug_level >= 4) {
417: aproc_dbg(p);
418: dbg_puts(": out\n");
419: }
420: #endif
1.14 ratchov 421: if (!p->ops->out(p, NULL))
1.1 ratchov 422: break;
423: }
1.12 ratchov 424: f->state &= ~FILE_WINUSE;
1.7 ratchov 425: }
426: if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
1.15 ratchov 427: #ifdef DEBUG
428: if (debug_level >= 3) {
429: file_dbg(f);
430: dbg_puts(": disconnected\n");
431: }
432: #endif
1.7 ratchov 433: f->state |= (FILE_EOF | FILE_HUP);
1.1 ratchov 434: }
1.4 ratchov 435: if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
1.15 ratchov 436: #ifdef DEBUG
437: if (debug_level >= 3) {
438: file_dbg(f);
439: dbg_puts(": eof\n");
440: }
441: #endif
1.2 ratchov 442: p = f->rproc;
1.12 ratchov 443: if (p) {
444: f->state |= FILE_RINUSE;
1.15 ratchov 445: #ifdef DEBUG
446: if (debug_level >= 3) {
447: aproc_dbg(p);
448: dbg_puts(": eof\n");
449: }
450: #endif
1.2 ratchov 451: p->ops->eof(p, NULL);
1.12 ratchov 452: f->state &= ~FILE_RINUSE;
453: }
1.1 ratchov 454: f->state &= ~FILE_EOF;
455: }
1.4 ratchov 456: if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
1.15 ratchov 457: #ifdef DEBUG
458: if (debug_level >= 3) {
459: file_dbg(f);
460: dbg_puts(": hup\n");
461: }
462: #endif
1.2 ratchov 463: p = f->wproc;
1.12 ratchov 464: if (p) {
465: f->state |= FILE_WINUSE;
1.15 ratchov 466: #ifdef DEBUG
467: if (debug_level >= 3) {
468: aproc_dbg(p);
469: dbg_puts(": hup\n");
470: }
471: #endif
1.2 ratchov 472: p->ops->hup(p, NULL);
1.12 ratchov 473: f->state &= ~FILE_WINUSE;
474: }
1.1 ratchov 475: f->state &= ~FILE_HUP;
476: }
477: fnext = LIST_NEXT(f, entry);
1.4 ratchov 478: if (f->state & FILE_ZOMB)
1.3 ratchov 479: file_del(f);
1.4 ratchov 480: f = fnext;
1.1 ratchov 481: }
482: if (LIST_EMPTY(&file_list)) {
1.15 ratchov 483: #ifdef DEBUG
484: if (debug_level >= 3)
485: dbg_puts("no files anymore...\n");
486: #endif
1.1 ratchov 487: return 0;
488: }
489: return 1;
490: }
491:
1.19 ratchov 492: /*
493: * handler for SIGALRM, invoked periodically
494: */
495: void
496: file_sigalrm(int i)
497: {
498: /* nothing to do, we only want poll() to return EINTR */
499: }
500:
501:
1.1 ratchov 502: void
1.4 ratchov 503: filelist_init(void)
1.1 ratchov 504: {
1.19 ratchov 505: static struct sigaction sa;
506: struct itimerval it;
1.1 ratchov 507: sigset_t set;
508:
509: sigemptyset(&set);
510: (void)sigaddset(&set, SIGPIPE);
511: if (sigprocmask(SIG_BLOCK, &set, NULL))
512: err(1, "sigprocmask");
513: LIST_INIT(&file_list);
1.19 ratchov 514: if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
515: perror("clock_gettime");
516: exit(1);
517: }
518: sa.sa_flags = SA_RESTART;
519: sa.sa_handler = file_sigalrm;
520: sigfillset(&sa.sa_mask);
521: if (sigaction(SIGALRM, &sa, NULL) < 0) {
522: perror("sigaction");
523: exit(1);
524: }
525: it.it_interval.tv_sec = 0;
526: it.it_interval.tv_usec = TIMER_USEC;
527: it.it_value.tv_sec = 0;
528: it.it_value.tv_usec = TIMER_USEC;
529: if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
530: perror("setitimer");
531: exit(1);
532: }
1.12 ratchov 533: timo_init();
1.15 ratchov 534: #ifdef DEBUG
535: dbg_sync = 0;
536: #endif
1.1 ratchov 537: }
538:
539: void
1.4 ratchov 540: filelist_done(void)
1.1 ratchov 541: {
1.19 ratchov 542: struct itimerval it;
1.15 ratchov 543: #ifdef DEBUG
544: struct file *f;
545:
546: if (!LIST_EMPTY(&file_list)) {
547: LIST_FOREACH(f, &file_list, entry) {
548: file_dbg(f);
1.19 ratchov 549: dbg_puts(" not closed\n");
1.15 ratchov 550: }
551: dbg_panic();
552: }
553: dbg_sync = 1;
554: dbg_flush();
555: #endif
1.19 ratchov 556: it.it_value.tv_sec = 0;
557: it.it_value.tv_usec = 0;
558: it.it_interval.tv_sec = 0;
559: it.it_interval.tv_usec = 0;
560: if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
561: perror("setitimer");
562: exit(1);
563: }
1.12 ratchov 564: timo_done();
1.1 ratchov 565: }
566:
1.4 ratchov 567: unsigned
1.14 ratchov 568: file_read(struct file *f, unsigned char *data, unsigned count)
1.4 ratchov 569: {
1.14 ratchov 570: unsigned n;
1.15 ratchov 571: #ifdef DEBUG
1.19 ratchov 572: struct timespec ts0, ts1;
573: long us;
1.15 ratchov 574:
575: if (!(f->state & FILE_ROK)) {
576: file_dbg(f);
577: dbg_puts(": read: bad state\n");
578: dbg_panic();
579: }
1.19 ratchov 580: clock_gettime(CLOCK_MONOTONIC, &ts0);
1.15 ratchov 581: #endif
1.14 ratchov 582: n = f->ops->read(f, data, count);
1.15 ratchov 583: #ifdef DEBUG
1.20 ratchov 584: if (n > 0)
585: f->cycles = 0;
1.19 ratchov 586: clock_gettime(CLOCK_MONOTONIC, &ts1);
587: us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
588: us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
589: if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
1.15 ratchov 590: dbg_puts(f->name);
591: dbg_puts(": read ");
592: dbg_putu(n);
593: dbg_puts(" bytes in ");
594: dbg_putu(us);
595: dbg_puts("us\n");
596: }
597: #endif
1.14 ratchov 598: return n;
1.4 ratchov 599: }
1.1 ratchov 600:
601: unsigned
1.14 ratchov 602: file_write(struct file *f, unsigned char *data, unsigned count)
1.1 ratchov 603: {
1.14 ratchov 604: unsigned n;
1.15 ratchov 605: #ifdef DEBUG
1.19 ratchov 606: struct timespec ts0, ts1;
607: long us;
1.15 ratchov 608:
609: if (!(f->state & FILE_WOK)) {
610: file_dbg(f);
611: dbg_puts(": write: bad state\n");
612: dbg_panic();
613: }
1.19 ratchov 614: clock_gettime(CLOCK_MONOTONIC, &ts0);
1.15 ratchov 615: #endif
1.14 ratchov 616: n = f->ops->write(f, data, count);
1.15 ratchov 617: #ifdef DEBUG
1.20 ratchov 618: if (n > 0)
619: f->cycles = 0;
1.19 ratchov 620: clock_gettime(CLOCK_MONOTONIC, &ts1);
621: us = 1000000L * (ts1.tv_sec - ts0.tv_sec);
622: us += (ts1.tv_nsec - ts0.tv_nsec) / 1000;
623: if (debug_level >= 4 || (debug_level >= 2 && us >= 5000)) {
1.15 ratchov 624: dbg_puts(f->name);
625: dbg_puts(": wrote ");
626: dbg_putu(n);
627: dbg_puts(" bytes in ");
628: dbg_putu(us);
629: dbg_puts("us\n");
630: }
631: #endif
1.14 ratchov 632: return n;
1.1 ratchov 633: }
634:
635: void
636: file_eof(struct file *f)
637: {
1.4 ratchov 638: struct aproc *p;
639:
1.15 ratchov 640: #ifdef DEBUG
641: if (debug_level >= 3) {
642: file_dbg(f);
643: dbg_puts(": eof requested\n");
644: }
645: #endif
1.12 ratchov 646: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 647: p = f->rproc;
1.12 ratchov 648: if (p) {
649: f->state |= FILE_RINUSE;
1.15 ratchov 650: #ifdef DEBUG
651: if (debug_level >= 3) {
652: aproc_dbg(p);
653: dbg_puts(": eof\n");
654: }
655: #endif
1.4 ratchov 656: p->ops->eof(p, NULL);
1.12 ratchov 657: f->state &= ~FILE_RINUSE;
658: }
1.4 ratchov 659: if (f->state & FILE_ZOMB)
660: file_del(f);
661: } else {
662: f->state &= ~FILE_ROK;
663: f->state |= FILE_EOF;
664: }
1.1 ratchov 665: }
666:
667: void
668: file_hup(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(": hup requested\n");
676: }
677: #endif
1.12 ratchov 678: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4 ratchov 679: p = f->wproc;
1.12 ratchov 680: if (p) {
681: f->state |= FILE_WINUSE;
1.15 ratchov 682: #ifdef DEBUG
683: if (debug_level >= 3) {
684: aproc_dbg(p);
685: dbg_puts(": hup\n");
686: }
687: #endif
1.4 ratchov 688: p->ops->hup(p, NULL);
1.12 ratchov 689: f->state &= ~FILE_WINUSE;
690: }
1.4 ratchov 691: if (f->state & FILE_ZOMB)
692: file_del(f);
693: } else {
694: f->state &= ~FILE_WOK;
695: f->state |= FILE_HUP;
1.9 ratchov 696: }
697: }
698:
699: void
700: file_close(struct file *f)
701: {
702: struct aproc *p;
703:
1.15 ratchov 704: #ifdef DEBUG
705: if (debug_level >= 3) {
706: file_dbg(f);
707: dbg_puts(": closing\n");
708: }
709: #endif
1.18 ratchov 710: if (f->wproc == NULL && f->rproc == NULL)
711: f->state |= FILE_ZOMB;
1.12 ratchov 712: if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.9 ratchov 713: p = f->rproc;
1.12 ratchov 714: if (p) {
715: f->state |= FILE_RINUSE;
1.15 ratchov 716: #ifdef DEBUG
717: if (debug_level >= 3) {
718: aproc_dbg(p);
719: dbg_puts(": eof\n");
720: }
721: #endif
1.9 ratchov 722: p->ops->eof(p, NULL);
1.12 ratchov 723: f->state &= ~FILE_RINUSE;
724: }
1.9 ratchov 725: p = f->wproc;
1.12 ratchov 726: if (p) {
727: f->state |= FILE_WINUSE;
1.15 ratchov 728: #ifdef DEBUG
729: if (debug_level >= 3) {
730: aproc_dbg(p);
731: dbg_puts(": hup\n");
732: }
733: #endif
1.9 ratchov 734: p->ops->hup(p, NULL);
1.12 ratchov 735: f->state &= ~FILE_WINUSE;
736: }
1.9 ratchov 737: if (f->state & FILE_ZOMB)
738: file_del(f);
739: } else {
740: f->state &= ~(FILE_ROK | FILE_WOK);
741: f->state |= (FILE_EOF | FILE_HUP);
1.4 ratchov 742: }
1.1 ratchov 743: }