xref: /openbsd/usr.bin/dig/lib/isc/unix/app.c (revision 1fb015a8)
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