summaryrefslogtreecommitdiffstats
path: root/include/sudo_event.h
blob: 67dfb20bdcdbf6e8aa5de81aef5416b9d15c1778 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/*
 * SPDX-License-Identifier: ISC
 *
 * Copyright (c) 2013-2015, 2017 Todd C. Miller <Todd.Miller@sudo.ws>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef SUDO_EVENT_H
#define SUDO_EVENT_H

#include <time.h>	/* for struct timespec */
#include <signal.h>	/* for sigatomic_t and NSIG */
#include "sudo_queue.h"

struct timeval;		/* for deprecated APIs */

/* Event types (keep in sync with sudo_plugin.h) */
#define SUDO_EV_TIMEOUT		0x01	/* fire after timeout */
#define SUDO_EV_READ		0x02	/* fire when readable */
#define SUDO_EV_WRITE		0x04	/* fire when writable */
#define SUDO_EV_PERSIST		0x08	/* persist until deleted */
#define SUDO_EV_SIGNAL		0x10	/* fire on signal receipt */
#define SUDO_EV_SIGINFO		0x20	/* fire on signal receipt (siginfo) */

/* User-settable events for sudo_ev_init() (SUDO_EV_TIMEOUT not valid here) */
#define SUDO_EV_MASK		(SUDO_EV_READ|SUDO_EV_WRITE|SUDO_EV_PERSIST|SUDO_EV_SIGNAL|SUDO_EV_SIGINFO)

/* Event flags (internal) */
#define SUDO_EVQ_INSERTED	0x01	/* event is on the event queue */
#define SUDO_EVQ_ACTIVE		0x02	/* event is on the active queue */
#define SUDO_EVQ_TIMEOUTS	0x04	/* event is on the timeouts queue */

/* Event loop flags */
#define SUDO_EVLOOP_ONCE	0x01	/* Only run once through the loop */
#define SUDO_EVLOOP_NONBLOCK	0x02	/* Do not block in event loop */

/* Event base flags (internal) */
#define SUDO_EVBASE_LOOPONCE	SUDO_EVLOOP_ONCE
#define SUDO_EVBASE_LOOPEXIT	0x02
#define SUDO_EVBASE_LOOPBREAK	0x04
#define SUDO_EVBASE_LOOPCONT	0x08
#define SUDO_EVBASE_GOT_EXIT	0x10
#define SUDO_EVBASE_GOT_BREAK	0x20
#define SUDO_EVBASE_GOT_MASK	0xf0

/* Must match sudo_plugin_ev_callback_t in sudo_plugin.h */
typedef void (*sudo_ev_callback_t)(int fd, int what, void *closure);

/*
 * Container for SUDO_EV_SIGINFO events that gets passed as the closure
 * pointer.  This allows us to pass a siginfo_t without changing everything.
 */
struct sudo_ev_siginfo_container {
    void *closure;
    siginfo_t *siginfo;
    char si_buf[1];
};

/* Member of struct sudo_event_base. */
struct sudo_event {
    TAILQ_ENTRY(sudo_event) entries;
    TAILQ_ENTRY(sudo_event) active_entries;
    TAILQ_ENTRY(sudo_event) timeouts_entries;
    struct sudo_event_base *base; /* base this event belongs to */
    int fd;			/* fd/signal we are interested in */
    short events;		/* SUDO_EV_* flags (in) */
    short revents;		/* SUDO_EV_* flags (out) */
    short flags;		/* internal event flags */
    short pfd_idx;		/* index into pfds array (XXX) */
    sudo_ev_callback_t callback;/* user-provided callback */
    struct timespec timeout;	/* for SUDO_EV_TIMEOUT */
    void *closure;		/* user-provided data pointer */
};
TAILQ_HEAD(sudo_event_list, sudo_event);

struct sudo_event_base {
    struct sudo_event_list events; /* tail queue of all events */
    struct sudo_event_list active; /* tail queue of active events */
    struct sudo_event_list timeouts; /* tail queue of timeout events */
    struct sudo_event signal_event; /* storage for signal pipe event */
    struct sudo_event_list signals[NSIG]; /* array of signal event tail queues */
    struct sigaction *orig_handlers[NSIG]; /* original signal handlers */
    siginfo_t *siginfo[NSIG];	/* detailed signal info */
    sig_atomic_t signal_pending[NSIG]; /* pending signals */
    sig_atomic_t signal_caught;	/* at least one signal caught */
    int num_handlers;		/* number of installed handlers */
    int signal_pipe[2];		/* so we can wake up on signal */
#if defined(HAVE_POLL) || defined(HAVE_PPOLL)
    struct pollfd *pfds;	/* array of struct pollfd */
    int pfd_max;		/* size of the pfds array */
    int pfd_high;		/* highest slot used */
    int pfd_free;		/* idx of next free entry or pfd_max if full */
#else
    void *readfds_in;		/* read I/O descriptor set (in) */
    void *writefds_in;		/* write I/O descriptor set (in) */
    void *readfds_out;		/* read I/O descriptor set (out) */
    void *writefds_out;		/* write I/O descriptor set (out) */
    int maxfd;			/* max fd we can store in readfds/writefds */
    int highfd;			/* highest fd to pass as 1st arg to select */
#endif /* HAVE_POLL */
    unsigned int flags;		/* SUDO_EVBASE_* */
};

/* Allocate a new event base. */
sudo_dso_public struct sudo_event_base *sudo_ev_base_alloc_v1(void);
#define sudo_ev_base_alloc() sudo_ev_base_alloc_v1()

/* Free an event base. */
sudo_dso_public void sudo_ev_base_free_v1(struct sudo_event_base *base);
#define sudo_ev_base_free(_a) sudo_ev_base_free_v1((_a))

/* Set the default event base. */
sudo_dso_public void sudo_ev_base_setdef_v1(struct sudo_event_base *base);
#define sudo_ev_base_setdef(_a) sudo_ev_base_setdef_v1((_a))

/* Allocate a new event. */
sudo_dso_public struct sudo_event *sudo_ev_alloc_v1(int fd, short events, sudo_ev_callback_t callback, void *closure);
#define sudo_ev_alloc(_a, _b, _c, _d) sudo_ev_alloc_v1((_a), (_b), (_c), (_d))

/* Free an event. */
sudo_dso_public void sudo_ev_free_v1(struct sudo_event *ev);
#define sudo_ev_free(_a) sudo_ev_free_v1((_a))

/* Set an event struct that was pre-allocated. */
sudo_dso_public int sudo_ev_set_v1(struct sudo_event *ev, int fd, short events, sudo_ev_callback_t callback, void *closure);
#define sudo_ev_set(_a, _b, _c, _d, _e) sudo_ev_set_v1((_a), (_b), (_c), (_d), (_e))

/* Add an event, returns 0 on success, -1 on error */
sudo_dso_public int sudo_ev_add_v1(struct sudo_event_base *head, struct sudo_event *ev, const struct timeval *timo, bool tohead);
sudo_dso_public int sudo_ev_add_v2(struct sudo_event_base *head, struct sudo_event *ev, const struct timespec *timo, bool tohead);
#define sudo_ev_add(_a, _b, _c, _d) sudo_ev_add_v2((_a), (_b), (_c), (_d))

/* Delete an event, returns 0 on success, -1 on error */
sudo_dso_public int sudo_ev_del_v1(struct sudo_event_base *head, struct sudo_event *ev);
#define sudo_ev_del(_a, _b) sudo_ev_del_v1((_a), (_b))

/* Dispatch events, returns SUDO_CB_SUCCESS, SUDO_CB_BREAK or SUDO_CB_ERROR */
sudo_dso_public int sudo_ev_dispatch_v1(struct sudo_event_base *head);
#define sudo_ev_dispatch(_a) sudo_ev_dispatch_v1((_a))

/* Main event loop, returns SUDO_CB_SUCCESS, SUDO_CB_BREAK or SUDO_CB_ERROR */
sudo_dso_public int sudo_ev_loop_v1(struct sudo_event_base *head, int flags);
#define sudo_ev_loop(_a, _b) sudo_ev_loop_v1((_a), (_b))

/* Return pending event types, fills in ts if non-NULL and there is a timeout */
sudo_dso_public int sudo_ev_pending_v1(struct sudo_event *ev, short events, struct timespec *ts);
#define sudo_ev_pending(_a, _b, _c) sudo_ev_pending_v1((_a), (_b), (_c))

/* Return the remaining timeout associated with an event (deprecated). */
sudo_dso_public int sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timeval *tv);
sudo_dso_public int sudo_ev_get_timeleft_v2(struct sudo_event *ev, struct timespec *tv);
#define sudo_ev_get_timeleft(_a, _b) sudo_ev_get_timeleft_v2((_a), (_b))

/* Cause the event loop to exit after one run through. */
sudo_dso_public void sudo_ev_loopexit_v1(struct sudo_event_base *base);
#define sudo_ev_loopexit(_a) sudo_ev_loopexit_v1((_a))

/* Break out of the event loop right now. */
sudo_dso_public void sudo_ev_loopbreak_v1(struct sudo_event_base *base);
#define sudo_ev_loopbreak(_a) sudo_ev_loopbreak_v1((_a))

/* Rescan for events and restart the event loop. */
sudo_dso_public void sudo_ev_loopcontinue_v1(struct sudo_event_base *base);
#define sudo_ev_loopcontinue(_a) sudo_ev_loopcontinue_v1((_a))

/* Returns true if event loop stopped due to sudo_ev_loopexit(). */
sudo_dso_public bool sudo_ev_got_exit_v1(struct sudo_event_base *base);
#define sudo_ev_got_exit(_a) sudo_ev_got_exit_v1((_a))

/* Returns true if event loop stopped due to sudo_ev_loopbreak(). */
sudo_dso_public bool sudo_ev_got_break_v1(struct sudo_event_base *base);
#define sudo_ev_got_break(_a) sudo_ev_got_break_v1((_a))

/* Return the fd associated with an event. */
#define sudo_ev_get_fd(_ev) ((_ev) ? (_ev)->fd : -1)

/* Return the (absolute) timeout associated with an event or NULL. */
#define sudo_ev_get_timeout(_ev) \
    (ISSET((_ev)->flags, SUDO_EVQ_TIMEOUTS) ? &(_ev)->timeout : NULL)

/* Return the base an event is associated with or NULL. */
#define sudo_ev_get_base(_ev) ((_ev) ? (_ev)->base : NULL)

/* Set the base an event is associated with. */
#define sudo_ev_set_base(_ev, _b) ((_ev)->base = (_b))

/* Magic pointer value to use self pointer as callback arg. */
#define sudo_ev_self_cbarg() ((void *)-1)

/* Add an event to the base's active queue and mark it active (internal). */
void sudo_ev_activate(struct sudo_event_base *base, struct sudo_event *ev);

/*
 * Backend implementation.
 */
int sudo_ev_base_alloc_impl(struct sudo_event_base *base);
void sudo_ev_base_free_impl(struct sudo_event_base *base);
int sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev);
int sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev);
int sudo_ev_scan_impl(struct sudo_event_base *base, int flags);

#endif /* SUDO_EVENT_H */