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
main(int argc,char * argv[])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
usage(void)344 usage(void)
345 {
346 fprintf(stderr, "usage: resolvd [-dv]\n");
347 exit(1);
348 }
349
350 void
route_receive(int fd)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
zeroslot(struct rdns_proposal * tab)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
findslot(struct rdns_proposal * tab)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
handle_route_message(struct rt_msghdr * rtm,struct sockaddr ** rti_info)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
get_rtaddrs(int addrs,struct sockaddr * sa,struct sockaddr ** rti_info)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
solicit_dns_proposals(int routesock)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
regen_resolvconf(const char * why)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
cmp(const void * a,const void * b)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
open_unwind_ctl(void)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
syslog_vstrerror(int e,int priority,const char * fmt,va_list ap)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
syslog_err(int ecode,const char * fmt,...)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
syslog_errx(int ecode,const char * fmt,...)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
syslog_warn(const char * fmt,...)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
syslog_warnx(const char * fmt,...)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
syslog_info(const char * fmt,...)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
syslog_debug(const char * fmt,...)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
warnx_verbose(const char * fmt,...)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