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