1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /*! \file */
18
19 #include <stddef.h>
20 #include <errno.h>
21 #include <signal.h>
22 #include <sys/time.h>
23 #include <time.h>
24
25 #include <isc/app.h>
26 #include <isc/event.h>
27
28 #include <string.h>
29 #include <isc/task.h>
30 #include <isc/util.h>
31
32 /*%
33 * For BIND9 internal applications built with threads, we use a single app
34 * context and let multiple worker, I/O, timer threads do actual jobs.
35 * For other cases (including BIND9 built without threads) an app context acts
36 * as an event loop dispatching various events.
37 */
38 #include "../timer_p.h"
39 #include "../task_p.h"
40 #include "socket_p.h"
41
42 /*
43 * The application context of this module. This implementation actually
44 * doesn't use it. (This may change in the future).
45 */
46
47 typedef struct isc_appctx {
48 isc_eventlist_t on_run;
49 int shutdown_requested;
50 int running;
51
52 /*!
53 * We assume that 'want_shutdown' can be read and written atomically.
54 */
55 int want_shutdown;
56
57 isc_taskmgr_t *taskmgr;
58 isc_socketmgr_t *socketmgr;
59 isc_timermgr_t *timermgr;
60 } isc_appctx_t;
61
62 static isc_appctx_t isc_g_appctx;
63
64 static isc_result_t isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
65 isc_taskaction_t action, void *arg);
66
67 static isc_result_t
isc_app_ctxstart(isc_appctx_t * ctx)68 isc_app_ctxstart(isc_appctx_t *ctx) {
69 /*
70 * Start an ISC library application.
71 */
72
73 ISC_LIST_INIT(ctx->on_run);
74
75 ctx->shutdown_requested = 0;
76 ctx->running = 0;
77 ctx->want_shutdown = 0;
78
79 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
80 UNEXPECTED_ERROR(__FILE__, __LINE__,
81 "isc_app_ctxstart() signal: %s",
82 strerror(errno));
83 return ISC_R_UNEXPECTED;
84 }
85 return ISC_R_SUCCESS;
86 }
87
88 isc_result_t
isc_app_start(void)89 isc_app_start(void) {
90 /* The remaining members will be initialized in ctxstart() */
91
92 return (isc_app_ctxstart((isc_appctx_t *)&isc_g_appctx));
93 }
94
95 isc_result_t
isc_app_onrun(isc_task_t * task,isc_taskaction_t action,void * arg)96 isc_app_onrun(isc_task_t *task, isc_taskaction_t action,
97 void *arg)
98 {
99 return (isc_app_ctxonrun((isc_appctx_t *)&isc_g_appctx,
100 task, action, arg));
101 }
102
103 isc_result_t
isc_app_ctxonrun(isc_appctx_t * ctx,isc_task_t * task,isc_taskaction_t action,void * arg)104 isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
105 isc_taskaction_t action, void *arg)
106 {
107 isc_event_t *event;
108 isc_task_t *cloned_task = NULL;
109 isc_result_t result;
110
111 if (ctx->running) {
112 result = ISC_R_ALREADYRUNNING;
113 goto unlock;
114 }
115
116 /*
117 * Note that we store the task to which we're going to send the event
118 * in the event's "sender" field.
119 */
120 isc_task_attach(task, &cloned_task);
121 event = isc_event_allocate(cloned_task, ISC_APPEVENT_SHUTDOWN,
122 action, arg, sizeof(*event));
123 if (event == NULL) {
124 isc_task_detach(&cloned_task);
125 result = ISC_R_NOMEMORY;
126 goto unlock;
127 }
128
129 ISC_LIST_APPEND(ctx->on_run, event, ev_link);
130
131 result = ISC_R_SUCCESS;
132
133 unlock:
134 return (result);
135 }
136
137 /*!
138 * Event loop for nonthreaded programs.
139 */
140 static isc_result_t
evloop(isc_appctx_t * ctx)141 evloop(isc_appctx_t *ctx) {
142 isc_result_t result;
143
144 while (!ctx->want_shutdown) {
145 int n;
146 struct timespec when, now, diff, zero ={0, 0};
147 struct timeval tv, *tvp;
148 isc_socketwait_t *swait;
149 int readytasks;
150 int call_timer_dispatch = 0;
151
152 readytasks = isc_taskmgr_ready(ctx->taskmgr);
153 if (readytasks) {
154 tv.tv_sec = 0;
155 tv.tv_usec = 0;
156 tvp = &tv;
157 call_timer_dispatch = 1;
158 } else {
159 result = isc_timermgr_nextevent(ctx->timermgr, &when);
160 if (result != ISC_R_SUCCESS)
161 tvp = NULL;
162 else {
163 clock_gettime(CLOCK_MONOTONIC, &now);
164 timespecsub(&when, &now, &diff);
165 if (timespeccmp(&diff, &zero, <=)) {
166 call_timer_dispatch = 1;
167 memset(&tv, 0, sizeof(tv));
168 } else
169 TIMESPEC_TO_TIMEVAL(&tv, &diff);
170 tvp = &tv;
171 }
172 }
173
174 swait = NULL;
175 n = isc_socketmgr_waitevents(ctx->socketmgr, tvp, &swait);
176
177 if (n == 0 || call_timer_dispatch) {
178 /*
179 * We call isc_timermgr_dispatch() only when
180 * necessary, in order to reduce overhead. If the
181 * select() call indicates a timeout, we need the
182 * dispatch. Even if not, if we set the 0-timeout
183 * for the select() call, we need to check the timer
184 * events. In the 'readytasks' case, there may be no
185 * timeout event actually, but there is no other way
186 * to reduce the overhead.
187 * Note that we do not have to worry about the case
188 * where a new timer is inserted during the select()
189 * call, since this loop only runs in the non-thread
190 * mode.
191 */
192 isc_timermgr_dispatch(ctx->timermgr);
193 }
194 if (n > 0)
195 (void)isc_socketmgr_dispatch(ctx->socketmgr, swait);
196 (void)isc_taskmgr_dispatch(ctx->taskmgr);
197 }
198 return (ISC_R_SUCCESS);
199 }
200
201 static isc_result_t
isc_app_ctxrun(isc_appctx_t * ctx)202 isc_app_ctxrun(isc_appctx_t *ctx) {
203 int result;
204 isc_event_t *event, *next_event;
205 isc_task_t *task;
206
207 if (!ctx->running) {
208 ctx->running = 1;
209
210 /*
211 * Post any on-run events (in FIFO order).
212 */
213 for (event = ISC_LIST_HEAD(ctx->on_run);
214 event != NULL;
215 event = next_event) {
216 next_event = ISC_LIST_NEXT(event, ev_link);
217 ISC_LIST_UNLINK(ctx->on_run, event, ev_link);
218 task = event->ev_sender;
219 event->ev_sender = NULL;
220 isc_task_sendanddetach(&task, &event);
221 }
222
223 }
224
225 (void) isc_taskmgr_dispatch(ctx->taskmgr);
226 result = evloop(ctx);
227 return (result);
228 }
229
230 isc_result_t
isc_app_run(void)231 isc_app_run(void) {
232 return (isc_app_ctxrun((isc_appctx_t *)&isc_g_appctx));
233 }
234
235 static isc_result_t
isc_app_ctxshutdown(isc_appctx_t * ctx)236 isc_app_ctxshutdown(isc_appctx_t *ctx) {
237 int want_kill = 1;
238
239 REQUIRE(ctx->running);
240
241 if (ctx->shutdown_requested)
242 want_kill = 0;
243 else
244 ctx->shutdown_requested = 1;
245
246 if (want_kill) {
247 if (ctx != &isc_g_appctx)
248 /* BIND9 internal, but using multiple contexts */
249 ctx->want_shutdown = 1;
250 else {
251 ctx->want_shutdown = 1;
252 }
253 }
254
255 return (ISC_R_SUCCESS);
256 }
257
258 isc_result_t
isc_app_shutdown(void)259 isc_app_shutdown(void) {
260 return (isc_app_ctxshutdown((isc_appctx_t *)&isc_g_appctx));
261 }
262