xref: /openbsd/usr.sbin/radiusd/radiusd_module.c (revision 882428cd)
1 /*	$OpenBSD: radiusd_module.c,v 1.26 2024/11/21 13:43:10 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* radiusd_module.c -- helper functions for radiusd modules */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/uio.h>
24 
25 #include <err.h>
26 #include <errno.h>
27 #include <event.h>
28 #include <fcntl.h>
29 #include <imsg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <unistd.h>
35 #include <pwd.h>
36 
37 #include "radiusd.h"
38 #include "radiusd_module.h"
39 #include "imsg_subr.h"
40 
41 static void	(*module_config_set) (void *, const char *, int,
42 		    char * const *) = NULL;
43 static void	(*module_start_module) (void *) = NULL;
44 static void	(*module_stop_module) (void *) = NULL;
45 static void	(*module_userpass) (void *, u_int, const char *, const char *)
46 		    = NULL;
47 static void	(*module_access_request) (void *, u_int, const u_char *,
48 		    size_t) = NULL;
49 static void	(*module_next_response) (void *, u_int, const u_char *,
50 		    size_t) = NULL;
51 static void	(*module_request_decoration) (void *, u_int, const u_char *,
52 		    size_t) = NULL;
53 static void	(*module_response_decoration) (void *, u_int, const u_char *,
54 		    size_t, const u_char *, size_t) = NULL;
55 static void	(*module_accounting_request) (void *, u_int, const u_char *,
56 		    size_t) = NULL;
57 static void	(*module_dispatch_control) (void *, struct imsg *) = NULL;
58 
59 struct module_base {
60 	void			*ctx;
61 	struct imsgbuf		 ibuf;
62 	bool			 priv_dropped;
63 
64 	/* Buffer for receiving the RADIUS packet */
65 	u_char			*radpkt;
66 	int			 radpktsiz;
67 	int			 radpktoff;
68 	u_char			*radpkt2;
69 	int			 radpkt2siz;	/* allocated size */
70 	int			 radpkt2len;	/* actual size */
71 
72 #ifdef USE_LIBEVENT
73 	struct module_imsgbuf	*module_imsgbuf;
74 	bool			 writeready;
75 	bool			 stopped;
76 	bool			 ev_onhandler;
77 	struct event		 ev;
78 #endif
79 };
80 
81 static int	 module_common_radpkt(struct module_base *, uint32_t, u_int,
82 		    const u_char *, size_t);
83 static int	 module_recv_imsg(struct module_base *);
84 static int	 module_imsg_handler(struct module_base *, struct imsg *);
85 #ifdef USE_LIBEVENT
86 static void	 module_on_event(int, short, void *);
87 #endif
88 static void	 module_reset_event(struct module_base *);
89 
90 struct module_base *
module_create(int sock,void * ctx,struct module_handlers * handler)91 module_create(int sock, void *ctx, struct module_handlers *handler)
92 {
93 	struct module_base	*base;
94 
95 	if ((base = calloc(1, sizeof(struct module_base))) == NULL)
96 		return (NULL);
97 
98 	if (imsgbuf_init(&base->ibuf, sock) == -1) {
99 		free(base);
100 		return (NULL);
101 	}
102 	base->ctx = ctx;
103 
104 	module_userpass = handler->userpass;
105 	module_access_request = handler->access_request;
106 	module_next_response = handler->next_response;
107 	module_config_set = handler->config_set;
108 	module_request_decoration = handler->request_decoration;
109 	module_response_decoration = handler->response_decoration;
110 	module_accounting_request = handler->accounting_request;
111 	module_start_module = handler->start;
112 	module_stop_module = handler->stop;
113 	module_dispatch_control = handler->dispatch_control;
114 
115 	return (base);
116 }
117 
118 void
module_start(struct module_base * base)119 module_start(struct module_base *base)
120 {
121 #ifdef USE_LIBEVENT
122 	int	 ival;
123 
124 	if ((ival = fcntl(base->ibuf.fd, F_GETFL)) == -1)
125 		err(1, "Failed to F_GETFL");
126 	if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) == -1)
127 		err(1, "Failed to setup NONBLOCK");
128 	event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base);
129 	event_add(&base->ev, NULL);
130 #endif
131 }
132 
133 int
module_run(struct module_base * base)134 module_run(struct module_base *base)
135 {
136 	int	 ret;
137 
138 	ret = module_recv_imsg(base);
139 	if (ret == 0)
140 		imsgbuf_flush(&base->ibuf);
141 
142 	return (ret);
143 }
144 
145 void
module_destroy(struct module_base * base)146 module_destroy(struct module_base *base)
147 {
148 	if (base != NULL) {
149 		free(base->radpkt);
150 		free(base->radpkt2);
151 		imsgbuf_clear(&base->ibuf);
152 	}
153 	free(base);
154 }
155 
156 void
module_load(struct module_base * base)157 module_load(struct module_base *base)
158 {
159 	struct radiusd_module_load_arg	 load;
160 
161 	memset(&load, 0, sizeof(load));
162 	if (module_userpass != NULL)
163 		load.cap |= RADIUSD_MODULE_CAP_USERPASS;
164 	if (module_access_request != NULL)
165 		load.cap |= RADIUSD_MODULE_CAP_ACCSREQ;
166 	if (module_next_response != NULL)
167 		load.cap |= RADIUSD_MODULE_CAP_NEXTRES;
168 	if (module_request_decoration != NULL)
169 		load.cap |= RADIUSD_MODULE_CAP_REQDECO;
170 	if (module_response_decoration != NULL)
171 		load.cap |= RADIUSD_MODULE_CAP_RESDECO;
172 	if (module_accounting_request != NULL)
173 		load.cap |= RADIUSD_MODULE_CAP_ACCTREQ;
174 	if (module_dispatch_control != NULL)
175 		load.cap |= RADIUSD_MODULE_CAP_CONTROL;
176 	imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load,
177 	    sizeof(load));
178 	imsgbuf_flush(&base->ibuf);
179 }
180 
181 void
module_drop_privilege(struct module_base * base,int nochroot)182 module_drop_privilege(struct module_base *base, int nochroot)
183 {
184 	struct passwd	*pw;
185 
186 	tzset();
187 
188 	/* Drop the privilege */
189 	if ((pw = getpwnam(RADIUSD_USER)) == NULL)
190 		goto on_fail;
191 	if (nochroot == 0 && chroot(pw->pw_dir) == -1)
192 		goto on_fail;
193 	if (chdir("/") == -1)
194 		goto on_fail;
195 	if (setgroups(1, &pw->pw_gid) ||
196 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
197 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
198 		goto on_fail;
199 	base->priv_dropped = true;
200 
201 on_fail:
202 	return;
203 }
204 
205 int
module_notify_secret(struct module_base * base,const char * secret)206 module_notify_secret(struct module_base *base, const char *secret)
207 {
208 	int		 ret;
209 
210 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET,
211 	    0, 0, -1, secret, strlen(secret) + 1);
212 	module_reset_event(base);
213 
214 	return (ret);
215 }
216 
217 int
module_send_message(struct module_base * base,uint32_t cmd,const char * fmt,...)218 module_send_message(struct module_base *base, uint32_t cmd, const char *fmt,
219     ...)
220 {
221 	char	*msg;
222 	va_list	 ap;
223 	int	 ret;
224 
225 	if (fmt == NULL)
226 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0);
227 	else {
228 		va_start(ap, fmt);
229 		vasprintf(&msg, fmt, ap);
230 		va_end(ap);
231 		if (msg == NULL)
232 			return (-1);
233 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg,
234 		    strlen(msg) + 1);
235 		free(msg);
236 	}
237 	module_reset_event(base);
238 
239 	return (ret);
240 }
241 
242 int
module_userpass_ok(struct module_base * base,u_int q_id,const char * msg)243 module_userpass_ok(struct module_base *base, u_int q_id, const char *msg)
244 {
245 	int		 ret;
246 	struct iovec	 iov[2];
247 
248 	iov[0].iov_base = &q_id;
249 	iov[0].iov_len = sizeof(q_id);
250 	iov[1].iov_base = (char *)msg;
251 	iov[1].iov_len = strlen(msg) + 1;
252 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK,
253 	    0, 0, -1, iov, 2);
254 	module_reset_event(base);
255 
256 	return (ret);
257 }
258 
259 int
module_userpass_fail(struct module_base * base,u_int q_id,const char * msg)260 module_userpass_fail(struct module_base *base, u_int q_id, const char *msg)
261 {
262 	int		 ret;
263 	struct iovec	 iov[2];
264 
265 	iov[0].iov_base = &q_id;
266 	iov[0].iov_len = sizeof(q_id);
267 	iov[1].iov_base = (char *)msg;
268 	iov[1].iov_len = strlen(msg) + 1;
269 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL,
270 	    0, 0, -1, iov, 2);
271 	module_reset_event(base);
272 
273 	return (ret);
274 }
275 
276 int
module_accsreq_answer(struct module_base * base,u_int q_id,const u_char * pkt,size_t pktlen)277 module_accsreq_answer(struct module_base *base, u_int q_id, const u_char *pkt,
278     size_t pktlen)
279 {
280 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER,
281 	    q_id, pkt, pktlen));
282 }
283 
284 int
module_accsreq_next(struct module_base * base,u_int q_id,const u_char * pkt,size_t pktlen)285 module_accsreq_next(struct module_base *base, u_int q_id, const u_char *pkt,
286     size_t pktlen)
287 {
288 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_NEXT,
289 	    q_id, pkt, pktlen));
290 }
291 
292 int
module_accsreq_aborted(struct module_base * base,u_int q_id)293 module_accsreq_aborted(struct module_base *base, u_int q_id)
294 {
295 	int	 ret;
296 
297 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED,
298 	    0, 0, -1, &q_id, sizeof(u_int));
299 	module_reset_event(base);
300 
301 	return (ret);
302 }
303 
304 int
module_reqdeco_done(struct module_base * base,u_int q_id,const u_char * pkt,size_t pktlen)305 module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
306     size_t pktlen)
307 {
308 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE,
309 	    q_id, pkt, pktlen));
310 }
311 
312 int
module_resdeco_done(struct module_base * base,u_int q_id,const u_char * pkt,size_t pktlen)313 module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
314     size_t pktlen)
315 {
316 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE,
317 	    q_id, pkt, pktlen));
318 }
319 
320 static int
module_common_radpkt(struct module_base * base,uint32_t imsg_type,u_int q_id,const u_char * pkt,size_t pktlen)321 module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id,
322     const u_char *pkt, size_t pktlen)
323 {
324 	int		 ret = 0, off = 0, len, siz;
325 	struct iovec	 iov[2];
326 	struct radiusd_module_radpkt_arg	 ans;
327 
328 	len = pktlen;
329 	ans.q_id = q_id;
330 	ans.pktlen = pktlen;
331 	ans.final = false;
332 
333 	while (!ans.final) {
334 		siz = MAX_IMSGSIZE - sizeof(ans);
335 		if (len - off <= siz) {
336 			ans.final = true;
337 			siz = len - off;
338 		}
339 		iov[0].iov_base = &ans;
340 		iov[0].iov_len = sizeof(ans);
341 		if (siz > 0) {
342 			iov[1].iov_base = (u_char *)pkt + off;
343 			iov[1].iov_len = siz;
344 		}
345 		ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov,
346 		    (siz > 0)? 2 : 1);
347 		if (ret == -1)
348 			break;
349 		off += siz;
350 	}
351 	module_reset_event(base);
352 
353 	return (ret);
354 }
355 
356 static int
module_recv_imsg(struct module_base * base)357 module_recv_imsg(struct module_base *base)
358 {
359 	ssize_t		 n;
360 	struct imsg	 imsg;
361 
362 	if ((n = imsgbuf_read(&base->ibuf)) != 1) {
363 		if (n == -1)
364 			syslog(LOG_ERR, "%s: imsgbuf_read(): %m", __func__);
365 		module_stop(base);
366 		return (-1);
367 	}
368 	for (;;) {
369 		if ((n = imsg_get(&base->ibuf, &imsg)) == -1) {
370 			syslog(LOG_ERR, "%s: imsg_get(): %m", __func__);
371 			module_stop(base);
372 			return (-1);
373 		}
374 		if (n == 0)
375 			break;
376 		module_imsg_handler(base, &imsg);
377 		imsg_free(&imsg);
378 	}
379 	module_reset_event(base);
380 
381 	return (0);
382 }
383 
384 static int
module_imsg_handler(struct module_base * base,struct imsg * imsg)385 module_imsg_handler(struct module_base *base, struct imsg *imsg)
386 {
387 	ssize_t	 datalen;
388 
389 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
390 	switch (imsg->hdr.type) {
391 	case IMSG_RADIUSD_MODULE_SET_CONFIG:
392 	    {
393 		struct radiusd_module_set_arg	 *arg;
394 		struct radiusd_module_object	 *val;
395 		u_int				  i;
396 		size_t				  off;
397 		char				**argv;
398 
399 		arg = (struct radiusd_module_set_arg *)imsg->data;
400 		off = sizeof(struct radiusd_module_set_arg);
401 
402 		if ((argv = calloc(sizeof(const char *), arg->nparamval))
403 		    == NULL) {
404 			module_send_message(base, IMSG_NG,
405 			    "Out of memory: %s", strerror(errno));
406 			break;
407 		}
408 		for (i = 0; i < arg->nparamval; i++) {
409 			if (datalen - off <
410 			    sizeof(struct radiusd_module_object))
411 				break;
412 			val = (struct radiusd_module_object *)
413 			    ((caddr_t)imsg->data + off);
414 			if (datalen - off < val->size)
415 				break;
416 			argv[i] = (char *)(val + 1);
417 			off += val->size;
418 		}
419 		if (i >= arg->nparamval)
420 			module_config_set(base->ctx, arg->paramname,
421 			    arg->nparamval, argv);
422 		else
423 			module_send_message(base, IMSG_NG,
424 			    "Internal protocol error");
425 		free(argv);
426 
427 		break;
428 	    }
429 	case IMSG_RADIUSD_MODULE_START:
430 		if (module_start_module != NULL) {
431 			module_start_module(base->ctx);
432 			if (!base->priv_dropped) {
433 				syslog(LOG_ERR, "Module tried to start with "
434 				    "root privileges");
435 				abort();
436 			}
437 		} else {
438 			if (!base->priv_dropped) {
439 				syslog(LOG_ERR, "Module tried to start with "
440 				    "root privileges");
441 				abort();
442 			}
443 			module_send_message(base, IMSG_OK, NULL);
444 		}
445 		break;
446 	case IMSG_RADIUSD_MODULE_STOP:
447 		module_stop(base);
448 		break;
449 	case IMSG_RADIUSD_MODULE_USERPASS:
450 	    {
451 		struct radiusd_module_userpass_arg *userpass;
452 
453 		if (module_userpass == NULL) {
454 			syslog(LOG_ERR, "Received USERPASS message, but "
455 			    "module doesn't support");
456 			break;
457 		}
458 		if (datalen <
459 		    (ssize_t)sizeof(struct radiusd_module_userpass_arg)) {
460 			syslog(LOG_ERR, "Received USERPASS message, but "
461 			    "length is wrong");
462 			break;
463 		}
464 		userpass = (struct radiusd_module_userpass_arg *)imsg->data;
465 		module_userpass(base->ctx, userpass->q_id, userpass->user,
466 		    (userpass->has_pass)? userpass->pass : NULL);
467 		explicit_bzero(userpass,
468 		    sizeof(struct radiusd_module_userpass_arg));
469 		break;
470 	    }
471 	case IMSG_RADIUSD_MODULE_ACCSREQ:
472 	case IMSG_RADIUSD_MODULE_NEXTRES:
473 	case IMSG_RADIUSD_MODULE_REQDECO:
474 	case IMSG_RADIUSD_MODULE_RESDECO0_REQ:
475 	case IMSG_RADIUSD_MODULE_RESDECO:
476 	case IMSG_RADIUSD_MODULE_ACCTREQ:
477 	    {
478 		struct radiusd_module_radpkt_arg	*accessreq;
479 		int					 chunklen;
480 		const char				*typestr;
481 
482 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) {
483 			if (module_access_request == NULL) {
484 				syslog(LOG_ERR, "Received ACCSREQ message, but "
485 				    "module doesn't support");
486 				break;
487 			}
488 			typestr = "ACCSREQ";
489 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES) {
490 			if (module_next_response == NULL) {
491 				syslog(LOG_ERR, "Received NEXTRES message, but "
492 				    "module doesn't support");
493 				break;
494 			}
495 			typestr = "NEXTRES";
496 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCTREQ) {
497 			if (module_accounting_request == NULL) {
498 				syslog(LOG_ERR, "Received ACCTREQ message, but "
499 				    "module doesn't support");
500 				break;
501 			}
502 			typestr = "ACCTREQ";
503 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) {
504 			if (module_request_decoration == NULL) {
505 				syslog(LOG_ERR, "Received REQDECO message, but "
506 				    "module doesn't support");
507 				break;
508 			}
509 			typestr = "REQDECO";
510 		} else {
511 			if (module_response_decoration == NULL) {
512 				syslog(LOG_ERR, "Received RESDECO message, but "
513 				    "module doesn't support");
514 				break;
515 			}
516 			if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ)
517 				typestr = "RESDECO0_REQ";
518 			else
519 				typestr = "RESDECO";
520 		}
521 
522 		if (datalen <
523 		    (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) {
524 			syslog(LOG_ERR, "Received %s message, but "
525 			    "length is wrong", typestr);
526 			break;
527 		}
528 		accessreq = (struct radiusd_module_radpkt_arg *)imsg->data;
529 		if (base->radpktsiz < accessreq->pktlen) {
530 			u_char *nradpkt;
531 			if ((nradpkt = realloc(base->radpkt,
532 			    accessreq->pktlen)) == NULL) {
533 				syslog(LOG_ERR, "Could not handle received "
534 				    "%s message: %m", typestr);
535 				base->radpktoff = 0;
536 				goto accsreq_out;
537 			}
538 			base->radpkt = nradpkt;
539 			base->radpktsiz = accessreq->pktlen;
540 		}
541 		chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg);
542 		if (chunklen > base->radpktsiz - base->radpktoff){
543 			syslog(LOG_ERR,
544 			    "Could not handle received %s message: "
545 			    "received length is too big", typestr);
546 			base->radpktoff = 0;
547 			goto accsreq_out;
548 		}
549 		memcpy(base->radpkt + base->radpktoff,
550 		    (caddr_t)(accessreq + 1), chunklen);
551 		base->radpktoff += chunklen;
552 		if (!accessreq->final)
553 			goto accsreq_out;
554 		if (base->radpktoff != accessreq->pktlen) {
555 			syslog(LOG_ERR,
556 			    "Could not handle received %s "
557 			    "message: length is mismatch", typestr);
558 			base->radpktoff = 0;
559 			goto accsreq_out;
560 		}
561 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ)
562 			module_access_request(base->ctx, accessreq->q_id,
563 			    base->radpkt, base->radpktoff);
564 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES)
565 			module_next_response(base->ctx, accessreq->q_id,
566 			    base->radpkt, base->radpktoff);
567 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO)
568 			module_request_decoration(base->ctx, accessreq->q_id,
569 			    base->radpkt, base->radpktoff);
570 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ) {
571 			/* preserve request */
572 			if (base->radpktoff > base->radpkt2siz) {
573 				u_char *nradpkt;
574 				if ((nradpkt = realloc(base->radpkt2,
575 				    base->radpktoff)) == NULL) {
576 					syslog(LOG_ERR, "Could not handle "
577 					    "received %s message: %m", typestr);
578 					base->radpktoff = 0;
579 					goto accsreq_out;
580 				}
581 				base->radpkt2 = nradpkt;
582 				base->radpkt2siz = base->radpktoff;
583 			}
584 			memcpy(base->radpkt2, base->radpkt, base->radpktoff);
585 			base->radpkt2len = base->radpktoff;
586 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO) {
587 			module_response_decoration(base->ctx, accessreq->q_id,
588 			    base->radpkt2, base->radpkt2len, base->radpkt,
589 			    base->radpktoff);
590 			base->radpkt2len = 0;
591 		} else
592 			module_accounting_request(base->ctx, accessreq->q_id,
593 			    base->radpkt, base->radpktoff);
594 		base->radpktoff = 0;
595  accsreq_out:
596 		break;
597 	    }
598 	case IMSG_RADIUSD_MODULE_CTRL_UNBIND:
599 		goto forward_msg;
600 		break;
601 	default:
602 		if (imsg->hdr.type >= IMSG_RADIUSD_MODULE_MIN) {
603  forward_msg:
604 			if (module_dispatch_control == NULL) {
605 				const char msg[] =
606 				    "the module doesn't handle any controls";
607 				imsg_compose(&base->ibuf, IMSG_NG,
608 				    imsg->hdr.peerid, 0, -1, msg, sizeof(msg));
609 			} else
610 				module_dispatch_control(base->ctx, imsg);
611 		}
612 	}
613 
614 	return (0);
615 }
616 
617 void
module_stop(struct module_base * base)618 module_stop(struct module_base *base)
619 {
620 	if (module_stop_module != NULL)
621 		module_stop_module(base->ctx);
622 #ifdef USE_LIBEVENT
623 	event_del(&base->ev);
624 	base->stopped = true;
625 #endif
626 	close(base->ibuf.fd);
627 }
628 
629 #ifdef USE_LIBEVENT
630 static void
module_on_event(int fd,short evmask,void * ctx)631 module_on_event(int fd, short evmask, void *ctx)
632 {
633 	struct module_base	*base = ctx;
634 	int			 ret;
635 
636 	base->ev_onhandler = true;
637 	if (evmask & EV_WRITE) {
638 		base->writeready = true;
639 		if (imsgbuf_write(&base->ibuf) == -1) {
640 			syslog(LOG_ERR, "%s: imsgbuf_write: %m", __func__);
641 			module_stop(base);
642 			return;
643 		}
644 		base->writeready = false;
645 	}
646 	if (evmask & EV_READ) {
647 		ret = module_recv_imsg(base);
648 		if (ret < 0)
649 			return;
650 	}
651 	base->ev_onhandler = false;
652 	module_reset_event(base);
653 	return;
654 }
655 #endif
656 
657 static void
module_reset_event(struct module_base * base)658 module_reset_event(struct module_base *base)
659 {
660 #ifdef USE_LIBEVENT
661 	short		 evmask = 0;
662 	struct timeval	*tvp = NULL, tv = { 0, 0 };
663 
664 	if (base->ev_onhandler)
665 		return;
666 	if (base->stopped)
667 		return;
668 	event_del(&base->ev);
669 
670 	evmask |= EV_READ;
671 	if (imsgbuf_queuelen(&base->ibuf) > 0) {
672 		if (!base->writeready)
673 			evmask |= EV_WRITE;
674 		else
675 			tvp = &tv;	/* fire immediately */
676 	}
677 	event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base);
678 	if (event_add(&base->ev, tvp) == -1)
679 		syslog(LOG_ERR, "event_add() failed in %s()", __func__);
680 #endif
681 }
682 
683 int
module_imsg_compose(struct module_base * base,uint32_t type,uint32_t id,pid_t pid,int fd,const void * data,size_t datalen)684 module_imsg_compose(struct module_base *base, uint32_t type, uint32_t id,
685     pid_t pid, int fd, const void *data, size_t datalen)
686 {
687 	int	 ret;
688 
689 	if ((ret = imsg_compose(&base->ibuf, type, id, pid, fd, data, datalen))
690 	    != -1)
691 		module_reset_event(base);
692 
693 	return (ret);
694 }
695 
696 int
module_imsg_composev(struct module_base * base,uint32_t type,uint32_t id,pid_t pid,int fd,const struct iovec * iov,int iovcnt)697 module_imsg_composev(struct module_base *base, uint32_t type, uint32_t id,
698     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
699 {
700 	int	 ret;
701 
702 	if ((ret = imsg_composev(&base->ibuf, type, id, pid, fd, iov, iovcnt))
703 	    != -1)
704 		module_reset_event(base);
705 
706 	return (ret);
707 }
708