/*
* convert a client id to a string. If it's already
* ascii, leave it be. Otherwise, convert it to hex.
*/
extern char*
toid(uchar *p, int n)
{
int i;
char *s;
/*
* find a binding for an id or hardware address
*/
static int
lockopen(char *file)
{
char err[ERRMAX];
int fd, tries;
for(tries = 0; tries < 5; tries++){
fd = open(file, ORDWR);
if(fd >= 0)
return fd;
errstr(err, sizeof err);
if(strstr(err, "lock")){
/* wait for other process to let go of lock */
sleep(250);
/*
* synchronize cached binding with file. the file always wins.
*/
static int
syncbinding(Binding *b, int returnfd)
{
char buf[512];
int i, fd;
Dir *d;
snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
fd = lockopen(buf);
if(fd < 0){
/* assume someone else is using it */
b->lease = time(0) + OfferTimeout;
return -1;
}
static void
lognolease(Binding *b)
{
/* renew the old binding, and hope it eventually goes away */
b->offer = 5*60;
commitbinding(b);
/* complain if we haven't in the last 5 minutes */
if(now - b->lastcomplained < 5*60)
return;
syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
b->lastcomplained = now;
}
/*
* find a free binding for a hw addr or id on the same network as iip
*/
extern Binding*
idtobinding(char *id, Info *iip, int ping)
{
Binding *b, *oldest;
int oldesttime;
/*
* first look for an old binding that matches. that way
* clients will tend to keep the same ip addresses.
*/
for(b = bcache; b; b = b->next){
if(b->boundto && strcmp(b->boundto, id) == 0){
if(!samenet(b->ip, iip))
continue;
/* check with the other servers */
syncbinding(b, 0);
if(strcmp(b->boundto, id) == 0)
return b;
}
}
/*
* look for oldest binding that we think is unused
*/
for(;;){
oldest = nil;
oldesttime = 0;
for(b = bcache; b; b = b->next){
if(b->tried != now)
if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
if(oldest == nil || b->lasttouched < oldesttime){
/* sync and check again */
syncbinding(b, 0);
if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
if(oldest == nil || b->lasttouched < oldesttime){
oldest = b;
oldesttime = b->lasttouched;
}
}
}
if(oldest == nil)
break;
/* make sure noone is still using it */
oldest->tried = now;
if(ping == 0 || icmpecho(oldest->ip) == 0)
return oldest;
lognolease(oldest); /* sets lastcomplained */
}
/* try all bindings */
for(b = bcache; b; b = b->next){
syncbinding(b, 0);
if(b->tried != now)
if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
b->tried = now;
if(ping == 0 || icmpecho(b->ip) == 0)
return b;
/*
* find an offer for this id
*/
extern Binding*
idtooffer(char *id, Info *iip)
{
Binding *b;
/* look for an offer to this id */
for(b = bcache; b; b = b->next){
if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
/* make sure some other system hasn't stolen it */
syncbinding(b, 0);
if(b->lease < now
|| (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
return b;
}
}
return 0;
}
/*
* commit a lease, this could fail
*/
extern int
commitbinding(Binding *b)
{
int fd;
long now;