1 /* $OpenBSD: resolvd.c,v 1.32 2022/12/09 18:22:35 tb Exp $ */ 2 /* 3 * Copyright (c) 2021 Florian Obser <florian@openbsd.org> 4 * Copyright (c) 2021 Theo de Raadt <deraadt@openbsd.org> 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 #include <sys/types.h> 20 #include <sys/event.h> 21 #include <sys/socket.h> 22 #include <sys/stat.h> 23 #include <sys/syslog.h> 24 #include <sys/time.h> 25 #include <sys/un.h> 26 #include <netdb.h> 27 28 #include <arpa/inet.h> 29 #include <netinet/in.h> 30 #include <net/if.h> 31 #include <net/route.h> 32 33 #include <err.h> 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <event.h> 37 #include <signal.h> 38 #include <stddef.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 44 #define ROUTE_SOCKET_BUF_SIZE 16384 45 #define ASR_MAXNS 10 46 #define _PATH_LOCKFILE "/dev/resolvd.lock" 47 #define _PATH_UNWIND_SOCKET "/dev/unwind.sock" 48 #define _PATH_RESCONF "/etc/resolv.conf" 49 #define _PATH_RESCONF_NEW "/etc/resolv.conf.new" 50 51 #ifndef nitems 52 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 53 #endif 54 55 __dead void usage(void); 56 57 struct rdns_proposal { 58 uint32_t if_index; 59 int af; 60 int prio; 61 char ip[INET6_ADDRSTRLEN]; 62 }; 63 64 void route_receive(int); 65 void handle_route_message(struct rt_msghdr *, struct sockaddr **); 66 void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); 67 void solicit_dns_proposals(int); 68 void regen_resolvconf(const char *reason); 69 int cmp(const void *, const void *); 70 int findslot(struct rdns_proposal *); 71 void zeroslot(struct rdns_proposal *); 72 73 struct rdns_proposal learned[ASR_MAXNS]; 74 int resolvfd = -1; 75 int newkevent = 1; 76 77 #ifndef SMALL 78 int open_unwind_ctl(void); 79 int check_unwind = 1, unwind_running = 0; 80 81 struct loggers { 82 __dead void (*err)(int, const char *, ...) 83 __attribute__((__format__ (printf, 2, 3))); 84 __dead void (*errx)(int, const char *, ...) 85 __attribute__((__format__ (printf, 2, 3))); 86 void (*warn)(const char *, ...) 87 __attribute__((__format__ (printf, 1, 2))); 88 void (*warnx)(const char *, ...) 89 __attribute__((__format__ (printf, 1, 2))); 90 void (*info)(const char *, ...) 91 __attribute__((__format__ (printf, 1, 2))); 92 void (*debug)(const char *, ...) 93 __attribute__((__format__ (printf, 1, 2))); 94 }; 95 96 void warnx_verbose(const char *, ...) 97 __attribute__((__format__ (printf, 1, 2))); 98 99 const struct loggers conslogger = { 100 err, 101 errx, 102 warn, 103 warnx, 104 warnx_verbose, /* info */ 105 warnx_verbose /* debug */ 106 }; 107 108 __dead void syslog_err(int, const char *, ...) 109 __attribute__((__format__ (printf, 2, 3))); 110 __dead void syslog_errx(int, const char *, ...) 111 __attribute__((__format__ (printf, 2, 3))); 112 void syslog_warn(const char *, ...) 113 __attribute__((__format__ (printf, 1, 2))); 114 void syslog_warnx(const char *, ...) 115 __attribute__((__format__ (printf, 1, 2))); 116 void syslog_info(const char *, ...) 117 __attribute__((__format__ (printf, 1, 2))); 118 void syslog_debug(const char *, ...) 119 __attribute__((__format__ (printf, 1, 2))); 120 void syslog_vstrerror(int, int, const char *, va_list) 121 __attribute__((__format__ (printf, 3, 0))); 122 123 int verbose = 0; 124 125 const struct loggers syslogger = { 126 syslog_err, 127 syslog_errx, 128 syslog_warn, 129 syslog_warnx, 130 syslog_info, 131 syslog_debug 132 }; 133 134 const struct loggers *logger = &conslogger; 135 136 #define lerr(_e, _f...) logger->err((_e), _f) 137 #define lerrx(_e, _f...) logger->errx((_e), _f) 138 #define lwarn(_f...) logger->warn(_f) 139 #define lwarnx(_f...) logger->warnx(_f) 140 #define linfo(_f...) logger->info(_f) 141 #define ldebug(_f...) logger->debug(_f) 142 #else 143 #define lerr(x...) do {} while(0) 144 #define lerrx(x...) do {} while(0) 145 #define lwarn(x...) do {} while(0) 146 #define lwarnx(x...) do {} while(0) 147 #define linfo(x...) do {} while(0) 148 #define ldebug(x...) do {} while(0) 149 #endif /* SMALL */ 150 151 enum { 152 KQ_ROUTE, 153 KQ_RESOLVE_CONF, 154 #ifndef SMALL 155 KQ_UNWIND, 156 #endif 157 KQ_TOTAL 158 }; 159 160 int 161 main(int argc, char *argv[]) 162 { 163 struct timespec one = {1, 0}; 164 int kq, ch, debug = 0, routesock; 165 int rtfilter, nready, lockfd; 166 struct kevent kev[KQ_TOTAL]; 167 #ifndef SMALL 168 int unwindsock = -1; 169 #endif 170 171 while ((ch = getopt(argc, argv, "dv")) != -1) { 172 switch (ch) { 173 case 'd': 174 debug = 1; 175 break; 176 case 'v': 177 #ifndef SMALL 178 verbose++; 179 #endif 180 break; 181 default: 182 usage(); 183 } 184 } 185 186 argc -= optind; 187 argv += optind; 188 if (argc > 0) 189 usage(); 190 191 /* Check for root privileges. */ 192 if (geteuid()) 193 errx(1, "need root privileges"); 194 195 lockfd = open(_PATH_LOCKFILE, O_CREAT|O_RDWR|O_EXLOCK|O_NONBLOCK, 0600); 196 if (lockfd == -1) { 197 if (errno == EAGAIN) 198 errx(1, "already running"); 199 err(1, "%s", _PATH_LOCKFILE); 200 } 201 202 if (!debug) 203 daemon(0, 0); 204 205 #ifndef SMALL 206 if (!debug) { 207 openlog("resolvd", LOG_PID|LOG_NDELAY, LOG_DAEMON); 208 logger = &syslogger; 209 } 210 #endif 211 212 signal(SIGHUP, SIG_IGN); 213 214 if ((routesock = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) 215 lerr(1, "route socket"); 216 217 rtfilter = ROUTE_FILTER(RTM_PROPOSAL) | ROUTE_FILTER(RTM_IFANNOUNCE); 218 if (setsockopt(routesock, AF_ROUTE, ROUTE_MSGFILTER, &rtfilter, 219 sizeof(rtfilter)) == -1) 220 lerr(1, "setsockopt(ROUTE_MSGFILTER)"); 221 222 solicit_dns_proposals(routesock); 223 224 if (unveil(_PATH_RESCONF, "rwc") == -1) 225 lerr(1, "unveil " _PATH_RESCONF); 226 if (unveil(_PATH_RESCONF_NEW, "rwc") == -1) 227 lerr(1, "unveil " _PATH_RESCONF_NEW); 228 #ifndef SMALL 229 if (unveil(_PATH_UNWIND_SOCKET, "w") == -1) 230 lerr(1, "unveil " _PATH_UNWIND_SOCKET); 231 #endif 232 233 if (pledge("stdio unix rpath wpath cpath", NULL) == -1) 234 lerr(1, "pledge"); 235 236 if ((kq = kqueue()) == -1) 237 lerr(1, "kqueue"); 238 239 for(;;) { 240 int i; 241 242 #ifndef SMALL 243 if (!unwind_running && check_unwind) { 244 check_unwind = 0; 245 unwindsock = open_unwind_ctl(); 246 unwind_running = unwindsock != -1; 247 if (unwind_running) 248 regen_resolvconf("new unwind"); 249 } 250 #endif 251 252 if (newkevent) { 253 int kevi = 0; 254 255 if (routesock != -1) 256 EV_SET(&kev[kevi++], routesock, EVFILT_READ, 257 EV_ADD, 0, 0, 258 (void *)KQ_ROUTE); 259 if (resolvfd != -1) 260 EV_SET(&kev[kevi++], resolvfd, EVFILT_VNODE, 261 EV_ADD | EV_CLEAR, 262 NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE | NOTE_WRITE, 0, 263 (void *)KQ_RESOLVE_CONF); 264 265 #ifndef SMALL 266 if (unwind_running) { 267 EV_SET(&kev[kevi++], unwindsock, EVFILT_READ, 268 EV_ADD, 0, 0, 269 (void *)KQ_UNWIND); 270 } 271 #endif /* SMALL */ 272 273 if (kevent(kq, kev, kevi, NULL, 0, NULL) == -1) 274 lerr(1, "kevent"); 275 newkevent = 0; 276 } 277 278 nready = kevent(kq, NULL, 0, kev, KQ_TOTAL, NULL); 279 if (nready == -1) { 280 if (errno == EINTR) 281 continue; 282 lerr(1, "kevent"); 283 } 284 285 if (nready == 0) 286 continue; 287 288 for (i = 0; i < nready; i++) { 289 unsigned short fflags = kev[i].fflags; 290 291 switch ((int)(long)kev[i].udata) { 292 case KQ_ROUTE: 293 route_receive(routesock); 294 break; 295 296 case KQ_RESOLVE_CONF: 297 if (fflags & (NOTE_DELETE | NOTE_RENAME)) { 298 close(resolvfd); 299 resolvfd = -1; 300 regen_resolvconf("file delete/rename"); 301 } 302 if (fflags & (NOTE_TRUNCATE | NOTE_WRITE)) { 303 /* some editors truncate and write */ 304 if (fflags & NOTE_TRUNCATE) 305 nanosleep(&one, NULL); 306 regen_resolvconf("file trunc/write"); 307 } 308 break; 309 310 #ifndef SMALL 311 case KQ_UNWIND: { 312 uint8_t buf[1024]; 313 ssize_t n; 314 315 n = read(unwindsock, buf, sizeof(buf)); 316 if (n == -1) { 317 if (errno == EAGAIN || errno == EINTR) 318 continue; 319 } 320 if (n == 0 || n == -1) { 321 if (n == -1) 322 check_unwind = 1; 323 newkevent = 1; 324 close(unwindsock); 325 unwindsock = -1; 326 unwind_running = 0; 327 regen_resolvconf("unwind closed"); 328 } else 329 lwarnx("read %ld from unwind ctl", n); 330 break; 331 } 332 #endif 333 334 default: 335 lwarnx("unknown kqueue event on %lu", 336 kev[i].ident); 337 } 338 } 339 } 340 return 0; 341 } 342 343 __dead void 344 usage(void) 345 { 346 fprintf(stderr, "usage: resolvd [-dv]\n"); 347 exit(1); 348 } 349 350 void 351 route_receive(int fd) 352 { 353 uint8_t rsock_buf[ROUTE_SOCKET_BUF_SIZE]; 354 struct sockaddr *sa, *rti_info[RTAX_MAX]; 355 struct rt_msghdr *rtm; 356 ssize_t n; 357 358 rtm = (struct rt_msghdr *) rsock_buf; 359 if ((n = read(fd, rsock_buf, sizeof(rsock_buf))) == -1) { 360 if (errno == EAGAIN || errno == EINTR) 361 return; 362 lwarn("%s: read error", __func__); 363 return; 364 } 365 366 if (n == 0) 367 lerr(1, "routing socket closed"); 368 369 if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { 370 lwarnx("partial rtm of %zd in buffer", n); 371 return; 372 } 373 374 if (rtm->rtm_version != RTM_VERSION) 375 return; 376 377 if (rtm->rtm_pid == getpid()) 378 return; 379 380 sa = (struct sockaddr *)(rsock_buf + rtm->rtm_hdrlen); 381 get_rtaddrs(rtm->rtm_addrs, sa, rti_info); 382 handle_route_message(rtm, rti_info); 383 } 384 385 void 386 zeroslot(struct rdns_proposal *tab) 387 { 388 tab->prio = 0; 389 tab->af = 0; 390 tab->if_index = 0; 391 tab->ip[0] = '\0'; 392 } 393 394 int 395 findslot(struct rdns_proposal *tab) 396 { 397 int i; 398 399 for (i = 0; i < ASR_MAXNS; i++) 400 if (tab[i].prio == 0) 401 return i; 402 403 /* New proposals might be important, so replace the last slot */ 404 i = ASR_MAXNS - 1; 405 zeroslot(&tab[i]); 406 return i; 407 } 408 409 void 410 handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) 411 { 412 struct rdns_proposal learning[nitems(learned)]; 413 struct sockaddr_rtdns *rtdns; 414 struct if_announcemsghdr *ifan; 415 size_t addrsz; 416 int rdns_count, af, i; 417 char *src; 418 419 memcpy(learning, learned, sizeof learned); 420 421 switch (rtm->rtm_type) { 422 case RTM_IFANNOUNCE: 423 ifan = (struct if_announcemsghdr *)rtm; 424 if (ifan->ifan_what == IFAN_ARRIVAL) 425 return; 426 /* Delete proposals learned from departing interfaces */ 427 for (i = 0; i < ASR_MAXNS; i++) 428 if (learning[i].if_index == ifan->ifan_index) 429 zeroslot(&learning[i]); 430 break; 431 case RTM_PROPOSAL: 432 if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { 433 #ifndef SMALL 434 check_unwind = 1; 435 #endif /* SMALL */ 436 return; 437 } 438 439 if (!(rtm->rtm_addrs & RTA_DNS)) 440 return; 441 442 rtdns = (struct sockaddr_rtdns*)rti_info[RTAX_DNS]; 443 src = rtdns->sr_dns; 444 af = rtdns->sr_family; 445 446 switch (af) { 447 case AF_INET: 448 addrsz = sizeof(struct in_addr); 449 break; 450 case AF_INET6: 451 addrsz = sizeof(struct in6_addr); 452 break; 453 default: 454 lwarnx("ignoring invalid RTM_PROPOSAL"); 455 return; 456 } 457 458 if ((rtdns->sr_len - 2) % addrsz != 0) { 459 lwarnx("ignoring invalid RTM_PROPOSAL"); 460 return; 461 } 462 rdns_count = (rtdns->sr_len - 463 offsetof(struct sockaddr_rtdns, sr_dns)) / addrsz; 464 465 /* New proposal from interface means previous proposals expire */ 466 for (i = 0; i < ASR_MAXNS; i++) 467 if (learning[i].af == af && 468 learning[i].if_index == rtm->rtm_index) 469 zeroslot(&learning[i]); 470 471 /* Add the new proposals */ 472 for (i = 0; i < rdns_count; i++) { 473 struct sockaddr_storage ss; 474 struct sockaddr_in *sin = (struct sockaddr_in *)&ss; 475 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; 476 int new, err; 477 478 memset(&ss, 0, sizeof(ss)); 479 ss.ss_family = af; 480 new = findslot(learning); 481 switch (af) { 482 case AF_INET: 483 memcpy(&sin->sin_addr, src, addrsz); 484 ss.ss_len = sizeof(*sin); 485 break; 486 case AF_INET6: 487 memcpy(&sin6->sin6_addr, src, addrsz); 488 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) 489 sin6->sin6_scope_id = rtm->rtm_index; 490 ss.ss_len = sizeof(*sin6); 491 break; 492 } 493 src += addrsz; 494 495 if ((err = getnameinfo((struct sockaddr *)&ss, ss.ss_len, 496 learning[new].ip, sizeof(learning[new].ip), 497 NULL, 0, NI_NUMERICHOST)) == 0) { 498 learning[new].prio = rtm->rtm_priority; 499 learning[new].if_index = rtm->rtm_index; 500 learning[new].af = af; 501 } else 502 lwarnx("getnameinfo: %s", gai_strerror(err)); 503 } 504 break; 505 default: 506 return; 507 } 508 509 /* Sort proposals, based upon priority */ 510 if (mergesort(learning, ASR_MAXNS, sizeof(learning[0]), cmp) == -1) { 511 lwarn("mergesort"); 512 return; 513 } 514 515 /* Eliminate duplicate IPs per interface */ 516 for (i = 0; i < ASR_MAXNS - 1; i++) { 517 int j; 518 519 if (learning[i].prio == 0) 520 continue; 521 522 for (j = i + 1; j < ASR_MAXNS; j++) { 523 if (learning[i].if_index == learning[j].if_index && 524 strcmp(learning[i].ip, learning[j].ip) == 0) { 525 zeroslot(&learning[j]); 526 } 527 } 528 } 529 530 /* If proposal result is different, rebuild the file */ 531 if (memcmp(learned, learning, sizeof(learned)) != 0) { 532 memcpy(learned, learning, sizeof(learned)); 533 regen_resolvconf("route proposals"); 534 } 535 } 536 537 #define ROUNDUP(a) \ 538 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) 539 540 void 541 get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) 542 { 543 int i; 544 545 for (i = 0; i < RTAX_MAX; i++) { 546 if (addrs & (1 << i)) { 547 rti_info[i] = sa; 548 sa = (struct sockaddr *)((char *)(sa) + 549 ROUNDUP(sa->sa_len)); 550 } else 551 rti_info[i] = NULL; 552 } 553 } 554 555 void 556 solicit_dns_proposals(int routesock) 557 { 558 struct rt_msghdr rtm; 559 struct iovec iov[1]; 560 int iovcnt = 0; 561 562 memset(&rtm, 0, sizeof(rtm)); 563 564 rtm.rtm_version = RTM_VERSION; 565 rtm.rtm_type = RTM_PROPOSAL; 566 rtm.rtm_msglen = sizeof(rtm); 567 rtm.rtm_tableid = 0; 568 rtm.rtm_index = 0; 569 rtm.rtm_seq = arc4random(); 570 rtm.rtm_priority = RTP_PROPOSAL_SOLICIT; 571 572 iov[iovcnt].iov_base = &rtm; 573 iov[iovcnt++].iov_len = sizeof(rtm); 574 575 if (writev(routesock, iov, iovcnt) == -1) 576 lwarn("failed to send solicitation"); 577 } 578 579 void 580 regen_resolvconf(const char *why) 581 { 582 struct iovec iov[UIO_MAXIOV]; 583 int i, fd, len, iovcnt = 0; 584 585 linfo("rebuilding: %s", why); 586 587 if ((fd = open(_PATH_RESCONF_NEW, O_CREAT|O_TRUNC|O_RDWR, 0644)) == -1) { 588 lwarn(_PATH_RESCONF_NEW); 589 return; 590 } 591 592 memset(iov, 0, sizeof(iov)); 593 594 #ifndef SMALL 595 if (unwind_running) { 596 len = asprintf((char **)&iov[iovcnt].iov_base, 597 "nameserver 127.0.0.1 # resolvd: unwind\n"); 598 if (len < 0) { 599 lwarn("asprintf"); 600 goto err; 601 } 602 iov[iovcnt++].iov_len = len; 603 } 604 605 #endif /* SMALL */ 606 for (i = 0; i < ASR_MAXNS; i++) { 607 if (learned[i].prio != 0) { 608 char ifnambuf[IF_NAMESIZE], *ifnam; 609 610 ifnam = if_indextoname(learned[i].if_index, 611 ifnambuf); 612 len = asprintf((char **)&iov[iovcnt].iov_base, 613 "%snameserver %s # resolvd: %s\n", 614 #ifndef SMALL 615 unwind_running ? "#" : "", 616 #else 617 "", 618 #endif 619 learned[i].ip, 620 ifnam ? ifnam : ""); 621 if (len < 0) { 622 lwarn("asprintf"); 623 goto err; 624 } 625 iov[iovcnt++].iov_len = len; 626 } 627 } 628 629 /* Replay user-managed lines from old resolv.conf file */ 630 if (resolvfd == -1) 631 resolvfd = open(_PATH_RESCONF, O_RDWR); 632 if (resolvfd != -1) { 633 char *line = NULL; 634 size_t linesize = 0; 635 ssize_t linelen; 636 FILE *fp; 637 int fd2; 638 639 if ((fd2 = dup(resolvfd)) == -1) 640 goto err; 641 lseek(fd2, 0, SEEK_SET); 642 fp = fdopen(fd2, "r"); 643 if (fp == NULL) { 644 close(fd2); 645 goto err; 646 } 647 while ((linelen = getline(&line, &linesize, fp)) != -1) { 648 char *end = strchr(line, '\n'); 649 if (end) 650 *end = '\0'; 651 if (strstr(line, "# resolvd: ")) 652 continue; 653 len = asprintf((char **)&iov[iovcnt].iov_base, "%s\n", 654 line); 655 if (len < 0) { 656 lwarn("asprintf"); 657 free(line); 658 fclose(fp); 659 goto err; 660 } 661 iov[iovcnt++].iov_len = len; 662 if (iovcnt >= UIO_MAXIOV) { 663 lwarnx("too many user-managed lines"); 664 free(line); 665 fclose(fp); 666 goto err; 667 } 668 } 669 free(line); 670 fclose(fp); 671 } 672 673 if (iovcnt > 0) { 674 if (writev(fd, iov, iovcnt) == -1) { 675 lwarn("writev"); 676 goto err; 677 } 678 } 679 680 if (fsync(fd) == -1) { 681 lwarn("fsync"); 682 goto err; 683 } 684 if (rename(_PATH_RESCONF_NEW, _PATH_RESCONF) == -1) 685 goto err; 686 687 if (resolvfd == -1) { 688 close(fd); 689 resolvfd = open(_PATH_RESCONF, O_RDWR); 690 } else { 691 dup2(fd, resolvfd); 692 close(fd); 693 } 694 695 newkevent = 1; 696 goto out; 697 698 err: 699 if (fd != -1) 700 close(fd); 701 unlink(_PATH_RESCONF_NEW); 702 out: 703 for (i = 0; i < iovcnt; i++) 704 free(iov[i].iov_base); 705 706 } 707 708 int 709 cmp(const void *a, const void *b) 710 { 711 const struct rdns_proposal *rpa = a, *rpb = b; 712 713 return (rpa->prio < rpb->prio) ? -1 : (rpa->prio > rpb->prio); 714 } 715 716 #ifndef SMALL 717 int 718 open_unwind_ctl(void) 719 { 720 static struct sockaddr_un sun; 721 int s; 722 723 if (sun.sun_family == 0) { 724 sun.sun_family = AF_UNIX; 725 strlcpy(sun.sun_path, _PATH_UNWIND_SOCKET, sizeof(sun.sun_path)); 726 } 727 728 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) { 729 if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 730 close(s); 731 s = -1; 732 } 733 } 734 newkevent = 1; 735 return s; 736 } 737 738 void 739 syslog_vstrerror(int e, int priority, const char *fmt, va_list ap) 740 { 741 char *s; 742 743 if (vasprintf(&s, fmt, ap) == -1) { 744 syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror"); 745 exit(1); 746 } 747 syslog(priority, "%s: %s", s, strerror(e)); 748 free(s); 749 } 750 751 __dead void 752 syslog_err(int ecode, const char *fmt, ...) 753 { 754 va_list ap; 755 756 va_start(ap, fmt); 757 syslog_vstrerror(errno, LOG_CRIT, fmt, ap); 758 va_end(ap); 759 exit(ecode); 760 } 761 762 __dead void 763 syslog_errx(int ecode, const char *fmt, ...) 764 { 765 va_list ap; 766 767 va_start(ap, fmt); 768 vsyslog(LOG_CRIT, fmt, ap); 769 va_end(ap); 770 exit(ecode); 771 } 772 773 void 774 syslog_warn(const char *fmt, ...) 775 { 776 va_list ap; 777 778 va_start(ap, fmt); 779 syslog_vstrerror(errno, LOG_ERR, fmt, ap); 780 va_end(ap); 781 } 782 783 void 784 syslog_warnx(const char *fmt, ...) 785 { 786 va_list ap; 787 788 va_start(ap, fmt); 789 vsyslog(LOG_ERR, fmt, ap); 790 va_end(ap); 791 } 792 793 void 794 syslog_info(const char *fmt, ...) 795 { 796 va_list ap; 797 798 va_start(ap, fmt); 799 vsyslog(LOG_INFO, fmt, ap); 800 va_end(ap); 801 } 802 803 void 804 syslog_debug(const char *fmt, ...) 805 { 806 va_list ap; 807 808 va_start(ap, fmt); 809 vsyslog(LOG_DEBUG, fmt, ap); 810 va_end(ap); 811 } 812 813 void 814 warnx_verbose(const char *fmt, ...) 815 { 816 va_list ap; 817 818 va_start(ap, fmt); 819 if (verbose) 820 vwarnx(fmt, ap); 821 va_end(ap); 822 } 823 824 #endif /* SMALL */ 825