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