Revision 238 (by dpavlin, 2004/03/08 17:46:16) tagging openisis 0.9.0
/*
	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 */