Annotation of embedaddon/sudo/common/event.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * Copyright (c) 2013-2014 Todd C. Miller <Todd.Miller@courtesan.com>
        !             3:  *
        !             4:  * Permission to use, copy, modify, and distribute this software for any
        !             5:  * purpose with or without fee is hereby granted, provided that the above
        !             6:  * copyright notice and this permission notice appear in all copies.
        !             7:  *
        !             8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !             9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            15:  */
        !            16: 
        !            17: #include <config.h>
        !            18: 
        !            19: #include <sys/types.h>
        !            20: #include <sys/time.h>
        !            21: #include <stdio.h>
        !            22: #ifdef STDC_HEADERS
        !            23: # include <stdlib.h>
        !            24: # include <stddef.h>
        !            25: #else
        !            26: # ifdef HAVE_STDLIB_H
        !            27: #  include <stdlib.h>
        !            28: # endif
        !            29: #endif /* STDC_HEADERS */
        !            30: #ifdef HAVE_STDBOOL_H
        !            31: # include <stdbool.h>
        !            32: #else
        !            33: # include "compat/stdbool.h"
        !            34: #endif /* HAVE_STDBOOL_H */
        !            35: #ifdef HAVE_STRING_H
        !            36: # include <string.h>
        !            37: #endif /* HAVE_STRING_H */
        !            38: #ifdef HAVE_STRINGS_H
        !            39: # include <strings.h>
        !            40: #endif /* HAVE_STRINGS_H */
        !            41: #ifdef HAVE_UNISTD_H
        !            42: # include <unistd.h>
        !            43: #endif /* HAVE_UNISTD_H */
        !            44: #include <errno.h>
        !            45: 
        !            46: #include "missing.h"
        !            47: #include "alloc.h"
        !            48: #include "fatal.h"
        !            49: #include "sudo_debug.h"
        !            50: #include "sudo_event.h"
        !            51: #include "sudo_util.h"
        !            52: 
        !            53: /* XXX - use non-exiting allocators? */
        !            54: 
        !            55: struct sudo_event_base *
        !            56: sudo_ev_base_alloc(void)
        !            57: {
        !            58:     struct sudo_event_base *base;
        !            59:     debug_decl(sudo_ev_base_alloc, SUDO_DEBUG_EVENT)
        !            60: 
        !            61:     base = ecalloc(1, sizeof(*base));
        !            62:     TAILQ_INIT(&base->events);
        !            63:     TAILQ_INIT(&base->timeouts);
        !            64:     if (sudo_ev_base_alloc_impl(base) != 0) {
        !            65:        efree(base);
        !            66:        base = NULL;
        !            67:     }
        !            68: 
        !            69:     debug_return_ptr(base);
        !            70: }
        !            71: 
        !            72: void
        !            73: sudo_ev_base_free(struct sudo_event_base *base)
        !            74: {
        !            75:     struct sudo_event *ev, *next;
        !            76:     debug_decl(sudo_ev_base_free, SUDO_DEBUG_EVENT)
        !            77: 
        !            78:     /* Remove any existing events before freeing the base. */
        !            79:     TAILQ_FOREACH_SAFE(ev, &base->events, entries, next) {
        !            80:        sudo_ev_del(base, ev);
        !            81:     }
        !            82:     sudo_ev_base_free_impl(base);
        !            83:     efree(base);
        !            84: 
        !            85:     debug_return;
        !            86: }
        !            87: 
        !            88: struct sudo_event *
        !            89: sudo_ev_alloc(int fd, short events, sudo_ev_callback_t callback, void *closure)
        !            90: {
        !            91:     struct sudo_event *ev;
        !            92:     debug_decl(sudo_ev_alloc, SUDO_DEBUG_EVENT)
        !            93: 
        !            94:     /* XXX - sanity check events value */
        !            95: 
        !            96:     ev = ecalloc(1, sizeof(*ev));
        !            97:     ev->fd = fd;
        !            98:     ev->events = events;
        !            99:     ev->pfd_idx = -1;
        !           100:     ev->callback = callback;
        !           101:     ev->closure = closure;
        !           102: 
        !           103:     debug_return_ptr(ev);
        !           104: }
        !           105: 
        !           106: void
        !           107: sudo_ev_free(struct sudo_event *ev)
        !           108: {
        !           109:     debug_decl(sudo_ev_free, SUDO_DEBUG_EVENT)
        !           110: 
        !           111:     /* Make sure ev is not in use before freeing it. */
        !           112:     if (ISSET(ev->flags, SUDO_EVQ_INSERTED))
        !           113:        (void)sudo_ev_del(NULL, ev);
        !           114:     free(ev);
        !           115:     debug_return;
        !           116: }
        !           117: 
        !           118: int
        !           119: sudo_ev_add(struct sudo_event_base *base, struct sudo_event *ev,
        !           120:     struct timeval *timo, bool tohead)
        !           121: {
        !           122:     debug_decl(sudo_ev_add, SUDO_DEBUG_EVENT)
        !           123: 
        !           124:     /* If no base specified, use existing one. */
        !           125:     if (base == NULL) {
        !           126:        if (ev->base == NULL) {
        !           127:            sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: no base specified",
        !           128:                __func__);
        !           129:            debug_return_int(-1);
        !           130:        }
        !           131:        base = ev->base;
        !           132:     }
        !           133: 
        !           134:     /* Only add new events to the events list. */
        !           135:     if (ISSET(ev->flags, SUDO_EVQ_INSERTED)) {
        !           136:        /* If event no longer has a timeout, remove from timeouts queue. */
        !           137:        if (timo == NULL && ISSET(ev->flags, SUDO_EVQ_TIMEOUTS)) {
        !           138:            sudo_debug_printf(SUDO_DEBUG_INFO,
        !           139:                "%s: removing event %p from timeouts queue", __func__, ev);
        !           140:            CLR(ev->flags, SUDO_EVQ_TIMEOUTS);
        !           141:            TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
        !           142:        }
        !           143:     } else {
        !           144:        /* Add event to the base. */
        !           145:        sudo_debug_printf(SUDO_DEBUG_INFO, "%s: adding event %p to base %p",
        !           146:            __func__, ev, base);
        !           147:        if (ev->events & (SUDO_EV_READ|SUDO_EV_WRITE)) {
        !           148:            if (sudo_ev_add_impl(base, ev) != 0)
        !           149:                debug_return_int(-1);
        !           150:        }
        !           151:        ev->base = base;
        !           152:        if (tohead) {
        !           153:            TAILQ_INSERT_HEAD(&base->events, ev, entries);
        !           154:        } else {
        !           155:            TAILQ_INSERT_TAIL(&base->events, ev, entries);
        !           156:        }
        !           157:        SET(ev->flags, SUDO_EVQ_INSERTED);
        !           158:     }
        !           159:     /* Timeouts can be changed for existing events. */
        !           160:     if (timo != NULL) {
        !           161:        struct sudo_event *evtmp;
        !           162:        if (ISSET(ev->flags, SUDO_EVQ_TIMEOUTS)) {
        !           163:            /* Remove from timeouts list, then add back. */
        !           164:            TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
        !           165:        }
        !           166:        /* Convert to absolute time and insert in sorted order; O(n). */
        !           167:        gettimeofday(&ev->timeout, NULL);
        !           168:        ev->timeout.tv_sec += timo->tv_sec;
        !           169:        ev->timeout.tv_usec += timo->tv_usec;
        !           170:        TAILQ_FOREACH(evtmp, &base->timeouts, timeouts_entries) {
        !           171:            if (sudo_timevalcmp(timo, &evtmp->timeout, <))
        !           172:                break;
        !           173:        }
        !           174:        if (evtmp != NULL) {
        !           175:            TAILQ_INSERT_BEFORE(evtmp, ev, timeouts_entries);
        !           176:        } else {
        !           177:            TAILQ_INSERT_TAIL(&base->timeouts, ev, timeouts_entries);
        !           178:        }
        !           179:        SET(ev->flags, SUDO_EVQ_TIMEOUTS);
        !           180:     }
        !           181:     debug_return_int(0);
        !           182: }
        !           183: 
        !           184: int
        !           185: sudo_ev_del(struct sudo_event_base *base, struct sudo_event *ev)
        !           186: {
        !           187:     debug_decl(sudo_ev_del, SUDO_DEBUG_EVENT)
        !           188: 
        !           189:     /* Make sure event is really in the queue. */
        !           190:     if (!ISSET(ev->flags, SUDO_EVQ_INSERTED)) {
        !           191:        sudo_debug_printf(SUDO_DEBUG_INFO, "%s: event %p not in queue",
        !           192:            __func__, ev);
        !           193:        debug_return_int(0);
        !           194:     }
        !           195: 
        !           196:     /* Check for event base mismatch, if one is specified. */
        !           197:     if (base == NULL) {
        !           198:        if (ev->base == NULL) {
        !           199:            sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: no base specified",
        !           200:                __func__);
        !           201:            debug_return_int(-1);
        !           202:        }
        !           203:        base = ev->base;
        !           204:     } else if (base != ev->base) {
        !           205:        sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: mismatch base %p, ev->base %p",
        !           206:            __func__, base, ev->base);
        !           207:        debug_return_int(-1);
        !           208:     }
        !           209: 
        !           210:     sudo_debug_printf(SUDO_DEBUG_INFO, "%s: removing event %p from base %p",
        !           211:        __func__, ev, base);
        !           212: 
        !           213:     /* Call backend. */
        !           214:     if (ev->events & (SUDO_EV_READ|SUDO_EV_WRITE)) {
        !           215:        if (sudo_ev_del_impl(base, ev) != 0)
        !           216:            debug_return_int(-1);
        !           217:     }
        !           218: 
        !           219:     /* Unlink from event list. */
        !           220:     TAILQ_REMOVE(&base->events, ev, entries);
        !           221: 
        !           222:     /* Unlink from timeouts list. */
        !           223:     if (ISSET(ev->flags, SUDO_EVQ_TIMEOUTS))
        !           224:        TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
        !           225: 
        !           226:     /* Unlink from active list and update base pointers as needed. */
        !           227:     if (ISSET(ev->flags, SUDO_EVQ_ACTIVE))
        !           228:        TAILQ_REMOVE(&base->active, ev, active_entries);
        !           229: 
        !           230:     /* Mark event unused. */
        !           231:     ev->flags = 0;
        !           232:     ev->pfd_idx = -1;
        !           233: 
        !           234:     debug_return_int(0);
        !           235: }
        !           236: 
        !           237: /*
        !           238:  * Run main event loop.
        !           239:  * Returns 0 on success, 1 if no events registered  and -1 on error 
        !           240:  */
        !           241: int
        !           242: sudo_ev_loop(struct sudo_event_base *base, int flags)
        !           243: {
        !           244:     struct timeval now;
        !           245:     struct sudo_event *ev;
        !           246:     int nready, rc = 0;
        !           247:     debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
        !           248: 
        !           249:     /*
        !           250:      * If sudo_ev_loopexit() was called when events were not running
        !           251:      * the next invocation of sudo_ev_loop() only runs once.
        !           252:      * All other base flags are ignored unless we are running events.
        !           253:      */
        !           254:     if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT))
        !           255:        SET(flags, SUDO_EVLOOP_ONCE);
        !           256:     base->flags = 0;
        !           257: 
        !           258:     for (;;) {
        !           259: rescan:
        !           260:        /* Make sure we have some events. */
        !           261:        if (TAILQ_EMPTY(&base->events)) {
        !           262:            rc = 1;
        !           263:            break;
        !           264:        }
        !           265: 
        !           266:        /* Call backend to scan for I/O events. */
        !           267:        TAILQ_INIT(&base->active);
        !           268:        nready = sudo_ev_scan_impl(base, flags);
        !           269:        switch (nready) {
        !           270:        case -1:
        !           271:            if (errno == EINTR || errno == ENOMEM)
        !           272:                continue;
        !           273:            rc = -1;
        !           274:            goto done;
        !           275:        case 0:
        !           276:            /* Timed out, activate timeout events. */
        !           277:            gettimeofday(&now, NULL);
        !           278:            while ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
        !           279:                if (sudo_timevalcmp(&ev->timeout, &now, >))
        !           280:                    break;
        !           281:                /* Remove from timeouts list. */
        !           282:                CLR(ev->flags, SUDO_EVQ_TIMEOUTS);
        !           283:                TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
        !           284:                /* Make event active. */
        !           285:                ev->revents = SUDO_EV_TIMEOUT;
        !           286:                TAILQ_INSERT_TAIL(&base->active, ev, active_entries);
        !           287:                SET(ev->flags, SUDO_EVQ_ACTIVE);
        !           288:            }
        !           289:            if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
        !           290:                /* If nonblocking, return immediately if no active events. */
        !           291:                if (TAILQ_EMPTY(&base->active))
        !           292:                    goto done;
        !           293:            }
        !           294:            break;
        !           295:        default:
        !           296:            /* I/O events active, sudo_ev_scan_impl() already added them. */
        !           297:            break;
        !           298:        }
        !           299: 
        !           300:        /*
        !           301:         * Service each event in the active queue.
        !           302:         * We store the current event pointer in the base so that
        !           303:         * it can be cleared by sudo_ev_del().  This prevents a use
        !           304:         * after free if the callback frees its own event.
        !           305:         */
        !           306:        while ((ev = TAILQ_FIRST(&base->active)) != NULL) {
        !           307:            /* Pop first event off the active queue. */
        !           308:            CLR(ev->flags, SUDO_EVQ_ACTIVE);
        !           309:            TAILQ_REMOVE(&base->active, ev, active_entries);
        !           310:            /* Remove from base unless persistent. */
        !           311:            if (!ISSET(ev->events, SUDO_EV_PERSIST))
        !           312:                sudo_ev_del(base, ev);
        !           313:            ev->callback(ev->fd, ev->revents,
        !           314:                ev->closure == sudo_ev_self_cbarg() ? ev : ev->closure);
        !           315:            if (ISSET(base->flags, SUDO_EVBASE_LOOPBREAK)) {
        !           316:                /* Stop processing events immediately. */
        !           317:                SET(base->flags, SUDO_EVBASE_GOT_BREAK);
        !           318:                while ((ev = TAILQ_FIRST(&base->active)) != NULL) {
        !           319:                    CLR(ev->flags, SUDO_EVQ_ACTIVE);
        !           320:                    TAILQ_REMOVE(&base->active, ev, active_entries);
        !           321:                }
        !           322:                goto done;
        !           323:            }
        !           324:            if (ISSET(base->flags, SUDO_EVBASE_LOOPCONT)) {
        !           325:                /* Rescan events and start polling again. */
        !           326:                CLR(base->flags, SUDO_EVBASE_LOOPCONT);
        !           327:                if (!ISSET(flags, SUDO_EVLOOP_ONCE)) {
        !           328:                    while ((ev = TAILQ_FIRST(&base->active)) != NULL) {
        !           329:                        CLR(ev->flags, SUDO_EVQ_ACTIVE);
        !           330:                        TAILQ_REMOVE(&base->active, ev, active_entries);
        !           331:                    }
        !           332:                    goto rescan;
        !           333:                }
        !           334:            }
        !           335:        }
        !           336:        if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT)) {
        !           337:            /* exit loop after once through */
        !           338:            SET(base->flags, SUDO_EVBASE_GOT_EXIT);
        !           339:            goto done;
        !           340:        }
        !           341:        if (ISSET(flags, SUDO_EVLOOP_ONCE))
        !           342:            break;
        !           343:     }
        !           344: done:
        !           345:     base->flags &= SUDO_EVBASE_GOT_MASK;
        !           346:     debug_return_int(rc);
        !           347: }
        !           348: 
        !           349: void
        !           350: sudo_ev_loopexit(struct sudo_event_base *base)
        !           351: {
        !           352:     debug_decl(sudo_ev_loopexit, SUDO_DEBUG_EVENT)
        !           353:     SET(base->flags, SUDO_EVBASE_LOOPEXIT);
        !           354:     debug_return;
        !           355: }
        !           356: 
        !           357: void
        !           358: sudo_ev_loopbreak(struct sudo_event_base *base)
        !           359: {
        !           360:     debug_decl(sudo_ev_loopbreak, SUDO_DEBUG_EVENT)
        !           361:     SET(base->flags, SUDO_EVBASE_LOOPBREAK);
        !           362:     debug_return;
        !           363: }
        !           364: 
        !           365: void
        !           366: sudo_ev_loopcontinue(struct sudo_event_base *base)
        !           367: {
        !           368:     debug_decl(sudo_ev_loopcontinue, SUDO_DEBUG_EVENT)
        !           369:     SET(base->flags, SUDO_EVBASE_LOOPCONT);
        !           370:     debug_return;
        !           371: }
        !           372: 
        !           373: bool
        !           374: sudo_ev_got_exit(struct sudo_event_base *base)
        !           375: {
        !           376:     debug_decl(sudo_ev_got_exit, SUDO_DEBUG_EVENT)
        !           377:     debug_return_bool(ISSET(base->flags, SUDO_EVBASE_GOT_EXIT));
        !           378: }
        !           379: 
        !           380: bool
        !           381: sudo_ev_got_break(struct sudo_event_base *base)
        !           382: {
        !           383:     debug_decl(sudo_ev_got_break, SUDO_DEBUG_EVENT)
        !           384:     debug_return_bool(ISSET(base->flags, SUDO_EVBASE_GOT_BREAK));
        !           385: }
        !           386: 
        !           387: int
        !           388: sudo_ev_get_timeleft(struct sudo_event *ev, struct timeval *tv)
        !           389: {
        !           390:     struct timeval now;
        !           391:     debug_decl(sudo_ev_get_timeleft, SUDO_DEBUG_EVENT)
        !           392: 
        !           393:     if (!ISSET(ev->flags, SUDO_EVQ_TIMEOUTS)) {
        !           394:        sudo_timevalclear(tv);
        !           395:        debug_return_int(-1);
        !           396:     }
        !           397: 
        !           398:     gettimeofday(&now, NULL);
        !           399:     sudo_timevalsub(&ev->timeout, &now, tv);
        !           400:     if (tv->tv_sec < 0 || (tv->tv_sec == 0 && tv->tv_usec < 0))
        !           401:        sudo_timevalclear(tv);
        !           402:     debug_return_int(0);
        !           403: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>