chiark / gitweb /
Support for resource pools, based on the Apache model.
[mLib] / bres.c
CommitLineData
a759efa6 1/* -*-c-*-
2 *
e2a18bd0 3 * $Id: bres.c,v 1.3 2000/06/17 10:38:35 mdw Exp $
a759efa6 4 *
5 * Background reverse name resolution
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the mLib utilities library.
13 *
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with mLib; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: bres.c,v $
e2a18bd0 33 * Revision 1.3 2000/06/17 10:38:35 mdw
34 * Track changes to selbuf interface.
35 *
6c3a7cf7 36 * Revision 1.2 1999/10/30 11:28:39 mdw
37 * Fix include error, pointed out by Chris Rutter.
38 *
a759efa6 39 * Revision 1.1 1999/10/04 21:40:42 mdw
40 * Added background resolver from `fw'.
41 *
42 */
43
44/*----- Header files ------------------------------------------------------*/
45
46#include <errno.h>
47#include <signal.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51
52#include <sys/types.h>
53#include <sys/time.h>
54#include <unistd.h>
55#include <sys/wait.h>
56
57#include <sys/socket.h>
58#include <netinet/in.h>
59#include <arpa/inet.h>
60#include <netdb.h>
61
6c3a7cf7 62#include "alloc.h"
a759efa6 63#include "bres.h"
6c3a7cf7 64#include "report.h"
65#include "sel.h"
a759efa6 66
67/*----- Magic numbers -----------------------------------------------------*/
68
69#define BRES_MAX 15 /* Maximum number of resolvers */
70#define BRES_IDLE 60 /* Lifetime of an idle resolver */
71
72/*----- Static variables --------------------------------------------------*/
73
74#ifndef BRES_STANDALONE
75
76static bres_server servers[BRES_MAX]; /* Statically allocated servers */
77
78#define FREE ((bres_server *)&freelist)
79static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
80
81#define QUEUE ((bres_client *)&queue)
82static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
83
84static sel_state *sel;
85
86static const char *server = 0;
87
88#endif
89
90/*----- Background resolver protocol --------------------------------------*/
91
92/* --- Requests and responses --- *
93 *
94 * There are two types of requests: name and addr, corresponding to the
95 * standard @gethostbyname@ and @gethostbyaddr@ calls. There are two types
96 * of responses too: a positive response consists of an encoded equivalent of
97 * a @struct hostent@ structure containing the requested information; a
98 * negative response consists of an @h_errno@ value explaining the problem.
99 */
100
101#define BRES_BYNAME 0 /* Request: resolve given name */
102#define BRES_BYADDR 1 /* Request: resolve given address */
103
104#define BRES_HOSTENT 0 /* Response: resolved ok */
105#define BRES_ERROR 1 /* Response: resolution failed */
106
107/* --- Encodings --- *
108 *
109 * A string is encoded as a @size_t@ length followed by the actual data. The
110 * null terminator is not transmitted.
111 *
112 * Addresses for resolution are transmitted as raw @struct in_addr@
113 * structures.
114 *
115 * A @hostent@ structure is transmitted as a header containing fixed-size
116 * information, followed by the official name, an array of aliases, and an
117 * array of addresses. The number of items in the arrays is specified in the
118 * header.
119 *
120 * The implementation assumes that a complete request or reply is always
121 * sent. Undesirable blocking will occur if this is not the case. Both ends
122 * are assumed to trust each other. A protocol failure results in the child
123 * in question being terminated.
124 */
125
126typedef struct hostskel {
127 size_t nalias;
128 int addrtype;
129 size_t addrsz;
130 size_t naddr;
131} hostskel;
132
133/* --- @doread@, @dowrite@ --- *
134 *
135 * Arguments: @int fd@ = file descriptor
136 * @void *buf@ = buffer for data
137 * @size_t sz@ = size of data
138 *
139 * Returns: Zero if successful, nonzero otherwise.
140 *
141 * Use: Reads or writes a chunk of data. @EINTR@ errors are retried;
142 * incomplete reads and writes are continued from where they
143 * left off. End-of-file is considered an I/O error.
144 */
145
146static int doread(int fd, void *buf, size_t sz)
147{
148 char *p = buf;
149 while (sz) {
150 int r = read(fd, p, sz);
151 if (r < 0) {
152 if (errno == EINTR)
153 continue;
154 return (-1);
155 } else if (r == 0) {
156 errno = EIO;
157 return (-1);
158 }
159 sz -= r;
160 p += r;
161 }
162 return (0);
163}
164
165static int dowrite(int fd, const void *buf, size_t sz)
166{
167 const char *p = buf;
168 while (sz) {
169 int r = write(fd, p, sz);
170 if (r < 0) {
171 if (errno == EINTR)
172 continue;
173 return (-1);
174 } else if (r == 0) {
175 errno = EIO;
176 return (-1);
177 }
178 sz -= r;
179 p += r;
180 }
181 return (0);
182}
183
184/* --- @getstring@ --- *
185 *
186 * Arguments: @int fd@ = file descriptor to read
187 *
188 * Returns: String in heap-allocated block, or a null pointer.
189 *
190 * Use: Decodes a string.
191 */
192
193static char *getstring(int fd)
194{
195 size_t sz;
196 char *p;
197
198 if (doread(fd, &sz, sizeof(sz)) || (p = malloc(sz + 1)) == 0)
199 return (0);
200 if (doread(fd, p, sz)) {
201 free(p);
202 return (0);
203 }
204 p[sz] = 0;
205 return (p);
206}
207
208/* --- @putstring@ --- *
209 *
210 * Arguments: @int fd@ = file descriptor to write on
211 * @const char *p@ = pointer to string to write
212 *
213 * Returns: Zero if successful.
214 *
215 * Use: Encodes a string.
216 */
217
218static int putstring(int fd, const char *p)
219{
220 size_t sz = strlen(p);
221 if (dowrite(fd, &sz, sizeof(sz)) || dowrite(fd, p, sz))
222 return (-1);
223 return (0);
224}
225
226/* --- @gethost@ --- *
227 *
228 * Arguments: @int fd@ = file descriptor to read
229 *
230 * Returns: Pointer to heap-allocated @struct hostent@, or null.
231 *
232 * Use: Decodes a host structure. The resulting structure is all in
233 * one big heap block.
234 */
235
236#ifndef BRES_STANDALONE
237
238static struct hostent *gethost(int fd)
239{
240 hostskel hsk;
241 struct hostent *h;
242 char *name;
243 char **alias = 0;
244
245 /* --- Read the skeleton structure --- */
246
247 if (doread(fd, &hsk, sizeof(hsk)))
248 goto tidy_0;
249
250 /* --- Read the hostname and alias strings --- *
251 *
252 * Count the length of the strings as we go.
253 */
254
255 {
256 size_t sz =
257 sizeof(struct hostent) +
258 hsk.naddr * hsk.addrsz +
259 (hsk.naddr + hsk.nalias + 2) * sizeof(char *);
260
261 /* --- Read the primary host name --- */
262
263 if ((name = getstring(fd)) == 0)
264 goto tidy_0;
265 sz += strlen(name) + 1;
266
267 /* --- Read in the alias names --- */
268
269 if (hsk.nalias) {
270 int i;
271 if ((alias = malloc(hsk.nalias * sizeof(char *))) == 0)
272 goto tidy_1;
273 for (i = 0; i < hsk.nalias; i++)
274 alias[i] = 0;
275 for (i = 0; i < hsk.nalias; i++) {
276 if ((alias[i] = getstring(fd)) == 0)
277 goto tidy_2;
278 sz += strlen(alias[i]) + 1;
279 }
280 }
281
282 /* --- Allocate the output structure --- */
283
284 if ((h = malloc(sz)) == 0)
285 goto tidy_2;
286 }
287
288 /* --- Fill in the base structure --- */
289
290 h->h_addrtype = hsk.addrtype;
291 h->h_length = hsk.addrsz;
292
293 /* --- Start putting everything else in --- */
294
295 {
296 char **p = (char **)(h + 1);
297 char *a = (char *)(p + hsk.nalias + hsk.naddr + 2);
298 int i;
299
300 /* --- Start with the address table --- */
301
302 h->h_addr_list = p;
303 if (doread(fd, a, hsk.naddr * hsk.addrsz))
304 goto tidy_2;
305 for (i = 0; i < hsk.naddr; i++) {
306 struct in_addr in;
307 *p++ = a;
308 memcpy(&in, a, sizeof(in));
309 }
310 *p++ = 0;
311
312 /* --- Finally copy the strings over --- */
313
314#define PUT(_p) do { \
315 size_t _len = strlen(_p) + 1; \
316 memcpy(a, (_p), _len); \
317 a += _len; \
318} while (0)
319
320 h->h_name = a;
321 PUT(name);
e2a18bd0 322 xfree(name);
a759efa6 323 h->h_aliases = p;
324 for (i = 0; i < hsk.nalias; i++) {
325 *p++ = a;
326 PUT(alias[i]);
e2a18bd0 327 xfree(alias[i]);
a759efa6 328 }
329 *p++ = 0;
e2a18bd0 330 xfree(alias);
a759efa6 331
332#undef PUT
333 }
334
335 return (h);
336
337 /* --- Tidy up after various types of failure --- */
338
339tidy_2:
340 {
341 int i;
342 for (i = 0; i < hsk.nalias && alias[i]; i++)
e2a18bd0 343 xfree(alias[i]);
344 xfree(alias);
a759efa6 345 }
346tidy_1:
e2a18bd0 347 xfree(name);
a759efa6 348tidy_0:
349 return (0);
350}
351
352#endif
353
354/* --- @puthost@ --- *
355 *
356 * Arguments: @int fd@ = file descriptor
357 * @struct hostent *h@ = pointer to host structure
358 *
359 * Returns: Zero if successful.
360 *
361 * Use: Encodes a host structure.
362 */
363
364static int puthost(int fd, struct hostent *h)
365{
366 hostskel hsk;
367 int i;
368
369 /* --- Fill in and send the skeleton structure --- */
370
371 for (i = 0; h->h_aliases[i]; i++)
372 ;
373 hsk.nalias = i;
374 for (i = 0; h->h_addr_list[i]; i++)
375 ;
376 hsk.naddr = i;
377 hsk.addrtype = h->h_addrtype;
378 hsk.addrsz = h->h_length;
379 if (dowrite(fd, &hsk, sizeof(hsk)))
380 return (-1);
381
382 /* --- Send the name and alias strings --- */
383
384 if (putstring(fd, h->h_name))
385 return (-1);
386 for (i = 0; h->h_aliases[i]; i++) {
387 if (putstring(fd, h->h_aliases[i]))
388 return (-1);
389 }
390
391 /* --- Send the address data --- */
392
393 for (i = 0; h->h_addr_list[i]; i++) {
394 if (dowrite(fd, h->h_addr_list[i], hsk.addrsz))
395 return (-1);
396 }
397
398 /* --- OK, done --- */
399
400 return (0);
401}
402
403/*----- Resolver server ---------------------------------------------------*/
404
405/* --- @child@ --- *
406 *
407 * Arguments: @int rfd@ = output file descriptor for resolved hostnames
408 * @int cfd@ = input file descriptor for raw addresses
409 *
410 * Returns: Never.
411 *
412 * Use: Asynchronous name resolving process.
413 */
414
415static void child(int rfd, int cfd)
416{
417 /* --- Close other file descriptors --- */
418
419 {
420 int i;
421 int maxfd = sysconf(_SC_OPEN_MAX);
422
423 if (maxfd < 0)
424 maxfd = 256; /* Fingers crossed... */
425 for (i = 0; i < maxfd; i++) {
426 if (i != rfd && i != cfd && i != 1)
427 close(i);
428 }
429 }
430
431 /* --- Main request/response loop --- */
432
433 for (;;) {
434 int req, resp;
435 struct hostent *h;
436
437 /* --- Read the request --- */
438
439 if (doread(cfd, &req, sizeof(req)))
440 goto lose;
441
442 /* --- Process it into a host structure --- */
443
444 switch (req) {
445
446 /* --- Normal forward lookup --- */
447
448 case BRES_BYNAME: {
449 char *name = getstring(cfd);
450 if (!name)
451 goto lose;
452 h = gethostbyname(name);
453 free(name);
454 } break;
455
456 /* --- Forward lookup --- */
457
458 case BRES_BYADDR: {
459 struct in_addr addr;
460 char *p;
461 if (doread(cfd, &addr, sizeof(addr)))
462 goto lose;
463 if ((h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == 0)
464 goto fail;
465
466 /* --- Do a forward lookup to confirm --- */
467
468 {
469 size_t sz = strlen(h->h_name) + 1;
470 if ((p = malloc(sz)) == 0)
471 goto fail;
472 memcpy(p, h->h_name, sz);
473 }
474
475 h = gethostbyname(p);
476 free(p);
477 if (!h)
478 goto fail;
479 p = 0;
480 if (h) {
481 char **pp;
482 for (pp = h->h_addr_list; *pp; pp++) {
483 struct in_addr a;
484 memcpy(&a, *pp, sizeof(a));
485 if (a.s_addr == addr.s_addr) {
486 p = h->h_name;
487 break;
488 }
489 }
490 }
491 if (!p) {
492 h = 0;
493 h_errno = NO_RECOVERY;
494 }
495 fail:;
496 } break;
497
498 /* --- Unknown request -- may have lost sync --- */
499
500 default:
501 goto lose;
502 }
503
504 /* --- Transmit the response --- */
505
506 if (h) {
507 resp = BRES_HOSTENT;
508 if (dowrite(rfd, &resp, sizeof(resp)) || puthost(rfd, h))
509 goto lose;
510 } else {
511 resp = BRES_ERROR;
512 if (dowrite(rfd, &resp, sizeof(resp)) ||
513 dowrite(rfd, &h_errno, sizeof(h_errno)))
514 goto lose;
515 }
516 }
517
518lose:
519 _exit(1);
520}
521
522/* --- @main@ --- *
523 *
524 * Arguments: @int argc@ = number of command line arguments
525 * @char *argv[]@ = array of arguments
526 *
527 * Returns: Runs until killed or an error occurs.
528 *
529 * Use: A name resolver server process for mLib programs which need
530 * this sort of thing.
531 */
532
533#ifdef BRES_STANDALONE
534
535int main(int argc, char *argv[])
536{
537 if (isatty(STDIN_FILENO)) {
538 char *p = strrchr(argv[0], '/');
539 if (p)
540 p++;
541 else
542 p = argv[0];
543 fprintf(stderr,
544 "%s: don't run this program unless you know what you're doing.\n",
545 p);
546 exit(1);
547 }
548 child(STDOUT_FILENO, STDIN_FILENO);
549 return (1);
550}
551
552#endif
553
554/*----- Main code ---------------------------------------------------------*/
555
556#ifndef BRES_STANDALONE
557
558/* --- @zap@ --- *
559 *
560 * Arguments: @bres_server *rs@ = pointer to server block
561 *
562 * Returns: ---
563 *
564 * Use: Kills a server process, reaps the losing child and makes
565 * things generally clean again.
566 */
567
568static void zap(bres_server *rs)
569{
570 /* --- Close the pipes, kill the child, and reap it --- */
571
572 if (rs->kid != -1) {
573 close(rs->fd);
574 close(rs->f.fd);
575 kill(rs->kid, SIGTERM);
576 waitpid(rs->kid, 0, 0);
577 rs->kid = -1;
578 }
579
580 /* --- Move the server to the back of the list --- */
581
582 rs->next->prev = rs->prev;
583 rs->prev->next = rs->next;
584 rs->next = FREE;
585 rs->prev = FREE->prev;
586 FREE->prev->next = rs;
587 FREE->prev = rs;
588}
589
590/* --- @bres_abort@ --- *
591 *
592 * Arguments: @bres_client *rc@ = pointer to client block
593 *
594 * Returns: ---
595 *
596 * Use: Removes a queued job.
597 */
598
599void bres_abort(bres_client *rc)
600{
601 if (rc->q == BRES_BYNAME)
e2a18bd0 602 xfree(rc->u.name);
a759efa6 603 if (rc->rs) {
604 sel_rmfile(&rc->rs->f);
605 zap(rc->rs);
606 rc->rs = 0;
607 } else {
608 rc->next->prev = rc->prev;
609 rc->prev->next = rc->next;
610 }
611}
612
613/* --- @idle@ --- *
614 *
615 * Arguments: @struct timeval *tv@ = pointer to the current time
616 * @void *vp@ = pointer to a server block
617 *
618 * Returns: ---
619 *
620 * Use: Kills off a child which has been idle for too long.
621 */
622
623static void idle(struct timeval *tv, void *vp)
624{
625 bres_server *rs = vp;
626 zap(rs);
627}
628
629/* --- @answer@ --- *
630 *
631 * Arguments: @int fd@ = file descriptor which is ready
632 * @unsigned mode@ = what it's doing now
633 * @void *vp@ = pointer to server block
634 *
635 * Returns: ---
636 *
637 * Use: Retrieves an answer from a name resolver process.
638 */
639
640static void attach(bres_client */*rc*/);
641
642static void answer(int fd, unsigned mode, void *vp)
643{
644 bres_server *rs = vp;
645 bres_client *rc = rs->rc;
646 struct hostent *h = 0;
647 int resp;
648 int fail = 1;
649
650 /* --- Report the result to my client --- */
651
652 sel_rmfile(&rs->f);
653 h_errno = -1;
654 if (doread(fd, &resp, sizeof(resp)) == 0) {
655 switch (resp) {
656 case BRES_ERROR:
657 doread(fd, &h_errno, sizeof(h_errno));
658 fail = 0;
659 break;
660 case BRES_HOSTENT:
661 h = gethost(fd);
662 fail = 0;
663 break;
664 }
665 }
666 if (rc) {
667 rc->func(h, rc->p);
668 if (rc->q == BRES_BYNAME)
e2a18bd0 669 xfree(rc->u.name);
a759efa6 670 }
671 if (h)
e2a18bd0 672 xfree(h);
a759efa6 673 if (fail)
674 zap(rs);
675 if (!rc)
676 return;
677
678 /* --- Wrap up the various structures --- */
679
680 rs->rc = 0;
681 rc->rs = 0;
682 rs->next = FREE->next;
683 rs->prev = FREE;
684 FREE->next->prev = rs;
685 FREE->next = rs;
686
687 /* --- Tie a timer onto the server block --- */
688
689 {
690 struct timeval tv;
691
692 gettimeofday(&tv, 0);
693 tv.tv_sec += BRES_IDLE;
694 sel_addtimer(sel, &rs->t, &tv, idle, rs);
695 }
696
697 /* --- If there are any clients waiting, attach one --- */
698
699 if (QUEUE->next != QUEUE) {
700 rc = QUEUE->next;
701 QUEUE->next = rc->next;
702 rc->next->prev = QUEUE;
703 attach(rc);
704 }
705}
706
707/* --- @start@ --- *
708 *
709 * Arguments: @bres_server *rs@ = pointer to a server block
710 *
711 * Returns: Zero if OK, nonzero if something failed.
712 *
713 * Use: Starts up a child resolver process.
714 */
715
716static int start(bres_server *rs)
717{
718 int rfd[2], cfd[2];
719 pid_t kid;
720
721 /* --- Make the pipes --- */
722
723 if (pipe(rfd))
724 goto fail_0;
725 if (pipe(cfd))
726 goto fail_1;
727
728 /* --- Start up the child process --- */
729
730 if ((kid = fork()) < 0)
731 goto fail_2;
732 if (kid == 0) {
733 close(cfd[1]);
734 close(rfd[0]);
735
736 if (server) {
737 dup2(cfd[0], STDIN_FILENO);
738 dup2(rfd[1], STDOUT_FILENO);
739 close(cfd[0]);
740 close(rfd[1]);
741 execlp(server, server, (char *)0);
742 child(STDOUT_FILENO, STDIN_FILENO);
743 } else
744 child(rfd[1], cfd[0]);
745 _exit(1);
746 }
747
748 /* --- Fix up everything in the server block --- */
749
750 close(cfd[0]);
751 close(rfd[1]);
752 rs->fd = cfd[1];
753 sel_initfile(sel, &rs->f, rfd[0], SEL_READ, answer, rs);
754 rs->kid = kid;
755 return (0);
756
757 /* --- Fix up after errors --- */
758
759fail_2:
760 close(cfd[0]);
761 close(cfd[1]);
762fail_1:
763 close(rfd[0]);
764 close(rfd[1]);
765fail_0:
766 return (-1);
767}
768
769/* --- @attach@ --- *
770 *
771 * Arguments: @bres_client *rc@ = pointer to a client block
772 *
773 * Returns: ---
774 *
775 * Use: Attaches a client to a spare server (which is assumed to
776 * exist).
777 */
778
779static void attach(bres_client *rc)
780{
781 bres_server *rs;
782 int lose = 0;
783
784 /* --- Fix up the server ready for the job --- *
785 *
786 * If the server has a process, remove its timer. Otherwise, fork off a
787 * new resolver process. This is also where I go if I find that the child
788 * resolver process has lost while I wasn't looking. Only one attempt at
789 * forking is performed.
790 */
791
792again:
793 rs = FREE->next;
794 if (rs->kid != -1)
795 sel_rmtimer(&rs->t);
796 else {
797 if (lose || start(rs))
798 goto lost;
799 lose = 1;
800 }
801
802 /* --- Submit the job to the resolver --- */
803
804 {
805 struct sigaction sa, osa;
806 int e;
807
808 /* --- Ignore @SIGPIPE@ for now --- *
809 *
810 * This way I can trap @EPIPE@ and reap a losing child, if there was one.
811 */
812
813 sa.sa_handler = SIG_IGN;
814 sa.sa_flags = 0;
815 sigemptyset(&sa.sa_mask);
816 sigaction(SIGPIPE, &sa, &osa);
817
818 /* --- Write the new job to the child --- */
819
820 e = 0;
821 if (dowrite(rs->fd, &rc->q, sizeof(rc->q)))
822 e = errno;
823 else switch (rc->q) {
824 case BRES_BYADDR:
825 if (dowrite(rs->fd, &rc->u.addr, sizeof(rc->u.addr)))
826 e = errno;
827 break;
828 case BRES_BYNAME:
829 if (putstring(rs->fd, rc->u.name))
830 e = errno;
831 break;
832 }
833 sigaction(SIGPIPE, &osa, 0);
834
835 /* --- Sort out various errors --- *
836 *
837 * This was once more complicated, handling @EPIPE@ separately from other
838 * errors. Now everything's handled the same way.
839 */
840
841 if (e) {
842 zap(rs);
843 goto again;
844 }
845 }
846
847 /* --- Fiddle with lists so that everything's OK --- */
848
849 sel_addfile(&rs->f);
850 rs->next->prev = FREE;
851 FREE->next = rs->next;
852 rs->next = rs->prev = rs;
853 rs->rc = rc;
854 rc->rs = rs;
855 return;
856
857lost:
858 rc->func(0, rc->p);
859 if (rc->q == BRES_BYNAME)
e2a18bd0 860 xfree(rc->u.name);
a759efa6 861}
862
863/* --- @resolve@ --- *
864 *
865 * Arguments: @bres_client *rc@ = pointer to filled-in client block
866 *
867 * Returns: ---
868 *
869 * Use: Dispatcher for incoming resolution jobs.
870 */
871
872static void resolve(bres_client *rc)
873{
874 /* --- If there's a free server, plug it in --- */
875
876 rc->rs = 0;
877 if (FREE->next == FREE) {
878 rc->next = QUEUE;
879 rc->prev = QUEUE->prev;
880 QUEUE->prev->next = rc;
881 QUEUE->prev = rc;
882 } else
883 attach(rc);
884}
885
886/* --- @bres_byaddr@ --- *
887 *
888 * Arguments: @bres_client *rc@ = pointer to client block
889 * @struct in_addr addr@ = address to resolve
890 * @void (*func)(struct hostent *h, void *p)@ = handler function
891 * @void *p@ = argument for handler function
892 *
893 * Returns: ---
894 *
895 * Use: Adds an address lookup job to the queue. The job will be
896 * processed when there's a spare resolver process to deal with
897 * it.
898 */
899
900void bres_byaddr(bres_client *rc, struct in_addr addr,
901 void (*func)(struct hostent */*h*/, void */*p*/),
902 void *p)
903{
904 rc->q = BRES_BYADDR;
905 rc->u.addr = addr;
906 rc->func = func;
907 rc->p = p;
908 resolve(rc);
909}
910
911/* --- @bres_byname@ --- *
912 *
913 * Arguments: @bres_client *rc@ = pointer to client block
914 * @const char *name@ = name to resolve
915 * @void (*func)(struct hostent *h, void *p)@ = handler function
916 * @void *p@ = argument for handler function
917 *
918 * Returns: ---
919 *
920 * Use: Adds a name lookup job to the queue. The job will be
921 * processed when there's a spare resolver process to deal with
922 * it.
923 */
924
925void bres_byname(bres_client *rc, const char *name,
926 void (*func)(struct hostent */*h*/, void */*p*/),
927 void *p)
928{
929 rc->q = BRES_BYNAME;
930 rc->u.name = xstrdup(name);
931 rc->func = func;
932 rc->p = p;
933 resolve(rc);
934}
935
936/* --- @bres_exec@ --- *
937 *
938 * Arguments: @const char *file@ = file containing server code or null
939 *
940 * Returns: ---
941 *
942 * Use: Makes `bres' use a standalone server rather than copies of
943 * the current process. This can reduce memory consumption for
944 * large processes, at the expense of startup time (which
945 * shouldn't be too bad anyway, because of the resolver design).
946 * If the filename is null, a default set up at install time is
947 * used. It's probably a good idea to leave it alone.
948 */
949
950void bres_exec(const char *file)
951{
952 if (file)
953 server = file;
954 else
955 server = BRES_SERVER;
956}
957
958/* --- @bres_init@ --- *
959 *
960 * Arguments: @sel_state *s@ = pointer to select multiplexor
961 *
962 * Returns: ---
963 *
964 * Use: Initializes the background resolver for use.
965 */
966
967void bres_init(sel_state *s)
968{
969 int i;
970
971 sel = s;
972 for (i = 0; i < BRES_MAX; i++) {
973 servers[i].next = FREE;
974 servers[i].prev = FREE->prev;
975 servers[i].kid = -1;
976 servers[i].rc = 0;
977 FREE->prev->next = &servers[i];
978 FREE->prev = &servers[i];
979 }
980}
981
982#endif
983
984/*----- That's all, folks -------------------------------------------------*/