URI:
       tdb.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tdb.c (7590B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <ip.h>
            4 #include <bio.h>
            5 #include <ndb.h>
            6 #include <ctype.h>
            7 #include "dat.h"
            8 
            9 /*
           10  *  format of a binding entry:
           11  *        char ipaddr[32];
           12  *        char id[32];
           13  *        char hwa[32];
           14  *        char otime[10];
           15  */
           16 Binding *bcache;
           17 uchar bfirst[IPaddrlen];
           18 char *binddir = nil;
           19 char *xbinddir = "#9/ndb/dhcp";
           20 
           21 /*
           22  *  convert a byte array to hex
           23  */
           24 static char
           25 hex(int x)
           26 {
           27         if(x < 10)
           28                 return x + '0';
           29         return x - 10 + 'a';
           30 }
           31 extern char*
           32 tohex(char *hdr, uchar *p, int len)
           33 {
           34         char *s, *sp;
           35         int hlen;
           36 
           37         hlen = strlen(hdr);
           38         s = malloc(hlen + 2*len + 1);
           39         sp = s;
           40         strcpy(sp, hdr);
           41         sp += hlen;
           42         for(; len > 0; len--){
           43                 *sp++ = hex(*p>>4);
           44                 *sp++ = hex(*p & 0xf);
           45                 p++;
           46         }
           47         *sp = 0;
           48         return s;
           49 }
           50 
           51 /*
           52  *  convert a client id to a string.  If it's already
           53  *  ascii, leave it be.  Otherwise, convert it to hex.
           54  */
           55 extern char*
           56 toid(uchar *p, int n)
           57 {
           58         int i;
           59         char *s;
           60 
           61         for(i = 0; i < n; i++)
           62                 if(!isprint(p[i]))
           63                         return tohex("id", p, n);
           64         s = malloc(n + 1);
           65         memmove(s, p, n);
           66         s[n] = 0;
           67         return s;
           68 }
           69 
           70 /*
           71  *  increment an ip address
           72  */
           73 static void
           74 incip(uchar *ip)
           75 {
           76         int i, x;
           77 
           78         for(i = IPaddrlen-1; i >= 0; i--){
           79                 x = ip[i];
           80                 x++;
           81                 ip[i] = x;
           82                 if((x & 0x100) == 0)
           83                         break;
           84         }
           85 }
           86 
           87 /*
           88  *  find a binding for an id or hardware address
           89  */
           90 static int
           91 lockopen(char *file)
           92 {
           93         char err[ERRMAX];
           94         int fd, tries;
           95 
           96         for(tries = 0; tries < 5; tries++){
           97                 fd = open(file, OLOCK|ORDWR);
           98                 if(fd >= 0)
           99                         return fd;
          100                 errstr(err, sizeof err);
          101                 if(strstr(err, "lock")){
          102                         /* wait for other process to let go of lock */
          103                         sleep(250);
          104 
          105                         /* try again */
          106                         continue;
          107                 }
          108                 if(strstr(err, "exist") || strstr(err, "No such")){
          109                         /* no file, create an exclusive access file */
          110                         fd = create(file, ORDWR, DMEXCL|0666);
          111                         chmod(file, 0666);
          112                         if(fd >= 0)
          113                                 return fd;
          114                 }
          115         }
          116         return -1;
          117 }
          118 
          119 void
          120 setbinding(Binding *b, char *id, long t)
          121 {
          122         if(b->boundto)
          123                 free(b->boundto);
          124 
          125         b->boundto = strdup(id);
          126         b->lease = t;
          127 }
          128 
          129 static void
          130 parsebinding(Binding *b, char *buf)
          131 {
          132         long t;
          133         char *id, *p;
          134 
          135         /* parse */
          136         t = atoi(buf);
          137         id = strchr(buf, '\n');
          138         if(id){
          139                 *id++ = 0;
          140                 p = strchr(id, '\n');
          141                 if(p)
          142                         *p = 0;
          143         } else
          144                 id = "";
          145 
          146         /* replace any past info */
          147         setbinding(b, id, t);
          148 }
          149 
          150 static int
          151 writebinding(int fd, Binding *b)
          152 {
          153         Dir *d;
          154 
          155         seek(fd, 0, 0);
          156         if(fprint(fd, "%ld\n%s\n", b->lease, b->boundto) < 0)
          157                 return -1;
          158         d = dirfstat(fd);
          159         if(d == nil)
          160                 return -1;
          161         b->q.type = d->qid.type;
          162         b->q.path = d->qid.path;
          163         b->q.vers = d->qid.vers;
          164         free(d);
          165         return 0;
          166 }
          167 
          168 /*
          169  *  synchronize cached binding with file.  the file always wins.
          170  */
          171 int
          172 syncbinding(Binding *b, int returnfd)
          173 {
          174         char buf[512];
          175         int i, fd;
          176         Dir *d;
          177 
          178         if(binddir == nil)
          179                 binddir = unsharp(xbinddir);
          180 
          181         snprint(buf, sizeof(buf), "%s/%I", binddir, b->ip);
          182         fd = lockopen(buf);
          183         if(fd < 0){
          184                 /* assume someone else is using it */
          185                 b->lease = time(0) + OfferTimeout;
          186                 return -1;
          187         }
          188 
          189         /* reread if changed */
          190         d = dirfstat(fd);
          191         if(d != nil)        /* BUG? */
          192         if(d->qid.type != b->q.type || d->qid.path != b->q.path || d->qid.vers != b->q.vers){
          193                 i = read(fd, buf, sizeof(buf)-1);
          194                 if(i < 0)
          195                         i = 0;
          196                 buf[i] = 0;
          197                 parsebinding(b, buf);
          198                 b->lasttouched = d->mtime;
          199                 b->q.path = d->qid.path;
          200                 b->q.vers = d->qid.vers;
          201         }
          202 
          203         free(d);
          204 
          205         if(returnfd)
          206                 return fd;
          207 
          208         close(fd);
          209         return 0;
          210 }
          211 
          212 extern int
          213 samenet(uchar *ip, Info *iip)
          214 {
          215         uchar x[IPaddrlen];
          216 
          217         maskip(iip->ipmask, ip, x);
          218         return ipcmp(x, iip->ipnet) == 0;
          219 }
          220 
          221 /*
          222  *  create a record for each binding
          223  */
          224 extern void
          225 initbinding(uchar *first, int n)
          226 {
          227         while(n-- > 0){
          228                 iptobinding(first, 1);
          229                 incip(first);
          230         }
          231 }
          232 
          233 /*
          234  *  find a binding for a specific ip address
          235  */
          236 extern Binding*
          237 iptobinding(uchar *ip, int mk)
          238 {
          239         Binding *b;
          240 
          241         for(b = bcache; b; b = b->next){
          242                 if(ipcmp(b->ip, ip) == 0){
          243                         syncbinding(b, 0);
          244                         return b;
          245                 }
          246         }
          247 
          248         if(mk == 0)
          249                 return 0;
          250         b = malloc(sizeof(*b));
          251         memset(b, 0, sizeof(*b));
          252         ipmove(b->ip, ip);
          253         b->next = bcache;
          254         bcache = b;
          255         syncbinding(b, 0);
          256         return b;
          257 }
          258 
          259 static void
          260 lognolease(Binding *b)
          261 {
          262         /* renew the old binding, and hope it eventually goes away */
          263         b->offer = 5*60;
          264         commitbinding(b);
          265 
          266         /* complain if we haven't in the last 5 minutes */
          267         if(now - b->lastcomplained < 5*60)
          268                 return;
          269         syslog(0, blog, "dhcp: lease for %I to %s ended at %ld but still in use\n",
          270                 b->ip, b->boundto != nil ? b->boundto : "?", b->lease);
          271         b->lastcomplained = now;
          272 }
          273 
          274 /*
          275  *  find a free binding for a hw addr or id on the same network as iip
          276  */
          277 extern Binding*
          278 idtobinding(char *id, Info *iip, int ping)
          279 {
          280         Binding *b, *oldest;
          281         int oldesttime;
          282 
          283         /*
          284          *  first look for an old binding that matches.  that way
          285          *  clients will tend to keep the same ip addresses.
          286          */
          287         for(b = bcache; b; b = b->next){
          288                 if(b->boundto && strcmp(b->boundto, id) == 0){
          289                         if(!samenet(b->ip, iip))
          290                                 continue;
          291 
          292                         /* check with the other servers */
          293                         syncbinding(b, 0);
          294                         if(strcmp(b->boundto, id) == 0)
          295                                 return b;
          296                 }
          297         }
          298 
          299         /*
          300          *  look for oldest binding that we think is unused
          301          */
          302         for(;;){
          303                 oldest = nil;
          304                 oldesttime = 0;
          305                 for(b = bcache; b; b = b->next){
          306                         if(b->tried != now)
          307                         if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
          308                         if(oldest == nil || b->lasttouched < oldesttime){
          309                                 /* sync and check again */
          310                                 syncbinding(b, 0);
          311                                 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip))
          312                                 if(oldest == nil || b->lasttouched < oldesttime){
          313                                         oldest = b;
          314                                         oldesttime = b->lasttouched;
          315                                 }
          316                         }
          317                 }
          318                 if(oldest == nil)
          319                         break;
          320 
          321                 /* make sure noone is still using it */
          322                 oldest->tried = now;
          323                 if(ping == 0 || icmpecho(oldest->ip) == 0)
          324                         return oldest;
          325 
          326                 lognolease(oldest);        /* sets lastcomplained */
          327         }
          328 
          329         /* try all bindings */
          330         for(b = bcache; b; b = b->next){
          331                 syncbinding(b, 0);
          332                 if(b->tried != now)
          333                 if(b->lease < now && b->expoffer < now && samenet(b->ip, iip)){
          334                         b->tried = now;
          335                         if(ping == 0 || icmpecho(b->ip) == 0)
          336                                 return b;
          337 
          338                         lognolease(b);
          339                 }
          340         }
          341 
          342         /* nothing worked, give up */
          343         return 0;
          344 }
          345 
          346 /*
          347  *  create an offer
          348  */
          349 extern void
          350 mkoffer(Binding *b, char *id, long leasetime)
          351 {
          352         if(leasetime <= 0){
          353                 if(b->lease > now + minlease)
          354                         leasetime = b->lease - now;
          355                 else
          356                         leasetime = minlease;
          357         }
          358         if(b->offeredto)
          359                 free(b->offeredto);
          360         b->offeredto = strdup(id);
          361         b->offer = leasetime;
          362         b->expoffer = now + OfferTimeout;
          363 }
          364 
          365 /*
          366  *  find an offer for this id
          367  */
          368 extern Binding*
          369 idtooffer(char *id, Info *iip)
          370 {
          371         Binding *b;
          372 
          373         /* look for an offer to this id */
          374         for(b = bcache; b; b = b->next){
          375                 if(b->offeredto && strcmp(b->offeredto, id) == 0 && samenet(b->ip, iip)){
          376                         /* make sure some other system hasn't stolen it */
          377                         syncbinding(b, 0);
          378                         if(b->lease < now
          379                         || (b->boundto && strcmp(b->boundto, b->offeredto) == 0))
          380                                 return b;
          381                 }
          382         }
          383         return 0;
          384 }
          385 
          386 /*
          387  *  commit a lease, this could fail
          388  */
          389 extern int
          390 commitbinding(Binding *b)
          391 {
          392         int fd;
          393         long now;
          394 
          395         now = time(0);
          396 
          397         if(b->offeredto == 0)
          398                 return -1;
          399         fd = syncbinding(b, 1);
          400         if(fd < 0)
          401                 return -1;
          402         if(b->lease > now && b->boundto && strcmp(b->boundto, b->offeredto) != 0){
          403                 close(fd);
          404                 return -1;
          405         }
          406         setbinding(b, b->offeredto, now + b->offer);
          407         b->lasttouched = now;
          408 
          409         if(writebinding(fd, b) < 0){
          410                 close(fd);
          411                 return -1;
          412         }
          413         close(fd);
          414         return 0;
          415 }
          416 
          417 /*
          418  *  commit a lease, this could fail
          419  */
          420 extern int
          421 releasebinding(Binding *b, char *id)
          422 {
          423         int fd;
          424         long now;
          425 
          426         now = time(0);
          427 
          428         fd = syncbinding(b, 1);
          429         if(fd < 0)
          430                 return -1;
          431         if(b->lease > now && b->boundto && strcmp(b->boundto, id) != 0){
          432                 close(fd);
          433                 return -1;
          434         }
          435         b->lease = 0;
          436         b->expoffer = 0;
          437 
          438         if(writebinding(fd, b) < 0){
          439                 close(fd);
          440                 return -1;
          441         }
          442         close(fd);
          443         return 0;
          444 }