port of netbsd's tr
[minix.git] / commands / dhcpd / dhcpd.c
blobbd43077ae13aabfd76f424d6cca4f6e24f0a0253
1 /* dhcpd 1.15 - Dynamic Host Configuration Protocol daemon.
2 * Author: Kees J. Bot
3 * 11 Jun 1999
4 */
5 #include <sys/types.h>
6 #include <stdio.h>
7 #include <stddef.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <signal.h>
13 #include <string.h>
14 #include <time.h>
15 #include <limits.h>
16 #include <configfile.h>
17 #include <sys/ioctl.h>
18 #include <sys/asynchio.h>
19 #include <net/hton.h>
20 #include <net/gen/socket.h>
21 #include <net/gen/netdb.h>
22 #include <net/gen/in.h>
23 #include <net/gen/inet.h>
24 #include <net/gen/ether.h>
25 #include <net/gen/if_ether.h>
26 #include <net/gen/eth_hdr.h>
27 #include <net/gen/ip_hdr.h>
28 #include <net/gen/udp.h>
29 #include <net/gen/udp_hdr.h>
30 #include <net/gen/udp_io.h>
31 #include <net/gen/dhcp.h>
32 #include "arp.h"
33 #define EXTERN
34 #include "dhcpd.h"
36 char *configfile= PATH_DHCPCONF;
37 char *poolfile= PATH_DHCPPOOL;
38 static char *cachefile= PATH_DHCPCACHE;
39 static int qflag; /* True if printing cached DHCP data. */
40 static int aflag, rflag; /* True if adding or deleting pool addresses. */
42 #define BCAST_IP HTONL(0xFFFFFFFFUL)
44 /* We try to play with up to this many networks. */
45 #define N_NETS 32
46 static unsigned n_nets; /* Actual number of networks. */
48 void report(const char *label)
50 static FILE *logfp;
51 if(!logfp)
52 logfp = fopen("/usr/log/dhcp.log", "w");
53 if(logfp)
54 fprintf(logfp, "%s: %s: %s\n", program, label, strerror(errno));
57 void fatal(const char *label)
59 report(label);
60 exit(1);
63 void *allocate(size_t size)
65 void *mem;
67 if ((mem= malloc(size)) == nil) fatal("Can't allocate memory");
68 return mem;
71 /* Choose a DHCP xid based on the start time and network number. Not really
72 * random, but we don't have anything more random than the clock anyway.
74 #define XID(np) htonl(((u32_t) (np)->start << 8) | (np)->n)
76 static network_t *network[N_NETS];
78 int ifname2if(const char *name)
80 /* Translate an interface name to a number, -1 if bad. */
81 char *end;
82 unsigned long n;
84 if (*name++ != 'i' || *name++ != 'p') return -1;
85 n= strtoul(name, &end, 10);
86 if (end == name || *end != 0) return -1;
87 if (n >= N_NETS) return -1;
88 return n;
91 network_t *if2net(int n)
93 /* Translate an interface number to a network struct. */
94 int i;
96 for (i= 0; i < n_nets; i++) {
97 if (network[i]->n == n) return network[i];
99 return nil;
102 static ipaddr_t defaultmask(ipaddr_t ip)
104 /* Compute netmask by the oldfashioned Class rules. */
105 if (B(&ip)[0] < 0x80) return HTONL(0xFF000000UL); /* Class A. */
106 if (B(&ip)[0] < 0xC0) return HTONL(0xFFFF0000UL); /* Class B. */
107 if (B(&ip)[0] < 0xE0) return HTONL(0xFFFFFF00UL); /* Class C. */
108 return HTONL(0xFFFFFFFFUL); /* Multicast? Shouldn't happen... */
111 #define POOL_MAGIC HTONL(0x81F85D00UL)
113 typedef struct pool { /* Dynamic pool entry. */
114 u32_t magic; /* Pool file magic number. */
115 ipaddr_t ip; /* IP address. */
116 u32_t expire; /* When does/did the lease expire? */
117 u8_t len; /* Client ID length. */
118 u8_t unused[19]; /* Space for extensions. */
119 u8_t clid[CLID_MAX]; /* Client ID of current/last user. */
120 } pool_t;
122 static int openpool(int mode)
124 /* Open the dynamic pool and lock it, return fd on success or -1. */
125 int fd;
126 struct flock lck;
128 if ((fd= open(poolfile, mode, 0644)) < 0) {
129 if (errno != ENOENT) fatal(poolfile);
130 return -1;
132 if (mode != O_RDONLY) {
133 lck.l_type= F_WRLCK;
134 lck.l_whence= SEEK_SET;
135 lck.l_start= 0;
136 lck.l_len= 0;
137 if (fcntl(fd, F_SETLKW, &lck) < 0) fatal(poolfile);
139 return fd;
142 static int readpool(int fd, pool_t *entry)
144 /* Read one pool table entry, return true unless EOF. */
145 ssize_t r;
147 if ((r= read(fd, entry, sizeof(*entry))) < 0) fatal(poolfile);
148 if (r == 0) return 0;
150 if (r != sizeof(*entry) || entry->magic != POOL_MAGIC) {
151 fprintf(stderr, "%s: %s: Pool table is corrupt\n",
152 program, poolfile);
153 close(fd);
154 return 0;
156 return 1;
159 #if !__minix_vmd /* No fsync() for Minix. */
160 #define fsync(fd) sync()
161 #endif
163 static void writepool(int fd, pool_t *entry)
165 /* (Over)write a pool table entry. */
166 if (write(fd, entry, sizeof(*entry)) < 0
167 || (entry->expire > now && fsync(fd) < 0)
169 fatal(poolfile);
173 static ipaddr_t findpool(u8_t *client, size_t len, ipaddr_t ifip)
175 /* Look for a client ID in the dynamic address pool within the same network
176 * as 'ifip'. Select an unused one for a new client if necessary. Return
177 * 0 if nothing is available, otherwise the IP address we can offer.
179 int fd, found;
180 pool_t entry, oldest;
181 dhcp_t dhcp;
182 u8_t *pmask;
183 ipaddr_t mask;
185 /* Any information available on the network the client is at? */
186 if (!makedhcp(&dhcp, nil, 0, nil, 0, ifip, ifip, nil)) return 0;
188 if ((fd= openpool(O_RDWR)) < 0) return 0;
190 (void) gettag(&dhcp, DHCP_TAG_NETMASK, &pmask, nil);
191 memcpy(&mask, pmask, sizeof(mask));
193 oldest.expire= NEVER;
194 while ((found= readpool(fd, &entry))) {
195 /* Deleted entry? */
196 if (entry.ip == 0) continue;
198 /* Correct network? */
199 if (((entry.ip ^ ifip) & mask) != 0) continue;
201 /* Client present? */
202 if (entry.len == len && memcmp(entry.clid, client, len) == 0) break;
204 /* Oldest candidate for a new lease? */
205 entry.expire= ntohl(entry.expire);
206 if (entry.expire < oldest.expire) oldest= entry;
208 close(fd);
210 if (found) return entry.ip;
211 if (oldest.expire <= now) return oldest.ip;
212 return 0;
215 static int commitpool(ipaddr_t ip, u8_t *client, size_t len, time_t expire)
217 /* Commit a new binding to stable storage, return true on success. */
218 int fd;
219 pool_t entry;
221 if ((fd= openpool(O_RDWR)) < 0) return 0;
223 do {
224 if (!readpool(fd, &entry)) {
225 close(fd);
226 return 0;
228 } while (entry.ip != ip);
230 entry.expire= htonl(expire);
231 entry.len= len;
232 memcpy(entry.clid, client, len);
233 if (lseek(fd, -(off_t)sizeof(entry), SEEK_CUR) == -1) fatal(poolfile);
234 writepool(fd, &entry);
235 close(fd);
236 return 1;
239 static void updatepool(int add, const char *name)
241 /* Add a new IP address to the dynamic pool. */
242 ipaddr_t ip;
243 int fd, i;
244 pool_t entry;
245 struct hostent *he;
246 off_t off, off0;
248 if ((he= gethostbyname(name)) == nil || he->h_addrtype != AF_INET) {
249 fprintf(stderr, "%s: %s: Unknown host\n", program, name);
250 exit(1);
252 for (i= 0; he->h_addr_list[i] != nil; i++) {}
253 if (i != 1) {
254 fprintf(stderr, "%s: %s has %d addresses\n", program, name, i);
255 exit(1);
257 memcpy(&ip, he->h_addr_list[0], sizeof(ip));
259 if ((fd= openpool(O_RDWR|O_CREAT)) < 0) fatal(poolfile);
261 off= 0;
262 off0= -1;
263 while (readpool(fd, &entry)) {
264 if (add) {
265 if (entry.ip == ip) {
266 fprintf(stderr, "%s: %s: %s is already present\n",
267 program, poolfile, name);
268 exit(1);
270 if (entry.ip == 0 && off0 == -1) off0= off;
271 } else {
272 if (entry.ip == ip) {
273 memset(&entry, 0, sizeof(entry));
274 entry.magic= POOL_MAGIC;
275 entry.ip= 0;
276 if (lseek(fd, off, SEEK_SET) == -1) fatal(poolfile);
277 writepool(fd, &entry);
280 off+= sizeof(entry);
283 if (add) {
284 if (off0 != -1 && lseek(fd, off0, SEEK_SET) == -1) fatal(poolfile);
285 memset(&entry, 0, sizeof(entry));
286 entry.magic= POOL_MAGIC;
287 entry.ip= ip;
288 writepool(fd, &entry);
290 close(fd);
293 static void cachedhcp(int n, dhcp_t *dp)
295 /* Store a DHCP packet in a cache where those who care can find it. */
296 static int inited;
297 FILE *fp;
298 int fd;
299 int mode;
301 if (test > 0) return;
303 if (!inited) {
304 /* First time, clear store and also save my pid. */
305 if ((fp= fopen(PATH_DHCPPID, "w")) != nil) {
306 if (fprintf(fp, "%d\n", getpid()) == EOF || fclose(fp) == EOF) {
307 fatal(PATH_DHCPPID);
310 inited= 1;
311 mode= O_WRONLY | O_CREAT | O_TRUNC;
312 } else {
313 mode= O_WRONLY;
316 dp->xid= htonl(now); /* To tell how old this data is. */
318 if ((fd= open(cachefile, mode, 0666)) < 0
319 || lseek(fd, (off_t) n * sizeof(*dp), SEEK_SET) == -1
320 || write(fd, dp, sizeof(*dp)) < 0
321 || close(fd) < 0
323 if (errno != ENOENT) fatal(cachefile);
327 static void printdata(void)
329 /* Show the contents of the cache and the dynamic pool. */
330 int fd;
331 dhcp_t d;
332 ssize_t r;
333 int i;
334 pool_t entry;
335 unsigned long expire;
336 char delta[3*sizeof(u32_t)];
338 initdhcpconf();
340 if ((fd= open(cachefile, O_RDONLY)) < 0) fatal(cachefile);
341 i= 0;
342 while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) {
343 if (d.yiaddr != 0) {
344 printf("DHCP data for network %d:\n", i);
345 printdhcp(&d);
347 i++;
349 if (r < 0) fatal(cachefile);
350 close(fd);
352 if ((fd= openpool(O_RDONLY)) >= 0) {
353 printf("Dynamic address pool since %ld:\n", (long) now);
354 while (readpool(fd, &entry)) {
355 if (entry.ip == 0) continue;
356 expire= ntohl(entry.expire);
357 if (expire == 0) {
358 strcpy(delta, "unused");
359 } else
360 if (expire == 0xFFFFFFFFUL) {
361 strcpy(delta, "infinite");
362 } else
363 if (expire < now) {
364 sprintf(delta, "-%lu", now - expire);
365 } else {
366 sprintf(delta, "+%lu", expire - now);
368 printf("\t%-15s %8s ", inet_ntoa(entry.ip), delta);
369 for (i= 0; i < entry.len; i++) {
370 printf("%02X", entry.clid[i]);
372 fputc('\n', stdout);
374 close(fd);
378 static udpport_t portbyname(const char *name)
380 struct servent *se;
382 if ((se= getservbyname(name, "udp")) == nil) {
383 fprintf(stderr, "%s: Unknown port \"%s\"\n", program, name);
384 exit(1);
386 return se->s_port;
389 static int sendpacket(network_t *np, void *data, size_t len)
391 /* Send out a packet using a filedescriptor that is probably in async mode,
392 * so first dup() a sync version, then write. Return true on success.
394 int fd;
395 ssize_t r;
397 if ((fd= dup(np->fdp->fd)) < 0) fatal("Can't dup()");
398 if ((r= write(fd, data, len)) < 0) {
399 report(np->fdp->device);
400 sleep(10);
402 close(fd);
403 return r >= 0;
406 static size_t servdhcp(network_t *np, buf_t *bp, size_t dlen)
408 buf_t *abp= nil;
409 ipaddr_t cip, ifip;
410 u8_t defclid[1+sizeof(bp->dhcp->chaddr)];
411 u8_t *pdata, *client, *class, *server, *reqip, *lease;
412 u32_t expire;
413 size_t len, cilen, calen;
414 int type, dyn;
415 u8_t atype;
416 static char NAKMESS[] = "IP address requested isn't yours";
418 if (test > 0) return 0;
420 /* The IP address of the interface close to the client. */
421 ifip= bp->dhcp->giaddr != 0 ? bp->dhcp->giaddr : np->ip;
423 /* All kinds of DHCP tags. */
424 if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
425 type= *pdata;
426 } else {
427 type= -1; /* BOOTP? */
430 if (!gettag(bp->dhcp, DHCP_TAG_CLIENTID, &client, &cilen)) {
431 defclid[0]= bp->dhcp->htype;
432 memcpy(defclid+1, bp->dhcp->chaddr, bp->dhcp->hlen);
433 client= defclid;
434 cilen= 1+bp->dhcp->hlen;
437 if (!gettag(bp->dhcp, DHCP_TAG_CLASSID, &class, &calen)) {
438 calen= 0;
441 if (!gettag(bp->dhcp, DHCP_TAG_SERVERID, &server, nil)) {
442 server= B(&np->ip);
445 if (!gettag(bp->dhcp, DHCP_TAG_REQIP, &reqip, nil)) {
446 reqip= nil;
449 /* I'm a server? Then see if I know this client. */
450 if ((np->flags & NF_SERVING)
451 && bp->dhcp->op == DHCP_BOOTREQUEST
452 && between(1, bp->dhcp->hlen, sizeof(bp->dhcp->chaddr))
453 && (server == nil || memcmp(server, &np->ip, sizeof(np->ip)) == 0)
455 get_buf(&abp);
457 /* Is the client in my tables? */
458 (void) makedhcp(abp->dhcp, class, calen, client, cilen, 0, ifip, nil);
459 cip= abp->dhcp->yiaddr;
461 dyn= 0;
462 /* If not, do we have a dynamic address? */
463 if (cip == 0 && (cip= findpool(client, cilen, ifip)) != 0) dyn= 1;
465 if (type == DHCP_INFORM) {
466 /* The client already has an address, it just wants information.
467 * We only answer if we could answer a normal request (cip != 0),
468 * unless configured to answer anyone.
470 if (cip != 0 || (np->flags & NF_INFORM)) cip= bp->dhcp->ciaddr;
473 if (cip == 0 || !makedhcp(abp->dhcp, class, calen,
474 client, cilen, cip, ifip, nil)) {
475 put_buf(&abp);
478 if (abp != nil) {
479 if (gettag(abp->dhcp, DHCP_TAG_LEASE, &lease, nil)) {
480 memcpy(&expire, lease, sizeof(expire));
481 expire= now + ntohl(expire);
482 if (expire < now) expire= 0xFFFFFFFFUL;
483 } else {
484 if (dyn) {
485 /* A dynamic address must have a lease. */
486 fprintf(stderr, "%s: No lease set for address %s\n",
487 program, inet_ntoa(cip));
488 exit(1);
490 lease= nil;
491 expire= 0xFFFFFFFFUL;
494 /* What does our client want, and what do we say? */
495 switch (type) {
496 case DHCP_DISCOVER:
497 atype= DHCP_OFFER;
499 /* Assign this address for a short moment. */
500 if (dyn && !commitpool(cip, client, cilen, now + DELTA_FAST)) {
501 put_buf(&abp);
503 break;
505 case -1:/* BOOTP */
506 case DHCP_REQUEST:
507 case DHCP_INFORM:
508 atype= DHCP_ACK;
509 /* The address wanted must be the address we offer. */
510 if ((reqip != nil && memcmp(reqip, &cip, sizeof(cip)) != 0)
511 || (bp->dhcp->ciaddr != 0 && bp->dhcp->ciaddr != cip)
513 atype= DHCP_NAK;
514 } else
515 if (dyn && type == DHCP_REQUEST) {
516 /* Assign this address for the duration of the lease. */
517 if (!commitpool(cip, client, cilen, expire)) put_buf(&abp);
519 break;
521 case DHCP_DECLINE:
522 /* Our client doesn't want the offered address! */
523 if (dyn
524 && reqip != nil
525 && memcmp(reqip, &cip, sizeof(cip)) == 0
527 int i;
529 fprintf(stderr, "%s: ", program);
530 for (i= 0; i < cilen; i++) {
531 fprintf(stderr, "%02X", client[i]);
533 fprintf(stderr, " declines %s", inet_ntoa(cip));
534 if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
535 fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
537 fputc('\n', stderr);
539 /* Disable address for the duration of the lease. */
540 (void) commitpool(cip, nil, 0, expire);
542 put_buf(&abp);
543 break;
545 case DHCP_RELEASE:
546 /* Our client is nice enough to return its address. */
547 if (dyn) (void) commitpool(cip, client, cilen, now);
548 put_buf(&abp);
549 break;
551 default: /* Anything else is ignored. */
552 put_buf(&abp);
556 if (abp != nil) {
557 /* Finish the return packet. */
558 abp->dhcp->htype= bp->dhcp->htype;
559 abp->dhcp->hlen= bp->dhcp->hlen;
560 abp->dhcp->hops= 0;
561 abp->dhcp->xid= bp->dhcp->xid;
562 abp->dhcp->secs= 0;
563 abp->dhcp->flags= bp->dhcp->flags;
564 abp->dhcp->ciaddr= 0;
565 abp->dhcp->yiaddr= atype == DHCP_NAK ? 0 : cip;
566 if (atype == DHCP_NAK) abp->dhcp->siaddr= 0;
567 abp->dhcp->giaddr= bp->dhcp->giaddr;
568 memcpy(abp->dhcp->chaddr,bp->dhcp->chaddr,sizeof(bp->dhcp->chaddr));
570 settag(abp->dhcp, DHCP_TAG_SERVERID, &np->ip, sizeof(np->ip));
572 if (lease == nil) {
573 /* No lease specified? Then give an infinite lease. */
574 settag(abp->dhcp, DHCP_TAG_LEASE, &expire, sizeof(expire));
577 if (type == DHCP_INFORM) {
578 /* Oops, this one has a fixed address, so no lease business. */
579 abp->dhcp->yiaddr= 0;
580 settag(abp->dhcp, DHCP_TAG_LEASE, nil, 0);
581 settag(abp->dhcp, DHCP_TAG_RENEWAL, nil, 0);
582 settag(abp->dhcp, DHCP_TAG_REBINDING, nil, 0);
585 if (atype == DHCP_NAK) {
586 /* A NAK doesn't need much. */
587 memset(abp->dhcp->sname, 0, sizeof(abp->dhcp->sname));
588 memset(abp->dhcp->file, 0, sizeof(abp->dhcp->file));
589 memset(abp->dhcp->options, 255, sizeof(abp->dhcp->options));
590 settag(abp->dhcp, DHCP_TAG_MESSAGE, NAKMESS, sizeof(NAKMESS));
593 settag(abp->dhcp, DHCP_TAG_TYPE, &atype, sizeof(atype));
595 /* Figure out where to send this to. */
596 abp->udpio->uih_src_addr= np->ip;
597 abp->udpio->uih_src_port= port_server;
598 if (bp->dhcp->giaddr != 0) {
599 abp->udpio->uih_dst_addr= bp->dhcp->giaddr;
600 abp->udpio->uih_dst_port= port_server;
601 } else
602 if (bp->dhcp->flags & DHCP_FLAGS_BCAST) {
603 abp->udpio->uih_dst_addr= BCAST_IP;
604 abp->udpio->uih_dst_port= port_client;
605 } else
606 if (bp->udpio->uih_src_addr != 0
607 && bp->udpio->uih_dst_addr == np->ip
609 abp->udpio->uih_dst_addr= bp->udpio->uih_src_addr;
610 abp->udpio->uih_dst_port= port_client;
611 } else {
612 abp->udpio->uih_dst_addr= BCAST_IP;
613 abp->udpio->uih_dst_port= port_client;
615 abp->udpio->uih_ip_opt_len= 0;
616 abp->udpio->uih_data_len= sizeof(dhcp_t);
618 /* Copy the packet to the input buffer, and return the new size. */
619 memcpy(bp->buf, abp->buf, sizeof(bp->buf));
620 put_buf(&abp);
621 return sizeof(udp_io_hdr_t) + sizeof(dhcp_t);
625 /* I'm a relay? If it is a not already a relayed request then relay. */
626 if ((np->flags & NF_RELAYING)
627 && bp->dhcp->op == DHCP_BOOTREQUEST
628 && bp->dhcp->giaddr == 0
630 bp->dhcp->giaddr= np->ip;
631 bp->udpio->uih_src_addr= np->ip;
632 bp->udpio->uih_src_port= port_server;
633 bp->udpio->uih_dst_addr= np->server;
634 bp->udpio->uih_dst_port= port_server;
635 return dlen;
638 /* I'm a relay? If the server sends a reply to me then relay back. */
639 if ((np->flags & NF_RELAYING)
640 && bp->dhcp->op == DHCP_BOOTREPLY
641 && bp->dhcp->giaddr == np->ip
643 bp->dhcp->giaddr= 0;
644 bp->udpio->uih_src_addr= np->ip;
645 bp->udpio->uih_src_port= port_server;
646 bp->udpio->uih_dst_addr= BCAST_IP;
647 bp->udpio->uih_dst_port= port_client;
648 return dlen;
651 /* Don't know what to do otherwise, so doing nothing seems wise. */
652 return 0;
655 static void onsig(int sig)
657 switch (sig) {
658 case SIGUSR1: debug++; break;
659 case SIGUSR2: debug= 0; break;
663 static void usage(void)
665 fprintf(stderr,
666 "Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n",
667 program);
668 exit(1);
671 int main(int argc, char **argv)
673 int i;
674 network_t *np;
675 struct sigaction sa;
676 ssize_t r;
677 buf_t *bp;
678 static struct timeval eventtv;
680 main:
681 r = -1;
682 bp = nil;
683 program= argv[0];
684 start= now= time(nil);
686 debug= 0;
687 i= 1;
688 while (i < argc && argv[i][0] == '-') {
689 char *opt= argv[i++]+1;
691 if (opt[0] == '-' && opt[1] == 0) break; /* -- */
693 while (*opt != 0) switch (*opt++) {
694 case 'f':
695 if (*opt == 0) {
696 if (i == argc) usage();
697 opt= argv[i++];
699 configfile= opt;
700 opt= "";
701 break;
702 case 'c':
703 if (*opt == 0) {
704 if (i == argc) usage();
705 opt= argv[i++];
707 cachefile= opt;
708 opt= "";
709 break;
710 case 'p':
711 if (*opt == 0) {
712 if (i == argc) usage();
713 opt= argv[i++];
715 poolfile= opt;
716 opt= "";
717 break;
718 case 't':
719 test= 1;
720 if (between('0', *opt, '9')) test= strtoul(opt, &opt, 10);
721 break;
722 case 'd':
723 debug= 1;
724 if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10);
725 break;
726 case 'q':
727 qflag= 1;
728 break;
729 case 'a':
730 aflag= 1;
731 break;
732 case 'r':
733 rflag= 1;
734 break;
735 default:
736 usage();
739 if (aflag + rflag + qflag > 1) usage();
741 if (aflag || rflag) {
742 /* Add or remove addresses from the dynamic pool. */
743 while (i < argc) updatepool(aflag, argv[i++]);
744 exit(0);
747 if (i != argc) usage();
749 if (qflag) {
750 /* Only show the contents of the cache and dynamic pool to the user. */
751 printdata();
752 exit(0);
755 /* BOOTP ports. */
756 port_server= portbyname("bootps");
757 port_client= portbyname("bootpc");
759 sa.sa_handler= onsig;
760 sigemptyset(&sa.sa_mask);
761 sa.sa_flags= 0;
762 sigaction(SIGUSR1, &sa, nil);
763 sigaction(SIGUSR2, &sa, nil);
765 /* Initial configuration. */
766 for (i= 0; i < N_NETS; i++) {
767 int fd;
768 ipaddr_t ip, mask;
770 /* Is there something there? */
771 if ((fd= open(ipdev(i), O_RDWR|O_NONBLOCK)) < 0) {
772 if (errno != ENOENT && errno != ENODEV && errno != ENXIO) {
773 fatal(ipdev(i));
775 continue;
777 close(fd);
779 network[n_nets++]= np= newnetwork();
780 np->n= i;
782 /* Ethernet? */
783 if (opendev(np, FT_ETHERNET, 1)) {
784 np->type= B(&np->eth)[0] != 'Z' ? NT_ETHERNET : NT_SINK;
785 if (debug >= 1) {
786 printf("%s: Ethernet address is %s%s\n",
787 np->fdp->device, ether_ntoa(&np->eth),
788 np->type == NT_SINK ? " (sink)" : "");
790 closedev(np, FT_ETHERNET);
793 /* Only true Ethernets worry about DHCP. */
794 if (np->type != NT_ETHERNET) np->renew= np->rebind= np->lease= NEVER;
797 /* Try to find my interfaces in the DHCP table. */
798 for (i= 0; i < n_nets; i++) {
799 ipaddr_t cip;
800 u8_t clid[1+DHCP_HLEN_ETH];
801 size_t cilen;
803 np= network[i];
804 if (np->flags & NF_BOUND) continue;
806 if (np->type == NT_IP) {
807 cilen= 0;
808 } else {
809 ether2clid(clid, &np->eth);
810 cilen= 1+DHCP_HLEN_ETH;
813 /* Try to find an Ethernet address, or the IP address of an already
814 * configured network. If we have data we get an IP address back.
816 get_buf(&bp);
817 (void) makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
818 clid, cilen, np->ip, 0, np);
819 cip= bp->dhcp->yiaddr;
821 /* Gather information on the interface. */
822 if (cip != 0
823 && makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
824 clid, cilen, cip, cip, np)
825 && test < 2
827 u8_t *pdata;
828 u16_t mtu;
830 cachedhcp(np->n, bp->dhcp);
831 np->ip= cip;
832 (void) gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil);
833 memcpy(&np->mask, pdata, sizeof(np->mask));
834 if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
835 memcpy(&np->gateway, pdata, sizeof(np->gateway));
836 } else {
837 np->gateway= 0;
839 if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
840 memcpy(&mtu, pdata, sizeof(mtu));
841 mtu= ntohs(mtu);
842 } else {
843 mtu= 0;
845 set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
846 if (debug >= 1) {
847 printf("%s: IP address is %s\n",
848 ipdev(np->n), cidr_ntoa(np->ip, np->mask));
850 np->flags |= NF_BOUND;
851 np->renew= np->rebind= np->lease= NEVER;
852 np->sol_ct= N_SOLICITS;
853 np->solicit= 0;
855 /* Other (previous) interfaces may have been defined. */
856 i= 0;
858 put_buf(&bp);
861 for (;;) {
862 now= time(nil);
863 event= NEVER;
865 /* Is it time to request/renew a lease? */
866 for (i= 0; i < n_nets; i++) {
867 np= network[i];
869 if (np->renew <= now) {
870 u8_t type;
871 static u8_t taglist[] = {
872 DHCP_TAG_NETMASK, DHCP_TAG_GATEWAY, DHCP_TAG_DNS,
873 DHCP_TAG_HOSTNAME
875 u8_t ethclid[1+DHCP_HLEN_ETH];
877 /* We may have lost our binding or even our lease. */
878 if (np->rebind <= now) np->server= BCAST_IP;
880 if (np->lease <= now) {
881 if (np->flags & NF_BOUND) closedev(np, FT_ALL);
883 if ((np->flags & (NF_BOUND | NF_POSSESSIVE)) == NF_BOUND) {
884 set_ipconf(ipdev(np->n), np->ip= 0, np->mask= 0, 0);
885 if (debug >= 1) {
886 printf("%s: Interface disabled (lease expired)\n",
887 ipdev(np->n));
890 np->flags &= ~NF_BOUND;
893 /* See if we can open the network we need to send on. */
894 if (!(np->flags & NF_BOUND)) {
895 if (!opendev(np, FT_ETHERNET, 1)) continue;
896 } else {
897 if (!opendev(np, FT_BOOTPC, 1)) continue;
900 if (!(np->flags & NF_NEGOTIATING)) {
901 /* We need to start querying a DHCP server. */
902 np->start= now;
903 np->delta= DELTA_FIRST;
904 np->flags |= NF_NEGOTIATING;
907 /* Fill in a DHCP query packet. */
908 get_buf(&bp);
909 dhcp_init(bp->dhcp);
910 bp->dhcp->op= DHCP_BOOTREQUEST;
911 bp->dhcp->htype= DHCP_HTYPE_ETH;
912 bp->dhcp->hlen= DHCP_HLEN_ETH;
913 bp->dhcp->xid= XID(np);
914 bp->dhcp->secs= htons(now - np->start > 0xFFFF
915 ? 0xFFFF : now - np->start);
916 memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
918 if (np->lease <= now) {
919 /* First time, or my old server is unresponsive. */
920 type= DHCP_DISCOVER;
921 } else {
922 /* Request an offered address or renew an address. */
923 type= DHCP_REQUEST;
924 if (np->flags & NF_BOUND) {
925 /* A renewal, I claim my current address. */
926 bp->dhcp->ciaddr= np->ip;
927 } else {
928 /* Nicely ask for the address just offered. */
929 settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip,
930 sizeof(np->ip));
931 settag(bp->dhcp, DHCP_TAG_SERVERID, &np->server,
932 sizeof(np->server));
935 settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
937 /* My client ID. Simply use the default. */
938 ether2clid(ethclid, &np->eth);
939 settag(bp->dhcp, DHCP_TAG_CLIENTID, ethclid, sizeof(ethclid));
941 /* The Class ID may serve to recognize Minix hosts. */
942 settag(bp->dhcp, DHCP_TAG_CLASSID, "Minix", 5);
944 /* The few tags that Minix can make good use of. */
945 settag(bp->dhcp, DHCP_TAG_REQPAR, taglist, sizeof(taglist));
947 /* Some weird sites use a hostname, not a client ID. */
948 if (np->hostname != nil) {
949 settag(bp->dhcp, DHCP_TAG_HOSTNAME,
950 np->hostname, strlen(np->hostname));
953 bp->udpio->uih_src_addr= np->ip;
954 bp->udpio->uih_dst_addr= np->server;
955 bp->udpio->uih_src_port= port_client;
956 bp->udpio->uih_dst_port= port_server;
957 bp->udpio->uih_ip_opt_len= 0;
958 bp->udpio->uih_data_len= sizeof(dhcp_t);
960 if (!(np->flags & NF_BOUND)) {
961 /* Rebind over Ethernet. */
962 udp2ether(bp, np);
963 if (sendpacket(np, bp->eth,
964 sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
965 + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
966 if (debug >= 1) {
967 printf("%s: Broadcast DHCP %s\n",
968 np->fdp->device, dhcptypename(type));
969 if (debug >= 2) printdhcp(bp->dhcp);
972 } else {
973 /* Renew over UDP. */
974 if (sendpacket(np, bp->udpio, sizeof(udp_io_hdr_t)
975 + sizeof(dhcp_t))) {
976 if (debug >= 1) {
977 printf("%s: Sent DHCP %s to %s\n",
978 np->fdp->device,
979 dhcptypename(type),
980 inet_ntoa(np->server));
981 if (debug >= 2) printdhcp(bp->dhcp);
985 put_buf(&bp);
987 /* When to continue querying a DHCP server? */
988 if (np->flags & NF_BOUND) {
989 /* Still bound, keep halving time till next event. */
990 time_t e, d;
992 e= now < np->rebind ? np->rebind : np->lease;
993 d= (e - now) / 2;
994 if (d < DELTA_SLOW) d= DELTA_SLOW;
995 np->renew= now + d;
996 if (np->renew > e) np->renew= e;
997 } else {
998 /* Not bound, be desparate. */
999 np->renew= now + np->delta;
1000 if ((np->delta *= 2) > DELTA_FAST) np->delta= DELTA_FAST;
1003 if (np->renew < event) event= np->renew;
1006 /* Read DHCP responses. */
1007 for (i= 0; i < n_nets; i++) {
1008 np= network[i];
1009 if (!(np->flags & NF_NEGOTIATING)) continue;
1011 if (!(np->flags & NF_BOUND)) {
1012 if (!opendev(np, FT_ETHERNET, 0)) continue;
1013 get_buf(&np->fdp->bp);
1014 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->eth,
1015 BUF_ETH_SIZE);
1016 } else {
1017 if (!opendev(np, FT_BOOTPC, 0)) continue;
1018 get_buf(&np->fdp->bp);
1019 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio,
1020 BUF_UDP_SIZE);
1022 if (r != -1) break;
1023 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1024 report(np->fdp->device);
1025 sleep(10);
1029 /* Is there a response? */
1030 if (i < n_nets) {
1031 give_buf(&bp, &np->fdp->bp);
1032 if (((!(np->flags & NF_BOUND)
1033 && r >= (sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
1034 + sizeof(udp_hdr_t) + offsetof(dhcp_t, options))
1035 && ether2udp(bp)
1036 && bp->udpio->uih_dst_port == port_client)
1038 ((np->flags & NF_BOUND)
1039 && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)))
1040 && bp->dhcp->op == DHCP_BOOTREPLY
1041 && bp->dhcp->htype == DHCP_HTYPE_ETH
1042 && bp->dhcp->hlen == DHCP_HLEN_ETH
1043 && bp->dhcp->xid == XID(np)
1044 && memcmp(bp->dhcp->chaddr, &np->eth, sizeof(np->eth)) == 0
1046 /* Pfew! We got a DHCP reply! */
1047 u8_t *pdata;
1048 size_t len;
1049 int type;
1050 ipaddr_t mask, gateway, relay, server;
1051 u16_t mtu;
1052 u32_t lease, renew, rebind, t;
1054 relay= bp->udpio->uih_src_addr;
1055 if (gettag(bp->dhcp, DHCP_TAG_SERVERID, &pdata, nil)) {
1056 memcpy(&server, pdata, sizeof(server));
1057 } else {
1058 server= relay;
1061 if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
1062 type= pdata[0];
1063 } else {
1064 type= DHCP_ACK; /* BOOTP? */
1067 if (debug >= 1) {
1068 printf("%s: Got a DHCP %s from %s",
1069 np->fdp->device, dhcptypename(type), inet_ntoa(server));
1070 printf(relay != server ? " through %s\n" : "\n",
1071 inet_ntoa(relay));
1072 if (debug >= 2) printdhcp(bp->dhcp);
1075 if (gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil)) {
1076 memcpy(&mask, pdata, sizeof(mask));
1077 } else {
1078 mask= defaultmask(bp->dhcp->ciaddr);
1081 if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
1082 memcpy(&mtu, pdata, sizeof(mtu));
1083 mtu= ntohs(mtu);
1084 } else {
1085 mtu= 0;
1088 if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
1089 memcpy(&gateway, pdata, sizeof(gateway));
1090 } else {
1091 gateway= 0;
1094 lease= NEVER;
1095 if (gettag(bp->dhcp, DHCP_TAG_LEASE, &pdata, nil)) {
1096 memcpy(&lease, pdata, sizeof(lease));
1097 lease= ntohl(lease);
1100 rebind= lease - lease / 8;
1101 if (gettag(bp->dhcp, DHCP_TAG_REBINDING, &pdata, nil)) {
1102 memcpy(&t, pdata, sizeof(t));
1103 t= ntohl(t);
1104 if (t < rebind) rebind= t;
1107 renew= lease / 2;
1108 if (gettag(bp->dhcp, DHCP_TAG_RENEWAL, &pdata, nil)) {
1109 memcpy(&t, pdata, sizeof(t));
1110 t= ntohl(t);
1111 if (t < renew) renew= t;
1114 if (type == DHCP_OFFER && np->rebind <= np->renew) {
1115 /* It's an offer for an address and we haven't taken one
1116 * yet. It's all the same to us, so take this one.
1118 np->ip= bp->dhcp->yiaddr;
1119 np->mask= mask;
1120 np->server= server;
1121 np->gateway= gateway;
1122 np->delta= DELTA_FIRST;
1123 np->renew= now;
1124 np->rebind= np->lease= now + DELTA_FAST;
1126 /* Send out an ARP request to see if the offered address
1127 * is in use already.
1129 make_arp(bp, np);
1130 if (sendpacket(np, bp->eth, sizeof(arp46_t))) {
1131 if (debug >= 2) {
1132 printf("Sent ARP for %s\n", inet_ntoa(np->ip));
1135 np->flags &= ~NF_CONFLICT;
1138 if (type == DHCP_ACK && !(np->flags & NF_CONFLICT)) {
1139 /* An acknowledgment. The address is all mine. */
1140 cachedhcp(np->n, bp->dhcp);
1141 np->ip= bp->dhcp->yiaddr;
1142 np->mask= mask;
1143 np->server= server;
1144 set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
1145 if (debug >= 1) {
1146 printf("%s: Address set to %s\n",
1147 ipdev(np->n), cidr_ntoa(np->ip, np->mask));
1149 if (lease >= NEVER - now) {
1150 /* The lease is infinite! */
1151 np->renew= np->rebind= np->lease= NEVER;
1152 } else {
1153 np->lease= now + lease;
1154 np->renew= now + renew;
1155 np->rebind= now + rebind;
1157 if (test >= 3) {
1158 np->renew= now + 60;
1159 np->rebind= test >= 4 ? np->renew : np->renew + 60;
1160 np->lease= test >= 5 ? np->rebind : np->rebind + 60;
1162 if (!(np->flags & NF_IRDP)) {
1163 np->sol_ct= (np->flags & NF_BOUND) ? 1 : N_SOLICITS;
1164 np->solicit= 0;
1166 np->flags &= ~NF_NEGOTIATING;
1167 np->flags |= NF_BOUND;
1168 closedev(np, FT_ETHERNET);
1169 closedev(np, FT_BOOTPC);
1172 if (type == DHCP_ACK && (np->flags & NF_CONFLICT)) {
1173 /* Alas there is a conflict. Decline to use the address. */
1174 u8_t ethclid[1+DHCP_HLEN_ETH];
1175 static char USED[]= "Address in use by 00:00:00:00:00:00";
1177 type= DHCP_DECLINE;
1178 dhcp_init(bp->dhcp);
1179 bp->dhcp->op= DHCP_BOOTREQUEST;
1180 bp->dhcp->htype= DHCP_HTYPE_ETH;
1181 bp->dhcp->hlen= DHCP_HLEN_ETH;
1182 bp->dhcp->xid= XID(np);
1183 bp->dhcp->secs= 0;
1184 memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
1185 settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip, sizeof(np->ip));
1186 settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
1187 ether2clid(ethclid, &np->eth);
1188 settag(bp->dhcp, DHCP_TAG_CLIENTID,ethclid,sizeof(ethclid));
1189 strcpy(USED+18, ether_ntoa(&np->conflict));
1190 settag(bp->dhcp, DHCP_TAG_MESSAGE, USED, strlen(USED));
1192 bp->udpio->uih_src_port= port_client;
1193 bp->udpio->uih_dst_port= port_server;
1194 bp->udpio->uih_ip_opt_len= 0;
1195 bp->udpio->uih_data_len= sizeof(dhcp_t);
1196 udp2ether(bp, np);
1198 if (sendpacket(np, bp->eth,
1199 sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
1200 + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
1201 if (debug >= 1) {
1202 printf("%s: Broadcast DHCP %s\n",
1203 np->fdp->device, dhcptypename(type));
1204 if (debug >= 2) printdhcp(bp->dhcp);
1208 np->renew= np->rebind= np->lease= now + DELTA_FAST;
1209 np->delta= DELTA_FIRST;
1212 if (type == DHCP_NAK) {
1213 /* Oops, a DHCP server doesn't like me, start over! */
1214 np->renew= np->rebind= np->lease= now + DELTA_FAST;
1215 np->delta= DELTA_FIRST;
1217 fprintf(stderr, "%s: Got a NAK from %s",
1218 program, inet_ntoa(server));
1219 if (relay != server) {
1220 fprintf(stderr, " through %s", inet_ntoa(relay));
1222 if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
1223 fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
1225 fputc('\n', stderr);
1227 } else
1228 if (!(np->flags & NF_BOUND)
1229 && np->rebind > now
1230 && r >= sizeof(arp46_t)
1231 && is_arp_me(bp, np)
1233 /* Oh no, someone else is using the address offered to me! */
1234 np->flags |= NF_CONFLICT;
1236 fprintf(stderr, "%s: %s: %s offered by ",
1237 program,
1238 np->fdp->device,
1239 inet_ntoa(np->ip));
1240 fprintf(stderr, "%s is already in use by %s\n",
1241 inet_ntoa(np->server),
1242 ether_ntoa(&np->conflict));
1244 put_buf(&bp);
1245 if (np->renew < event) event= np->renew;
1248 /* Perform router solicitations. */
1249 for (i= 0; i < n_nets; i++) {
1250 np= network[i];
1251 if (!(np->flags & NF_BOUND)) continue;
1253 if (np->solicit <= now) {
1254 if (!opendev(np, FT_ICMP, 1)) continue;
1255 np->solicit= NEVER;
1257 get_buf(&bp);
1258 if (np->gateway != 0) {
1259 /* No IRDP response seen yet, advertise the router given
1260 * by DHCP to my own interface.
1262 icmp_advert(bp, np);
1263 if (sendpacket(np, bp->ip, sizeof(ip_hdr_t) + 16)) {
1264 if (debug >= 2) {
1265 printf("%s: Sent advert for %s to self\n",
1266 np->fdp->device, inet_ntoa(np->gateway));
1269 np->solicit= now + DELTA_ADV/2;
1272 if (np->sol_ct >= 0 && --np->sol_ct >= 0) {
1273 /* Send a router solicitation. */
1274 icmp_solicit(bp);
1275 if (sendpacket(np, bp->ip, sizeof(*bp->ip) + 8)) {
1276 if (debug >= 2) {
1277 printf("%s: Broadcast router solicitation\n",
1278 np->fdp->device);
1281 np->solicit= now + DELTA_SOL;
1282 } else {
1283 /* No response, or not soliciting right now. */
1284 closedev(np, FT_ICMP);
1287 put_buf(&bp);
1289 if (np->solicit < event) event= np->solicit;
1292 /* Read router adverts. */
1293 for (i= 0; i < n_nets; i++) {
1294 np= network[i];
1295 if (!(np->flags & NF_BOUND)) continue;
1296 if (np->sol_ct < 0) continue;
1298 if (!opendev(np, FT_ICMP, 0)) continue;
1299 get_buf(&np->fdp->bp);
1300 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->ip, BUF_IP_SIZE);
1301 if (r != -1) break;
1302 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1303 report(np->fdp->device);
1304 sleep(10);
1308 /* Is there an advert? */
1309 if (i < n_nets && r >= sizeof(ip_hdr_t) + 8) {
1310 ipaddr_t router;
1312 give_buf(&bp, &np->fdp->bp);
1313 if ((router= icmp_is_advert(bp)) != 0) {
1314 if (debug >= 2) {
1315 printf("%s: Router advert received from %s\n",
1316 np->fdp->device, inet_ntoa(router));
1318 np->solicit= NEVER;
1319 np->sol_ct= -1;
1320 np->flags |= NF_IRDP;
1321 closedev(np, FT_ICMP);
1323 put_buf(&bp);
1326 /* We start serving if all the interfaces so marked are configured. */
1327 for (i= 0; i < n_nets; i++) {
1328 np= network[i];
1329 if ((np->flags & NF_RELAYING) && (np->flags & NF_BOUND)) {
1330 if (((np->ip ^ np->server) & np->mask) == 0) {
1331 /* Don't relay to a server that is on this same net. */
1332 np->flags &= ~NF_RELAYING;
1335 if (!(np->flags & (NF_SERVING|NF_RELAYING))) continue;
1336 if (!(np->flags & NF_BOUND)) { serving= 0; break; }
1337 serving= 1;
1340 /* Read DHCP requests. */
1341 for (i= 0; i < n_nets; i++) {
1342 np= network[i];
1343 if (!(np->flags & NF_BOUND)) continue;
1344 if (!(np->flags & (NF_SERVING|NF_RELAYING)) || !serving) continue;
1346 if (!opendev(np, FT_BOOTPS, 0)) continue;
1347 get_buf(&np->fdp->bp);
1348 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio, BUF_UDP_SIZE);
1350 if (r != -1) break;
1351 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1352 report(np->fdp->device);
1353 sleep(10);
1357 /* Is there a request? */
1358 if (i < n_nets
1359 && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)
1361 give_buf(&bp, &np->fdp->bp);
1363 if (debug >= 1) {
1364 printf("%s: Got DHCP packet from %s to ",
1365 np->fdp->device, inet_ntoa(bp->udpio->uih_src_addr));
1366 printf("%s\n", inet_ntoa(bp->udpio->uih_dst_addr));
1367 if (debug >= 2) printdhcp(bp->dhcp);
1370 /* Can we do something with this DHCP packet? */
1371 if ((r= servdhcp(np, bp, r)) > 0) {
1372 /* Yes, we have something to send somewhere. */
1373 if (sendpacket(np, bp->udpio, r)) {
1374 if (debug >= 1) {
1375 printf("%s: Sent DHCP packet to %s\n",
1376 np->fdp->device,
1377 inet_ntoa(bp->udpio->uih_dst_addr));
1378 if (debug >= 2) printdhcp(bp->dhcp);
1382 put_buf(&bp);
1385 if (debug >= 1) {
1386 static char *lastbrk;
1387 extern char _end;
1389 if (sbrk(0) != lastbrk) {
1390 lastbrk= sbrk(0);
1391 printf("Memory use = %lu\n",
1392 (unsigned long) (lastbrk - &_end));
1394 fflush(stdout);
1397 /* Bail out if not a server, and there is nothing else to do ever. */
1398 if (!serving && event == NEVER) break;
1400 /* Wait for something to do. */
1401 eventtv.tv_sec= event;
1402 if (asyn_wait(&asyn, 0, event == NEVER ? nil : &eventtv) < 0) {
1403 if (errno != EINTR) {
1404 report("asyn_wait()");
1405 sleep(10);
1409 if (debug >= 1) printf("Nothing more to do! Starting over...\n");
1410 sleep(2);
1411 goto main;
1413 return 0;