#ifndef LINT
static char *rcsid="$Id: serv_common.c,v 1.17 2001/09/20 05:56:39 crosser Exp $";
#endif

/*
	$Log: serv_common.c,v $
	Revision 1.17  2001/09/20 05:56:39  crosser
	chroot() for whosond
	
	Revision 1.16  1999/10/06 15:54:03  crosser
	fix for kluge about initializing private fields
	Initialization for global parameters
	
	Revision 1.15  1999/10/03 21:18:03  crosser
	First (probably) working version with new run time config parser.
	Also added listenq parameter (backlog size for listen()) and
	renamed wtest to whoson (and with man page).  Release 2.00beta1.
	
	Revision 1.14  1999/10/02 21:29:30  crosser
	work in progress on rtconfig
	
	Revision 1.13  1999/07/27 17:17:18  crosser
	remove include version.h

	Revision 1.12  1998/07/26 20:17:39  crosser
	LIST command

	Revision 1.11  1998/07/26 14:06:40  crosser
	stupid change made by mistake

	Revision 1.10  1998/07/12 16:43:57  crosser
	Change protocol: responce now is terminated with empty line

	Revision 1.9  1998/07/05 00:26:18  crosser
	Change copyright

	Revision 1.8  1998/07/05 00:01:27  crosser
	add user and group global parms

	Revision 1.7  1998/07/02 18:17:12  crosser
	make expiry work again

	Revision 1.6  1998/07/02 18:01:15  crosser
	change API

	Revision 1.5  1998/07/02 15:37:07  crosser
	make right responce if req invalid

	Revision 1.4  1998/07/01 20:01:05  crosser
	fix chain corruption when replacing

	Revision 1.3  1998/07/01 08:04:58  crosser
	Use lhash,
	make global pseudo-server

	Revision 1.2  1998/07/01 05:18:09  crosser
	minor warnings fix

	Revision 1.1  1998/07/01 05:01:22  crosser
	Initial revision

	Revision 1.3  1998/05/05 19:08:16  crosser
	allow multiline entry (and ignore)

	Revision 1.2  1998/04/29 10:02:21  crosser
	fix TODO list

	Revision 1.1  1998/04/28 18:37:21  crosser
	Initial revision

*/

/*
	WHAT IS IT:
		Implementation of experimental "whoson" protocol
	AUTHOR:
		Eugene G. Crosser <crosser@average.org>
	COPYRIGHT:
		Public domain
*/

#include "config.h"

#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>

#include "lhash.h"

#include "whosond.h"
#include "serv_common.h"
#include "rtconfig.h"
#include "report.h"

#include "rtc_begin.h"
#include "serv_common_cfg.h"
#include "rtc_middle.h"
#include "serv_common_cfg.h"
#include "rtc_end.h"

#define MAXREQL 1024
#define MAXADDR 32
#define MAXNAME 128
#define CACHESIZE 999999999L
#define TTL 300 /* ttl is 5 min by default; may set in global section */

char *newroot=NULL;
uid_t runuid=0;
gid_t rungid=0;
unsigned long cachesize;
unsigned long incache=0L,maxcache=0L;
time_t starttime;
struct timeval oldtime;
double intvl1=0L,intvl2=0L,intvl3=0L;
unsigned long requests=0L;

static int ttl; 

typedef struct _crec {
	struct _crec *next;
	struct _crec *prev;
	time_t ttl;
	char addr[MAXADDR];
	char name[MAXNAME];
} crec_t;

static crec_t *first=NULL,*last=NULL;
static time_t now;
static LHASH *chash=(LHASH*)0;

static unsigned long ch_hash(crec_t *entry1)
{
	return lh_strhash(entry1->addr);
}

static int ch_cmp(crec_t *entry1,crec_t *entry2)
{
	return strcmp(entry1->addr,entry2->addr);
}

static char *do_login(char *arg1,char *arg2)
{
	crec_t *newrec,*tmp;

	if (!*arg1) return "*LOGIN addr-spec required";

	if (incache >= cachesize)
		return "-LOGIN Too many entries";
	if ((newrec=(crec_t*)malloc(sizeof(crec_t))) == NULL)
		return "-LOGIN no memory for the new entry";
	strncpy(newrec->addr,arg1,MAXADDR);
	strncpy(newrec->name,arg2,MAXNAME);
	if ((tmp=(crec_t*)lh_insert(chash,(char*)newrec))) {
		incache--;
		DPRINT(("replacing \"%s\" -> \"%s\" with \"%s\" -> \"%s\"\n",
			tmp->addr,tmp->name,newrec->addr,newrec->name))
		if (tmp->next) tmp->next->prev=tmp->prev;
		else last=tmp->prev;
		if (tmp->prev) tmp->prev->next=tmp->next;
		else first=tmp->next;
		free(tmp);
	}
	incache++;
	if (incache > maxcache) maxcache=incache;
	newrec->next=NULL;
	newrec->prev=last;
	newrec->ttl=now+ttl;
	if (last) {
		last->next=newrec;
		last=newrec;
	} else {
		first=last=newrec;
	}
	return "+LOGIN OK";
}

static char *do_logout(char *arg1,char *arg2) {
	crec_t *tmp,dummy_entry;

	if (!*arg1) return "*LOGOUT addr-spec required";

	strncpy(dummy_entry.addr,arg1,sizeof(dummy_entry.addr)-1);
	dummy_entry.addr[sizeof(dummy_entry.addr)-1]='\0';
	tmp=(crec_t*)lh_delete(chash,(char*)&dummy_entry);
	if (tmp) {
		incache--;
		if (tmp->next) tmp->next->prev=tmp->prev;
		else last=tmp->prev;
		if (tmp->prev) tmp->prev->next=tmp->next;
		else first=tmp->next;
		free(tmp);
		return "+LOGOUT record deleted";
	}

	return "+LOGOUT no such record, nothing done";
}

static char *do_query(char *arg1,char *arg2) {
	crec_t *tmp,dummy_entry;
	static char buf[MAXNAME+4];

	if (!(*arg1)) return "*QUERY addr-spec required";
	if (*arg2) return "*QUERY only one argument allowed";

	strncpy(dummy_entry.addr,arg1,sizeof(dummy_entry.addr)-1);
	dummy_entry.addr[sizeof(dummy_entry.addr)-1]='\0';
	tmp=(crec_t*)lh_retrieve(chash,(char*)&dummy_entry);
	if (tmp) {
		buf[0]='+';
		strncpy(buf+1,tmp->name,MAXNAME);
		buf[MAXNAME+1]='\0';
		return buf;
	}

	return "-Not logged in";
}

static char *do_cmd(char *verb,char *arg1,char *arg2) {
	static char buf[512];

	sprintf(buf," version %s build %s\r\n",VERSION,__DATE__);
	sprintf(buf+strlen(buf)," running from %s total %lu requests, cache now %lu, max %lu\r\n",
		asctime(localtime(&starttime)),
		requests,
		incache,
		maxcache);
	sprintf(buf+strlen(buf)," load %10.3f %10.3f %10.3f %10.3f",
		1.0e+6/intvl1,
		1.0e+6/intvl2,
		1.0e+6/intvl3,
		(double)requests/(now-starttime));
	return buf;
}

static void do_cleanup(void) {
	crec_t *prev;

	while (first) {
		if (first->ttl > now) break;

		DPRINT(("expiring \"%s\" -> \"%s\"\n",
				first->addr,first->name))
		prev=first;
		first=first->next;
		if (first) first->prev=NULL;
		(void)lh_delete(chash,(char*)prev);
		free(prev);
		incache--;
	}
	if (!first) last=NULL;
}

void do_request(char *req,char *retbuf,int retsize) {
	char buf[MAXREQL];
	char *p,*endb,*verb,*arg1,*arg2,*rc;
	struct timeval cmdtime;
	unsigned long intvl;

	gettimeofday(&cmdtime,NULL);
	(void)time(&now);
	intvl=(cmdtime.tv_sec-oldtime.tv_sec)*1000000+
		(cmdtime.tv_sec-oldtime.tv_sec);
	intvl1=intvl*100.0e-3+intvl1*900.0e-3;
	intvl2=intvl*010.0e-3+intvl2*990.0e-3;
	intvl3=intvl*001.0e-3+intvl3*999.0e-3;
	oldtime=cmdtime;
	requests++;

	strncpy(buf,req,sizeof(buf)-1);
	buf[sizeof(buf)-1]='\0';
	if ((endb=strchr(buf,'\n'))) *endb='\0';
	if ((endb=strchr(buf,'\r'))) *endb='\0';
	if (endb) while (isspace(*(endb-1))) *(--endb)='\0';
	else endb=buf+sizeof(buf)-1;
	DPRINT(("Got: \"%s\"\n",buf))

	p=buf;
	while (p<endb && isspace(*p)) p++;
	verb=p;
	while (p<endb && !isspace(*p)) p++;
	*p='\0';p++;
	while (p<endb && isspace(*p)) p++;
	arg1=p;
	while (p<endb && !isspace(*p)) p++;
	*p='\0';p++;
	while (p<endb && isspace(*p)) p++;
	arg2=p;
	DPRINT(("verb=\"%s\" arg1=\"%s\" arg2=\"%s\"\n",verb,arg1,arg2))

	do_cleanup();

	if (strcasecmp(verb,"LOGIN") == 0) rc=do_login(arg1,arg2);
	else if (strcasecmp(verb,"LOGOUT") == 0) rc=do_logout(arg1,arg2);
	else if (strcasecmp(verb,"QUERY") == 0) rc=do_query(arg1,arg2);
	else if (strcasecmp(verb,"LIST") == 0) rc=do_cmd(verb,arg1,arg2);
	else rc="*Protocol error: no valid verb found";
	DPRINT(("returning: \"%s\"\n",rc))

	strncpy(retbuf,rc,retsize-5);
	retbuf[retsize-5]='\0';
	strcat(retbuf,"\r\n\r\n");
}

struct _evdesc global_serv_init(void *priv)
{
	struct _evdesc evdesc;
	wso_serv_global_cfg *rec=(wso_serv_global_cfg*)priv;
	struct passwd *pw;
	struct group *gr;

	ttl=rec->ttl;
	if (ttl == 0) ttl=TTL;
	cachesize=rec->cachesize;
	if (cachesize == 0) cachesize=CACHESIZE;
	if (rec->chroot) newroot=rec->chroot;
	if ((rec->user) && ((pw=getpwnam(rec->user)))) {
		runuid=pw->pw_uid;
		if (!rungid) rungid=pw->pw_gid;
	} else {
		ERRLOG((LOG_ERR,"bad user name \"%s\"",
			rec->user?rec->user:"(null)"))
	}
	if ((rec->group) && ((gr=getgrnam(rec->group)))) {
		rungid=gr->gr_gid;
	} else {
		ERRLOG((LOG_ERR,"bad group name \"%s\"",
			rec->group?rec->group:"(null)"))
	}
	chash=lh_new(ch_hash,ch_cmp);
	(void)time(&starttime);
	gettimeofday(&oldtime,NULL);
	memset(&evdesc,0,sizeof(struct _evdesc));
	evdesc.fd=-1;
	return evdesc;
}


syntax highlighted by Code2HTML, v. 0.9.1