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>