#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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;sbuf+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;isize;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 && sugsizenext=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 (sugsizel) 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 || sizenext; } 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 (offsbuf+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 && offssize=%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=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))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;scangot) 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(offsbuf[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->freel->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;iname) 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_cmdparname) cmd=cmdt++; else cmd=&chat_cmdint[i++]; assert(cmd->npars<=CHAT_CMD_MAXPARS); if (!strcmp(cmd->name,cmds)) break; } if (inpars!=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<