Files
sics/tecs/coc_client.c
2002-08-22 11:06:28 +00:00

517 lines
13 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include "myc_err.h"
#include "coc_util.h"
#include "coc_client.h"
#include "sys_util.h"
#include "myc_str.h"
/* --- non ANSI signal --- */
#ifndef SIGPIPE
#define SIGPIPE 13
#endif
#define COC_NETTMO 5
#define COC_RESTMO 60
#define ARG_CHAR 1
#define ARG_INT 2
#define ARG_FLT 3
#define ARG_ARR 4
/*-------------------------------------------------------------------------*/
int CocConnect(CocConn *conn) { /* connect, but do not start */
int i;
struct sockaddr_in sadr;
ERR_I(CocCreateSockAdr(&sadr, conn->host, conn->port));
ERR_SI(conn->fd=socket(AF_INET, SOCK_STREAM, 0));
i = 1;
ERR_SI(setsockopt(conn->fd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int))); /* allow quick port reuse */
i=connect(conn->fd, (struct sockaddr *)&sadr, sizeof(sadr));
if (i<0) {
if (errno==ECONNREFUSED) return(1);
ERR_COD(errno);
}
return(0);
OnError: return(-1);
}
int CocOpen(CocConn *conn) { /* connect and start, if not succsessful */
int i, try, tmo;
ERR_I(i=CocConnect(conn));
if (i==0) return(0);
/* connection refused */
if (conn->startcmd[0]=='\0') {
CocDelay(500);
ErrTxt("connect",1); return(1);
}
printf("Starting TecsServer ...\n\n%s\n", conn->startcmd);
ERR_I(system(conn->startcmd));
try=15;
tmo=100; /* wait total ca. 10 sec. for max. 15 tries */
while (try>0) {
try--;
CocDelay(tmo); tmo=tmo*5/4;
ERR_I(i=CocConnect(conn));
if (i==0) {
printf("\n... connected to TecsServer\n");
return(0);
}
}
ERR_MSG("can not start TECS, too many retries");
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocInitClient(CocConn *conn, char *host, int port, char *magic, int bufsize, char *startcmd) {
int iret;
assert(conn!=NULL);
if (bufsize==0) bufsize=1024;
StrLink(&conn->cmdbuf, conn->cmdbuf_);
StrLink(&conn->resbuf, conn->resbuf_);
conn->port=port;
ERR_I(str_copy(conn->host, host));
ERR_I(str_copy(conn->magic, magic));
ERR_I(str_copy(conn->startcmd, startcmd));
conn->fd=-1;
ERR_I(iret=CocOpen(conn));
if (iret==0) {
iret=CocSendMagic(conn, conn->magic);
if (iret<0) {
CocCloseClient(conn);
iret=1;
}
}
return(iret);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocSendMagic(CocConn *conn, char *magic) {
char *err;
int siz, n;
StrClear(&conn->resbuf); /* use return buffer for command in order to preserve command buffer */
ERR_I(StrPut(&conn->resbuf, "", COC_MAGIC));
ERR_I(StrPut(&conn->resbuf, magic, COC_SEP));
ERR_I(CocSend(conn->fd, conn->resbuf.buf, conn->resbuf.wrpos));
ERR_I(CocRecv(conn->fd, &conn->resbuf, COC_NETTMO, NULL));
ERR_P(err=StrGet(&conn->resbuf, NULL, COC_SEP));
if (err[0]!=COC_MAGIC) ERR_MSG("magic synch error");
if (err[1]!='\0') { ErrMsg(err+1); ErrTxt(": (response from server)",0); goto OnError; }
ERR_I(StrGetEnd(&conn->resbuf));
return(0);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocCheck(CocConn *conn) {
if (conn->fd<0) return(1);
ERR_I(CocSend(conn->fd, "_quit", 6));
ERR_I(CocRecv(conn->fd, &conn->resbuf, COC_NETTMO, NULL));
return(0);
OnError:
if (ErrCode==ECONNRESET || ErrCode==EPIPE) return(1);
return(-1);
}
/*-------------------------------------------------------------------------*/
int CocTryCmd(CocConn *conn) {
int iret=-1, siz;
signal(SIGPIPE, SIG_IGN);
if (conn->fd<0) {
ERR_I(iret=CocConnect(conn)); /* was CocOpen earlier */
if (iret == 1) {
CocDelay(500);
ErrTxt("connect",1); return (-1);
}
iret=-1;
ERR_I(CocSendMagic(conn, conn->magic));
}
ERR_I(CocSend(conn->fd, conn->cmdbuf.buf, conn->cmdbuf.wrpos));
ERR_I(CocRecv(conn->fd, &conn->resbuf, COC_NETTMO, NULL));
return(0);
OnError:
if (ErrCode==ECONNRESET || ErrCode==EPIPE) return(-2);
return(iret);
}
/*-------------------------------------------------------------------------*/
int CocCmdWithRetry(CocConn *conn) {
int cnt, iret;
if (conn==NULL) ERR_MSG("not connected");
cnt=3;
while (1) {
cnt--;
if (cnt<=0) {
ERR_I(CocTryCmd(conn));
break;
}
iret=CocTryCmd(conn);
if (iret>=0) break;
close(conn->fd);
conn->fd=-1;
if (iret==-1) goto OnError; /* severe error */
ErrShort(ErrMessage);
ErrShort("try to reconnect");
}
return(0);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
void CocReset(CocConn *conn) {
assert(conn!=NULL);
StrClear(&conn->cmdbuf);
if (conn->synch!=COC_SYN0) {
conn->synch=COC_SYN0;
} else {
conn->synch=COC_SYN1;
}
StrPut(&conn->cmdbuf, "", conn->synch);
conn->nargs=0;
}
/*-------------------------------------------------------------------------*/
int CocPushArg(CocConn *conn, const char *name, void *value, int type, int size) {
StrBuf *buf;
int n;
assert(conn!=NULL);
assert(NULL==strchr(name, ' '));
buf=&conn->cmdbuf;
n=conn->nargs;
if (n>=sizeof(conn->args)) ERR_MSG("too many return arguments");
conn->args[n].adr=value;
conn->args[n].type=type;
conn->args[n].size=size;
conn->args[n].cmd=buf->buf + buf->wrpos;
conn->nargs=n+1;
if (value==NULL) {
ERR_I(StrPut(buf, name, ' '));
} else {
ERR_I(StrPut(buf, name, COC_SEP));
}
return(0);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocPutStr(CocConn *conn, const char *name, const char *value) {
StrBuf *buf;
ERR_I(CocPushArg(conn, name, NULL, ARG_CHAR, 0));
ERR_I(StrPut(&conn->cmdbuf, value, COC_SEP));
return(0);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocPutFloat(CocConn *conn, const char *name, float value) {
ERR_I(CocPushArg(conn, name, NULL, ARG_FLT, 0));
ERR_I(StrPutFloat(&conn->cmdbuf, value, COC_SEP));
return(0);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocPutArray(CocConn *conn, const char *name, float *value, int value_size) {
ERR_I(CocPushArg(conn, name, NULL, ARG_ARR, 0));
ERR_I(StrPutArray(&conn->cmdbuf, value, value_size));
return(0);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocPutInt(CocConn *conn, const char *name, int value) {
ERR_I(CocPushArg(conn, name, NULL, ARG_INT, 0));
ERR_I(StrPutInt(&conn->cmdbuf, value, COC_SEP));
return(0);
OnError: return(-1);
}
/*-------------------------------------------------------------------------*/
int CocGetStr(CocConn *conn, const char *name, char *value, int value_len) {
return(CocPushArg(conn, name, value, ARG_CHAR, value_len));
}
/*-------------------------------------------------------------------------*/
int CocGetFloat(CocConn *conn, const char *name, float *value) {
return(CocPushArg(conn, name, value, ARG_FLT, 0));
}
/*-------------------------------------------------------------------------*/
int CocGetInt(CocConn *conn, const char *name, int *value) {
return(CocPushArg(conn, name, value, ARG_INT, 0));
}
/*-------------------------------------------------------------------------*/
int CocGetArray(CocConn *conn, const char *name, float *value, int value_size) {
return(CocPushArg(conn, name, value, ARG_ARR, value_size));
}
/*-------------------------------------------------------------------------*/
int CocDoIt(CocConn *conn, char *res, int res_len) {
StrBuf *buf;
int i, pending;
CocArg *a;
char *resp, *t, tag;
int iret=0;
assert(conn!=NULL);
ERR_I(CocCmdWithRetry(conn));
buf=&conn->resbuf;
while (buf->buf[0] != conn->synch) { /* throw away unsynchronized answers */
ERR_I(CocRecv(conn->fd, buf, COC_NETTMO, NULL));
}
pending=conn->nargs;
*res='\0';
while (1) {
if (buf->buf[0] != conn->synch) ERR_MSG("synch error");
ERR_P(StrGet(buf, NULL, conn->synch));
i=0;
a=conn->args;
while (pending>0 && i<conn->nargs) {
if (a->type!=0) {
resp=buf->buf + buf->rdpos;
tag=*resp;
if (tag==COC_DELAYED) { /* delayed response */
resp++;
ERR_P(StrGet(buf, NULL, COC_SEP));
} else {
pending--;
if (tag==COC_ERR || tag==COC_TRM) { /* error response */
str_ncat(res, a->cmd, res_len);
resp++;
ERR_P(StrGet(buf, NULL, COC_SEP));
str_ncat(res, ": ", res_len);
iret=1;
if (tag==COC_TRM) return(1);
} else {
if (a->adr==NULL) {
t=strchr(res, COC_SEP);
str_ncat(res, a->cmd, res_len);
t=strchr(t, ' ');
assert(t!=NULL); /* truncate argument */
*t=COC_SEP;
str_ncat(res, "=", res_len);
ERR_P(StrGet(buf, NULL, COC_SEP));
} else {
str_ncat(res, a->cmd, res_len);
str_ncat(res, "=", res_len);
if (a->type==ARG_INT) {
ERR_I(StrGetInt(buf, (int *)a->adr, COC_SEP));
} else if (a->type==ARG_FLT) {
ERR_I(StrGetFloat(buf, (float *)a->adr, COC_SEP));
} else if (a->type==ARG_ARR) {
ERR_I(StrGetArray(buf, (float *)a->adr, a->size));
resp="<array>";
} else if (a->type==ARG_CHAR) {
ERR_P(StrNGet(buf, (char *)a->adr, a->size, COC_SEP));
} else {
ERR_MSG("unknown type");
}
a->type=0; /* done */
}
}
str_ncat(res, resp, res_len);
str_ncat(res, "\n", res_len);
}
}
i++; a++;
}
ERR_I(StrGetEnd(buf));
if (pending==0) return(iret); /* no more delayed response awaiting */
ERR_I(CocRecv(conn->fd, buf, COC_RESTMO, NULL));
}
return(iret);
OnError: return(-1);
}
static int interrupt, logFd=0;
void CocIntSignal(int sig) {
char buf[4];
int siz;
if (logFd) {
interrupt=2;
buf[0]=COC_CLRLOG;
buf[1]=COC_SEP; /* send "clear log" message */
printf("\nCLRLOG\n");
CocSend(logFd, buf, 2);
} else {
printf("\ninterrupt\n");
interrupt=1;
}
}
int CocWatchLog(CocConn *conn, char *loglist) {
char cbuf[1024];
StrBuf *buf;
int fd, iret;
int i, siz, n;
sys_ctrl_init();
fd=conn->fd;
CocReset(conn);
ERR_I(CocPutStr(conn, "loglist", loglist));
ERR_I(CocDoIt(conn, cbuf, sizeof(cbuf)));
buf=&conn->resbuf;
interrupt=0;
logFd=fd;
signal(SIGINT, CocIntSignal);
while (!interrupt) {
logFd=fd;
ERR_I(iret=CocRecv(fd, buf, 60, &logFd));
if (iret>0) {
if (buf->buf[0]==COC_CLRLOG) { /* confirmed "clear log" message */
interrupt=3;
} else { /* it's a log message */
StrReset(buf);
while (!StrEnd(buf)) {
ERR_P(StrGet(buf, cbuf, COC_SEP));
printf("%s", cbuf);
}
}
}
}
signal(SIGINT, SIG_DFL);
logFd=0;
if (interrupt==1) { /* send "clear log" message */
cbuf[0]=COC_CLRLOG;
cbuf[1]=COC_SEP; /* send "clear log" message */
printf("\nclrlog\n");
CocSend(fd, cbuf, 2);
do {
ERR_I(CocRecv(fd, buf, 10, NULL));
} while (buf->buf[0]!=COC_CLRLOG);
}
return(0);
OnError:
signal(SIGINT, SIG_DFL);
return(-1);
}
int CocShowLog(CocConn *conn, char *loglist, int start, int lines) {
char cbuf[1024];
StrBuf *buf;
int fd, iret;
int i, siz, n;
fd=conn->fd;
CocReset(conn);
if (start>0) {
ERR_I(CocPutInt(conn, "logstart", start));
}
ERR_I(CocPutInt(conn, "loglines", lines));
ERR_I(CocPutStr(conn, "logshow", loglist));
ERR_I(CocDoIt(conn, cbuf, sizeof(cbuf)));
buf=&conn->resbuf;
while (1) {
ERR_I(iret=CocRecv(fd, buf, 10, NULL));
if (iret<=0) break;
if (buf->buf[0]==COC_CLRLOG) { /* end of log message */
break;
} else { /* it's a log message */
StrReset(buf);
while (!StrEnd(buf)) {
ERR_P(StrGet(buf, cbuf, COC_SEP));
printf("%s", cbuf);
}
}
}
return(0);
OnError:
signal(SIGINT, SIG_DFL);
return(-1);
}
void CocCloseClient(CocConn *conn) {
assert(conn!=NULL);
close(conn->fd);
conn->fd=-1;
}
/*-------------------------------------------------------------------------*/
static char rbuf[256];
int CocSet(CocConn *conn, const char *name, const char *value) {
int iret;
assert(conn!=NULL);
CocReset(conn);
ERR_I(CocPutStr(conn, name, value));
ERR_I(iret=CocDoIt(conn, rbuf, sizeof(rbuf)));
if (iret) ERR_MSG(rbuf);
return(0);
OnError: return(-1);
}
int CocGetN(CocConn *conn, const char *name, char *value, int reslen) {
int iret;
assert(conn!=NULL);
CocReset(conn);
ERR_I(CocGetStr(conn, name, value, reslen));
ERR_I(iret=CocDoIt(conn, rbuf, sizeof(rbuf)));
if (iret) ERR_MSG(rbuf);
return(0);
OnError: return(-1);
}
int CocSetGetN(CocConn *conn, const char *name, const char *cmd, char *value, int reslen) {
int iret;
assert(conn!=NULL);
CocReset(conn);
ERR_I(CocPutStr(conn, name, cmd));
ERR_I(CocDoIt(conn, value, reslen));
return(0);
OnError: return(-1);
}