tools/llvm: Do not build with symbols
[minix3.git] / minix / commands / dhcpd / dhcpd.c
blobd88cb231495dfb7b52e7b6b932983f56581d6b3d
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 <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 int lwip;
50 void report(const char *label)
52 static FILE *logfp;
53 if(!logfp)
54 logfp = fopen("/usr/log/dhcp.log", "w");
55 if(logfp)
56 fprintf(logfp, "%s: %s: %s\n", program, label, strerror(errno));
59 void fatal(const char *label)
61 report(label);
62 exit(1);
65 void *allocate(size_t size)
67 void *mem;
69 if ((mem= malloc(size)) == nil) fatal("Can't allocate memory");
70 return mem;
73 /* Choose a DHCP xid based on the start time and network number. Not really
74 * random, but we don't have anything more random than the clock anyway.
76 #define XID(np) htonl(((u32_t) (np)->start << 8) | (np)->n)
78 static network_t *network[N_NETS];
80 int ifname2if(const char *name)
82 /* Translate an interface name to a number, -1 if bad. */
83 char *end;
84 unsigned long n;
86 if (*name++ != 'i' || *name++ != 'p') return -1;
87 n= strtoul(name, &end, 10);
88 if (end == name || *end != 0) return -1;
89 if (n >= N_NETS) return -1;
90 return n;
93 network_t *if2net(int n)
95 /* Translate an interface number to a network struct. */
96 int i;
98 for (i= 0; i < n_nets; i++) {
99 if (network[i]->n == n) return network[i];
101 return nil;
104 static ipaddr_t defaultmask(ipaddr_t ip)
106 /* Compute netmask by the oldfashioned Class rules. */
107 if (B(&ip)[0] < 0x80) return htonl(0xFF000000UL); /* Class A. */
108 if (B(&ip)[0] < 0xC0) return htonl(0xFFFF0000UL); /* Class B. */
109 if (B(&ip)[0] < 0xE0) return htonl(0xFFFFFF00UL); /* Class C. */
110 return htonl(0xFFFFFFFFUL); /* Multicast? Shouldn't happen... */
113 #define POOL_MAGIC htonl(0x81F85D00UL)
115 typedef struct pool { /* Dynamic pool entry. */
116 u32_t magic; /* Pool file magic number. */
117 ipaddr_t ip; /* IP address. */
118 u32_t expire; /* When does/did the lease expire? */
119 u8_t len; /* Client ID length. */
120 u8_t unused[19]; /* Space for extensions. */
121 u8_t clid[CLID_MAX]; /* Client ID of current/last user. */
122 } pool_t;
124 static int openpool(int mode)
126 /* Open the dynamic pool and lock it, return fd on success or -1. */
127 int fd;
128 struct flock lck;
130 if ((fd= open(poolfile, mode, 0644)) < 0) {
131 if (errno != ENOENT) fatal(poolfile);
132 return -1;
134 if (mode != O_RDONLY) {
135 lck.l_type= F_WRLCK;
136 lck.l_whence= SEEK_SET;
137 lck.l_start= 0;
138 lck.l_len= 0;
139 if (fcntl(fd, F_SETLKW, &lck) < 0) fatal(poolfile);
141 return fd;
144 static int readpool(int fd, pool_t *entry)
146 /* Read one pool table entry, return true unless EOF. */
147 ssize_t r;
149 if ((r= read(fd, entry, sizeof(*entry))) < 0) fatal(poolfile);
150 if (r == 0) return 0;
152 if (r != sizeof(*entry) || entry->magic != POOL_MAGIC) {
153 fprintf(stderr, "%s: %s: Pool table is corrupt\n",
154 program, poolfile);
155 close(fd);
156 return 0;
158 return 1;
161 static void writepool(int fd, pool_t *entry)
163 /* (Over)write a pool table entry. */
164 if (write(fd, entry, sizeof(*entry)) < 0
165 || (entry->expire > now && fsync(fd) < 0)
167 fatal(poolfile);
171 static ipaddr_t findpool(u8_t *client, size_t len, ipaddr_t ifip)
173 /* Look for a client ID in the dynamic address pool within the same network
174 * as 'ifip'. Select an unused one for a new client if necessary. Return
175 * 0 if nothing is available, otherwise the IP address we can offer.
177 int fd, found;
178 pool_t entry, oldest;
179 dhcp_t dhcp;
180 u8_t *pmask;
181 ipaddr_t mask;
183 /* Any information available on the network the client is at? */
184 if (!makedhcp(&dhcp, nil, 0, nil, 0, ifip, ifip, nil)) return 0;
186 if ((fd= openpool(O_RDWR)) < 0) return 0;
188 (void) gettag(&dhcp, DHCP_TAG_NETMASK, &pmask, nil);
189 memcpy(&mask, pmask, sizeof(mask));
191 oldest.expire= NEVER;
192 while ((found= readpool(fd, &entry))) {
193 /* Deleted entry? */
194 if (entry.ip == 0) continue;
196 /* Correct network? */
197 if (((entry.ip ^ ifip) & mask) != 0) continue;
199 /* Client present? */
200 if (entry.len == len && memcmp(entry.clid, client, len) == 0) break;
202 /* Oldest candidate for a new lease? */
203 entry.expire= ntohl(entry.expire);
204 if (entry.expire < oldest.expire) oldest= entry;
206 close(fd);
208 if (found) return entry.ip;
209 if (oldest.expire <= now) return oldest.ip;
210 return 0;
213 static int commitpool(ipaddr_t ip, u8_t *client, size_t len, time_t expire)
215 /* Commit a new binding to stable storage, return true on success. */
216 int fd;
217 pool_t entry;
219 if ((fd= openpool(O_RDWR)) < 0) return 0;
221 do {
222 if (!readpool(fd, &entry)) {
223 close(fd);
224 return 0;
226 } while (entry.ip != ip);
228 entry.expire= htonl(expire);
229 entry.len= len;
230 memcpy(entry.clid, client, len);
231 if (lseek(fd, -(off_t)sizeof(entry), SEEK_CUR) == -1) fatal(poolfile);
232 writepool(fd, &entry);
233 close(fd);
234 return 1;
237 static void updatepool(int add, const char *name)
239 /* Add a new IP address to the dynamic pool. */
240 ipaddr_t ip;
241 int fd, i;
242 pool_t entry;
243 struct hostent *he;
244 off_t off, off0;
246 if ((he= gethostbyname(name)) == nil || he->h_addrtype != AF_INET) {
247 fprintf(stderr, "%s: %s: Unknown host\n", program, name);
248 exit(1);
250 for (i= 0; he->h_addr_list[i] != nil; i++) {}
251 if (i != 1) {
252 fprintf(stderr, "%s: %s has %d addresses\n", program, name, i);
253 exit(1);
255 memcpy(&ip, he->h_addr_list[0], sizeof(ip));
257 if ((fd= openpool(O_RDWR|O_CREAT)) < 0) fatal(poolfile);
259 off= 0;
260 off0= -1;
261 while (readpool(fd, &entry)) {
262 if (add) {
263 if (entry.ip == ip) {
264 fprintf(stderr, "%s: %s: %s is already present\n",
265 program, poolfile, name);
266 exit(1);
268 if (entry.ip == 0 && off0 == -1) off0= off;
269 } else {
270 if (entry.ip == ip) {
271 memset(&entry, 0, sizeof(entry));
272 entry.magic= POOL_MAGIC;
273 entry.ip= 0;
274 if (lseek(fd, off, SEEK_SET) == -1) fatal(poolfile);
275 writepool(fd, &entry);
278 off+= sizeof(entry);
281 if (add) {
282 if (off0 != -1 && lseek(fd, off0, SEEK_SET) == -1) fatal(poolfile);
283 memset(&entry, 0, sizeof(entry));
284 entry.magic= POOL_MAGIC;
285 entry.ip= ip;
286 writepool(fd, &entry);
288 close(fd);
291 static void cachedhcp(int n, dhcp_t *dp)
293 /* Store a DHCP packet in a cache where those who care can find it. */
294 static int inited;
295 FILE *fp;
296 int fd;
297 int mode;
299 if (test > 0) return;
301 if (!inited) {
302 /* First time, clear store and also save my pid. */
303 if ((fp= fopen(PATH_DHCPPID, "w")) != nil) {
304 if (fprintf(fp, "%d\n", getpid()) == EOF || fclose(fp) == EOF) {
305 fatal(PATH_DHCPPID);
308 inited= 1;
309 mode= O_WRONLY | O_CREAT | O_TRUNC;
310 } else {
311 mode= O_WRONLY;
314 dp->xid= htonl(now); /* To tell how old this data is. */
316 if ((fd= open(cachefile, mode, 0666)) < 0
317 || lseek(fd, (off_t) n * sizeof(*dp), SEEK_SET) == -1
318 || write(fd, dp, sizeof(*dp)) < 0
319 || close(fd) < 0
321 if (errno != ENOENT) fatal(cachefile);
325 static void printdata(void)
327 /* Show the contents of the cache and the dynamic pool. */
328 int fd;
329 dhcp_t d;
330 ssize_t r;
331 int i;
332 pool_t entry;
333 unsigned long expire;
334 char delta[3*sizeof(u32_t)];
336 initdhcpconf();
338 if ((fd= open(cachefile, O_RDONLY)) < 0) fatal(cachefile);
339 i= 0;
340 while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) {
341 if (d.yiaddr != 0) {
342 printf("DHCP data for network %d:\n", i);
343 printdhcp(&d);
345 i++;
347 if (r < 0) fatal(cachefile);
348 close(fd);
350 if ((fd= openpool(O_RDONLY)) >= 0) {
351 printf("Dynamic address pool since %ld:\n", (long) now);
352 while (readpool(fd, &entry)) {
353 if (entry.ip == 0) continue;
354 expire= ntohl(entry.expire);
355 if (expire == 0) {
356 strcpy(delta, "unused");
357 } else
358 if (expire == 0xFFFFFFFFUL) {
359 strcpy(delta, "infinite");
360 } else
361 if (expire < now) {
362 sprintf(delta, "-%lu", now - expire);
363 } else {
364 sprintf(delta, "+%lu", expire - now);
366 printf("\t%-15s %8s ", inet_ntoa(entry.ip), delta);
367 for (i= 0; i < entry.len; i++) {
368 printf("%02X", entry.clid[i]);
370 fputc('\n', stdout);
372 close(fd);
376 static udpport_t portbyname(const char *name)
378 struct servent *se;
380 if ((se= getservbyname(name, "udp")) == nil) {
381 fprintf(stderr, "%s: Unknown port \"%s\"\n", program, name);
382 exit(1);
384 return se->s_port;
387 static int sendpacket(network_t *np, void *data, size_t len)
389 /* Send out a packet using a filedescriptor that is probably in async mode,
390 * so first dup() a sync version, then write. Return true on success.
392 int fd;
393 ssize_t r;
395 if ((fd= dup(np->fdp->fd)) < 0) fatal("Can't dup()");
396 if ((r= write(fd, data, len)) < 0) {
397 report(np->fdp->device);
398 sleep(10);
400 close(fd);
401 return r >= 0;
404 static size_t servdhcp(network_t *np, buf_t *bp, size_t dlen)
406 buf_t *abp= nil;
407 ipaddr_t cip, ifip;
408 u8_t defclid[1+sizeof(bp->dhcp->chaddr)];
409 u8_t *pdata, *client, *class, *server, *reqip, *lease;
410 u32_t expire;
411 size_t len, cilen, calen;
412 int type, dyn;
413 u8_t atype;
414 static char NAKMESS[] = "IP address requested isn't yours";
416 if (test > 0) return 0;
418 /* The IP address of the interface close to the client. */
419 ifip= bp->dhcp->giaddr != 0 ? bp->dhcp->giaddr : np->ip;
421 /* All kinds of DHCP tags. */
422 if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
423 type= *pdata;
424 } else {
425 type= -1; /* BOOTP? */
428 if (!gettag(bp->dhcp, DHCP_TAG_CLIENTID, &client, &cilen)) {
429 defclid[0]= bp->dhcp->htype;
430 memcpy(defclid+1, bp->dhcp->chaddr, bp->dhcp->hlen);
431 client= defclid;
432 cilen= 1+bp->dhcp->hlen;
435 if (!gettag(bp->dhcp, DHCP_TAG_CLASSID, &class, &calen)) {
436 calen= 0;
439 if (!gettag(bp->dhcp, DHCP_TAG_SERVERID, &server, nil)) {
440 server= B(&np->ip);
443 if (!gettag(bp->dhcp, DHCP_TAG_REQIP, &reqip, nil)) {
444 reqip= nil;
447 /* I'm a server? Then see if I know this client. */
448 if ((np->flags & NF_SERVING)
449 && bp->dhcp->op == DHCP_BOOTREQUEST
450 && between(1, bp->dhcp->hlen, sizeof(bp->dhcp->chaddr))
451 && (server == nil || memcmp(server, &np->ip, sizeof(np->ip)) == 0)
453 get_buf(&abp);
455 /* Is the client in my tables? */
456 (void) makedhcp(abp->dhcp, class, calen, client, cilen, 0, ifip, nil);
457 cip= abp->dhcp->yiaddr;
459 dyn= 0;
460 /* If not, do we have a dynamic address? */
461 if (cip == 0 && (cip= findpool(client, cilen, ifip)) != 0) dyn= 1;
463 if (type == DHCP_INFORM) {
464 /* The client already has an address, it just wants information.
465 * We only answer if we could answer a normal request (cip != 0),
466 * unless configured to answer anyone.
468 if (cip != 0 || (np->flags & NF_INFORM)) cip= bp->dhcp->ciaddr;
471 if (cip == 0 || !makedhcp(abp->dhcp, class, calen,
472 client, cilen, cip, ifip, nil)) {
473 put_buf(&abp);
476 if (abp != nil) {
477 if (gettag(abp->dhcp, DHCP_TAG_LEASE, &lease, nil)) {
478 memcpy(&expire, lease, sizeof(expire));
479 expire= now + ntohl(expire);
480 if (expire < now) expire= 0xFFFFFFFFUL;
481 } else {
482 if (dyn) {
483 /* A dynamic address must have a lease. */
484 fprintf(stderr, "%s: No lease set for address %s\n",
485 program, inet_ntoa(cip));
486 exit(1);
488 lease= nil;
489 expire= 0xFFFFFFFFUL;
492 /* What does our client want, and what do we say? */
493 switch (type) {
494 case DHCP_DISCOVER:
495 atype= DHCP_OFFER;
497 /* Assign this address for a short moment. */
498 if (dyn && !commitpool(cip, client, cilen, now + DELTA_FAST)) {
499 put_buf(&abp);
501 break;
503 case -1:/* BOOTP */
504 case DHCP_REQUEST:
505 case DHCP_INFORM:
506 atype= DHCP_ACK;
507 /* The address wanted must be the address we offer. */
508 if ((reqip != nil && memcmp(reqip, &cip, sizeof(cip)) != 0)
509 || (bp->dhcp->ciaddr != 0 && bp->dhcp->ciaddr != cip)
511 atype= DHCP_NAK;
512 } else
513 if (dyn && type == DHCP_REQUEST) {
514 /* Assign this address for the duration of the lease. */
515 if (!commitpool(cip, client, cilen, expire)) put_buf(&abp);
517 break;
519 case DHCP_DECLINE:
520 /* Our client doesn't want the offered address! */
521 if (dyn
522 && reqip != nil
523 && memcmp(reqip, &cip, sizeof(cip)) == 0
525 int i;
527 fprintf(stderr, "%s: ", program);
528 for (i= 0; i < cilen; i++) {
529 fprintf(stderr, "%02X", client[i]);
531 fprintf(stderr, " declines %s", inet_ntoa(cip));
532 if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
533 fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
535 fputc('\n', stderr);
537 /* Disable address for the duration of the lease. */
538 (void) commitpool(cip, nil, 0, expire);
540 put_buf(&abp);
541 break;
543 case DHCP_RELEASE:
544 /* Our client is nice enough to return its address. */
545 if (dyn) (void) commitpool(cip, client, cilen, now);
546 put_buf(&abp);
547 break;
549 default: /* Anything else is ignored. */
550 put_buf(&abp);
554 if (abp != nil) {
555 /* Finish the return packet. */
556 abp->dhcp->htype= bp->dhcp->htype;
557 abp->dhcp->hlen= bp->dhcp->hlen;
558 abp->dhcp->hops= 0;
559 abp->dhcp->xid= bp->dhcp->xid;
560 abp->dhcp->secs= 0;
561 abp->dhcp->flags= bp->dhcp->flags;
562 abp->dhcp->ciaddr= 0;
563 abp->dhcp->yiaddr= atype == DHCP_NAK ? 0 : cip;
564 if (atype == DHCP_NAK) abp->dhcp->siaddr= 0;
565 abp->dhcp->giaddr= bp->dhcp->giaddr;
566 memcpy(abp->dhcp->chaddr,bp->dhcp->chaddr,sizeof(bp->dhcp->chaddr));
568 settag(abp->dhcp, DHCP_TAG_SERVERID, &np->ip, sizeof(np->ip));
570 if (lease == nil) {
571 /* No lease specified? Then give an infinite lease. */
572 settag(abp->dhcp, DHCP_TAG_LEASE, &expire, sizeof(expire));
575 if (type == DHCP_INFORM) {
576 /* Oops, this one has a fixed address, so no lease business. */
577 abp->dhcp->yiaddr= 0;
578 settag(abp->dhcp, DHCP_TAG_LEASE, nil, 0);
579 settag(abp->dhcp, DHCP_TAG_RENEWAL, nil, 0);
580 settag(abp->dhcp, DHCP_TAG_REBINDING, nil, 0);
583 if (atype == DHCP_NAK) {
584 /* A NAK doesn't need much. */
585 memset(abp->dhcp->sname, 0, sizeof(abp->dhcp->sname));
586 memset(abp->dhcp->file, 0, sizeof(abp->dhcp->file));
587 memset(abp->dhcp->options, 255, sizeof(abp->dhcp->options));
588 settag(abp->dhcp, DHCP_TAG_MESSAGE, NAKMESS, sizeof(NAKMESS));
591 settag(abp->dhcp, DHCP_TAG_TYPE, &atype, sizeof(atype));
593 /* Figure out where to send this to. */
594 abp->udpio->uih_src_addr= np->ip;
595 abp->udpio->uih_src_port= port_server;
596 if (bp->dhcp->giaddr != 0) {
597 abp->udpio->uih_dst_addr= bp->dhcp->giaddr;
598 abp->udpio->uih_dst_port= port_server;
599 } else
600 if (bp->dhcp->flags & DHCP_FLAGS_BCAST) {
601 abp->udpio->uih_dst_addr= BCAST_IP;
602 abp->udpio->uih_dst_port= port_client;
603 } else
604 if (bp->udpio->uih_src_addr != 0
605 && bp->udpio->uih_dst_addr == np->ip
607 abp->udpio->uih_dst_addr= bp->udpio->uih_src_addr;
608 abp->udpio->uih_dst_port= port_client;
609 } else {
610 abp->udpio->uih_dst_addr= BCAST_IP;
611 abp->udpio->uih_dst_port= port_client;
613 abp->udpio->uih_ip_opt_len= 0;
614 abp->udpio->uih_data_len= sizeof(dhcp_t);
616 /* Copy the packet to the input buffer, and return the new size. */
617 memcpy(bp->buf, abp->buf, sizeof(bp->buf));
618 put_buf(&abp);
619 return sizeof(udp_io_hdr_t) + sizeof(dhcp_t);
623 /* I'm a relay? If it is a not already a relayed request then relay. */
624 if ((np->flags & NF_RELAYING)
625 && bp->dhcp->op == DHCP_BOOTREQUEST
626 && bp->dhcp->giaddr == 0
628 bp->dhcp->giaddr= np->ip;
629 bp->udpio->uih_src_addr= np->ip;
630 bp->udpio->uih_src_port= port_server;
631 bp->udpio->uih_dst_addr= np->server;
632 bp->udpio->uih_dst_port= port_server;
633 return dlen;
636 /* I'm a relay? If the server sends a reply to me then relay back. */
637 if ((np->flags & NF_RELAYING)
638 && bp->dhcp->op == DHCP_BOOTREPLY
639 && bp->dhcp->giaddr == np->ip
641 bp->dhcp->giaddr= 0;
642 bp->udpio->uih_src_addr= np->ip;
643 bp->udpio->uih_src_port= port_server;
644 bp->udpio->uih_dst_addr= BCAST_IP;
645 bp->udpio->uih_dst_port= port_client;
646 return dlen;
649 /* Don't know what to do otherwise, so doing nothing seems wise. */
650 return 0;
653 static void onsig(int sig)
655 switch (sig) {
656 case SIGUSR1: debug++; break;
657 case SIGUSR2: debug= 0; break;
661 static void usage(void)
663 fprintf(stderr,
664 "Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n",
665 program);
666 exit(1);
669 int main(int argc, char **argv)
671 int i;
672 network_t *np;
673 struct sigaction sa;
674 ssize_t r;
675 buf_t *bp;
676 static struct timeval eventtv;
678 main:
679 r = -1;
680 bp = nil;
681 program= argv[0];
682 start= now= time(nil);
684 debug= 0;
685 i= 1;
686 while (i < argc && argv[i][0] == '-') {
687 char *opt= argv[i++]+1;
689 if (opt[0] == '-' && opt[1] == 0) break; /* -- */
691 if (strcmp(opt, "-lwip") == 0) {
692 lwip = 1;
693 continue;
696 while (*opt != 0) switch (*opt++) {
697 case 'f':
698 if (*opt == 0) {
699 if (i == argc) usage();
700 opt= argv[i++];
702 configfile= opt;
703 opt= "";
704 break;
705 case 'c':
706 if (*opt == 0) {
707 if (i == argc) usage();
708 opt= argv[i++];
710 cachefile= opt;
711 opt= "";
712 break;
713 case 'p':
714 if (*opt == 0) {
715 if (i == argc) usage();
716 opt= argv[i++];
718 poolfile= opt;
719 opt= "";
720 break;
721 case 't':
722 test= 1;
723 if (between('0', *opt, '9')) test= strtoul(opt, &opt, 10);
724 break;
725 case 'd':
726 debug= 1;
727 if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10);
728 break;
729 case 'q':
730 qflag= 1;
731 break;
732 case 'a':
733 aflag= 1;
734 break;
735 case 'r':
736 rflag= 1;
737 break;
738 default:
739 usage();
742 if (aflag + rflag + qflag > 1) usage();
744 if (aflag || rflag) {
745 /* Add or remove addresses from the dynamic pool. */
746 while (i < argc) updatepool(aflag, argv[i++]);
747 exit(0);
750 if (i != argc) usage();
752 if (qflag) {
753 /* Only show the contents of the cache and dynamic pool to the user. */
754 printdata();
755 exit(0);
758 /* BOOTP ports. */
759 port_server= portbyname("bootps");
760 port_client= portbyname("bootpc");
762 sa.sa_handler= onsig;
763 sigemptyset(&sa.sa_mask);
764 sa.sa_flags= 0;
765 sigaction(SIGUSR1, &sa, nil);
766 sigaction(SIGUSR2, &sa, nil);
768 /* Initial configuration. */
769 for (i= 0; i < N_NETS; i++) {
770 int fd;
771 ipaddr_t ip, mask;
773 /* Is there something there? */
774 if ((fd= open(ipdev(i), O_RDWR|O_NONBLOCK)) < 0) {
775 if (errno != ENOENT && errno != ENODEV && errno != ENXIO) {
776 fatal(ipdev(i));
778 continue;
780 close(fd);
782 network[n_nets++]= np= newnetwork();
783 np->n= i;
785 /* Ethernet? */
786 if (lwip) {
787 np->type = NT_ETHERNET;
788 } else if (opendev(np, FT_ETHERNET, 1)) {
789 np->type= B(&np->eth)[0] != 'Z' ? NT_ETHERNET : NT_SINK;
790 if (debug >= 1) {
791 printf("%s: Ethernet address is %s%s\n",
792 np->fdp->device, ether_ntoa(&np->eth),
793 np->type == NT_SINK ? " (sink)" : "");
795 closedev(np, FT_ETHERNET);
798 /* Only true Ethernets worry about DHCP. */
799 if (np->type != NT_ETHERNET) np->renew= np->rebind= np->lease= NEVER;
802 /* Try to find my interfaces in the DHCP table. */
803 for (i= 0; i < n_nets; i++) {
804 ipaddr_t cip;
805 u8_t clid[1+DHCP_HLEN_ETH];
806 size_t cilen;
808 np= network[i];
809 if (np->flags & NF_BOUND) continue;
811 if (np->type == NT_IP) {
812 cilen= 0;
813 } else {
814 ether2clid(clid, &np->eth);
815 cilen= 1+DHCP_HLEN_ETH;
818 /* Try to find an Ethernet address, or the IP address of an already
819 * configured network. If we have data we get an IP address back.
821 get_buf(&bp);
822 (void) makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
823 clid, cilen, np->ip, 0, np);
824 cip= bp->dhcp->yiaddr;
826 /* Gather information on the interface. */
827 if (cip != 0
828 && makedhcp(bp->dhcp, (u8_t *) "Minix", 5,
829 clid, cilen, cip, cip, np)
830 && test < 2
832 u8_t *pdata;
833 u16_t mtu;
835 cachedhcp(np->n, bp->dhcp);
836 np->ip= cip;
837 (void) gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil);
838 memcpy(&np->mask, pdata, sizeof(np->mask));
839 if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
840 memcpy(&np->gateway, pdata, sizeof(np->gateway));
841 } else {
842 np->gateway= 0;
844 if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
845 memcpy(&mtu, pdata, sizeof(mtu));
846 mtu= ntohs(mtu);
847 } else {
848 mtu= 0;
850 set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
851 if (debug >= 1) {
852 printf("%s: IP address is %s\n",
853 ipdev(np->n), cidr_ntoa(np->ip, np->mask));
855 np->flags |= NF_BOUND;
856 np->renew= np->rebind= np->lease= NEVER;
857 np->sol_ct= N_SOLICITS;
858 np->solicit= 0;
860 /* Other (previous) interfaces may have been defined. */
861 i= 0;
863 put_buf(&bp);
866 for (;;) {
867 now= time(nil);
868 event= NEVER;
870 /* Is it time to request/renew a lease? */
871 for (i= 0; i < n_nets; i++) {
872 np= network[i];
874 if (np->renew <= now) {
875 u8_t type;
876 static u8_t taglist[] = {
877 DHCP_TAG_NETMASK, DHCP_TAG_GATEWAY, DHCP_TAG_DNS,
878 DHCP_TAG_HOSTNAME
880 u8_t ethclid[1+DHCP_HLEN_ETH];
882 /* We may have lost our binding or even our lease. */
883 if (np->rebind <= now) np->server= BCAST_IP;
885 if (np->lease <= now) {
886 if (np->flags & NF_BOUND) closedev(np, FT_ALL);
888 if ((np->flags & (NF_BOUND | NF_POSSESSIVE)) == NF_BOUND) {
889 set_ipconf(ipdev(np->n), np->ip= 0, np->mask= 0, 0);
890 if (debug >= 1) {
891 printf("%s: Interface disabled (lease expired)\n",
892 ipdev(np->n));
895 np->flags &= ~NF_BOUND;
898 /* See if we can open the network we need to send on. */
899 if (!(np->flags & NF_BOUND)) {
900 if (!opendev(np, FT_ETHERNET, 1)) continue;
901 } else {
902 if (!opendev(np, FT_BOOTPC, 1)) continue;
905 if (!(np->flags & NF_NEGOTIATING)) {
906 /* We need to start querying a DHCP server. */
907 np->start= now;
908 np->delta= DELTA_FIRST;
909 np->flags |= NF_NEGOTIATING;
912 /* Fill in a DHCP query packet. */
913 get_buf(&bp);
914 dhcp_init(bp->dhcp);
915 bp->dhcp->op= DHCP_BOOTREQUEST;
916 bp->dhcp->htype= DHCP_HTYPE_ETH;
917 bp->dhcp->hlen= DHCP_HLEN_ETH;
918 bp->dhcp->xid= XID(np);
919 bp->dhcp->secs= htons(now - np->start > 0xFFFF
920 ? 0xFFFF : now - np->start);
921 memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
923 if (np->lease <= now) {
924 /* First time, or my old server is unresponsive. */
925 type= DHCP_DISCOVER;
926 } else {
927 /* Request an offered address or renew an address. */
928 type= DHCP_REQUEST;
929 if (np->flags & NF_BOUND) {
930 /* A renewal, I claim my current address. */
931 bp->dhcp->ciaddr= np->ip;
932 } else {
933 /* Nicely ask for the address just offered. */
934 settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip,
935 sizeof(np->ip));
936 settag(bp->dhcp, DHCP_TAG_SERVERID, &np->server,
937 sizeof(np->server));
940 settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
942 /* My client ID. Simply use the default. */
943 ether2clid(ethclid, &np->eth);
944 settag(bp->dhcp, DHCP_TAG_CLIENTID, ethclid, sizeof(ethclid));
946 /* The Class ID may serve to recognize Minix hosts. */
947 settag(bp->dhcp, DHCP_TAG_CLASSID, "Minix", 5);
949 /* The few tags that Minix can make good use of. */
950 settag(bp->dhcp, DHCP_TAG_REQPAR, taglist, sizeof(taglist));
952 /* Some weird sites use a hostname, not a client ID. */
953 if (np->hostname != nil) {
954 settag(bp->dhcp, DHCP_TAG_HOSTNAME,
955 np->hostname, strlen(np->hostname));
958 bp->udpio->uih_src_addr= np->ip;
959 bp->udpio->uih_dst_addr= np->server;
960 bp->udpio->uih_src_port= port_client;
961 bp->udpio->uih_dst_port= port_server;
962 bp->udpio->uih_ip_opt_len= 0;
963 bp->udpio->uih_data_len= sizeof(dhcp_t);
965 if (!(np->flags & NF_BOUND)) {
966 /* Rebind over Ethernet. */
967 udp2ether(bp, np);
968 if (sendpacket(np, (lwip ? bp->ip : (void *) bp->eth),
969 (lwip ? 0 : sizeof(eth_hdr_t)) + sizeof(ip_hdr_t)
970 + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
971 if (debug >= 1) {
972 printf("%s: Broadcast DHCP %s\n",
973 np->fdp->device, dhcptypename(type));
974 if (debug >= 2) printdhcp(bp->dhcp);
977 } else {
978 /* Renew over UDP. */
979 if (sendpacket(np, bp->udpio, sizeof(udp_io_hdr_t)
980 + sizeof(dhcp_t))) {
981 if (debug >= 1) {
982 printf("%s: Sent DHCP %s to %s\n",
983 np->fdp->device,
984 dhcptypename(type),
985 inet_ntoa(np->server));
986 if (debug >= 2) printdhcp(bp->dhcp);
990 put_buf(&bp);
992 /* When to continue querying a DHCP server? */
993 if (np->flags & NF_BOUND) {
994 /* Still bound, keep halving time till next event. */
995 time_t e, d;
997 e= now < np->rebind ? np->rebind : np->lease;
998 d= (e - now) / 2;
999 if (d < DELTA_SLOW) d= DELTA_SLOW;
1000 np->renew= now + d;
1001 if (np->renew > e) np->renew= e;
1002 } else {
1003 /* Not bound, be desparate. */
1004 np->renew= now + np->delta;
1005 if ((np->delta *= 2) > DELTA_FAST) np->delta= DELTA_FAST;
1008 if (np->renew < event) event= np->renew;
1011 /* Read DHCP responses. */
1012 for (i= 0; i < n_nets; i++) {
1013 np= network[i];
1014 if (!(np->flags & NF_NEGOTIATING)) continue;
1016 if (!(np->flags & NF_BOUND)) {
1017 if (!opendev(np, FT_ETHERNET, 0)) continue;
1018 get_buf(&np->fdp->bp);
1019 r= asyn_read(&asyn, np->fdp->fd,
1020 lwip ? np->fdp->bp->ip : (void *) np->fdp->bp->eth,
1021 lwip ? BUF_IP_SIZE : BUF_ETH_SIZE);
1022 } else {
1023 if (!opendev(np, FT_BOOTPC, 0)) continue;
1024 get_buf(&np->fdp->bp);
1025 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio,
1026 BUF_UDP_SIZE);
1028 if (r != -1) break;
1029 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1030 report(np->fdp->device);
1031 sleep(10);
1035 /* Is there a response? */
1036 if (i < n_nets) {
1037 give_buf(&bp, &np->fdp->bp);
1038 if (((!(np->flags & NF_BOUND)
1039 && r >= (lwip ? 0 : (sizeof(eth_hdr_t)) + sizeof(ip_hdr_t)
1040 + sizeof(udp_hdr_t) + offsetof(dhcp_t, options))
1041 && ether2udp(bp)
1042 && bp->udpio->uih_dst_port == port_client)
1044 ((np->flags & NF_BOUND)
1045 && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)))
1046 && bp->dhcp->op == DHCP_BOOTREPLY
1047 && bp->dhcp->htype == DHCP_HTYPE_ETH
1048 && bp->dhcp->hlen == DHCP_HLEN_ETH
1049 && bp->dhcp->xid == XID(np)
1050 && memcmp(bp->dhcp->chaddr, &np->eth, sizeof(np->eth)) == 0
1052 /* Pfew! We got a DHCP reply! */
1053 u8_t *pdata;
1054 size_t len;
1055 int type;
1056 ipaddr_t mask, gateway, relay, server;
1057 u16_t mtu;
1058 u32_t lease, renew, rebind, t;
1060 relay= bp->udpio->uih_src_addr;
1061 if (gettag(bp->dhcp, DHCP_TAG_SERVERID, &pdata, nil)) {
1062 memcpy(&server, pdata, sizeof(server));
1063 } else {
1064 server= relay;
1067 if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) {
1068 type= pdata[0];
1069 } else {
1070 type= DHCP_ACK; /* BOOTP? */
1073 if (debug >= 1) {
1074 printf("%s: Got a DHCP %s from %s",
1075 np->fdp->device, dhcptypename(type), inet_ntoa(server));
1076 printf(relay != server ? " through %s\n" : "\n",
1077 inet_ntoa(relay));
1078 if (debug >= 2) printdhcp(bp->dhcp);
1081 if (gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil)) {
1082 memcpy(&mask, pdata, sizeof(mask));
1083 } else {
1084 mask= defaultmask(bp->dhcp->ciaddr);
1087 if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) {
1088 memcpy(&mtu, pdata, sizeof(mtu));
1089 mtu= ntohs(mtu);
1090 } else {
1091 mtu= 0;
1094 if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) {
1095 memcpy(&gateway, pdata, sizeof(gateway));
1096 } else {
1097 gateway= 0;
1100 lease= NEVER;
1101 if (gettag(bp->dhcp, DHCP_TAG_LEASE, &pdata, nil)) {
1102 memcpy(&lease, pdata, sizeof(lease));
1103 lease= ntohl(lease);
1106 rebind= lease - lease / 8;
1107 if (gettag(bp->dhcp, DHCP_TAG_REBINDING, &pdata, nil)) {
1108 memcpy(&t, pdata, sizeof(t));
1109 t= ntohl(t);
1110 if (t < rebind) rebind= t;
1113 renew= lease / 2;
1114 if (gettag(bp->dhcp, DHCP_TAG_RENEWAL, &pdata, nil)) {
1115 memcpy(&t, pdata, sizeof(t));
1116 t= ntohl(t);
1117 if (t < renew) renew= t;
1120 if (type == DHCP_OFFER && np->rebind <= np->renew) {
1121 /* It's an offer for an address and we haven't taken one
1122 * yet. It's all the same to us, so take this one.
1124 np->ip= bp->dhcp->yiaddr;
1125 np->mask= mask;
1126 np->server= server;
1127 np->gateway= gateway;
1128 np->delta= DELTA_FIRST;
1129 np->renew= now;
1130 np->rebind= np->lease= now + DELTA_FAST;
1132 /* Send out an ARP request to see if the offered address
1133 * is in use already.
1135 make_arp(bp, np);
1136 if (sendpacket(np, bp->eth, sizeof(arp46_t))) {
1137 if (debug >= 2) {
1138 printf("Sent ARP for %s\n", inet_ntoa(np->ip));
1141 np->flags &= ~NF_CONFLICT;
1144 if (type == DHCP_ACK && !(np->flags & NF_CONFLICT)) {
1145 /* An acknowledgment. The address is all mine. */
1146 cachedhcp(np->n, bp->dhcp);
1147 np->ip= bp->dhcp->yiaddr;
1148 np->mask= mask;
1149 np->server= server;
1150 set_ipconf(ipdev(np->n), np->ip, np->mask, mtu);
1151 if (debug >= 1) {
1152 printf("%s: Address set to %s\n",
1153 ipdev(np->n), cidr_ntoa(np->ip, np->mask));
1155 if (lease >= NEVER - now) {
1156 /* The lease is infinite! */
1157 np->renew= np->rebind= np->lease= NEVER;
1158 } else {
1159 np->lease= now + lease;
1160 np->renew= now + renew;
1161 np->rebind= now + rebind;
1163 if (test >= 3) {
1164 np->renew= now + 60;
1165 np->rebind= test >= 4 ? np->renew : np->renew + 60;
1166 np->lease= test >= 5 ? np->rebind : np->rebind + 60;
1168 if (!(np->flags & NF_IRDP)) {
1169 np->sol_ct= (np->flags & NF_BOUND) ? 1 : N_SOLICITS;
1170 np->solicit= 0;
1172 np->flags &= ~NF_NEGOTIATING;
1173 np->flags |= NF_BOUND;
1174 closedev(np, FT_ETHERNET);
1175 closedev(np, FT_BOOTPC);
1178 if (type == DHCP_ACK && (np->flags & NF_CONFLICT)) {
1179 /* Alas there is a conflict. Decline to use the address. */
1180 u8_t ethclid[1+DHCP_HLEN_ETH];
1181 static char USED[]= "Address in use by 00:00:00:00:00:00";
1183 type= DHCP_DECLINE;
1184 dhcp_init(bp->dhcp);
1185 bp->dhcp->op= DHCP_BOOTREQUEST;
1186 bp->dhcp->htype= DHCP_HTYPE_ETH;
1187 bp->dhcp->hlen= DHCP_HLEN_ETH;
1188 bp->dhcp->xid= XID(np);
1189 bp->dhcp->secs= 0;
1190 memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth));
1191 settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip, sizeof(np->ip));
1192 settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1);
1193 ether2clid(ethclid, &np->eth);
1194 settag(bp->dhcp, DHCP_TAG_CLIENTID,ethclid,sizeof(ethclid));
1195 strcpy(USED+18, ether_ntoa(&np->conflict));
1196 settag(bp->dhcp, DHCP_TAG_MESSAGE, USED, strlen(USED));
1198 bp->udpio->uih_src_port= port_client;
1199 bp->udpio->uih_dst_port= port_server;
1200 bp->udpio->uih_ip_opt_len= 0;
1201 bp->udpio->uih_data_len= sizeof(dhcp_t);
1202 udp2ether(bp, np);
1204 if (sendpacket(np, bp->eth,
1205 sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
1206 + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
1207 if (debug >= 1) {
1208 printf("%s: Broadcast DHCP %s\n",
1209 np->fdp->device, dhcptypename(type));
1210 if (debug >= 2) printdhcp(bp->dhcp);
1214 np->renew= np->rebind= np->lease= now + DELTA_FAST;
1215 np->delta= DELTA_FIRST;
1218 if (type == DHCP_NAK) {
1219 /* Oops, a DHCP server doesn't like me, start over! */
1220 np->renew= np->rebind= np->lease= now + DELTA_FAST;
1221 np->delta= DELTA_FIRST;
1223 fprintf(stderr, "%s: Got a NAK from %s",
1224 program, inet_ntoa(server));
1225 if (relay != server) {
1226 fprintf(stderr, " through %s", inet_ntoa(relay));
1228 if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) {
1229 fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata);
1231 fputc('\n', stderr);
1233 } else
1234 if (!(np->flags & NF_BOUND)
1235 && np->rebind > now
1236 && r >= sizeof(arp46_t)
1237 && is_arp_me(bp, np)
1239 /* Oh no, someone else is using the address offered to me! */
1240 np->flags |= NF_CONFLICT;
1242 fprintf(stderr, "%s: %s: %s offered by ",
1243 program,
1244 np->fdp->device,
1245 inet_ntoa(np->ip));
1246 fprintf(stderr, "%s is already in use by %s\n",
1247 inet_ntoa(np->server),
1248 ether_ntoa(&np->conflict));
1250 put_buf(&bp);
1251 if (np->renew < event) event= np->renew;
1254 /* Perform router solicitations. */
1255 for (i= 0; i < n_nets; i++) {
1256 np= network[i];
1257 if (!(np->flags & NF_BOUND)) continue;
1259 if (np->solicit <= now) {
1260 if (!opendev(np, FT_ICMP, 1)) continue;
1261 np->solicit= NEVER;
1263 get_buf(&bp);
1264 if (np->gateway != 0) {
1265 /* No IRDP response seen yet, advertise the router given
1266 * by DHCP to my own interface.
1268 icmp_advert(bp, np);
1269 if (sendpacket(np, bp->ip, sizeof(ip_hdr_t) + 16)) {
1270 if (debug >= 2) {
1271 printf("%s: Sent advert for %s to self\n",
1272 np->fdp->device, inet_ntoa(np->gateway));
1275 np->solicit= now + DELTA_ADV/2;
1278 if (np->sol_ct >= 0 && --np->sol_ct >= 0) {
1279 /* Send a router solicitation. */
1280 icmp_solicit(bp);
1281 if (sendpacket(np, bp->ip, sizeof(*bp->ip) + 8)) {
1282 if (debug >= 2) {
1283 printf("%s: Broadcast router solicitation\n",
1284 np->fdp->device);
1287 np->solicit= now + DELTA_SOL;
1288 } else {
1289 /* No response, or not soliciting right now. */
1290 closedev(np, FT_ICMP);
1293 put_buf(&bp);
1295 if (np->solicit < event) event= np->solicit;
1298 /* Read router adverts. */
1299 for (i= 0; i < n_nets; i++) {
1300 np= network[i];
1301 if (!(np->flags & NF_BOUND)) continue;
1302 if (np->sol_ct < 0) continue;
1304 if (!opendev(np, FT_ICMP, 0)) continue;
1305 get_buf(&np->fdp->bp);
1306 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->ip, BUF_IP_SIZE);
1307 if (r != -1) break;
1308 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1309 report(np->fdp->device);
1310 sleep(10);
1314 /* Is there an advert? */
1315 if (i < n_nets && r >= sizeof(ip_hdr_t) + 8) {
1316 ipaddr_t router;
1318 give_buf(&bp, &np->fdp->bp);
1319 if ((router= icmp_is_advert(bp)) != 0) {
1320 if (debug >= 2) {
1321 printf("%s: Router advert received from %s\n",
1322 np->fdp->device, inet_ntoa(router));
1324 np->solicit= NEVER;
1325 np->sol_ct= -1;
1326 np->flags |= NF_IRDP;
1327 closedev(np, FT_ICMP);
1329 put_buf(&bp);
1332 /* We start serving if all the interfaces so marked are configured. */
1333 for (i= 0; i < n_nets; i++) {
1334 np= network[i];
1335 if ((np->flags & NF_RELAYING) && (np->flags & NF_BOUND)) {
1336 if (((np->ip ^ np->server) & np->mask) == 0) {
1337 /* Don't relay to a server that is on this same net. */
1338 np->flags &= ~NF_RELAYING;
1341 if (!(np->flags & (NF_SERVING|NF_RELAYING))) continue;
1342 if (!(np->flags & NF_BOUND)) { serving= 0; break; }
1343 serving= 1;
1346 /* Read DHCP requests. */
1347 for (i= 0; i < n_nets; i++) {
1348 np= network[i];
1349 if (!(np->flags & NF_BOUND)) continue;
1350 if (!(np->flags & (NF_SERVING|NF_RELAYING)) || !serving) continue;
1352 if (!opendev(np, FT_BOOTPS, 0)) continue;
1353 get_buf(&np->fdp->bp);
1354 r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio, BUF_UDP_SIZE);
1356 if (r != -1) break;
1357 if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) {
1358 report(np->fdp->device);
1359 sleep(10);
1363 /* Is there a request? */
1364 if (i < n_nets
1365 && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options)
1367 give_buf(&bp, &np->fdp->bp);
1369 if (debug >= 1) {
1370 printf("%s: Got DHCP packet from %s to ",
1371 np->fdp->device, inet_ntoa(bp->udpio->uih_src_addr));
1372 printf("%s\n", inet_ntoa(bp->udpio->uih_dst_addr));
1373 if (debug >= 2) printdhcp(bp->dhcp);
1376 /* Can we do something with this DHCP packet? */
1377 if ((r= servdhcp(np, bp, r)) > 0) {
1378 /* Yes, we have something to send somewhere. */
1379 if (sendpacket(np, bp->udpio, r)) {
1380 if (debug >= 1) {
1381 printf("%s: Sent DHCP packet to %s\n",
1382 np->fdp->device,
1383 inet_ntoa(bp->udpio->uih_dst_addr));
1384 if (debug >= 2) printdhcp(bp->dhcp);
1388 put_buf(&bp);
1391 if (debug >= 1) {
1392 static char *lastbrk;
1393 extern char _end;
1395 if (sbrk(0) != lastbrk) {
1396 lastbrk= sbrk(0);
1397 printf("Memory use = %lu\n",
1398 (unsigned long) (lastbrk - &_end));
1400 fflush(stdout);
1403 /* Bail out if not a server, and there is nothing else to do ever. */
1404 if (!serving && event == NEVER) break;
1406 /* Wait for something to do. */
1407 eventtv.tv_sec= event;
1408 if (asyn_wait(&asyn, 0, event == NEVER ? nil : &eventtv) < 0) {
1409 if (errno != EINTR) {
1410 report("asyn_wait()");
1411 sleep(10);
1415 if (debug >= 1) printf("Nothing more to do! Starting over...\n");
1416 sleep(2);
1417 goto main;
1419 return 0;