#include #include #include #include #include #include #include #include #include #include #include "myc_mem.h" #include "sys_util.h" #include "myc_err.h" #include "coc_logfile.h" #include "coc_util.h" #include "coc_server.h" #include "myc_str.h" #include "myc_time.h" #define COC_NUL COC_SEP typedef struct _CocVar { struct _CocVar *next; char name[32]; void *var; int access; int type; int size; void *strucType; int (*hdl)(int, void *, int); int pending; } CocVar; CocVar *serverVarList=NULL; static CocVar **varListHandle=&serverVarList; static CocVar *lastDef=NULL; typedef struct { CocVar *var; void *base; int mode; } Pend; typedef struct _CocClient { struct _CocClient *next; int fd; int mode; Pend pend[16]; int npend; int logmask; char synch; void *data; } CocClient; void *(*setData)(void *); /* static CocClientData **clData; static int clDataSize; */ DeclStrBuf(buf, COC_CMD_LEN); DeclStrBuf(bufo, COC_RES_LEN); static fd_set mask, rmask; static int maxfd; CocClient *cList; static int mainFd; static int modified; static char loglist[16]; void CocVarList(void **varList) { if (varList==NULL) { varListHandle=(CocVar **)varList; } else { varListHandle=&serverVarList; } } void CocList() { CocVar *p; p=*varListHandle; while (p!=NULL) { printf("%s %d ", p->name, p->type); p=p->next; } printf("\n"); } CocVar *CocFindVar1(const char *name) { CocVar *p; p=*varListHandle; while (p!=NULL && 0!=strcasecmp(p->name,name)) p=p->next; return(p); } void *CocFindVar(const char *name, void **adr) { CocVar *p, *t; const char *f; void *base; char nam[32]; f=str_split(nam, name, '.'); if (f==NULL) { f=str_split(nam, name, '-'); if (f!=NULL) { if (f[0]!='>') { f=NULL; } else { f++; } } } if (f!=NULL) { if (adr!=NULL) *adr=NULL; p=CocFindVar1(nam); if (p==NULL) { return(NULL); } t=p->strucType; if (t==NULL) { return(NULL); } str_copy(nam, t->name); str_append(nam, ":"); str_append(nam, f); if (adr!=NULL) { base=p->var; if (p->type==COC_PTR) base=*(void **)base; *adr=base; } } else if (adr!=NULL) { *adr=NULL; } p=CocFindVar1(nam); if (p!=NULL && p->type==COC_ALIAS) { /* recursive call for alias */ p=CocFindVar(p->var, adr); } return(p); } void *CocIntPtr(int *ptr) { return(ptr); } void *CocFltPtr(float *ptr) { return(ptr); } void *CocChrPtr(char *ptr) { return(ptr); } void *CocDefVar(const char *name, void *var, int type, int size, int access) { CocVar *p; const char *f; void *adr; assert(varListHandle!=NULL); p=CocFindVar1(name); if (p==NULL) { NEW(p,CocVar); p->next=*varListHandle; *varListHandle=p; str_copy(p->name, name); p->type=type; p->size=size; } else { assert(p->type==type); } p->var=var; p->access=access; /* printf("define %s %d\n", name, (int)var); */ lastDef=p; return(p); OnError: assert(0); } void CocHdl(int (*handler)(int, void *, int)) { assert(lastDef!=NULL); lastDef->hdl=handler; } int *CocSizePtr(void) { assert(lastDef!=NULL && lastDef->type==COC_ARRAY); return &lastDef->size; } void CocDefVarS(const char *name, const char *tname, void *var, int type) { CocVar *p, *t; assert(type==COC_PTR || type==COC_STRUCT); p=CocDefVar(name, var, type, 0, COC_RDONLY); p->strucType=CocDefVar(tname, NULL, COC_TYPE, 0, COC_RDONLY); } char err_name[64]; int CocGetThisVar(CocVar *var, void *base, StrBuf *buf, int separator) { void *adr; int iret; if (base==NULL) { adr=var->var; if (adr==NULL) ERR_MSG("NULL pointer accessed"); } else { /* dereference */ adr=(char *)base + (int)var->var; } /* printf("get %s %d\n", name, (int)adr); */ if (var->type==COC_CHAR) { ERR_P(StrNGet(buf, (char *)adr, var->size, separator)); } else if (var->type==COC_INT) { ERR_I(StrGetInt(buf, (int *)adr, separator)); } else if (var->type==COC_FLT) { ERR_I(StrGetFloat(buf, (float *)adr, separator)); } else if (var->type==COC_ARRAY) { ERR_I(StrGetArray(buf, (float *)adr, var->size)); } else { ERR_MSG("unknown type"); } return(0); OnError: return(-1); } int CocPutThisVar(CocVar *var, void *base, StrBuf *buf, int separator) { void *adr; int iret; if (base==NULL) { adr=var->var; if (adr==NULL) ERR_MSG("NULL pointer accessed"); } else { /* dereference */ adr=(char *)base + (int)var->var; } /* printf("put %s %d\n", name, (int)adr); */ if (var->type==COC_CHAR) { ERR_I(StrPut(buf, adr, separator)); } else if (var->type==COC_INT) { ERR_I(StrPutInt(buf, *(int *)adr, separator)); } else if (var->type==COC_FLT) { ERR_I(StrPutFloat(buf, *(float *)adr, separator)); } else if (var->type==COC_ARRAY) { ERR_I(StrPutArray(buf, (float *)adr, var->size)); } else { ERR_MSG("unknown type"); } return(0); OnError: return(-1); } int CocGetVar(const char *name, StrBuf *buf, int separator) { CocVar *var; void *base; var=CocFindVar(name, &base); if (var==NULL) ERR_MSG("undefined variable"); ERR_I(CocGetThisVar(var, base, buf, separator)); return(0); OnError: str_copy(err_name, name); ErrTxt(err_name,0); return(-1); } int CocPutVar(const char *name, StrBuf *buf, int separator) { CocVar *var; void *base; var=CocFindVar(name, &base); if (var==NULL) ERR_MSG("undefined variable"); ERR_I(CocPutThisVar(var, base, buf, separator)); return(0); OnError: str_copy(err_name, name); ErrTxt(err_name,0); return(-1); } void CocFreeVarList(void) { CocVar *p, *v; v=*varListHandle; while (v!=NULL) { p=v; v=p->next; p->next=NULL; FREE(p); } *varListHandle=NULL; } char *CocReadVars(char *str, char stop){ int i, l; char *eql, *cr, buf[80]; StrBuf sbuf; /* interprete variables until stop character appeares */ i=sscanf(str, "%79s%n", buf, &l); while (i>0 && buf[0]!=stop) { if (buf[0]=='!') { cr=strchr(str, '\n'); if (cr==NULL) return strchr(str, '\0'); str=cr+1; } else { str+=l; eql=strchr(buf,'='); if (eql==NULL) ERR_MSG("syntax error"); *eql='\0'; StrLink(&sbuf, eql+1); ERR_I(CocGetVar(buf, &sbuf, ' ')); } i=sscanf(str, "%79s%n", buf, &l); } return str; OnError: return NULL; } void CocToClients(int mask, char *str) { int iret; CocClient *cl; cl=cList->next; while (cl!=NULL) { if (cl->logmask & mask) { iret=CocSend(cl->fd, str, strlen(str)+1); if (iret<0) { cl->logmask=0; /* disable logging for dead clients */ logfileOut(LOG_MAIN, "(%d) disconnected while logging\n", cl->fd); } } cl=cl->next; } } int CocInitServer(void *(*setDataRtn)(void *), int port) { int i; struct sockaddr_in sadr; char *err; setData=setDataRtn; /* clDataSize=clientDataSize; clData=clientData; */ NEW(cList,CocClient); /* empty header */ ERR_SI(mainFd=socket(AF_INET, SOCK_STREAM, 0)); i = 1; ERR_SI(setsockopt(mainFd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int))); /* allow quick port reuse */ ERR_I(CocCreateSockAdr(&sadr, NULL, port)); ERR_SI(bind(mainFd, (struct sockaddr *)&sadr, sizeof(sadr))); logfileOutRtn=CocToClients; logfileOut(LOG_INFO, "created server on port %d\n", port); ERR_SI(listen(mainFd, 8)); FD_ZERO(&mask); FD_SET(mainFd, &mask); maxfd=mainFd+1; CocDefStr(loglist, COC_RDWR); return(0); OnError: return(-1); } int CocHandleThis(CocVar *var, void *base, StrBuf *outBuf, int mode, int fd) { int iret; if (var->hdl!=NULL) { iret=var->hdl(mode, base, fd); if (iret<0) { /* error */ ErrShow(var->name); ERR_I(StrPut(outBuf, "", COC_ERR)); /* signal error message */ ERR_I(StrPut(outBuf, ErrMessage, COC_SEP)); return(0); } if (iret==COC_DRD) { ERR_I(StrPut(outBuf, "", COC_DELAYED)); /* delayed response message */ ERR_I(StrPut(outBuf, "", COC_SEP)); /* empty message */ return(COC_DRD); } if (iret!=0 && iret!=COC_DWR) ERR_MSG("illegal return value from handler"); } else { iret=0; } ERR_I(CocPutThisVar(var, base, outBuf, COC_NUL)); return(iret); OnError: return(-1); } int CocPushThisHandler(CocVar *var, CocClient *cl, void *base, int mode) { int n; if (mode==COC_DWR) { if (var->pending) return(0); var->pending=1; } n=cl->npend; if (n>=sizeof(cl->pend)) { ERR_MSG("too many commands") } cl->pend[n].var=var; cl->pend[n].base=base; cl->pend[n].mode=mode; cl->npend=n+1; return(0); OnError: return(-1); } int CocCallHandlers(void) { CocClient *cl; CocVar *var; Pend *p; int i, iret, mode, delayedRead; char synch; DeclStrBuf(bufr, COC_RES_LEN); cl=cList->next; while (cl!=NULL) { delayedRead=0; /* treat delayed write handlers first */ for (i=0; inpend; i++) { p=&cl->pend[i]; mode=p->mode; var=p->var; assert(var!=NULL && var->hdl!=NULL); if (mode==COC_DWR) { var->pending=0; ERR_I(var->hdl(mode, p->base, cl->fd)); p->mode=0; } else { delayedRead=1; } } if (delayedRead) { StrClear(&bufr); synch=cl->synch; ERR_I(StrPut(&bufr, "", cl->synch)); for (i=0; inpend; i++) { p=&cl->pend[i]; mode=p->mode; var=p->var; assert(var!=NULL && var->hdl!=NULL); if (mode==COC_DRD) { iret=var->hdl(mode, p->base, cl->fd); if (iret<0) { /* error */ ERR_I(StrPut(&bufr, "", COC_ERR)); /* signal error message */ ERR_I(StrPut(&bufr, ErrMessage, COC_SEP)); } ERR_I(CocPutThisVar(var, p->base, &bufr, COC_NUL)); } } if (cl->synch==synch) { /* send only if no new request from client received */ assert(cl->fd!=0); ERR_I(CocSend(cl->fd, bufr.buf, bufr.wrpos)); } } cl->npend=0; cl=cl->next; } return(0); OnError: cl->npend=0; return(-1); } void CocShowHandlers(char *buf, int buf_len) { CocClient *cl; Pend *p; int i; buf[0]='\0'; cl=cList->next; while (cl!=NULL) { for (i=0; inpend; i++) { p=&cl->pend[i]; if (p->mode==COC_DWR) { assert(p->var!=NULL); if (buf[0]!='\0') str_ncat(buf, ", ", buf_len); str_ncat(buf, p->var->name, buf_len); } } cl=cl->next; } return; } int CocHandle1Request(int tmo_msec, int fd) { struct sockaddr_in cadr; struct hostent *h; struct timeval tmo={0,1}; CocClient *cl, *cl0; CocVar *var; int i, lmask, newfd, n, iret; sys_adr_len cadrlen; /* see sys_util.h */ char *err, *cmd, *arg, *varname; void *base; rmask=mask; if (fd>0) FD_SET(fd, &rmask); tmo.tv_sec=tmo_msec / 1000; tmo.tv_usec=(tmo_msec % 1000)*1000+1; if (fd>=maxfd) maxfd=fd+1; ERR_SI(i=select(maxfd,&rmask,NULL,NULL,&tmo)); if (fd>0 && FD_ISSET(fd, &rmask)) return(1); /* event on fd */ if (i==0) return(0); /* timeout */ if (FD_ISSET(mainFd, &rmask)) { cadrlen=sizeof(cadr); ERR_SI(newfd=accept(mainFd, (struct sockaddr *)&cadr, &cadrlen)); FD_SET(newfd, &mask); if (newfd>=maxfd) maxfd=newfd+1; NEW(cl, CocClient); cl->fd=newfd; cl->mode=0; if (setData!=NULL) { ERR_P(cl->data = setData(NULL)); /* create new client data object */ } /* ERR_SP(cList=calloc(1,sizeof(CocClient)+clDataSize)); */ cl->next=cList->next; cList->next=cl; h=gethostbyaddr((void *)&cadr.sin_addr, 4, AF_INET); if (h==NULL) { logfileOut(LOG_INFO, "(%d) open from %s\n", newfd, "local"); } else { logfileOut(LOG_INFO, "(%d) open from %s\n", newfd, h->h_name); } } else { cl0=cList; cl=cl0->next; while (cl!=NULL) { if (FD_ISSET(cl->fd, &rmask)) { iret=CocRecv(cl->fd, &buf, -1, NULL); if (iret<=0) { logfileOut(LOG_INFO, "(%d) disconnected\n",cl->fd); close(cl->fd); FD_CLR(cl->fd, &mask); cl0->next=cl->next; FREE(cl); cl=cl0; } else { cl->logmask=0; /* stop output to log client */ lmask=0; StrReset(&buf); StrClear(&bufo); err=NULL; logfileOut(LOG_NET, "(%d) ", cl->fd); logfileOutBuf(LOG_NET, &buf); /* cl->npend=0; why that ? */ logfileOut(LOG_NET, "\n"); cl->synch=buf.buf[0]; ERR_P(StrGet(&buf, NULL, cl->synch)); ERR_I(StrPut(&bufo, "", cl->synch)); while (!StrEnd(&buf)) { ERR_P(varname=StrGet(&buf, NULL, ' ')); if (cl->synch==COC_MAGIC) { /* access code */ if (0==strcmp(varname,"rdacs")) { logfileOut(LOG_INFO, "set read mode\n"); cl->mode=1; ERR_I(StrPut(&bufo, "", COC_SEP)); /* empty message */ } else if (0==strcmp(varname,"rwacs")) { logfileOut(LOG_INFO, "set write mode\n"); cl->mode=2; ERR_I(StrPut(&bufo, "", COC_SEP)); /* empty message */ } else { ERR_I(StrPut(&bufo, "", COC_ERR)); /* signal error message */ ERR_I(StrPut(&bufo, "bad access code", COC_SEP)); } ERR_P(StrGet(&buf, NULL, COC_NUL)); /* skip separator */ } else if (cl->mode==0) { ERR_I(StrPut(&bufo, "", COC_ERR)); /* signal terminal error message */ ERR_I(StrPut(&bufo, "no access", COC_SEP)); ERR_P(StrGet(&buf, NULL, COC_NUL)); /* skip separator */ } else { if (cl->data!=NULL) { setData(cl->data); } var=CocFindVar(varname, &base); if (var==NULL) { ERR_I(StrPut(&bufo, "", COC_ERR)); /* signal error message */ ERR_I(StrPut(&bufo, "undefined variable", COC_SEP)); ERR_P(StrGet(&buf, NULL, COC_NUL)); /* skip separator */ } else if (buf.seen) { /* separator was there: set mode */ if (var->access > cl->mode) { ERR_I(StrPut(&bufo, "", COC_ERR)); /* signal error message */ ERR_I(StrPut(&bufo, "no access", COC_SEP)); ERR_P(StrGet(&buf, NULL, COC_NUL)); /* skip separator */ } else { ERR_I(CocGetThisVar(var, base, &buf, COC_SEP)); if (0==strcmp(var->name,"loglist")) { str_upcase(loglist, loglist); if (NULL!=strchr(loglist,'A')) lmask = lmask | LOG_ALL; if (NULL!=strchr(loglist,'S')) lmask = lmask | LOG_SER; if (NULL!=strchr(loglist,'N')) lmask = lmask | LOG_NET; if (NULL!=strchr(loglist,'I')) lmask = lmask | LOG_INFO; if (NULL!=strchr(loglist,'M')) lmask = lmask | LOG_MAIN; ERR_I(StrPut(&bufo, "", COC_NUL)); /* o.k. */ } else { ERR_I(iret=CocHandleThis(var, base, &bufo, COC_WR, cl->fd)); if (iret) ERR_I(CocPushThisHandler(var, cl, base, iret)); } modified=1; } } else { ERR_P(StrGet(&buf, NULL, COC_NUL)); /* skip separator */ ERR_I(iret=CocHandleThis(var, base, &bufo, COC_RD, cl->fd)); if (iret) ERR_I(CocPushThisHandler(var, cl, base, iret)); } } } ERR_I(StrGetEnd(&buf)); logfileOut(LOG_NET, " "); logfileOutBuf(LOG_NET, &bufo); logfileOut(LOG_NET, "\n"); ERR_I(CocSend(cl->fd, bufo.buf, bufo.wrpos)); cl->logmask=lmask; } } cl0=cl; cl=cl->next; } } if (modified) return(2); return(3); OnError: return(-1); } int CocHandleRequests(int tmo_msec, int fd) { int tdif, iret, tim0; if (modified && fd==0) { /* earlier modification */ modified=0; return(2); } tim0=mycMsecSince(0); tdif=tmo_msec; while (tdif>=0) { ERR_I(iret=CocHandle1Request(tdif, fd)); if (fd==0) { if (iret==2) return(2); /* modification of a variable */ } else { if (iret==1) return(1); /* event on fd */ } if (iret==0) return(0); /* timeout */ tdif = tmo_msec - mycMsecSince(tim0); } return(0); /* timeout */ OnError: return(-1); } void CocCloseServer() { CocClient *cl, *cl0; cl=cList->next; while (cl!=NULL) { close(cl->fd); cl0=cl; cl=cl->next; FREE(cl0); } FREE(cList); close(mainFd); }