xref: /openbsd-src/usr.bin/dig/lib/isc/unix/app.c (revision 1fb015a8af3a7e9b85db2510147a155826ef04d9)
15185a700Sflorian /*
25185a700Sflorian  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian  *
45185a700Sflorian  * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian  * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian  * copyright notice and this permission notice appear in all copies.
75185a700Sflorian  *
85185a700Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian  * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian  */
165185a700Sflorian 
175185a700Sflorian /*! \file */
185185a700Sflorian 
195185a700Sflorian #include <stddef.h>
205185a700Sflorian #include <errno.h>
215185a700Sflorian #include <signal.h>
225185a700Sflorian #include <sys/time.h>
234465bcfbSjsg #include <time.h>
245185a700Sflorian 
255185a700Sflorian #include <isc/app.h>
265185a700Sflorian #include <isc/event.h>
275185a700Sflorian 
285185a700Sflorian #include <string.h>
295185a700Sflorian #include <isc/task.h>
305185a700Sflorian #include <isc/util.h>
315185a700Sflorian 
325185a700Sflorian /*%
335185a700Sflorian  * For BIND9 internal applications built with threads, we use a single app
345185a700Sflorian  * context and let multiple worker, I/O, timer threads do actual jobs.
355185a700Sflorian  * For other cases (including BIND9 built without threads) an app context acts
365185a700Sflorian  * as an event loop dispatching various events.
375185a700Sflorian  */
385185a700Sflorian #include "../timer_p.h"
395185a700Sflorian #include "../task_p.h"
405185a700Sflorian #include "socket_p.h"
415185a700Sflorian 
425185a700Sflorian /*
435185a700Sflorian  * The application context of this module.  This implementation actually
445185a700Sflorian  * doesn't use it. (This may change in the future).
455185a700Sflorian  */
465185a700Sflorian 
478b553854Sflorian typedef struct isc_appctx {
485185a700Sflorian 	isc_eventlist_t		on_run;
49*1fb015a8Sflorian 	int		shutdown_requested;
50*1fb015a8Sflorian 	int		running;
515185a700Sflorian 
525185a700Sflorian 	/*!
535185a700Sflorian 	 * We assume that 'want_shutdown' can be read and written atomically.
545185a700Sflorian 	 */
55*1fb015a8Sflorian 	int		want_shutdown;
565185a700Sflorian 
575185a700Sflorian 	isc_taskmgr_t		*taskmgr;
585185a700Sflorian 	isc_socketmgr_t		*socketmgr;
595185a700Sflorian 	isc_timermgr_t		*timermgr;
608b553854Sflorian } isc_appctx_t;
615185a700Sflorian 
628b553854Sflorian static isc_appctx_t isc_g_appctx;
638b553854Sflorian 
648b553854Sflorian static isc_result_t isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
658b553854Sflorian     isc_taskaction_t action, void *arg);
665185a700Sflorian 
678b553854Sflorian static isc_result_t
isc_app_ctxstart(isc_appctx_t * ctx)688b553854Sflorian isc_app_ctxstart(isc_appctx_t *ctx) {
695185a700Sflorian 	/*
705185a700Sflorian 	 * Start an ISC library application.
715185a700Sflorian 	 */
725185a700Sflorian 
735185a700Sflorian 	ISC_LIST_INIT(ctx->on_run);
745185a700Sflorian 
75*1fb015a8Sflorian 	ctx->shutdown_requested = 0;
76*1fb015a8Sflorian 	ctx->running = 0;
77*1fb015a8Sflorian 	ctx->want_shutdown = 0;
785185a700Sflorian 
7976e675f5Sjung 	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
805185a700Sflorian 		UNEXPECTED_ERROR(__FILE__, __LINE__,
8176e675f5Sjung 				 "isc_app_ctxstart() signal: %s",
8240adc7c5Sjung 				 strerror(errno));
8376e675f5Sjung 		return ISC_R_UNEXPECTED;
845185a700Sflorian 	}
8576e675f5Sjung 	return ISC_R_SUCCESS;
865185a700Sflorian }
875185a700Sflorian 
885185a700Sflorian isc_result_t
isc_app_start(void)898b553854Sflorian isc_app_start(void) {
905185a700Sflorian 	/* The remaining members will be initialized in ctxstart() */
915185a700Sflorian 
928b553854Sflorian 	return (isc_app_ctxstart((isc_appctx_t *)&isc_g_appctx));
935185a700Sflorian }
945185a700Sflorian 
955185a700Sflorian isc_result_t
isc_app_onrun(isc_task_t * task,isc_taskaction_t action,void * arg)968b553854Sflorian isc_app_onrun(isc_task_t *task, isc_taskaction_t action,
975185a700Sflorian 	      void *arg)
985185a700Sflorian {
998b553854Sflorian 	return (isc_app_ctxonrun((isc_appctx_t *)&isc_g_appctx,
1005185a700Sflorian 				  task, action, arg));
1015185a700Sflorian }
1025185a700Sflorian 
1035185a700Sflorian isc_result_t
isc_app_ctxonrun(isc_appctx_t * ctx,isc_task_t * task,isc_taskaction_t action,void * arg)1048b553854Sflorian isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
1055185a700Sflorian 		  isc_taskaction_t action, void *arg)
1065185a700Sflorian {
1075185a700Sflorian 	isc_event_t *event;
1085185a700Sflorian 	isc_task_t *cloned_task = NULL;
1095185a700Sflorian 	isc_result_t result;
1105185a700Sflorian 
1115185a700Sflorian 	if (ctx->running) {
1125185a700Sflorian 		result = ISC_R_ALREADYRUNNING;
1135185a700Sflorian 		goto unlock;
1145185a700Sflorian 	}
1155185a700Sflorian 
1165185a700Sflorian 	/*
1175185a700Sflorian 	 * Note that we store the task to which we're going to send the event
1185185a700Sflorian 	 * in the event's "sender" field.
1195185a700Sflorian 	 */
1205185a700Sflorian 	isc_task_attach(task, &cloned_task);
1215185a700Sflorian 	event = isc_event_allocate(cloned_task, ISC_APPEVENT_SHUTDOWN,
1225185a700Sflorian 				   action, arg, sizeof(*event));
1235185a700Sflorian 	if (event == NULL) {
1245185a700Sflorian 		isc_task_detach(&cloned_task);
1255185a700Sflorian 		result = ISC_R_NOMEMORY;
1265185a700Sflorian 		goto unlock;
1275185a700Sflorian 	}
1285185a700Sflorian 
1295185a700Sflorian 	ISC_LIST_APPEND(ctx->on_run, event, ev_link);
1305185a700Sflorian 
1315185a700Sflorian 	result = ISC_R_SUCCESS;
1325185a700Sflorian 
1335185a700Sflorian  unlock:
1345185a700Sflorian 	return (result);
1355185a700Sflorian }
1365185a700Sflorian 
1375185a700Sflorian /*!
1385185a700Sflorian  * Event loop for nonthreaded programs.
1395185a700Sflorian  */
1405185a700Sflorian static isc_result_t
evloop(isc_appctx_t * ctx)1418b553854Sflorian evloop(isc_appctx_t *ctx) {
1425185a700Sflorian 	isc_result_t result;
1435185a700Sflorian 
1445185a700Sflorian 	while (!ctx->want_shutdown) {
1455185a700Sflorian 		int n;
146427f8978Sflorian 		struct timespec when, now, diff, zero ={0, 0};
1475185a700Sflorian 		struct timeval tv, *tvp;
1485185a700Sflorian 		isc_socketwait_t *swait;
149*1fb015a8Sflorian 		int readytasks;
150*1fb015a8Sflorian 		int call_timer_dispatch = 0;
1515185a700Sflorian 
1528b553854Sflorian 		readytasks = isc_taskmgr_ready(ctx->taskmgr);
1535185a700Sflorian 		if (readytasks) {
1545185a700Sflorian 			tv.tv_sec = 0;
1555185a700Sflorian 			tv.tv_usec = 0;
1565185a700Sflorian 			tvp = &tv;
157*1fb015a8Sflorian 			call_timer_dispatch = 1;
1585185a700Sflorian 		} else {
1598b553854Sflorian 			result = isc_timermgr_nextevent(ctx->timermgr, &when);
1605185a700Sflorian 			if (result != ISC_R_SUCCESS)
1615185a700Sflorian 				tvp = NULL;
1625185a700Sflorian 			else {
163b53d8310Sflorian 				clock_gettime(CLOCK_MONOTONIC, &now);
164427f8978Sflorian 				timespecsub(&when, &now, &diff);
165427f8978Sflorian 				if (timespeccmp(&diff, &zero, <=)) {
166*1fb015a8Sflorian 					call_timer_dispatch = 1;
167427f8978Sflorian 					memset(&tv, 0, sizeof(tv));
168427f8978Sflorian 				} else
169427f8978Sflorian 					TIMESPEC_TO_TIMEVAL(&tv, &diff);
1705185a700Sflorian 				tvp = &tv;
1715185a700Sflorian 			}
1725185a700Sflorian 		}
1735185a700Sflorian 
1745185a700Sflorian 		swait = NULL;
1758b553854Sflorian 		n = isc_socketmgr_waitevents(ctx->socketmgr, tvp, &swait);
1765185a700Sflorian 
1775185a700Sflorian 		if (n == 0 || call_timer_dispatch) {
1785185a700Sflorian 			/*
1798b553854Sflorian 			 * We call isc_timermgr_dispatch() only when
1805185a700Sflorian 			 * necessary, in order to reduce overhead.  If the
1815185a700Sflorian 			 * select() call indicates a timeout, we need the
1825185a700Sflorian 			 * dispatch.  Even if not, if we set the 0-timeout
1835185a700Sflorian 			 * for the select() call, we need to check the timer
1845185a700Sflorian 			 * events.  In the 'readytasks' case, there may be no
1855185a700Sflorian 			 * timeout event actually, but there is no other way
1865185a700Sflorian 			 * to reduce the overhead.
1875185a700Sflorian 			 * Note that we do not have to worry about the case
1885185a700Sflorian 			 * where a new timer is inserted during the select()
1895185a700Sflorian 			 * call, since this loop only runs in the non-thread
1905185a700Sflorian 			 * mode.
1915185a700Sflorian 			 */
1928b553854Sflorian 			isc_timermgr_dispatch(ctx->timermgr);
1935185a700Sflorian 		}
1945185a700Sflorian 		if (n > 0)
1958b553854Sflorian 			(void)isc_socketmgr_dispatch(ctx->socketmgr, swait);
1968b553854Sflorian 		(void)isc_taskmgr_dispatch(ctx->taskmgr);
1975185a700Sflorian 	}
1985185a700Sflorian 	return (ISC_R_SUCCESS);
1995185a700Sflorian }
2005185a700Sflorian 
2018b553854Sflorian static isc_result_t
isc_app_ctxrun(isc_appctx_t * ctx)2028b553854Sflorian isc_app_ctxrun(isc_appctx_t *ctx) {
2035185a700Sflorian 	int result;
2045185a700Sflorian 	isc_event_t *event, *next_event;
2055185a700Sflorian 	isc_task_t *task;
2065185a700Sflorian 
2075185a700Sflorian 	if (!ctx->running) {
208*1fb015a8Sflorian 		ctx->running = 1;
2095185a700Sflorian 
2105185a700Sflorian 		/*
2115185a700Sflorian 		 * Post any on-run events (in FIFO order).
2125185a700Sflorian 		 */
2135185a700Sflorian 		for (event = ISC_LIST_HEAD(ctx->on_run);
2145185a700Sflorian 		     event != NULL;
2155185a700Sflorian 		     event = next_event) {
2165185a700Sflorian 			next_event = ISC_LIST_NEXT(event, ev_link);
2175185a700Sflorian 			ISC_LIST_UNLINK(ctx->on_run, event, ev_link);
2185185a700Sflorian 			task = event->ev_sender;
2195185a700Sflorian 			event->ev_sender = NULL;
2205185a700Sflorian 			isc_task_sendanddetach(&task, &event);
2215185a700Sflorian 		}
2225185a700Sflorian 
2235185a700Sflorian 	}
2245185a700Sflorian 
2258b553854Sflorian 	(void) isc_taskmgr_dispatch(ctx->taskmgr);
2265185a700Sflorian 	result = evloop(ctx);
2275185a700Sflorian 	return (result);
2285185a700Sflorian }
2295185a700Sflorian 
2305185a700Sflorian isc_result_t
isc_app_run(void)2318b553854Sflorian isc_app_run(void) {
2328b553854Sflorian 	return (isc_app_ctxrun((isc_appctx_t *)&isc_g_appctx));
2335185a700Sflorian }
2345185a700Sflorian 
2358b553854Sflorian static isc_result_t
isc_app_ctxshutdown(isc_appctx_t * ctx)2368b553854Sflorian isc_app_ctxshutdown(isc_appctx_t *ctx) {
237*1fb015a8Sflorian 	int want_kill = 1;
2385185a700Sflorian 
2395185a700Sflorian 	REQUIRE(ctx->running);
2405185a700Sflorian 
2415185a700Sflorian 	if (ctx->shutdown_requested)
242*1fb015a8Sflorian 		want_kill = 0;
2435185a700Sflorian 	else
244*1fb015a8Sflorian 		ctx->shutdown_requested = 1;
2455185a700Sflorian 
2465185a700Sflorian 	if (want_kill) {
2475185a700Sflorian 		if (ctx != &isc_g_appctx)
2485185a700Sflorian 			/* BIND9 internal, but using multiple contexts */
249*1fb015a8Sflorian 			ctx->want_shutdown = 1;
2505185a700Sflorian 		else {
251*1fb015a8Sflorian 			ctx->want_shutdown = 1;
2525185a700Sflorian 		}
2535185a700Sflorian 	}
2545185a700Sflorian 
2555185a700Sflorian 	return (ISC_R_SUCCESS);
2565185a700Sflorian }
2575185a700Sflorian 
2585185a700Sflorian isc_result_t
isc_app_shutdown(void)2598b553854Sflorian isc_app_shutdown(void) {
2608b553854Sflorian 	return (isc_app_ctxshutdown((isc_appctx_t *)&isc_g_appctx));
2615185a700Sflorian }
262