#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