+#include <stdlib.h>
+#include <syslog.h>
+#include <signal.h>
+#include <assert.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <netinet/in.h>
+#include <linux/un.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+#include <slang.h>
+#include <time.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <sys/file.h>
+#include <sys/resource.h>
+#include <fnmatch.h>
+#include <setjmp.h>
+#include <sys/socket.h>
+#include <stddef.h>
+#include <pthread.h>
+
+#include "common.h"
+#include "dataserv.h"
+
+const int safesigs[]={SIGQUIT,SIGINT,SIGTERM,SIGIOT,SIGALRM,-1}; //SIGCHLD->childsignal anyway
+char vbnexact;
+const char *upsc_sshpath="/usr/bin/ssh"; /* upsetupconn()... */
+const char *upsc_dstport=DS_PORT;
+
+void chk(const void *p)
+{
+ vbsanity();
+ if (p) return;
+ FATAL(CRIT,"NULL-check failure, memory exhausted? FATAL: %m");
+}
+
+#ifndef NDEBUG
+static void *vbchecknull=NULL;
+static struct varbuf *vbcheckhead=(struct varbuf *)&vbchecknull;
+static unsigned vbchecknum;
+#ifdef FATALSIG_INSANE
+static char vbcheckno;
+#else
+#define vbcheckno (0)
+#endif
+pthread_mutex_t vbcheckmx=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+
+static void vbchecklock (void)
+{ if (pthread_mutex_lock (&vbcheckmx)) FATAL(CRIT,"Mutex ""lock on vbcheck mx"); }
+static void vbcheckunlock(void)
+{ if (pthread_mutex_unlock(&vbcheckmx)) FATAL(CRIT,"Mutex unlock on vbcheck mx"); }
+
+static char vbchecklist(struct varbuf *vb)
+{
+struct varbuf *vbs;
+unsigned tot=0;
+
+ vbchecklock();
+ assert(!vbchecknull);
+ assert(!!vb);
+ for (vbs=vbcheckhead;vbs;vbs=vbs->checknext,tot++)
+ if (vb==vbs) break;
+ if (!vbs) {
+ vbcheckunlock();
+ return(0);
+ }
+ assert(vb->checknext);
+ while (tot++,(vbs=vbs->checknext)!=vbchecknull) assert(vb!=vbs);
+ assert(tot==vbchecknum+1);
+ vbcheckunlock();
+ return(1);
+}
+
+void vbcheck(struct varbuf *vb)
+{
+struct varbufnode *vbn;
+char *s;
+volatile char touch;
+
+ if (vbcheckno) return; vbchecklock();
+ assert(vbchecklist(vb));
+ if (vb->done) assert(!!vb->f);
+ if (vb->free) assert(!!vb->l);
+ assert((!vb->f)==(!vb->f));
+ for (vbn=vb->f;vbn;vbn=vbn->next) {
+ if (vbn==vb->f) assert(vb->done<=vbn->size);
+ if (vbn==vb->l) assert(vb->free<=vbn->size);
+ for (s=vbn->buf;s<vbn->buf+vbn->size;s++) touch=*s;
+ }
+ vbcheckunlock();
+}
+
+void vbsanity(void)
+{
+struct varbuf *vb;
+ dbg("vbsanity()\n");
+ if (vbcheckno) return; vbchecklock();
+ assert(!vbchecknull);
+ for (vb=vbcheckhead;vb!=(struct varbuf *)&vbchecknull;vb=vb->checknext) vbcheck(vb);
+ vbcheckunlock();
+}
+#endif
+
+#ifdef VBDEBUG
+void vbdebug(struct varbuf *vb,const char *msg)
+{
+struct varbufnode *vbn;
+ fprintf(stderr,"vbdebug(%s): vb=%p",msg,vb);
+ vbcheck(vb);
+ if (!vb) {
+ fprintf(stderr,"\n");
+ return;
+ }
+ fprintf(stderr,", first=%p, last=%p, done=%u, free=%u\n",vb->f,vb->l,vb->done,vb->free);
+ for (vbn=vb->f;vbn;vbn=vbn->next) {
+size_t i;
+ fprintf(stderr," *VBN: %p: size=%u, buf:",vbn,vbn->size);
+ for (i=0;i<vbn->size;i++) {
+char c=vbn->buf[i];
+ if (isprint(c)) fprintf(stderr," '%c'",c);
+ else fprintf(stderr," %02x",(unsigned char)c);
+ }
+ fprintf(stderr,"\n");
+ }
+ fprintf(stderr,"vbdebug(%s): finishing...\n",msg);
+}
+#endif
+
+struct varbufnode *vbnnew(size_t sugsize)
+{
+struct varbufnode *r;
+ if (!vbnexact && sugsize<VB_DEFSIZE) sugsize=VB_DEFSIZE;
+ chk(r=malloc(sizeof(*r)+sugsize));
+ r->next=NULL;
+ r->size=sugsize;
+ return(r);
+}
+
+void vbgrow(struct varbuf *vb,size_t sugsize)
+{
+struct varbufnode *vbn;
+ vbdebug(vb,"vbgrow");
+ assert(!vb->free);
+ if (!vbnexact) {
+ if (vb->l && vb->l->size>sugsize) sugsize=vb->l->size;
+ if (sugsize*2<=VB_MAXSIZE) sugsize*=2;
+ else if (sugsize<VB_MAXSIZE) sugsize=VB_MAXSIZE;
+ }
+ vbn=vbnnew(sugsize);
+ if (vb->l) vb->l->next=vbn;
+ else vb->f=vbn;
+ vb->l=vbn;
+ vb->free=vbn->size;
+}
+
+void vbinit(struct varbuf *vb)
+{
+#ifndef NDEBUG
+ if (!vbcheckno) assert(!vbchecklist(vb));
+#endif
+ bzero(vb,sizeof(*vb));
+#ifndef NDEBUG
+ vbchecklock();
+ vb->checknext=vbcheckhead;
+ vbcheckhead=vb;
+ vbchecknum++;
+ vbcheckunlock();
+#endif
+ vbcheck(vb);
+}
+
+struct varbufnode *vbremone(struct varbuf *vb)
+{
+struct varbufnode *r;
+ vbdebug(vb,"vbremone");
+ assert(vb->f);
+ r=vb->f->next;
+ free(vb->f);
+ vb->done=0;
+ if (!(vb->f=r)) {
+ vb->l=NULL;
+ vb->free=0;
+ }
+ return(r);
+}
+
+void vbclear(struct varbuf *vb)
+{
+ vbdebug(vb,"vbclear");
+ while (vb->f) vbremone(vb);
+}
+
+void vbrem(struct varbuf *vb)
+{
+#ifndef NDEBUG
+struct varbuf **vbp;
+#endif
+
+ vbdebug(vb,"vbrem");
+ vbclear(vb);
+#ifndef NDEBUG
+ vbchecklock();
+ assert(vbchecknum>0);
+ for (vbp=&vbcheckhead;*vbp;vbp=&(*vbp)->checknext)
+ if (*vbp==vb) break;
+ *vbp=vb->checknext;
+ vbchecknum--;
+ bzero(vb,sizeof(*vb));
+ vbcheckunlock();
+#endif
+}
+
+size_t vbsize(struct varbuf *vb,size_t maxsize)
+{
+size_t size=0;
+struct varbufnode *vbn=vb->f;
+
+ vbdebug(vb,"vbsize");
+ while (vbn && (!maxsize || size<maxsize)) {
+ size+=VBNSIZE(vb,vbn);
+ vbn=vbn->next;
+ }
+ return(size);
+}
+
+#define GENVARBUFC(argmid) \
+ void vbput##argmid(struct varbuf *vb,const argmid arg) \
+ { vbwrite(vb,&arg,sizeof(arg)); } \
+ char vbget##argmid(struct varbuf *vb,argmid *argp) \
+ { if (sizeof(*argp)>1 && !VBCHKSIZE(vb,sizeof(*argp))) return(0); \
+ return(vbread(vb,argp,sizeof(*argp))); }
+
+#ifndef INLINE_PUTGETCHAR
+GENVARBUFC(char)
+#endif
+GENVARBUFC(short)
+GENVARBUFC(int)
+
+#undef GENVARBUFC
+
+void vbwrite(struct varbuf *vb,const void *buf,size_t count)
+{
+size_t now;
+
+ vbdebug(vb,"vbwrite");
+ if (!count) return;
+ if (vb->free) {
+ assert(vb->l);
+ memcpy(vb->l->buf+VBNSIZEL(vb,vb->l),buf,(now=min(vb->free,count)));
+ count-=now;
+ vb->free-=now;
+ if (!count) return;
+ buf+=now;
+ }
+ vbgrow(vb,count);
+ assert(vb->free>=count);
+ assert(vb->l&&vb->l->size==vb->free);
+ memcpy(vb->l->buf,buf,count);
+ vb->free-=count;
+ assert(vb->free>=0);
+}
+
+extern size_t vbputstring(struct varbuf *vb,const char *s)
+{
+size_t l=strlen(s);
+ vbwrite(vb,s,l);
+ return(l);
+}
+
+ssize_t vbwritefd(struct varbuf *vb,int fd)
+{
+ssize_t now,got=0;
+
+ vbdebug(vb,"vbwritefd");
+ for (;;) {
+ if (!vb->free) vbgrow(vb,0);
+ now=read(fd,vb->l->buf+vb->l->size-vb->free,vb->free);
+ dbg("vbwritefd: now=%d\n",now);
+ if (now<0) {
+ if (errno==EAGAIN) break;
+ return(now);
+ }
+ if (!now) {
+ errno=ENODATA;
+ return(-1);
+ }
+ vb->free-=now;
+ assert(vb->free>=0);
+ }
+ return(got);
+}
+
+size_t vbpeek(struct varbuf *vb,ssize_t offs,void *buf,size_t count)
+{
+size_t got=0,now,size;
+char doread;
+struct varbufnode *vbn=vb->f;
+
+ vbdebug(vb,"vbpeek");
+ if ((doread=(offs==-1))) offs=0;
+ offs+=vb->done;
+ while (vbn && count) {
+ size=VBNSIZEL(vb,vbn);
+ if (offs<size) {
+ memcpy(buf,vbn->buf+offs,(now=min(count,size-offs)));
+ buf+=now;
+ count-=now;
+ got+=now;
+ offs+=now;
+ if (doread) {
+ assert(vbn==vb->f);
+ vb->done+=now;
+ }
+ }
+ offs-=size;
+ if (!VBNSIZE(vb,vb->f)) {
+ assert(doread);
+ vbremone(vb);
+ vbn=vb->f;
+ }
+ else vbn=vbn->next;
+ }
+ return(got);
+}
+
+ssize_t vbchrn(struct varbuf *vb,ssize_t offs,char c,char dir)
+{
+struct vbl {
+ struct vbl *next;
+ struct varbufnode *vbn;
+ } *vbl=NULL,*vbl2;
+struct varbufnode *vbn;
+ssize_t distoffs=0,size=vbsize(vb,0);
+
+ assert(!!dir);
+ if (offs==-1 || offs>=size) {
+ if (dir>0) return(-1);
+ else offs=size-1;
+ }
+ if (offs<0) return(-1);
+ vbn=vb->f;
+ offs+=vb->done;
+ distoffs=-vb->done;
+ while (vbn && offs>=vbn->size) {
+ if (dir<0) {
+ chk(vbl2=alloca(sizeof(*vbl2)));
+ vbl2->next=vbl;
+ vbl2->vbn=vbn;
+ vbl=vbl2;
+ }
+ offs-=vbn->size;
+ assert(offs>=0);
+ distoffs+=vbn->size;
+ vbn=vbn->next;
+ }
+ if (dir<0) for (;;) {
+ if (vbn && offs<VBNSIZEL(vb,vbn)) {
+ssize_t start=VBNSTART(vb,vbn); // FIXME: not really - GCC bug! (size_t crashes)
+ dbg("seeking back... start=%u,offs=%d,vbn->size=%u\n",start,offs,vbn->size);
+ while (offs>=start&&vbn->buf[offs]!=c) offs--;
+ if (offs>=start) return(distoffs+offs);
+ }
+ if (!vbl) break;
+ vbn=vbl->vbn;
+ vbl=vbl->next;
+ distoffs-=vbn->size;
+ offs=vbn->size-1;
+ assert(offs>=0);
+ }
+ if (dir>0) while (vbn) {
+size_t size=VBNSIZEL(vb,vbn);
+char *s;
+ dbg("vbn=%p,offs=%d,size=%d\n",vbn,offs,size);
+ assert(offs<=size);
+ if ((s=memchr(vbn->buf+offs,c,size-offs)))
+ return(distoffs+(s-vbn->buf));
+ distoffs+=vbn->size;
+ vbn=vbn->next;
+ offs=0;
+ }
+ return(-1);
+}
+
+ssize_t vbreadfd(struct varbuf *vb,int fd)
+{
+ssize_t now,got=0;
+
+ vbdebug(vb,"vbreadfd");
+ dbg("entering vbreadfd(fd=%d)\n",fd);
+ while (vb->f) {
+size_t size=VBNSIZE(vb,vb->f);
+ now=write(fd,vb->f->buf+vb->done,size);
+ dbg("vbreadfd: now=%d\n",now);
+ if (now<0 && errno!=EAGAIN) return(now);
+ if (now<=0) return(got);
+ vb->done+=now;
+ assert(vb->done<=size);
+ got+=now;
+ if (!VBNSIZE(vb,vb->f))
+ vbremone(vb);
+ }
+ return(got);
+}
+
+char quiet_null;
+
+static void fixupnulls(char *s,size_t len,const char *fname)
+{
+unsigned tot=0;
+char *d;
+
+ for (d=s;(d=memchr(d,'\0',s+len-d));) { *d++=' '; tot++; }
+ if (tot) logmsg((quiet_null?MYLOG_DEBUG:MYLOG_ERR),
+ "%s() replaced %u '\\0's by ' ' in: %.*s",fname,tot,(int)len,s);
+}
+
+char *vbgetline(struct varbuf *vb,char term)
+{
+size_t len,r;
+char *s;
+
+ if (!(len=vbchrn(vb,0,term,1)+1)) return(NULL);
+ assert(len>0);
+ chk(s=malloc(len));
+ r=vbread(vb,s,len); assert(r==len);
+ assert(s[len-1]==term);
+ s[len-1]='\0';
+ fixupnulls(s,len-1,"vbgetline");
+ return(s);
+}
+
+size_t chkvsprintf(char **sp,const char *fmt,va_list ap)
+{
+size_t bufsize=VB_DEFSIZE;
+int i;
+
+ for (*sp=NULL;;) {
+ chk(*sp=realloc(*sp,bufsize));
+ i=vsnprintf(*sp,bufsize,fmt,ap);
+ dbg("chkvsprintf: bufsize=%u,i=%d,fmt:%s\n",bufsize,i,fmt);
+ if (i>=0&&i<bufsize) break;
+ bufsize*=2;
+ }
+ assert(i>=0);
+ return(i);
+}
+
+size_t vbvprintf(struct varbuf *vb,const char *fmt,va_list ap,char remlf)
+{
+char *buf=NULL;
+size_t len;
+
+ len=chkvsprintf(&buf,fmt,ap);
+ if (remlf) {
+char *s,c;
+ for (s=buf;(c=*s);s++) {
+ if (c<0 || (c>=' '&&c!='\\')) {
+ vbputchar(vb,c);
+ continue;
+ }
+ vbputchar(vb,'\\');
+ switch (c) {
+ case '\n' : vbputchar(vb,'n'); break;
+ case '\r' : vbputchar(vb,'r'); break;
+ case 1 ... 9: //FALLTHRU
+ case 11 ... 12: //FALLTHRU
+ case 14 ... 31: vbprintf(vb,"x%02X",c); break;
+ case '\\' : vbputchar(vb,'\\'); break;
+ default: assert(0);
+ }
+ }
+ }
+ else vbwrite(vb,buf,len);
+ free(buf);
+ return(len);
+}
+
+size_t vbprintf(struct varbuf *vb,const char *fmt,...) //stub
+{
+va_list ap;
+size_t r;
+
+ va_start(ap,fmt);
+ r=vbvprintf(vb,fmt,ap,0);
+ va_end(ap);
+ return(r);
+}
+
+#ifdef PSTR_16BIT
+#define PSTR_MAX 65535
+#define PSTR_TYPE short
+#else
+#define PSTR_MAX 255
+#define PSTR_TYPE char
+#endif
+
+#define PSTR_PUTLEN2(vb,len,type) vbput##type((vb),(type)(len))
+#define PSTR_PUTLEN1(vb,len,type) PSTR_PUTLEN2(vb,len,type)
+#define PSTR_PUTLEN(vb,len) PSTR_PUTLEN1((vb),(len),PSTR_TYPE)
+
+void vbpstrputstring(struct varbuf *vb,const char *s)
+{
+size_t len=strlen(s);
+ if (len>PSTR_MAX) {
+ LOG(CRIT,"String longer (%u) than PSTRing max (%u)",len,PSTR_MAX);
+ len=PSTR_MAX;
+ dbg("vbpstrputstring-longer than MAX: \"%s\"\n",s);
+ }
+ PSTR_PUTLEN(vb,len);
+ vbwrite(vb,s,len);
+}
+
+size_t vbpstrcopy(struct varbuf *vbd,struct varbuf *vbs,ssize_t len)
+{
+size_t size,was;
+ if (len==-1) len=vbsize(vbs,0);
+ else if ((size=vbsize(vbs,len))<len) len=size;
+ was=len;
+ if (len>PSTR_MAX) {
+ LOG(CRIT,"String longer (%u) than PSTRing max (%u)",len,PSTR_MAX);
+ len=PSTR_MAX;
+ vbdebug(vbs,"vbpstrputstring-longer than MAX");
+ }
+ PSTR_PUTLEN(vbd,len);
+ vbcopy(vbd,vbs,len);
+ if (was!=len) vbdrop(vbs,was-len);
+ return(was);
+}
+
+void vbpstrprintf(struct varbuf *vb,const char *fmt,...)
+{
+va_list ap;
+char buf[PSTR_MAX+1];
+int i;
+
+ va_start(ap,fmt);
+ i=vsnprintf(buf,sizeof(buf),fmt,ap);
+ va_end(ap);
+ if (i==-1) {
+ LOG(CRIT,"Buffer overflow (max=%u) during PSTR printf",PSTR_MAX);
+ i=PSTR_MAX;
+ }
+ assert(i>=0 && i<=PSTR_MAX && !buf[i]);
+ vbpstrputstring(vb,buf);
+}
+
+char *vbpstrread(struct varbuf *vb)
+{
+unsigned PSTR_TYPE len,scan,chgd=0;
+char *buf;
+size_t r;
+
+ if (!vbpeek(vb,0,&len,sizeof(len))) return(NULL);
+ if (!VBCHKSIZE(vb,1+len)) return(NULL);
+ chk(buf=malloc(len+1));
+ vbdrop(vb,sizeof(len));
+ r=vbread(vb,buf,len); assert(r==len);
+ buf[len]='\0';
+ for (scan=0;scan<len;scan++)
+ if (!buf[scan]) {
+ buf[scan]='?';
+ chgd++;
+ }
+ if (chgd)
+ LOG(CRIT,"'\\0's found (%u total) in read PSTR \"%s\", changed to '?'",chgd,buf);
+ return(buf);
+}
+
+size_t vbcopy(struct varbuf *vbd,struct varbuf *vbs,ssize_t count)
+{
+size_t now,got,r;
+
+ vbdebug(vbd,"vbcopy-dest");
+ vbdebug(vbs,"vbcopy-src");
+ dbg("vbcopy-count=%d\n",count);
+ got=vbsize(vbs,(count==-1?0:count));
+ if (count==-1 || count>got) count=got;
+ else got=count;
+ dbg("got=%u, vbd->free=%u, count=%d\n",got,vbd->free,count);
+ if (!count) return(got);
+ if (vbd->free) {
+ dbg("vbd->free=%d path\n",vbd->free);
+ vbdebug(vbd,"vbd->free-s:vbd");
+ assert(vbd->f && vbd->l);
+ r=vbread(vbs,vbd->l->buf+VBNSIZEL(vbd,vbd->l),(now=min(count,vbd->free))); assert(r==now);
+ dbg("now=%u,vbd->free=%u,count=%u\n",now,vbd->free,count);
+ vbd->free-=now;
+ count-=now;
+ vbdebug(vbd,"vbd->free-e:vbd");
+ if (!count) return(got);
+ assert(!vbd->free);
+ }
+ dbg("vbs->done=%u\n",vbs->done);
+ assert(!vbd->free);
+ assert(!VBEMPTY(vbs));
+ if (vbs->done) {
+ dbg("vbs->done=%d path\n",vbs->done);
+ vbdebug(vbs,"vbs->done-s:vbs");
+ vbdebug(vbd,"vbs->done-s:vbd");
+ vbnexact=1;
+ vbgrow(vbd,(now=min(count,VBNSIZE(vbs,vbs->f))));
+ vbnexact=0;
+ assert(vbd->l && vbd->l->size==now && vbd->free==now);
+ r=vbread(vbs,vbd->l->buf,now); assert(r==now);
+ vbd->free=0;
+ count-=now;
+ assert(count>=0);
+ dbg("in vbs->done: count=%d, now=%u\n",count,now);
+ vbdebug(vbs,"vbs->done-e:vbs");
+ vbdebug(vbd,"vbs->done-e:vbd");
+ if (!count) return(got);
+ }
+ assert(!vbs->done);
+ vbdebug(vbs,"relink:vbs");
+ vbdebug(vbd,"relink:vbd");
+ while (count && VBNSIZEL(vbs,vbs->f)<=count) {
+struct varbufnode *vbn=vbs->f;
+ assert(!vbd->free);
+ if (!(vbs->f=vbn->next)) {
+ vbs->l=NULL;
+ count+=(vbd->free=vbs->free);
+ vbs->free=0;
+ }
+ count-=vbn->size;
+ if (vbd->l) {
+ vbd->l->next=vbn;
+ vbd->l=vbn;
+ }
+ else {
+ assert(!vbd->f);
+ vbd->f=vbd->l=vbn;
+ }
+ vbn->next=NULL;
+ }
+ if (!count) return(got);
+ assert(!vbd->free);
+ vbdebug(vbs,"final:vbs");
+ vbdebug(vbd,"final:vbd");
+ vbgrow(vbd,count);
+ assert(vbd->l && vbd->l->size>=count && vbd->free==vbd->l->size);
+ vbread(vbs,vbd->l->buf,count);
+ vbd->free-=count;
+ assert(vbd->free>=0);
+ return(got);
+}
+
+unsigned short vbxsum(struct varbuf *vb)
+{
+unsigned short xsum=0;
+size_t offs;
+struct varbufnode *vbn;
+
+ vbdebug(vb,"vbxsum");
+ vbn=vb->f;
+ offs=vb->done;
+ while (vbn) {
+size_t size=VBNSIZEL(vb,vbn);
+ assert(offs<size);
+ xsum+=(unsigned char)vbn->buf[offs];
+ if (++offs>=size) {
+ vbn=vbn->next;
+ offs=0;
+ }
+ }
+ dbg("vbxsum=%u\n",(unsigned)xsum);
+ return(xsum);
+}
+
+char vbremlast(struct varbuf *vb)
+{
+struct varbufnode *vbn;
+
+ vbdebug(vb,"vbremlast");
+ if (VBEMPTY(vb)) return(0);
+ assert(vb->l&&vb->free<=vb->l->size);
+ if (vb->free<vb->l->size) {
+ vb->free++;
+ vbdebug(vb,"vbremlast-stdend");
+ return(1);
+ }
+ assert(vb->f!=vb->l);
+ for (vbn=vb->f;vbn->next!=vb->l;vbn=vbn->next)
+ assert(vbn->next);
+ vbn->next=NULL;
+ free(vb->l);
+ vb->l=vbn;
+ assert(vb->l->size);
+ vb->free=1;
+ vbdebug(vb,"vbremlast-blasting-end");
+ return(1);
+}
+
+void vbdrop(struct varbuf *vb,size_t len)
+{
+struct varbufnode *vbn;
+
+ assert(VBCHKSIZE(vb,len));
+ vb->done+=len;
+ while (vb->f&&vb->done>=VBNSIZEL(vb,vb->f)) {
+ vb->done-=VBNSIZEL(vb,vb->f);
+ vbn=vb->f;
+ if (!(vb->f=vbn->next)) {
+ assert(vbn==vb->l);
+ vb->l=NULL;
+ vb->free=0;
+ }
+ free(vbn);
+ }
+ if (!vb->f) assert(!vb->done&&!vb->free&&!vb->l);
+}
+
+void vbgetlinef(struct varbuf *vb,FILE *f)
+{
+char buf[LOGF_FGETS_BUF],*s;
+
+ dbg("vbgetlinef(), tell=0x%lx\n",ftell(f));
+ for (;;) {
+ if (!fgets(buf,sizeof(buf),f)) {
+ dbg("vbgetlinef: fgets=NULL\n");
+ if (feof(f)) return;
+ FATAL(ERR,"linereading fail: %m");
+ }
+ dbg("vbgetlinef: fgets: %s\n",buf);
+ if ((s=strchr(buf,'\n'))) break;
+ vbputstring(vb,buf);
+ }
+ vbwrite(vb,buf,s-buf);
+ dbg("vbgetlinef(f), ftell=0x%lx\n",ftell(f));
+}
+
+ssize_t vbcopyline(struct varbuf *vbd,struct varbuf *vbs)
+{
+ssize_t len,r;
+ if ((len=vbchrn(vbs,0,'\n',1))==-1) return(-1);
+ r=vbcopy(vbd,vbs,len); assert(r==len);
+ vbdrop(vbs,1);
+ return(len);
+}
+
+char vbdropline(struct varbuf *vb)
+{
+ssize_t len;
+ if ((len=vbchrn(vb,0,'\n',1))==-1) return(0);
+ vbdrop(vb,len+1);
+ return(1);
+}
+
+char *vbmemorize(struct varbuf *vb,unsigned flg,size_t *sizep)
+{
+ssize_t len=vbsize(vb,0),r;
+char *s;
+ if (sizep) *sizep=len;
+ chk(s=malloc(len+!!(flg&VBMEM_ZERO)));
+ r=vbpeek(vb,(flg&VBMEM_REM?-1:0),s,len); assert(r==len);
+ if (flg&VBMEM_ZERO) {
+ s[len]='\0';
+ fixupnulls(s,len,"vbmemorize");
+ }
+ if (flg&VBMEM_REM) assert(VBEMPTY(vb));
+ return(s);
+}
+
+char *vbstringify(struct varbuf *vb,char rem)
+{ return vbmemorize(vb,VBMEM_ZERO|(rem?VBMEM_REM:0),NULL); }
+
+const char *signames[]={
+ "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "IOT", "BUS", "FPE", "KILL",
+ "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT",
+ "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU", "XFSZ", "VTALRM", "PROF",
+ "WINCH", "IO", "PWR", "UNUSED" };
+
+void abortsignal(int signo)
+{ //no reinstall! (deadlock!)
+#ifdef FATALSIG_INSANE
+ vbcheckno=1;
+#endif
+ signal(signo,SIG_DFL);
+ dbg("Got signal %d\n",signo);
+ exit(EXIT_FAILURE);
+}
+
+struct varbuf *chat_vbout;
+int chat_cmdpar;
+char *chat_cmdpars[CHAT_CMD_MAXPARS];
+
+static void chatfn_help(void);
+
+static const struct chat_cmdtab chat_cmdint[]={
+ {"help" ,0,chatfn_help ,"This help"},
+ {"quit" ,0,chatfn_quit ,"Close connection"},
+ {"exit" ,0,chatfn_quit ,"Close connection"},
+};
+
+static void chatfn_help(void)
+{
+int i;
+const struct chat_cmdtab *cmd,*cmdt;
+
+ vbputstring(chat_vbout,"000 Available commands:\n");
+ cmdt=chat_cmdtable;
+ for (i=0;i<NELEM(chat_cmdint);) {
+ if (cmdt->name) cmd=cmdt++;
+ else cmd=&chat_cmdint[i++];
+ vbprintf(chat_vbout,"000 %-10s[%d] - %s\n",cmd->name,(int)cmd->npars,cmd->help);
+ }
+ vbputstring(chat_vbout,"102 Help end.\n");
+}
+
+static inline void chat_parsepars(char *s)
+{
+char c,*d,quote;
+
+ chat_cmdpar=0;
+ if (*s) for (*s++='\0';*s&&isspace(*s);s++);
+ for (c=*s;c&&chat_cmdpar<CHAT_CMD_MAXPARS;chat_cmdpar++) {
+ quote=0;
+ for (chat_cmdpars[chat_cmdpar]=d=s;*s&&(quote||(!quote&&*s!=','));s++) {
+ if (*s=='"'||*s=='\'') {
+ if (quote==*s) { quote=0; continue; }
+ if (!quote) { quote=*s; continue; }
+ }
+ if (*s=='\\'&&s[1]) s++;
+ *d++=*s;
+ }
+ c=*s++; *d='\0';
+ }
+}
+
+void chat_proccmd(const char *who,char *buf)
+{
+char *s,*cmds;
+int i;
+const struct chat_cmdtab *cmd,*cmdt;
+
+ for (cmds=buf;*cmds&&isspace(*cmds);cmds++);
+ for (s=cmds;*s&&!isspace(*s);s++);
+ if (s==cmds) {
+ vbputstring(chat_vbout,"104 Empty command.\n");
+ return;
+ }
+ chat_parsepars(s);
+ cmdt=chat_cmdtable;
+ for (i=0;i<NELEM(chat_cmdint);) {
+ if (cmdt->name) cmd=cmdt++;
+ else cmd=&chat_cmdint[i++];
+ assert(cmd->npars<=CHAT_CMD_MAXPARS);
+ if (!strcmp(cmd->name,cmds)) break;
+ }
+ if (i<NELEM(chat_cmdint)) {
+ if (cmd->npars!=chat_cmdpar)
+ vbprintf(chat_vbout,"205 %d parameter%s found, %d expected!\n",chat_cmdpar,(chat_cmdpar==1?"":"s"),cmd->npars);
+ else {
+ vbprintf(chat_vbout,"001 Running command: %s\n",cmds);
+ LOG(DEBUG,"%s is executing user command \"%s\"",who,cmds);
+ (*cmd->fnc)();
+ }
+ }
+ else vbprintf(chat_vbout,"206 Unknown command \"%s\"!\n",cmds);
+}
+
+void crfilter(char *sr)
+{
+char *sw;
+ for (sw=sr;*sr;sr++)
+ if (*sr!='\r') *sw++=*sr;
+ *sw='\0';
+}
+
+static void tzchange(const char *tz)
+{
+static char *otz=NULL;
+
+ if (tz) {
+ free(otz);
+ if ((otz=getenv("TZ"))) chk(otz=strdup(otz));
+ setenv("TZ",tz,1);
+ }
+ else {
+ if (otz) {
+ setenv("TZ",otz,1);
+ free(otz); otz=NULL;
+ }
+ else unsetenv("TZ");
+ }
+ tzset();
+}
+
+char *gmctime(time_t tm)
+{
+char *r,*s;
+
+ tzchange("GMT");
+ r=ctime(&tm);
+ tzchange(NULL);
+ if ((s=strchr(r,'\n')))
+ *s='\0';
+ chk(r=strdup(r));
+ return(r);
+}
+
+unsigned char *bitshuffle(unsigned char *dst,unsigned char dstb,const unsigned char *src,unsigned char srcb,size_t bits,char lr)
+{
+unsigned char dstrem,*dstp,srcrem,now;
+const unsigned char *srcp;
+
+ if (!dst) chk(dst=malloc((bits+dstb-1)/dstb));
+ dstrem=0; dstp=dst-1;
+ srcrem=0; srcp=src-1;
+ while (bits) {
+ if (!dstrem) {
+ *++dstp=0;
+ dstrem=dstb;
+ }
+ if (!srcrem) {
+ srcp++;
+ srcrem=srcb;
+ }
+ dbg("dstrem=%u,srcrem=%u,bits=%u\n",dstrem,srcrem,bits);
+ assert(dstrem&&srcrem);
+ now=min(dstrem,srcrem);
+ assert(bits>=now);
+ dbg("1:*dstp=0x%02x,*srcp=0x%02x,dstrem=%u,srcrem=%u,now=%u\n",*dstp,*srcp,dstrem,srcrem,now);
+ *dstp|=((*srcp>>(lr?srcrem-now:srcb-srcrem))&((1U<<now)-1))<<(lr?dstrem-now:dstb-dstrem);
+ dstrem-=now;
+ srcrem-=now;
+ bits -=now;
+ dbg("2:*dstp=0x%02x,*srcp=0x%02x,dstrem=%u,srcrem=%u,now=%u\n",*dstp,*srcp,dstrem,srcrem,now);
+ }
+ return(dst);
+}
+
+char *enbase64(char *dst,const unsigned char *src,size_t bits)
+{
+char *s,*d;
+
+ if (!dst) chk(dst=malloc(((bits+23)/24)*4+1));
+ d=bitshuffle(dst,6,src,8,bits,1); assert(d==dst);
+ d=dst+(bits=(bits+5)/6);
+ for (s=dst;s<d;s++) switch (*s) {
+ case 0 ... 25: *s+='A'- 0; break;
+ case 26 ... 51: *s+='a'-26; break;
+ case 52 ... 61: *s+='0'-52; break;
+ case 62 : *s ='+' ; break;
+ case 63 : *s ='/' ; break;
+ default: assert(0);
+ }
+ while (bits++%4) *s++='=';
+ *s='\0';
+ return(dst);
+}
+
+unsigned char *debase64(unsigned char *dst,const char *src,size_t *dstlp)
+{
+size_t srcl,dstl;
+char *src2,*sd;
+const char *s;
+unsigned char *d;
+
+ chk(src2=alloca(strlen(src))); /* +1 not needed */
+ for (sd=src2,s=src;*s;s++) switch (*s) {
+ case 'A' ... 'Z': *sd++=*s-('A'- 0); break;
+ case 'a' ... 'z': *sd++=*s-('a'-26); break;
+ case '0' ... '9': *sd++=*s-('0'-52); break;
+ case '+' : *sd++= ( 62); break;
+ case '/' : *sd++= ( 63); break;
+ case '=' :
+ /* only 0, 1 or 2 '='(s) permitted */
+ if (!s[1] || (s[1]=='=' && (!s[2] || (s[2]=='=' && !s[3]))))
+ goto out;
+ /* FALLTHRU */
+ default: return(NULL);
+ }
+out:
+ srcl=sd-src2; if ((srcl%4)==1) return(NULL);
+ dstl=(srcl*6)/8;
+ if (!dst) chk(dst=malloc(dstl));
+ d=bitshuffle(dst,8,src2,6,dstl*8,1); assert(d==dst);
+ if (dstlp) *dstlp=dstl;
+ return(dst);
+}