1 /*
2  * SPDX-License-Identifier: ISC
3  *
4  * Copyright (c) 2013-2015, 2017 Todd C. Miller <Todd.Miller@sudo.ws>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #ifndef SUDO_EVENT_H
20 #define SUDO_EVENT_H
21 
22 #include <time.h>	/* for struct timespec */
23 #include <signal.h>	/* for sigatomic_t and NSIG */
24 #include "sudo_queue.h"
25 
26 struct timeval;		/* for deprecated APIs */
27 
28 /* Event types (keep in sync with sudo_plugin.h) */
29 #define SUDO_EV_TIMEOUT		0x01	/* fire after timeout */
30 #define SUDO_EV_READ		0x02	/* fire when readable */
31 #define SUDO_EV_WRITE		0x04	/* fire when writable */
32 #define SUDO_EV_PERSIST		0x08	/* persist until deleted */
33 #define SUDO_EV_SIGNAL		0x10	/* fire on signal receipt */
34 #define SUDO_EV_SIGINFO		0x20	/* fire on signal receipt (siginfo) */
35 
36 /* User-settable events for sudo_ev_init() (SUDO_EV_TIMEOUT not valid here) */
37 #define SUDO_EV_MASK		(SUDO_EV_READ|SUDO_EV_WRITE|SUDO_EV_PERSIST|SUDO_EV_SIGNAL|SUDO_EV_SIGINFO)
38 
39 /* Event flags (internal) */
40 #define SUDO_EVQ_INSERTED	0x01	/* event is on the event queue */
41 #define SUDO_EVQ_ACTIVE		0x02	/* event is on the active queue */
42 #define SUDO_EVQ_TIMEOUTS	0x04	/* event is on the timeouts queue */
43 
44 /* Event loop flags */
45 #define SUDO_EVLOOP_ONCE	0x01	/* Only run once through the loop */
46 #define SUDO_EVLOOP_NONBLOCK	0x02	/* Do not block in event loop */
47 
48 /* Event base flags (internal) */
49 #define SUDO_EVBASE_LOOPONCE	SUDO_EVLOOP_ONCE
50 #define SUDO_EVBASE_LOOPEXIT	0x02
51 #define SUDO_EVBASE_LOOPBREAK	0x04
52 #define SUDO_EVBASE_LOOPCONT	0x08
53 #define SUDO_EVBASE_GOT_EXIT	0x10
54 #define SUDO_EVBASE_GOT_BREAK	0x20
55 #define SUDO_EVBASE_GOT_MASK	0xf0
56 
57 /* Must match sudo_plugin_ev_callback_t in sudo_plugin.h */
58 typedef void (*sudo_ev_callback_t)(int fd, int what, void *closure);
59 
60 /*
61  * Container for SUDO_EV_SIGINFO events that gets passed as the closure
62  * pointer.  This allows us to pass a siginfo_t without changing everything.
63  */
64 struct sudo_ev_siginfo_container {
65     void *closure;
66     siginfo_t *siginfo;
67     char si_buf[1];
68 };
69 
70 /* Member of struct sudo_event_base. */
71 struct sudo_event {
72     TAILQ_ENTRY(sudo_event) entries;
73     TAILQ_ENTRY(sudo_event) active_entries;
74     TAILQ_ENTRY(sudo_event) timeouts_entries;
75     struct sudo_event_base *base; /* base this event belongs to */
76     int fd;			/* fd/signal we are interested in */
77     short events;		/* SUDO_EV_* flags (in) */
78     short revents;		/* SUDO_EV_* flags (out) */
79     short flags;		/* internal event flags */
80     short pfd_idx;		/* index into pfds array (XXX) */
81     sudo_ev_callback_t callback;/* user-provided callback */
82     struct timespec timeout;	/* for SUDO_EV_TIMEOUT */
83     void *closure;		/* user-provided data pointer */
84 };
85 TAILQ_HEAD(sudo_event_list, sudo_event);
86 
87 struct sudo_event_base {
88     struct sudo_event_list events; /* tail queue of all events */
89     struct sudo_event_list active; /* tail queue of active events */
90     struct sudo_event_list timeouts; /* tail queue of timeout events */
91     struct sudo_event signal_event; /* storage for signal pipe event */
92     struct sudo_event_list signals[NSIG]; /* array of signal event tail queues */
93     struct sigaction *orig_handlers[NSIG]; /* original signal handlers */
94     siginfo_t *siginfo[NSIG];	/* detailed signal info */
95     sig_atomic_t signal_pending[NSIG]; /* pending signals */
96     sig_atomic_t signal_caught;	/* at least one signal caught */
97     int num_handlers;		/* number of installed handlers */
98     int signal_pipe[2];		/* so we can wake up on signal */
99 #if defined(HAVE_POLL) || defined(HAVE_PPOLL)
100     struct pollfd *pfds;	/* array of struct pollfd */
101     int pfd_max;		/* size of the pfds array */
102     int pfd_high;		/* highest slot used */
103     int pfd_free;		/* idx of next free entry or pfd_max if full */
104 #else
105     fd_set *readfds_in;		/* read I/O descriptor set (in) */
106     fd_set *writefds_in;	/* write I/O descriptor set (in) */
107     fd_set *readfds_out;	/* read I/O descriptor set (out) */
108     fd_set *writefds_out;	/* write I/O descriptor set (out) */
109     int maxfd;			/* max fd we can store in readfds/writefds */
110     int highfd;			/* highest fd to pass as 1st arg to select */
111 #endif /* HAVE_POLL */
112     unsigned int flags;		/* SUDO_EVBASE_* */
113 };
114 
115 /* Allocate a new event base. */
116 sudo_dso_public struct sudo_event_base *sudo_ev_base_alloc_v1(void);
117 #define sudo_ev_base_alloc() sudo_ev_base_alloc_v1()
118 
119 /* Free an event base. */
120 sudo_dso_public void sudo_ev_base_free_v1(struct sudo_event_base *base);
121 #define sudo_ev_base_free(_a) sudo_ev_base_free_v1((_a))
122 
123 /* Set the default event base. */
124 sudo_dso_public void sudo_ev_base_setdef_v1(struct sudo_event_base *base);
125 #define sudo_ev_base_setdef(_a) sudo_ev_base_setdef_v1((_a))
126 
127 /* Allocate a new event. */
128 sudo_dso_public struct sudo_event *sudo_ev_alloc_v1(int fd, short events, sudo_ev_callback_t callback, void *closure);
129 #define sudo_ev_alloc(_a, _b, _c, _d) sudo_ev_alloc_v1((_a), (_b), (_c), (_d))
130 
131 /* Free an event. */
132 sudo_dso_public void sudo_ev_free_v1(struct sudo_event *ev);
133 #define sudo_ev_free(_a) sudo_ev_free_v1((_a))
134 
135 /* Set an event struct that was pre-allocated. */
136 sudo_dso_public int sudo_ev_set_v1(struct sudo_event *ev, int fd, short events, sudo_ev_callback_t callback, void *closure);
137 #define sudo_ev_set(_a, _b, _c, _d, _e) sudo_ev_set_v1((_a), (_b), (_c), (_d), (_e))
138 
139 /* Add an event, returns 0 on success, -1 on error */
140 sudo_dso_public int sudo_ev_add_v1(struct sudo_event_base *head, struct sudo_event *ev, const struct timeval *timo, bool tohead);
141 sudo_dso_public int sudo_ev_add_v2(struct sudo_event_base *head, struct sudo_event *ev, const struct timespec *timo, bool tohead);
142 #define sudo_ev_add(_a, _b, _c, _d) sudo_ev_add_v2((_a), (_b), (_c), (_d))
143 
144 /* Delete an event, returns 0 on success, -1 on error */
145 sudo_dso_public int sudo_ev_del_v1(struct sudo_event_base *head, struct sudo_event *ev);
146 #define sudo_ev_del(_a, _b) sudo_ev_del_v1((_a), (_b))
147 
148 /* Dispatch events, returns SUDO_CB_SUCCESS, SUDO_CB_BREAK or SUDO_CB_ERROR */
149 sudo_dso_public int sudo_ev_dispatch_v1(struct sudo_event_base *head);
150 #define sudo_ev_dispatch(_a) sudo_ev_dispatch_v1((_a))
151 
152 /* Main event loop, returns SUDO_CB_SUCCESS, SUDO_CB_BREAK or SUDO_CB_ERROR */
153 sudo_dso_public int sudo_ev_loop_v1(struct sudo_event_base *head, int flags);
154 #define sudo_ev_loop(_a, _b) sudo_ev_loop_v1((_a), (_b))
155 
156 /* Return pending event types, fills in ts if non-NULL and there is a timeout */
157 sudo_dso_public int sudo_ev_pending_v1(struct sudo_event *ev, short events, struct timespec *ts);
158 #define sudo_ev_pending(_a, _b, _c) sudo_ev_pending_v1((_a), (_b), (_c))
159 
160 /* Return the remaining timeout associated with an event (deprecated). */
161 sudo_dso_public int sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timeval *tv);
162 sudo_dso_public int sudo_ev_get_timeleft_v2(struct sudo_event *ev, struct timespec *tv);
163 #define sudo_ev_get_timeleft(_a, _b) sudo_ev_get_timeleft_v2((_a), (_b))
164 
165 /* Cause the event loop to exit after one run through. */
166 sudo_dso_public void sudo_ev_loopexit_v1(struct sudo_event_base *base);
167 #define sudo_ev_loopexit(_a) sudo_ev_loopexit_v1((_a))
168 
169 /* Break out of the event loop right now. */
170 sudo_dso_public void sudo_ev_loopbreak_v1(struct sudo_event_base *base);
171 #define sudo_ev_loopbreak(_a) sudo_ev_loopbreak_v1((_a))
172 
173 /* Rescan for events and restart the event loop. */
174 sudo_dso_public void sudo_ev_loopcontinue_v1(struct sudo_event_base *base);
175 #define sudo_ev_loopcontinue(_a) sudo_ev_loopcontinue_v1((_a))
176 
177 /* Returns true if event loop stopped due to sudo_ev_loopexit(). */
178 sudo_dso_public bool sudo_ev_got_exit_v1(struct sudo_event_base *base);
179 #define sudo_ev_got_exit(_a) sudo_ev_got_exit_v1((_a))
180 
181 /* Returns true if event loop stopped due to sudo_ev_loopbreak(). */
182 sudo_dso_public bool sudo_ev_got_break_v1(struct sudo_event_base *base);
183 #define sudo_ev_got_break(_a) sudo_ev_got_break_v1((_a))
184 
185 /* Return the fd associated with an event. */
186 #define sudo_ev_get_fd(_ev) ((_ev) ? (_ev)->fd : -1)
187 
188 /* Return the (absolute) timeout associated with an event or NULL. */
189 #define sudo_ev_get_timeout(_ev) \
190     (ISSET((_ev)->flags, SUDO_EVQ_TIMEOUTS) ? &(_ev)->timeout : NULL)
191 
192 /* Return the base an event is associated with or NULL. */
193 #define sudo_ev_get_base(_ev) ((_ev) ? (_ev)->base : NULL)
194 
195 /* Magic pointer value to use self pointer as callback arg. */
196 #define sudo_ev_self_cbarg() ((void *)-1)
197 
198 /* Add an event to the base's active queue and mark it active (internal). */
199 void sudo_ev_activate(struct sudo_event_base *base, struct sudo_event *ev);
200 
201 /*
202  * Backend implementation.
203  */
204 int sudo_ev_base_alloc_impl(struct sudo_event_base *base);
205 void sudo_ev_base_free_impl(struct sudo_event_base *base);
206 int sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev);
207 int sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev);
208 int sudo_ev_scan_impl(struct sudo_event_base *base, int flags);
209 
210 #endif /* SUDO_EVENT_H */
211