#pragma prototyped
static const char id[] = "\n@(#)$Id: cs.dbm (AT&T Bell Laboratories) 1995-05-09 $\0\n";
#include <ast.h>
#include <error.h>
#include <tok.h>
#include "FEATURE/ndbm"
#if !_lib_ndbm
int
main(int argc, char** argv)
{
NoP(argc);
NoP(argv);
error(3, "<ndbm.h> library required");
}
#else
#define keyptr dbm_keyptr
#define blkptr dbm_blkptr
#include <cs.h>
#include <error.h>
#include <ndbm.h>
typedef struct
{
DBM* dbm;
dev_t dev;
ino_t ino;
int ref;
} Db_t;
typedef struct
{
long blkptr;
int keyptr;
int readonly;
int scan;
Db_t* db;
} Con_t;
typedef struct
{
int active;
int conmax;
int dormant;
Con_t* con;
Db_t* dbs;
} State_t;
static void*
svc_init(void* handle, int fdmax)
{
State_t* state = (State_t*)handle;
state->conmax = fdmax;
if (!(state->con = newof(0, Con_t, state->conmax, 0)))
error(3, "out of space [con]");
if (!(state->dbs = newof(0, Db_t, state->conmax, 0)))
error(3, "out of space [dbs]");
cstimeout(CS_SVC_DORMANT * 1000L);
return(handle);
}
static int
svc_connect(void* handle, int fd, Cs_id_t* id, int clone, char** args)
{
register State_t* state = (State_t*)handle;
register Con_t* cp;
NoP(id);
NoP(clone);
NoP(args);
cp = state->con + fd;
cp->db = 0;
cp->scan = 0;
state->active++;
state->dormant = 0;
return(0);
}
static int
svc_read(void* handle, int fd)
{
register State_t* state = (State_t*)handle;
register Con_t* cp;
register Db_t* dp;
int n;
char* cmd;
char* s;
datum key;
datum val;
char msg[4 * PATH_MAX];
char ret[4 * PATH_MAX];
char* nxt = msg;
struct stat st;
cp = state->con + fd;
if ((n = csread(fd, nxt, sizeof(msg), CS_LINE)) <= 0) goto drop;
nxt[n - 1] = 0;
while (tokscan(nxt, &nxt, " %s %s %s", &cmd, &key.dptr, &val.dptr) > 0)
{
key.dsize = strlen(key.dptr) + 1;
val.dsize = strlen(val.dptr) + 1;
switch (*cmd)
{
case 'c':
if (!cp->db) goto notopen;
if (!--cp->db->ref) dbm_close(cp->db->dbm);
cp->db = 0;
n = sfsprintf(ret, sizeof(ret), "I closed\n", key.dptr);
break;
case 'g':
if (!cp->db) goto notopen;
val = dbm_fetch(cp->db->dbm, key);
if (val.dptr) n = sfsprintf(ret, sizeof(ret), "I %s\n", val.dptr);
else if (dbm_error(cp->db->dbm))
{
dbm_clearerr(cp->db->dbm);
n = sfsprintf(ret, sizeof(ret), "E db io error\n");
}
else n = sfsprintf(ret, sizeof(ret), "W %s not in db\n", key.dptr);
break;
case 'n':
if (!cp->db) goto notopen;
n = *((char*)key.dptr);
if (!cp->scan)
{
cp->scan = 1;
key = dbm_firstkey(cp->db->dbm);
}
else
{
cp->db->dbm->dbm_blkptr = cp->blkptr;
cp->db->dbm->dbm_keyptr = cp->keyptr;
key = dbm_nextkey(cp->db->dbm);
}
cp->blkptr = cp->db->dbm->dbm_blkptr;
cp->keyptr = cp->db->dbm->dbm_keyptr;
if (key.dptr)
{
if (n)
{
val = dbm_fetch(cp->db->dbm, key);
if (val.dptr) n = sfsprintf(ret, sizeof(ret), "I %s %s\n", key.dptr, val.dptr);
else
{
if (dbm_error(cp->db->dbm))
{
dbm_clearerr(cp->db->dbm);
n = sfsprintf(ret, sizeof(ret), "E db io error\n");
}
else n = sfsprintf(ret, sizeof(ret), "E %s not in db\n", key.dptr);
cp->scan = 0;
}
}
else n = sfsprintf(ret, sizeof(ret), "I %s\n", key.dptr);
}
else
{
if (dbm_error(cp->db->dbm))
{
dbm_clearerr(cp->db->dbm);
n = sfsprintf(ret, sizeof(ret), "E db io error\n");
}
else n = sfsprintf(ret, sizeof(ret), "W end of scan\n");
cp->scan = 0;
}
break;
case 'o':
if (cp->db)
{
if (!--cp->db->ref) dbm_close(cp->db->dbm);
cp->db = 0;
}
cp->scan = 0;
for (s = val.dptr; *s; s++)
if (*s == 'w') break;
cp->readonly = *((char*)val.dptr) && !*s;
sfsprintf(ret, sizeof(ret), "%s.dir", key.dptr);
if (!stat(ret, &st))
for (dp = state->dbs; dp < state->dbs + state->conmax; dp++)
if (dp->ref > 0 && dp->ino == st.st_ino && dp->dev == st.st_dev)
{
dp->ref++;
cp->db = dp;
}
if (!cp->db)
for (dp = state->dbs; dp < state->dbs + state->conmax; dp++)
if (dp->ref <= 0)
{
if (dp->dbm = dbm_open(key.dptr, O_RDWR|O_CREAT, 0666))
{
cp->db = dp;
dp->ref = 1;
if (stat(ret, &st))
{
st.st_dev = 0;
st.st_ino = 0;
}
dp->dev = st.st_dev;
dp->ino = st.st_ino;
}
break;
}
if (cp->db) n = sfsprintf(ret, sizeof(ret), "I %s opened\n", key.dptr);
else n = sfsprintf(ret, sizeof(ret), "E %s cannot open\n", key.dptr);
break;
case 'p':
if (!cp->db) goto notopen;
if (cp->readonly)
{
n = sfsprintf(ret, sizeof(ret), "E db is readonly\n");
break;
}
if (*((char*)val.dptr))
{
if (!dbm_store(cp->db->dbm, key, val, DBM_REPLACE)) n = sfsprintf(ret, sizeof(ret), "I entered\n");
else if (dbm_error(cp->db->dbm))
{
dbm_clearerr(cp->db->dbm);
n = sfsprintf(ret, sizeof(ret), "E db io error\n");
}
else n = sfsprintf(ret, sizeof(ret), "E %s not changed\n", key.dptr);
}
else if (!dbm_delete(cp->db->dbm, key)) n = sfsprintf(ret, sizeof(ret), "I deleted\n");
else if (dbm_error(cp->db->dbm))
{
dbm_clearerr(cp->db->dbm);
n = sfsprintf(ret, sizeof(ret), "E db io error\n");
}
else n = sfsprintf(ret, sizeof(ret), "W %s not in db\n", key.dptr);
break;
case 'q':
case 'Q':
n = sfsprintf(ret, sizeof(ret), "I quit\n");
break;
case 's':
if (!cp->db) goto notopen;
cp->scan = 0;
n = sfsprintf(ret, sizeof(ret), "I ready to scan\n");
break;
case 'v':
n = sfsprintf(ret, sizeof(ret), "I %s %s %u\n", id + 10, csname(0L), getpid());
break;
default:
n = sfsprintf(ret, sizeof(ret), "E invalid command %s\n", cmd);
break;
notopen:
n = sfsprintf(ret, sizeof(ret), "E db not open\n");
break;
}
if (cswrite(fd, ret, n) != n || *cmd == 'q') goto drop;
if (*cmd == 'Q')
{
if (state->active == 1) exit(0);
goto drop;
}
}
return(0);
drop:
if (cp->db && !--cp->db->ref) dbm_close(cp->db->dbm);
state->active--;
return(-1);
}
static int
svc_timeout(void* handle)
{
State_t* state = (State_t*)handle;
if (!state->active)
{
if (state->dormant)
exit(0);
state->dormant = 1;
}
return(0);
}
static int
svc_done(void* handle, int sig)
{
register State_t* state = (State_t*)handle;
register Db_t* dp;
NoP(sig);
for (dp = state->dbs; dp < state->dbs + state->conmax; dp++)
if (dp->ref > 0)
{
dp->ref = 0;
dbm_close(dp->dbm);
}
return(0);
}
int
main(int argc, char** argv)
{
static State_t state;
NoP(argc);
csserve(&state, argv[1], svc_init, svc_done, svc_connect, svc_read, NiL, svc_timeout);
exit(1);
}
#endif