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>