/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
/*
$Id: lses.c,v 1.18 2003/05/26 10:55:10 kripke Exp $
implementation of general db access functions.
*/
#include <stdlib.h>
#include <string.h> /* memset */
#ifdef _REENTRANT
#ifdef WIN32
# define WIN32_LEAN_AND_MEAN
# define NONAMELESSUNION
# include <windows.h>
/* they call it "thread local storage" */
# define pthread_key_t int
# define pthread_key_create( pkey, func ) *(pkey) = TlsAlloc()
# define pthread_setspecific( key, val ) TlsSetValue( key, val )
# define pthread_getspecific( key ) TlsGetValue( key )
#else
# include <pthread.h>
#endif
#endif
#include "lses.h"
/* ************************************************************
private types
*/
typedef struct {
Ses h;
/* int brk; */
} SesI;
enum {
LSES_MAX = 0x1000,
LSES_MSK = 0x0fff
};
/* ************************************************************
package data
*/
/* ************************************************************
private data
*/
static Ios stdio[3] = {
LIO_STDINITIALIZER( "&0", LIO_IN ),
LIO_STDINITIALIZER( "&1", LIO_OUT|1 ),
LIO_STDINITIALIZER( "&2", LIO_OUT|2 )
};
static SesI cses = { /* the default session */
{
0,
"",
{ stdio, stdio+1, stdio+2 },
-1,
0, 0, /* nxt, accnt */
{0},{0},{0}, /* times */
0,0,0,0 /* state */
}
};
static SesI *tab[ LSES_MAX ] = {
&cses
};
static Ses *htab[ LSES_MAX ];
static int maxid;
static const char oops[] = "out of memory\n";
#ifndef _REENTRANT
Session ses = &cses.h;
#else
int openisis_threaded = 42;
static pthread_key_t seskey;
#endif
/* ************************************************************
private functions
*/
static void *memordie ( int size )
{
void *p = malloc( size );
if ( p )
return p;
/* TODO: ask btree to release some cache */
lio_write( &lio_err, oops, sizeof(oops)-1 );
exit( 71/*EX_OSERR*/ ); /* exit handlers MUST NOT alloc memory */
return 0;
}
/* ************************************************************
package functions
*/
void lses_init () /* called from openIsisInit */
{
#ifdef _REENTRANT
pthread_key_create( &seskey, 0 ); /* TODO: use ses destr func */
pthread_setspecific( seskey, &cses.h );
#endif
} /* lses_init */
void lses_fini () /* called from openIsisFini */
{
int i, s = LSES_MAX;
/* lio_write( &lio_out, "fini time\n", 9 ); */
while ( s-- )
if ( tab[s] )
for ( i=0; i<3; i++ )
if ( tab[s]->h.io[i] )
LIO_CLOSE( tab[s]->h.io[i] );
} /* lses_fini */
int lses_open ( Session s, const char *name, int flags, SFunc *type )
{
int fd = 0;
const char *p = name;
lio_sfunc *func = type ? (lio_sfunc*)type : lio_stdio;
/* check for i<> */
while ( '0' <= *p && *p <= '9' )
fd = 10*fd + *p++ - '0';
if ( '<' != *p && '>' != *p )
p = name; /* forget it */
else {
if ( name == p && '>' == *p ) /* no fd given, > defaults to 1 */
fd = 1;
flags |= '<' == *p ? LIO_RD : LIO_WR;
p++;
}
if ( p == name ) { /* no fd given, find one */
for ( fd=0; s->io[fd]; )
if ( LSES_FILE_MAX == ++fd )
return -ERR_NOMEM;
} else if ( s->io[fd] ) { /* close existing */
func( s->io[fd], LIO_SCLOSE );
/* don't kill the statics :) */
if ( s->io[fd] < stdio || s->io[fd] > stdio+2 )
mFree( s->io[fd] );
}
s->io[fd] = (Ios*)mAlloc( func( 0, LIO_SSIZE ) );
if ( ! s->io[fd] )
return -ERR_NOMEM;
s->io[fd]->func = func;
s->io[fd]->name = mDup( p, -1 );
s->io[fd]->file = flags;
return func( s->io[fd], LIO_SOPEN );
} /* lses_open */
/* ************************************************************
public functions
*/
extern Ses *sGet ()
{
#ifdef _REENTRANT
Ses *ses;
if ( ! (ses = (Ses*)pthread_getspecific( seskey )) )
pthread_setspecific( seskey, ses = cSession( 0 ) );
#endif
return ses;
}
extern void sSet ( Ses *s )
{
#ifndef _REENTRANT
ses = s;
#else
pthread_setspecific( seskey, s );
#endif
}
Ses *cOpen ( Rec *args )
{
extern void linit();
linit();
if ( args ) { /* add args */
/* TODO: merge args */
cses.h.prop = rDup( args, -1, 0 );
}
return &cses.h;
} /* cOpen */
Ses *cSession ( Rec *args )
{
SesI *sesi = 0;
Ses *s = 0;
int id = 1;
/** XXX unless we actually free sessions,
we should start checking with maxid
or check for expiry
*/
while ( tab[id] )
if ( LSES_MAX == ++id )
return 0;
if ( maxid < id )
maxid = id;
sesi = tab[id] = (SesI*)malloc( sizeof(SesI) );
memset( sesi, 0, sizeof(SesI) );
s = &sesi->h;
s->id = id;
s->hash = -1; /* not hashed (hash is non-negative) */
s->prop = args ? rDup( args, -1, 0 ) : 0;
/* check props */
/* open files */
lses_open( s, "2>&2", 0, lio_stdio );
return s;
} /* cSession */
void *mAlloc ( int size )
{
void *p = memordie( size );
memset(p, 0, size);
return p;
} /* mAlloc */
void mFree ( void *mem )
{
if ( mem )
free( mem );
} /* mFree */
void *mDup ( const void *str, int sz )
{
void *m = memordie( 0<=sz ? sz : (sz = strlen( str ) + 1) );
memcpy( m, str, sz );
return m;
} /* mDup */
int sOpen ( const char *name, int flags, SFunc *type )
{
SESDECL
return lses_open( ses, name, flags, type );
} /* sOpen */
Ses *openIsisId2Session (int sid) {
if (0 > sid || LSES_MAX <= sid || 0 == tab[sid]) {
return (Ses*)0;
}
return &(tab[sid]->h);
} /* openIsisId2Session */
int openIsisSession2Id (Ses *s) {
if (0 == s) {
return -1;
}
return s->id;
} /* openIsisSession2Id */
Ses *cSesByName ( char *name, int nlen, Tm *now, Tm *expire )
{
char tbuf[20];
int h,i;
Ses *s;
if ( nlen < 0 )
nlen = strlen(name);
if ( nlen > 63 )
nlen = 63;
for ( i=nlen; i--; )
if ( !(0xe0 & name[i]) )
name[i] = '_';
h = lhash( name, nlen );
for ( s = htab[h & LSES_MSK]; s; s = s->nxt )
if ( h == s->hash
&& !memcmp( name, s->name, nlen )
&& !s->name[nlen]
) { /* match */
if ( !expire || s->atime.millis >= expire->millis )
goto gotit; /* valid */
if ( s->cur || s->que ) {
log_msg( LOG_WARN, "expired session %d %s still busy!",
s->id, s->name );
goto gotit; /* valid anyway */
}
goto refresh;
}
/* didn't find by name */
if ( ! expire ) /* bad thing -- we're going to run out of sessions ... */
i = maxid+1;
else /* check other expired */
for ( i=1; i<=maxid; i++ ) {
s = &tab[i]->h; /* sind wir heute defensiv ... */
if ( s && s->atime.millis < expire->millis
&& s->atime.millis && 0 <= s->hash /* else is none of our biz */
&& !s->cur && !s->que /* else is busy !? */
)
break;
}
if ( i > maxid ) {
s = cSession( 0 );
if ( ! s )
return 0;
} else /* got one */
log_msg( LOG_INFO, "reusing session %d '%s' atime %s",
s->id, s->name, s->atime.millis ? timeGtf(tbuf,&s->atime) : "-" );
if ( 0 <= s->hash ) { /* was hashed -- unlink */
Ses **spp = &htab[s->hash & LSES_MSK];
for ( ; *spp; spp = &(*spp)->nxt )
if ( s == *spp ) {
*spp = s->nxt;
break;
}
}
memset( s->name, 0, sizeof(s->name) );
memcpy( s->name, name, nlen );
s->hash = h;
s->nxt = htab[h & LSES_MSK];
htab[h & LSES_MSK] = s;
refresh:
s->ctime.millis = s->mtime.millis = now ? now->millis : 0;
s->accnt = 0;
if ( s->prop )
CLRREC( s->prop );
if ( s->res )
CLRREC( s->res );
gotit:
s->atime.millis = now ? now->millis : 0;
return s;
} /* cSesByName */