/************************************************************************
 *	Routines to interface with db					*
 *									*
 *	Copyright (c) 1998, S.R. van den Berg, The Netherlands		*
 *	#include "README"						*
 ************************************************************************/
#ifdef RCS
static /*const*/char rcsid[]=
 "$Id: dbops.c,v 1.13 1998/05/13 16:57:39 srb Exp $";
#endif

#include "config.h"

#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>

#include "sdb.h"
#include "dbops.h"

#define MX(a,b)		((a)>(b)?(a):(b))
#define STRLEN(x)	(sizeof(x)-1)
#define MS(a,b)		MX(STRLEN(a),STRLEN(b))

const char statedbfile[]=STATE_DB,cplib[]=CUCIPOP_LIB;
static const char dblockshare[]="__db_lock.share",
 dbmpoolshare[]="__db_mpool.share";
static DB_ENV dbenv;
static DB*db;
static int dbisopen,dbinited,dbrecover;
static DBC*cs;

static dounlink(const char*file)
{ char buf[STRLEN(cplib)+1+
   MX(STRLEN(statedbfile),MS(dblockshare,dbmpoolshare))+1];
  strcpy(buf,cplib);buf[STRLEN(cplib)]='/';strcpy(buf+STRLEN(cplib)+1,file);
  unlink(buf);
}

static void tryrecovery();

void initappdb()
{ if(!dbinited)
   { memset(&dbenv,0,sizeof dbenv);dbenv.mp_size=MEMORY_CACHE;
     dbinited=!db_appinit(cplib,0,&dbenv,
      /*DB_INIT_LOG|*/DB_INIT_LOCK|DB_CREATE|DB_INIT_MPOOL/*|DB_USE_ENVIRON*/);
#ifdef USE_DB
     if(!dbrecover&&!dbinited)
	tryrecovery();
#endif
   }
}

void exitappdb()
{ if(dbinited)
     db_appexit(&dbenv),dbinited=0;
}

static void tryrecovery()
{ dbrecover++;exitappdb();
  dounlink(dblockshare);dounlink(dbmpoolshare);
  initappdb();
}

void opendb()
{ int retry=2;
  if(!dbinited)
     return;
  for(;;)
   { int reterrno;
     if(dbrecover>1)
	dounlink(statedbfile);
     if(reterrno=db_open(statedbfile,DB_HASH,
      DB_CREATE,0622,&dbenv,(void*)0,&db))
      { syslog(LOG_ALERT,"Can't open %s %d",statedbfile,reterrno);
	if(reterrno!=EPERM&&reterrno!=EINVAL||!retry--)
	   break;
	tryrecovery();syslog(LOG_ALERT,"Attempting recovery %s",statedbfile);
      }
     else
      { dbisopen=1;
	break;
      }
   }
}

void closedb()
{ if(dbisopen)
   { if(cs)
	cs->c_close(cs),cs=0;
     db->close(db,0),dbisopen=0;
   }
}

static int checkrecover(int reterrno)
{ if(reterrno==EPERM)
   { closedb();
     syslog(LOG_ALERT,"Database %s corrupted, attempt recovery",statedbfile);
     tryrecovery();opendb();
   }
  return reterrno;
}

static DBT d,k;

int maxage(time_t dif)
{ return dif<-MAXSTATEAGE||dif>MAXSTATEAGE;
}

struct stateinfo*getstate(const char*key,time_t curt)
{ if(!dbisopen)
     return 0;
  ;{ static time_t lastcheck;
     if(lastcheck!=curt)
      { lastcheck=curt;
	if(!cs)
	 { db->cursor(db,0,&cs,0);d.data="";d.size=1;
	   if(!db->get(db,0,&d,&k,0))
	      cs->c_get(cs,&k,&d,DB_SET);
	 }
	if(!(cs->c_get(cs,&k,&d,DB_NEXT)&&cs->c_get(cs,&k,&d,DB_FIRST))&&
	 !(k.size==1&&!*(char*)k.data)&&	   /* exception for \0 entry */
	 (d.size!=sizeof(struct stateinfo)||
	  maxage(curt-((struct stateinfo*)d.data)->lastsuccess)&&
	  maxage(curt-((struct stateinfo*)d.data)->lastabort)))
	   cs->c_del(cs,0),cs->c_get(cs,&k,&d,DB_PREV);
	d.data="";d.size=1;db->put(db,0,&d,&k,0);
      }
   }
  k.size=strlen(key);k.data=(char*)key;
  if(checkrecover(db->get(db,0,&k,&d,0)))
     return 0;
  if(d.size!=sizeof(struct stateinfo))
   { db->del(db,0,&k,0);
     return 0;
   }
  return d.data;
}

void putstate(const char*key,const struct stateinfo*state)
{ if(!dbisopen)
     return;
  k.size=strlen(key);k.data=(char*)key;d.data=(char*)state;d.size=sizeof*state;
  db->put(db,0,&k,&d,0);
}


syntax highlighted by Code2HTML, v. 0.9.1