chiark / gitweb /
Support for resource pools, based on the Apache model.
[mLib] / bres.c
1 /* -*-c-*-
2  *
3  * $Id: bres.c,v 1.3 2000/06/17 10:38:35 mdw Exp $
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 $
33  * Revision 1.3  2000/06/17 10:38:35  mdw
34  * Track changes to selbuf interface.
35  *
36  * Revision 1.2  1999/10/30 11:28:39  mdw
37  * Fix include error, pointed out by Chris Rutter.
38  *
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
62 #include "alloc.h"
63 #include "bres.h"
64 #include "report.h"
65 #include "sel.h"
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
76 static bres_server servers[BRES_MAX];   /* Statically allocated servers */
77
78 #define FREE ((bres_server *)&freelist)
79 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
80
81 #define QUEUE ((bres_client *)&queue)
82 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
83
84 static sel_state *sel;
85
86 static 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
126 typedef 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
146 static 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
165 static 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
193 static 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
218 static 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
238 static 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);
322     xfree(name);
323     h->h_aliases = p;
324     for (i = 0; i < hsk.nalias; i++) {
325       *p++ = a;
326       PUT(alias[i]);
327       xfree(alias[i]);
328     }
329     *p++ = 0;
330     xfree(alias);
331  
332 #undef PUT
333   }
334
335   return (h);
336
337   /* --- Tidy up after various types of failure --- */
338
339 tidy_2:
340   {
341     int i;
342     for (i = 0; i < hsk.nalias && alias[i]; i++)
343       xfree(alias[i]);
344     xfree(alias);
345   }
346 tidy_1:
347   xfree(name);
348 tidy_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
364 static 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
415 static 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
518 lose:
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
535 int 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
568 static 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
599 void bres_abort(bres_client *rc)
600 {
601   if (rc->q == BRES_BYNAME)
602     xfree(rc->u.name);
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
623 static 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
640 static void attach(bres_client */*rc*/);
641
642 static 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)
669       xfree(rc->u.name);
670   }
671   if (h)
672     xfree(h);
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
716 static 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
759 fail_2:
760   close(cfd[0]);
761   close(cfd[1]);
762 fail_1:
763   close(rfd[0]);
764   close(rfd[1]);
765 fail_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
779 static 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
792 again:
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
857 lost:
858   rc->func(0, rc->p);
859   if (rc->q == BRES_BYNAME)
860     xfree(rc->u.name);
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
872 static 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
900 void 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
925 void 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
950 void 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
967 void 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 -------------------------------------------------*/