Home | History | Annotate | Line # | Download | only in libntp
      1 /*	$NetBSD: ntp_worker.c,v 1.7 2020/05/25 20:47:24 christos Exp $	*/
      2 
      3 /*
      4  * ntp_worker.c
      5  */
      6 #include <config.h>
      7 #include "ntp_workimpl.h"
      8 
      9 #ifdef WORKER
     10 
     11 #include <stdio.h>
     12 #include <ctype.h>
     13 #include <signal.h>
     14 
     15 #include "iosignal.h"
     16 #include "ntp_stdlib.h"
     17 #include "ntp_malloc.h"
     18 #include "ntp_syslog.h"
     19 #include "ntpd.h"
     20 #include "ntp_io.h"
     21 #include "ntp_assert.h"
     22 #include "ntp_unixtime.h"
     23 #include "intreswork.h"
     24 
     25 
     26 #define CHILD_MAX_IDLE	(3 * 60)	/* seconds, idle worker limit */
     27 
     28 blocking_child **	blocking_children;
     29 size_t			blocking_children_alloc;
     30 int			worker_per_query;	/* boolean */
     31 int			intres_req_pending;
     32 volatile u_int		blocking_child_ready_seen;
     33 volatile u_int		blocking_child_ready_done;
     34 
     35 
     36 #ifndef HAVE_IO_COMPLETION_PORT
     37 /*
     38  * pipe_socketpair()
     39  *
     40  * Provides an AF_UNIX socketpair on systems which have them, otherwise
     41  * pair of unidirectional pipes.
     42  */
     43 int
     44 pipe_socketpair(
     45 	int	caller_fds[2],
     46 	int *	is_pipe
     47 	)
     48 {
     49 	int	rc;
     50 	int	fds[2];
     51 	int	called_pipe;
     52 
     53 #ifdef HAVE_SOCKETPAIR
     54 	rc = socketpair(AF_UNIX, SOCK_STREAM, 0, &fds[0]);
     55 #else
     56 	rc = -1;
     57 #endif
     58 
     59 	if (-1 == rc) {
     60 		rc = pipe(&fds[0]);
     61 		called_pipe = TRUE;
     62 	} else {
     63 		called_pipe = FALSE;
     64 	}
     65 
     66 	if (-1 == rc)
     67 		return rc;
     68 
     69 	caller_fds[0] = fds[0];
     70 	caller_fds[1] = fds[1];
     71 	if (is_pipe != NULL)
     72 		*is_pipe = called_pipe;
     73 
     74 	return 0;
     75 }
     76 
     77 
     78 /*
     79  * close_all_except()
     80  *
     81  * Close all file descriptors except the given keep_fd.
     82  */
     83 void
     84 close_all_except(
     85 	int keep_fd
     86 	)
     87 {
     88 	int fd;
     89 
     90 	for (fd = 0; fd < keep_fd; fd++)
     91 		close(fd);
     92 
     93 	close_all_beyond(keep_fd);
     94 }
     95 
     96 
     97 /*
     98  * close_all_beyond()
     99  *
    100  * Close all file descriptors after the given keep_fd, which is the
    101  * highest fd to keep open.
    102  */
    103 void
    104 close_all_beyond(
    105 	int keep_fd
    106 	)
    107 {
    108 # ifdef HAVE_CLOSEFROM
    109 	closefrom(keep_fd + 1);
    110 # elif defined(F_CLOSEM)
    111 	/*
    112 	 * From 'Writing Reliable AIX Daemons,' SG24-4946-00,
    113 	 * by Eric Agar (saves us from doing 32767 system
    114 	 * calls)
    115 	 */
    116 	if (fcntl(keep_fd + 1, F_CLOSEM, 0) == -1)
    117 		msyslog(LOG_ERR, "F_CLOSEM(%d): %m", keep_fd + 1);
    118 # else	/* !HAVE_CLOSEFROM && !F_CLOSEM follows */
    119 	int fd;
    120 	int max_fd;
    121 
    122 	max_fd = GETDTABLESIZE();
    123 	for (fd = keep_fd + 1; fd < max_fd; fd++)
    124 		close(fd);
    125 # endif	/* !HAVE_CLOSEFROM && !F_CLOSEM */
    126 }
    127 #endif	/* HAVE_IO_COMPLETION_PORT */
    128 
    129 
    130 u_int
    131 available_blocking_child_slot(void)
    132 {
    133 	const size_t	each = sizeof(blocking_children[0]);
    134 	u_int		slot;
    135 	size_t		prev_alloc;
    136 	size_t		new_alloc;
    137 	size_t		prev_octets;
    138 	size_t		octets;
    139 
    140 	for (slot = 0; slot < blocking_children_alloc; slot++) {
    141 		if (NULL == blocking_children[slot])
    142 			return slot;
    143 		if (blocking_children[slot]->reusable) {
    144 			blocking_children[slot]->reusable = FALSE;
    145 			return slot;
    146 		}
    147 	}
    148 
    149 	prev_alloc = blocking_children_alloc;
    150 	prev_octets = prev_alloc * each;
    151 	new_alloc = blocking_children_alloc + 4;
    152 	octets = new_alloc * each;
    153 	blocking_children = erealloc_zero(blocking_children, octets,
    154 					  prev_octets);
    155 	blocking_children_alloc = new_alloc;
    156 
    157 	/* assume we'll never have enough workers to overflow u_int */
    158 	return (u_int)prev_alloc;
    159 }
    160 
    161 
    162 int
    163 queue_blocking_request(
    164 	blocking_work_req	rtype,
    165 	void *			req,
    166 	size_t			reqsize,
    167 	blocking_work_callback	done_func,
    168 	void *			context
    169 	)
    170 {
    171 	static u_int		intres_slot = UINT_MAX;
    172 	u_int			child_slot;
    173 	blocking_child *	c;
    174 	blocking_pipe_header	req_hdr;
    175 
    176 	req_hdr.octets = sizeof(req_hdr) + reqsize;
    177 	req_hdr.magic_sig = BLOCKING_REQ_MAGIC;
    178 	req_hdr.rtype = rtype;
    179 	req_hdr.done_func = done_func;
    180 	req_hdr.context = context;
    181 
    182 	child_slot = UINT_MAX;
    183 	if (worker_per_query || UINT_MAX == intres_slot ||
    184 	    blocking_children[intres_slot]->reusable)
    185 		child_slot = available_blocking_child_slot();
    186 	if (!worker_per_query) {
    187 		if (UINT_MAX == intres_slot)
    188 			intres_slot = child_slot;
    189 		else
    190 			child_slot = intres_slot;
    191 		if (0 == intres_req_pending)
    192 			intres_timeout_req(0);
    193 	}
    194 	intres_req_pending++;
    195 	INSIST(UINT_MAX != child_slot);
    196 	c = blocking_children[child_slot];
    197 	if (NULL == c) {
    198 		c = emalloc_zero(sizeof(*c));
    199 #ifdef WORK_FORK
    200 		c->req_read_pipe = -1;
    201 		c->req_write_pipe = -1;
    202 #endif
    203 #ifdef WORK_PIPE
    204 		c->resp_read_pipe = -1;
    205 		c->resp_write_pipe = -1;
    206 #endif
    207 		blocking_children[child_slot] = c;
    208 	}
    209 	req_hdr.child_idx = child_slot;
    210 
    211 	return send_blocking_req_internal(c, &req_hdr, req);
    212 }
    213 
    214 
    215 int queue_blocking_response(
    216 	blocking_child *		c,
    217 	blocking_pipe_header *		resp,
    218 	size_t				respsize,
    219 	const blocking_pipe_header *	req
    220 	)
    221 {
    222 	resp->octets = respsize;
    223 	resp->magic_sig = BLOCKING_RESP_MAGIC;
    224 	resp->rtype = req->rtype;
    225 	resp->context = req->context;
    226 	resp->done_func = req->done_func;
    227 
    228 	return send_blocking_resp_internal(c, resp);
    229 }
    230 
    231 
    232 void
    233 process_blocking_resp(
    234 	blocking_child *	c
    235 	)
    236 {
    237 	blocking_pipe_header *	resp;
    238 	void *			data;
    239 
    240 	/*
    241 	 * On Windows send_blocking_resp_internal() may signal the
    242 	 * blocking_response_ready event multiple times while we're
    243 	 * processing a response, so always consume all available
    244 	 * responses before returning to test the event again.
    245 	 */
    246 #ifdef WORK_THREAD
    247 	do {
    248 #endif
    249 		resp = receive_blocking_resp_internal(c);
    250 		if (NULL != resp) {
    251 			DEBUG_REQUIRE(BLOCKING_RESP_MAGIC ==
    252 				      resp->magic_sig);
    253 			data = (char *)resp + sizeof(*resp);
    254 			intres_req_pending--;
    255 			(*resp->done_func)(resp->rtype, resp->context,
    256 					   resp->octets - sizeof(*resp),
    257 					   data);
    258 			free(resp);
    259 		}
    260 #ifdef WORK_THREAD
    261 	} while (NULL != resp);
    262 #endif
    263 	if (!worker_per_query && 0 == intres_req_pending)
    264 		intres_timeout_req(CHILD_MAX_IDLE);
    265 	else if (worker_per_query)
    266 		req_child_exit(c);
    267 }
    268 
    269 void
    270 harvest_blocking_responses(void)
    271 {
    272 	size_t		idx;
    273 	blocking_child*	cp;
    274 	u_int		scseen, scdone;
    275 
    276 	scseen = blocking_child_ready_seen;
    277 	scdone = blocking_child_ready_done;
    278 	if (scdone != scseen) {
    279 		blocking_child_ready_done = scseen;
    280 		for (idx = 0; idx < blocking_children_alloc; idx++) {
    281 			cp = blocking_children[idx];
    282 			if (NULL == cp)
    283 				continue;
    284 			scseen = cp->resp_ready_seen;
    285 			scdone = cp->resp_ready_done;
    286 			if (scdone != scseen) {
    287 				cp->resp_ready_done = scseen;
    288 				process_blocking_resp(cp);
    289 			}
    290 		}
    291 	}
    292 }
    293 
    294 
    295 /*
    296  * blocking_child_common runs as a forked child or a thread
    297  */
    298 int
    299 blocking_child_common(
    300 	blocking_child	*c
    301 	)
    302 {
    303 	int say_bye;
    304 	blocking_pipe_header *req;
    305 
    306 	say_bye = FALSE;
    307 	while (!say_bye) {
    308 		req = receive_blocking_req_internal(c);
    309 		if (NULL == req) {
    310 			say_bye = TRUE;
    311 			continue;
    312 		}
    313 
    314 		DEBUG_REQUIRE(BLOCKING_REQ_MAGIC == req->magic_sig);
    315 
    316 		switch (req->rtype) {
    317 		case BLOCKING_GETADDRINFO:
    318 			if (blocking_getaddrinfo(c, req))
    319 				say_bye = TRUE;
    320 			break;
    321 
    322 		case BLOCKING_GETNAMEINFO:
    323 			if (blocking_getnameinfo(c, req))
    324 				say_bye = TRUE;
    325 			break;
    326 
    327 		default:
    328 			msyslog(LOG_ERR, "unknown req %d to blocking worker", req->rtype);
    329 			say_bye = TRUE;
    330 		}
    331 
    332 		free(req);
    333 	}
    334 
    335 	return 0;
    336 }
    337 
    338 
    339 /*
    340  * worker_idle_timer_fired()
    341  *
    342  * The parent starts this timer when the last pending response has been
    343  * received from the child, making it idle, and clears the timer when a
    344  * request is dispatched to the child.  Once the timer expires, the
    345  * child is sent packing.
    346  *
    347  * This is called when worker_idle_timer is nonzero and less than or
    348  * equal to current_time.
    349  */
    350 void
    351 worker_idle_timer_fired(void)
    352 {
    353 	u_int			idx;
    354 	blocking_child *	c;
    355 
    356 	DEBUG_REQUIRE(0 == intres_req_pending);
    357 
    358 	intres_timeout_req(0);
    359 	for (idx = 0; idx < blocking_children_alloc; idx++) {
    360 		c = blocking_children[idx];
    361 		if (NULL == c)
    362 			continue;
    363 		req_child_exit(c);
    364 	}
    365 }
    366 
    367 
    368 #else	/* !WORKER follows */
    369 int ntp_worker_nonempty_compilation_unit;
    370 #endif
    371