[BACK]Return to file.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / aucat

Annotation of src/usr.bin/aucat/file.c, Revision 1.13

1.13    ! ratchov     1: /*     $OpenBSD: file.c,v 1.12 2009/07/25 08:44:27 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:  *
                     27:  *     anoncvs@moule.caoua.org:/cvs/midish/timo.c rev 1.16
                     28:  *
                     29:  * A timeout is used to schedule the call of a routine (the callback)
                     30:  * there is a global list of timeouts that is processed inside the
                     31:  * event loop. Timeouts work as follows:
                     32:  *
                     33:  *     first the timo structure must be initialized with timo_set()
                     34:  *
                     35:  *     then the timeout is scheduled (only once) with timo_add()
                     36:  *
                     37:  *     if the timeout expires, the call-back is called; then it can
                     38:  *     be scheduled again if needed. It's OK to reschedule it again
                     39:  *     from the callback
                     40:  *
                     41:  *     the timeout can be aborted with timo_del(), it is OK to try to
                     42:  *     abort a timout that has expired
                     43:  *
1.1       ratchov    44:  */
1.12      ratchov    45:
1.1       ratchov    46: #include <sys/types.h>
                     47:
                     48: #include <err.h>
                     49: #include <errno.h>
                     50: #include <fcntl.h>
                     51: #include <poll.h>
                     52: #include <signal.h>
                     53: #include <stdio.h>
                     54: #include <stdlib.h>
                     55:
1.13    ! ratchov    56: #include "abuf.h"
        !            57: #include "aproc.h"
1.1       ratchov    58: #include "conf.h"
                     59: #include "file.h"
                     60:
                     61: #define MAXFDS 100
                     62:
1.4       ratchov    63: extern struct fileops listen_ops, pipe_ops;
1.12      ratchov    64:
                     65: struct timeval file_tv;
1.1       ratchov    66: struct filelist file_list;
1.12      ratchov    67: struct timo *timo_queue;
                     68: unsigned timo_abstime;
                     69:
                     70: /*
                     71:  * initialise a timeout structure, arguments are callback and argument
                     72:  * that will be passed to the callback
                     73:  */
                     74: void
                     75: timo_set(struct timo *o, void (*cb)(void *), void *arg)
                     76: {
                     77:        o->cb = cb;
                     78:        o->arg = arg;
                     79:        o->set = 0;
                     80: }
                     81:
                     82: /*
                     83:  * schedule the callback in 'delta' 24-th of microseconds. The timeout
                     84:  * must not be already scheduled
                     85:  */
                     86: void
                     87: timo_add(struct timo *o, unsigned delta)
                     88: {
                     89:        struct timo **i;
                     90:        unsigned val;
                     91:        int diff;
                     92:
                     93: #ifdef DEBUG
                     94:        if (o->set) {
                     95:                fprintf(stderr, "timo_set: already set\n");
                     96:                abort();
                     97:        }
                     98:        if (delta == 0) {
                     99:                fprintf(stderr, "timo_set: zero timeout is evil\n");
                    100:                abort();
                    101:        }
                    102: #endif
                    103:        val = timo_abstime + delta;
                    104:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
                    105:                diff = (*i)->val - val;
                    106:                if (diff > 0) {
                    107:                        break;
                    108:                }
                    109:        }
                    110:        o->set = 1;
                    111:        o->val = val;
                    112:        o->next = *i;
                    113:        *i = o;
                    114: }
                    115:
                    116: /*
                    117:  * abort a scheduled timeout
                    118:  */
                    119: void
                    120: timo_del(struct timo *o)
                    121: {
                    122:        struct timo **i;
                    123:
                    124:        for (i = &timo_queue; *i != NULL; i = &(*i)->next) {
                    125:                if (*i == o) {
                    126:                        *i = o->next;
                    127:                        o->set = 0;
                    128:                        return;
                    129:                }
                    130:        }
                    131:        DPRINTF("timo_del: not found\n");
                    132: }
                    133:
                    134: /*
                    135:  * routine to be called by the timer when 'delta' 24-th of microsecond
                    136:  * elapsed. This routine updates time referece used by timeouts and
                    137:  * calls expired timeouts
                    138:  */
                    139: void
                    140: timo_update(unsigned delta)
                    141: {
                    142:        struct timo *to;
                    143:        int diff;
                    144:
                    145:        /*
                    146:         * update time reference
                    147:         */
                    148:        timo_abstime += delta;
                    149:
                    150:        /*
                    151:         * remove from the queue and run expired timeouts
                    152:         */
                    153:        while (timo_queue != NULL) {
                    154:                /*
                    155:                 * there is no overflow here because + and - are
                    156:                 * modulo 2^32, they are the same for both signed and
                    157:                 * unsigned integers
                    158:                 */
                    159:                diff = timo_queue->val - timo_abstime;
                    160:                if (diff > 0)
                    161:                        break;
                    162:                to = timo_queue;
                    163:                timo_queue = to->next;
                    164:                to->set = 0;
                    165:                to->cb(to->arg);
                    166:        }
                    167: }
                    168:
                    169: /*
                    170:  * initialize timeout queue
                    171:  */
                    172: void
                    173: timo_init(void)
                    174: {
                    175:        timo_queue = NULL;
                    176:        timo_abstime = 0;
                    177: }
                    178:
                    179: /*
                    180:  * destroy timeout queue
                    181:  */
                    182: void
                    183: timo_done(void)
                    184: {
                    185:        if (timo_queue != NULL) {
                    186:                fprintf(stderr, "timo_done: timo_queue not empty!\n");
                    187:                abort();
                    188:        }
                    189:        timo_queue = (struct timo *)0xdeadbeef;
                    190: }
1.1       ratchov   191:
1.3       ratchov   192: void
                    193: file_dprint(int n, struct file *f)
                    194: {
1.6       ratchov   195: #ifdef DEBUG
1.4       ratchov   196:        if (debug_level < n)
                    197:                return;
                    198:        fprintf(stderr, "%s:%s <", f->ops->name, f->name);
                    199:        if (f->state & FILE_ROK)
                    200:                fprintf(stderr, "ROK");
                    201:        if (f->state & FILE_WOK)
                    202:                fprintf(stderr, "WOK");
                    203:        if (f->state & FILE_EOF)
                    204:                fprintf(stderr, "EOF");
                    205:        if (f->state & FILE_HUP)
                    206:                fprintf(stderr, "HUP");
                    207:        fprintf(stderr, ">");
1.6       ratchov   208: #endif
1.3       ratchov   209: }
                    210:
1.1       ratchov   211: struct file *
1.4       ratchov   212: file_new(struct fileops *ops, char *name, unsigned nfds)
1.1       ratchov   213: {
                    214:        struct file *f;
                    215:
                    216:        LIST_FOREACH(f, &file_list, entry)
1.4       ratchov   217:                nfds += f->ops->nfds(f);
1.11      ratchov   218:        if (nfds > MAXFDS) {
                    219:                DPRINTF("file_new: %s: too many polled files\n", name);
                    220:                return NULL;
                    221:        }
1.4       ratchov   222:        f = malloc(ops->size);
1.1       ratchov   223:        if (f == NULL)
1.4       ratchov   224:                err(1, "file_new: %s", ops->name);
                    225:        f->ops = ops;
1.1       ratchov   226:        f->name = name;
                    227:        f->state = 0;
                    228:        f->rproc = NULL;
                    229:        f->wproc = NULL;
                    230:        LIST_INSERT_HEAD(&file_list, f, entry);
1.4       ratchov   231:        DPRINTF("file_new: %s:%s\n", ops->name, f->name);
1.1       ratchov   232:        return f;
                    233: }
                    234:
                    235: void
                    236: file_del(struct file *f)
                    237: {
1.3       ratchov   238:        DPRINTF("file_del: ");
                    239:        file_dprint(1, f);
1.12      ratchov   240:        if (f->state & (FILE_RINUSE | FILE_WINUSE)) {
1.4       ratchov   241:                DPRINTF(": delayed\n");
                    242:                f->state |= FILE_ZOMB;
                    243:                return;
                    244:        } else {
                    245:                DPRINTF(": immediate\n");
                    246:                LIST_REMOVE(f, entry);
                    247:                f->ops->close(f);
                    248:                free(f);
                    249:        }
1.1       ratchov   250: }
                    251:
                    252: int
                    253: file_poll(void)
                    254: {
1.4       ratchov   255:        nfds_t nfds, n;
                    256:        short events, revents;
1.1       ratchov   257:        struct pollfd pfds[MAXFDS];
                    258:        struct file *f, *fnext;
1.2       ratchov   259:        struct aproc *p;
1.12      ratchov   260:        struct timeval tv;
                    261:        long delta_usec;
                    262:        int timo;
1.1       ratchov   263:
1.4       ratchov   264:        /*
1.13    ! ratchov   265:         * Fill the pfds[] array with files that are blocked on reading
        !           266:         * and/or writing, skipping those that are just waiting.
1.4       ratchov   267:         */
                    268:        DPRINTFN(4, "file_poll:");
1.1       ratchov   269:        nfds = 0;
                    270:        LIST_FOREACH(f, &file_list, entry) {
1.4       ratchov   271:                events = 0;
                    272:                if (f->rproc && !(f->state & FILE_ROK))
                    273:                        events |= POLLIN;
                    274:                if (f->wproc && !(f->state & FILE_WOK))
                    275:                        events |= POLLOUT;
                    276:                DPRINTFN(4, " %s(%x)", f->name, events);
                    277:                n = f->ops->pollfd(f, pfds + nfds, events);
                    278:                if (n == 0) {
1.1       ratchov   279:                        f->pfd = NULL;
                    280:                        continue;
                    281:                }
1.4       ratchov   282:                f->pfd = pfds + nfds;
                    283:                nfds += n;
1.1       ratchov   284:        }
1.4       ratchov   285:        DPRINTFN(4, "\n");
1.12      ratchov   286:        if (debug_level >= 4) {
                    287:                DPRINTF("file_poll: pfds[] =");
                    288:                for (n = 0; n < nfds; n++)
                    289:                        DPRINTF(" %x", pfds[n].events);
                    290:                DPRINTF("\n");
1.1       ratchov   291:        }
                    292:        if (LIST_EMPTY(&file_list)) {
                    293:                DPRINTF("file_poll: nothing to do...\n");
                    294:                return 0;
                    295:        }
1.9       ratchov   296:        if (nfds > 0) {
1.12      ratchov   297:                if (timo_queue) {
                    298:                        timo = (timo_queue->val - timo_abstime) / (2 * 1000);
                    299:                        if (timo == 0)
                    300:                                timo = 1;
                    301:                } else
                    302:                        timo = -1;
                    303:                if (poll(pfds, nfds, timo) < 0) {
1.9       ratchov   304:                        if (errno == EINTR)
                    305:                                return 1;
                    306:                        err(1, "file_poll: poll failed");
                    307:                }
1.12      ratchov   308:                gettimeofday(&tv, NULL);
                    309:                delta_usec = 1000000L * (tv.tv_sec - file_tv.tv_sec);
                    310:                delta_usec += tv.tv_usec - file_tv.tv_usec;
                    311:                if (delta_usec > 0) {
                    312:                        file_tv = tv;
                    313:                        timo_update(delta_usec);
                    314:                }
1.1       ratchov   315:        }
1.4       ratchov   316:        f = LIST_FIRST(&file_list);
                    317:        while (f != LIST_END(&file_list)) {
                    318:                if (f->pfd == NULL) {
                    319:                        f = LIST_NEXT(f, entry);
1.1       ratchov   320:                        continue;
1.4       ratchov   321:                }
                    322:                revents = f->ops->revents(f, f->pfd);
                    323:                if (!(f->state & FILE_ZOMB) && (revents & POLLIN)) {
                    324:                        revents &= ~POLLIN;
1.1       ratchov   325:                        f->state |= FILE_ROK;
                    326:                        DPRINTFN(3, "file_poll: %s rok\n", f->name);
1.12      ratchov   327:                        f->state |= FILE_RINUSE;
1.4       ratchov   328:                        for (;;) {
1.2       ratchov   329:                                p = f->rproc;
                    330:                                if (!p || !p->ops->in(p, NULL))
1.1       ratchov   331:                                        break;
                    332:                        }
1.12      ratchov   333:                        f->state &= ~FILE_RINUSE;
1.1       ratchov   334:                }
1.4       ratchov   335:                if (!(f->state & FILE_ZOMB) && (revents & POLLOUT)) {
                    336:                        revents &= ~POLLOUT;
1.1       ratchov   337:                        f->state |= FILE_WOK;
                    338:                        DPRINTFN(3, "file_poll: %s wok\n", f->name);
1.12      ratchov   339:                        f->state |= FILE_WINUSE;
1.4       ratchov   340:                        for (;;) {
1.2       ratchov   341:                                p = f->wproc;
                    342:                                if (!p || !p->ops->out(p, NULL))
1.1       ratchov   343:                                        break;
                    344:                        }
1.12      ratchov   345:                        f->state &= ~FILE_WINUSE;
1.7       ratchov   346:                }
                    347:                if (!(f->state & FILE_ZOMB) && (revents & POLLHUP)) {
                    348:                        DPRINTFN(2, "file_poll: %s: disconnected\n", f->name);
                    349:                        f->state |= (FILE_EOF | FILE_HUP);
1.1       ratchov   350:                }
1.4       ratchov   351:                if (!(f->state & FILE_ZOMB) && (f->state & FILE_EOF)) {
1.1       ratchov   352:                        DPRINTFN(2, "file_poll: %s: eof\n", f->name);
1.2       ratchov   353:                        p = f->rproc;
1.12      ratchov   354:                        if (p) {
                    355:                                f->state |= FILE_RINUSE;
1.2       ratchov   356:                                p->ops->eof(p, NULL);
1.12      ratchov   357:                                f->state &= ~FILE_RINUSE;
                    358:                        }
1.1       ratchov   359:                        f->state &= ~FILE_EOF;
                    360:                }
1.4       ratchov   361:                if (!(f->state & FILE_ZOMB) && (f->state & FILE_HUP)) {
1.1       ratchov   362:                        DPRINTFN(2, "file_poll: %s hup\n", f->name);
1.2       ratchov   363:                        p = f->wproc;
1.12      ratchov   364:                        if (p) {
                    365:                                f->state |= FILE_WINUSE;
1.2       ratchov   366:                                p->ops->hup(p, NULL);
1.12      ratchov   367:                                f->state &= ~FILE_WINUSE;
                    368:                        }
1.1       ratchov   369:                        f->state &= ~FILE_HUP;
                    370:                }
                    371:                fnext = LIST_NEXT(f, entry);
1.4       ratchov   372:                if (f->state & FILE_ZOMB)
1.3       ratchov   373:                        file_del(f);
1.4       ratchov   374:                f = fnext;
1.1       ratchov   375:        }
                    376:        if (LIST_EMPTY(&file_list)) {
                    377:                DPRINTFN(2, "file_poll: terminated\n");
                    378:                return 0;
                    379:        }
                    380:        return 1;
                    381: }
                    382:
                    383: void
1.4       ratchov   384: filelist_init(void)
1.1       ratchov   385: {
                    386:        sigset_t set;
                    387:
                    388:        sigemptyset(&set);
                    389:        (void)sigaddset(&set, SIGPIPE);
                    390:        if (sigprocmask(SIG_BLOCK, &set, NULL))
                    391:                err(1, "sigprocmask");
                    392:        LIST_INIT(&file_list);
1.12      ratchov   393:        timo_init();
                    394:        gettimeofday(&file_tv, NULL);
1.1       ratchov   395: }
                    396:
                    397: void
1.4       ratchov   398: filelist_done(void)
1.1       ratchov   399: {
                    400:        struct file *f;
                    401:
1.12      ratchov   402:        timo_done();
1.1       ratchov   403:        if (!LIST_EMPTY(&file_list)) {
1.4       ratchov   404:                fprintf(stderr, "filelist_done: list not empty:\n");
1.1       ratchov   405:                LIST_FOREACH(f, &file_list, entry) {
1.4       ratchov   406:                        fprintf(stderr, "\t");
                    407:                        file_dprint(0, f);
                    408:                        fprintf(stderr, "\n");
1.1       ratchov   409:                }
1.4       ratchov   410:                abort();
1.1       ratchov   411:        }
                    412: }
                    413:
1.4       ratchov   414: /*
1.13    ! ratchov   415:  * Close all listening sockets.
1.4       ratchov   416:  *
                    417:  * XXX: remove this
                    418:  */
                    419: void
                    420: filelist_unlisten(void)
1.1       ratchov   421: {
1.4       ratchov   422:        struct file *f, *fnext;
1.10      ratchov   423:
1.4       ratchov   424:        for (f = LIST_FIRST(&file_list); f != NULL; f = fnext) {
                    425:                fnext = LIST_NEXT(f, entry);
                    426:                if (f->ops == &listen_ops)
                    427:                        file_del(f);
1.1       ratchov   428:        }
                    429: }
                    430:
1.4       ratchov   431: unsigned
                    432: file_read(struct file *file, unsigned char *data, unsigned count)
                    433: {
                    434:        return file->ops->read(file, data, count);
                    435: }
1.1       ratchov   436:
                    437: unsigned
                    438: file_write(struct file *file, unsigned char *data, unsigned count)
                    439: {
1.4       ratchov   440:        return file->ops->write(file, data, count);
1.1       ratchov   441: }
                    442:
                    443: void
                    444: file_eof(struct file *f)
                    445: {
1.4       ratchov   446:        struct aproc *p;
                    447:
1.12      ratchov   448:        if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4       ratchov   449:                DPRINTFN(2, "file_eof: %s: immediate\n", f->name);
                    450:                p = f->rproc;
1.12      ratchov   451:                if (p) {
                    452:                        f->state |= FILE_RINUSE;
1.4       ratchov   453:                        p->ops->eof(p, NULL);
1.12      ratchov   454:                        f->state &= ~FILE_RINUSE;
                    455:                }
1.4       ratchov   456:                if (f->state & FILE_ZOMB)
                    457:                        file_del(f);
                    458:        } else {
                    459:                DPRINTFN(2, "file_eof: %s: delayed\n", f->name);
                    460:                f->state &= ~FILE_ROK;
                    461:                f->state |= FILE_EOF;
                    462:        }
1.1       ratchov   463: }
                    464:
                    465: void
                    466: file_hup(struct file *f)
                    467: {
1.4       ratchov   468:        struct aproc *p;
                    469:
1.12      ratchov   470:        if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.4       ratchov   471:                DPRINTFN(2, "file_hup: %s immediate\n", f->name);
                    472:                p = f->wproc;
1.12      ratchov   473:                if (p) {
                    474:                        f->state |= FILE_WINUSE;
1.4       ratchov   475:                        p->ops->hup(p, NULL);
1.12      ratchov   476:                        f->state &= ~FILE_WINUSE;
                    477:                }
1.4       ratchov   478:                if (f->state & FILE_ZOMB)
                    479:                        file_del(f);
                    480:        } else {
                    481:                DPRINTFN(2, "file_hup: %s: delayed\n", f->name);
                    482:                f->state &= ~FILE_WOK;
                    483:                f->state |= FILE_HUP;
1.9       ratchov   484:        }
                    485: }
                    486:
                    487: void
                    488: file_close(struct file *f)
                    489: {
                    490:        struct aproc *p;
                    491:
1.12      ratchov   492:        if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) {
1.9       ratchov   493:                DPRINTFN(2, "file_close: %s: immediate\n", f->name);
                    494:                p = f->rproc;
1.12      ratchov   495:                if (p) {
                    496:                        f->state |= FILE_RINUSE;
1.9       ratchov   497:                        p->ops->eof(p, NULL);
1.12      ratchov   498:                        f->state &= ~FILE_RINUSE;
                    499:                }
1.9       ratchov   500:                p = f->wproc;
1.12      ratchov   501:                if (p) {
                    502:                        f->state |= FILE_WINUSE;
1.9       ratchov   503:                        p->ops->hup(p, NULL);
1.12      ratchov   504:                        f->state &= ~FILE_WINUSE;
                    505:                }
1.9       ratchov   506:                if (f->state & FILE_ZOMB)
                    507:                        file_del(f);
                    508:        } else {
                    509:                DPRINTFN(2, "file_close: %s: delayed\n", f->name);
                    510:                f->state &= ~(FILE_ROK | FILE_WOK);
                    511:                f->state |= (FILE_EOF | FILE_HUP);
1.4       ratchov   512:        }
1.1       ratchov   513: }