Annotation of embedaddon/sudo/common/event.c, revision 1.1.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>