Files
seaclient/sicsconn.cpp
Markus Zolliker bc33026b2c start sea server even remotely over ssh
this works on neutron instruments (account equal host)
and on linse-c
2025-06-10 16:39:49 +02:00

607 lines
14 KiB
C++

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/select.h>
#include <string.h>
#include "sicsconn.h"
#include "instr_hosts.h"
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <signal.h>
int printit(void);
int uselect(int nfds,
fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
struct timeval *timeout)
{
sigset_t sigmask;
struct timespec tmo, *tmoPtr;
int result;
sigfillset(&sigmask);
if (timeout) {
tmo.tv_sec = timeout->tv_sec;
tmo.tv_nsec = timeout->tv_usec * 1000;
tmoPtr = &tmo;
} else {
tmoPtr = NULL;
}
result = pselect(nfds, readfds, writefds, exceptfds, tmoPtr, &sigmask);
return result;
}
static int sigpipe_ignored = 0;
int SicsSockAdr(struct sockaddr_in *sockaddr, const char *hostport,
int defaultPort, const char *server) {
// create sockadr from hostport (hostname and port number separated with colon)
// hostport may also be an instrument name, in this case server has to be defined
struct hostent *hostent; /* Host database entry */
struct in_addr addr; /* For 64/32 bit madness */
char *p;
char host[128], instr[32];
int port;
int l;
(void) memset((char *) sockaddr, '\0', sizeof(struct sockaddr_in));
if (hostport == NULL) hostport="";
p = strchr((char *)hostport, ':');
if (p != NULL) {
l = p - hostport;
port = atoi(p+1);
} else {
InstrHost((char *)server, (char *)hostport, instr, sizeof instr,
host, sizeof host, &port);
if (port == 0) {
port = atoi(hostport);
if (port != 0) {
l = 0;
} else {
l = strlen(hostport);
}
} else {
l = -1;
}
}
if (l>= 0) {
if (l >= (int)sizeof host) return -1;
strncpy(host, hostport, l);
host[l]='\0';
}
if (port == 0) port = defaultPort;
if (port == 0) return -1;
sockaddr->sin_family = AF_INET;
sockaddr->sin_port = htons((unsigned short) (port & 0xFFFF));
if (host[0]=='\0') {
addr.s_addr = INADDR_ANY;
} else {
hostent = gethostbyname(host);
if (hostent != NULL) {
memcpy((char *) &addr,
(char *) hostent->h_addr_list[0], (size_t) hostent->h_length);
} else {
addr.s_addr = inet_addr(host);
if (addr.s_addr == (unsigned long)-1) {
return -1; /* error */
}
}
}
/*
* There is a rumor that this assignment may require care on
* some 64 bit machines.
*/
sockaddr->sin_addr.s_addr = addr.s_addr;
return 0;
}
int SicsConnect(const char *hostport, const char *server, bool async) {
struct sockaddr_in sadr;
int iret, fd;
int oldopts;
iret = SicsSockAdr(&sadr, hostport, 8641, server);
if (iret < 0) return -1;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) return -1;
if (async) {
oldopts = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, oldopts | O_NONBLOCK);
}
iret = connect(fd, (struct sockaddr *)&sadr, sizeof(sadr));
if (iret < 0) {
switch (errno) {
case EINPROGRESS:
case EALREADY:
case EISCONN:
return fd;
}
return -1;
}
return fd;
}
int SicsConnectSuccess(int fd)
{
// returns 1 on success, 0 when pending, -1 on error
fd_set wmask, rmask;
struct timeval tmo = { 0, 0 };
int oldopts;
int ret;
oldopts = fcntl(fd, F_GETFL, 0);
assert(oldopts | O_NONBLOCK); /* fd must be in non-blocking mode */
FD_ZERO(&wmask);
FD_ZERO(&rmask);
FD_SET(fd, &wmask);
FD_SET(fd, &rmask);
ret = uselect(fd + 1, &rmask, &wmask, NULL, &tmo);
if (ret > 0) {
if (FD_ISSET(fd, &rmask)) { /* there may already be data for read */
if (recv(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */
ret = -1; /* first recv failed */
}
} else {
if (FD_ISSET(fd,&wmask)) {
if (send(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */
ret = -2; /* first send failed */
}
}
}
}
fcntl(fd, F_SETFL, oldopts & ~O_NONBLOCK); /* reset to blocking mode */
return ret;
}
SicsConnection::SicsConnection(QObject *parent) : QObject(parent) {
hostport = NULL;
userpass = NULL;
server = NULL;
state = disconnected;
bufline = "";
bufstate = buf_got_line;
connect_state = connect_start;
tries_wait_start = 0;
command2send = "";
tmo = 0;
if (!sigpipe_ignored) {
signal (SIGPIPE, SIG_IGN);
sigpipe_ignored = 1;
}
rdbuffer = "";
progressPos = 0;
rdpos = 0;
debug = 0;
}
//SicsConnection::SicsConnection(SicsConnection *copy) {
// hostport = strdup(copy->hostport);
// userpass = NULL;
// state = disconnected;
//}
SicsConnection::~SicsConnection() {
if (state != disconnected) {
close(fd);
fd = -1;
}
if (hostport) free(hostport);
if (userpass) free(userpass);
if (server) free(server);
}
void SicsConnection::setHost(const char *hostport, const char *server) {
if (this->hostport != NULL) free(this->hostport);
if (hostport != NULL) {
this->hostport = strdup(hostport);
} else {
this->hostport = NULL;
}
if (this->server != NULL) free(this->server);
this->server = strdup(server);
}
void SicsConnection::setUser(const char *userpass) {
if (this->userpass != NULL) free(this->userpass);
if (userpass != NULL) {
this->userpass = strdup(userpass);
} else {
this->userpass = NULL;
}
}
int SicsConnection::handleBuffer(void) {
return handleBuffer(0);
}
int SicsConnection::handleBuffer(int tmo) {
fd_set mask;
struct timeval tmov;
int iret = 1;
int pos;
char ch;
char line[128];
switch (connect_state) {
case connect_start:
fd = SicsConnect(hostport, server, true);
if (fd < 0) {
connect_state = connect_error; // what else?
return -1;
}
connect_state = connect_wait;
return 0;
case connect_wait:
iret = SicsConnectSuccess(fd);
if (iret <= 0) {
if (startServer) {
int isys = system(startServer);
tries_wait_start = 3;
if (isys) {
printf("can not connect to sea and failed to start the sea server\n");
printf("\n");
pos = strcspn(hostport, ":");
printf("please execute 'sea start' from the instrument account of %.*s\n", pos, hostport);
printf("\n");
exit(1);
}
}
startServer = NULL;
if (iret == 0) return iret; /* connection pending */
if (tries_wait_start > 0) {
tries_wait_start--;
connect_state = connect_start;
}
return iret;
}
connect_state = connect_login;
return 0;
case connect_login:
if (userpass == NULL) {
snprintf(line, sizeof line, "Spy 007\n");
} else {
snprintf(line, sizeof line, "%s", userpass);
}
bufstate = buf_sent_line;
send(fd, line, strlen(line), 0);
connect_state = connect_waitlogin;
// if (strcmp(server, "sea") == 0 && userpass != NULL) {
// printf("*** send login (%s %s)\n", server, line);
// }
return 0;
case connect_error:
return -2;
default: break;
}
FD_ZERO(&mask);
while (bufstate > buf_got_finished) {
do {
FD_SET(fd, &mask);
tmov.tv_sec = tmo;
tmov.tv_usec = 0;
iret = select(fd+1, &mask, NULL, NULL, &tmov);
if (iret < 0) {
perror("select");
close(fd);
fd = -1;
connect_state = connect_error;
state = disconnected;
return -2;
} else if (iret == 0) {
return -1; /* no more data */
}
iret = recv(fd, &ch, 1, 0);
if (iret <= 0) {
close(fd);
fd = -1;
connect_state = connect_error;
state = disconnected;
if (printit()) printf("zero on receive -> disconnected\n");
// printf("*** disconnected (zero on receive)\n");
return -2;
}
bufline.append(ch);
} while (ch != '\n');
iret = 0;
rdbuffer += bufline;
if (debug) {
// printf("%s bufline{%s}\n", debug, bufline.latin1());
}
if ((int)rdbuffer.length() >= progressPos + 1000) {
progress(rdbuffer.length() - progressPos);
progressPos = rdbuffer.length();
}
// if (strcmp(server, "sea") == 0 && userpass != NULL) {
// printf("*** bufline %s", bufline.latin1());
// }
if (bufstate == buf_got_start && bufline.startsWith("TRANSACTIONFINISHED")) {
bufstate = buf_got_finished;
iret = 1;
}
if (bufstate == buf_sent_transact && bufline.startsWith("TRANSACTIONSTART")) {
bufstate = buf_got_start;
}
if (bufstate == buf_sent_line) {
bufstate = buf_got_line;
if (connect_state == connect_waitlogin) {
if (debug) {
printf("connectlogin %s\n", bufline.latin1());
}
if (bufline.startsWith("Login OK\n")) {
connect_state = connect_done;
state = connected;
if (command2send != "") {
sendThis(command2send.latin1());
command2send = "";
if (state == disconnected) {
return -1;
}
bufstate = buf_sent_transact;
}
} else {
bufstate = buf_sent_line;
}
} else {
iret = 1;
}
}
bufline = "";
}
return iret;
}
int SicsConnection::getLine(QString &line) {
QString qline;
int pos, len;
if (state == disconnected) {
return -1;
}
handleBuffer(0);
if (rdpos >= (int)rdbuffer.length() && tmo > 0) {
if (handleBuffer(tmo) < 0) {
printf("timeout %d\n", tmo);
}
}
if (rdpos < (long)rdbuffer.length()) {
pos = rdbuffer.find('\n', rdpos);
if (pos < rdpos) { // lf not found, return up to end of buffer (is this correct?)
return 0; /***/
len = rdbuffer.length() - rdpos;
line = rdbuffer.mid(rdpos);
progressPos -= rdbuffer.length();
rdbuffer="";
rdpos = 0;
} else {
len = pos - rdpos;
line = rdbuffer.mid(rdpos,len);
rdpos = pos + 1;
//rdbuffer.remove(0, pos + 1);
}
if (debug) {
//printf("%s getLine{%s}{%s}\n", debug, line.latin1(), rdbuffer.mid(rdpos,9).latin1());
printf("%s getLine{%s}\n", debug, line.latin1());
}
if (line.startsWith("TRANSACTIONFINISHED")) {
if (state == replying) {
state = connected;
return 0;
}
}
return len;
} else {
if (debug) {
// printf("%s error\n", debug);
}
return -1;
}
return 0;
}
void SicsConnection::swallow() {
QString line;
bool done;
if (debug) {
printf("%s swallow start\n", debug);
}
while (state == replying) {
if (getLine(line) > 0) {
done = false;
handle(line, &done);
if (!done) {
//printf("*** swallow {%s}\n", line);
}
}
}
if (debug) {
printf("%s swallow end\n", debug);
}
}
int SicsConnection::sendThis(const char *str) {
int l, iret;
if (state != disconnected) {
l = strlen(str);
// if (strcmp(server, "sea") == 0 && userpass != NULL) {
// printf("*** send {%s}\n", str);
// }
iret = send(fd, str, l, 0);
if (iret != l) {
printf("sent %d of %d, disconnect\n", iret, l);
close(fd);
fd = -1;
state = disconnected;
return -1;
}
return 1;
}
return -1;
}
void SicsConnection::reconnect(void) {
if (state != disconnected) {
close(fd);
fd = -1;
state = disconnected;
}
connect_state = connect_start;
command2send.sprintf("fulltransact config listen 1\n");
}
void SicsConnection::disconnect(void) {
close(fd);
fd = -1;
state = disconnected;
}
int SicsConnection::sendCommand(const char *cmd) {
char *nl;
if (debug) {
printf("%s swallow before %s\n", debug, cmd);
}
swallow();
if (debug) {
printf("%s sendCommand %s\n", debug, cmd);
}
nl = strrchr((char *)cmd, '\n'); /* check if we have to add a final line break */
if (nl == NULL || nl[1] != '\0') {
command2send.sprintf("fulltransact %s\n", cmd);
} else {
command2send.sprintf("fulltransact %s", cmd);
}
if (state == disconnected) {
// printf("*** reconnect!\n");
connect_state = connect_start;
} else {
sendThis(command2send.latin1());
bufstate = buf_sent_transact;
command2send = "";
if (state == disconnected) {
return -1;
}
}
return 1;
}
int SicsConnection::getResponse() { // get BEGINNING of response
int iret;
QString line;
state = connected;
tmo = 10;
while (1) {
iret = getLine(line);
if (iret <= 0) {
if (iret == -2) {
if (printit()) printf("timeout on sea command\n");
}
//printf("error in getLine %d\n", iret);
if (debug) {
printf("%s error in getResponse {%s}\n", debug, rdbuffer.latin1() + rdpos);
}
return iret;
}
if (line.startsWith("TRANSACTIONSTART")) break;
if (debug) {
printf("%s getResponse handle %s\n", debug, line.latin1());
}
handle(line.latin1());
};
state = replying;
if (debug) {
printf("%s gotResponse\n", debug);
}
return 1;
}
int SicsConnection::command(const char *cmd) {
int iret;
int cnt, tries;
tries = 3;
do {
cnt = 5;
while (cnt > 0 && (connect_state != connect_done || bufstate > buf_got_finished)) {
handleBuffer(1);
cnt--;
}
iret = sendCommand(cmd);
handleBuffer(5);
iret = getResponse();
tries--;
} while (iret < 0 && tries > 0);
return iret;
}
int SicsConnection::handleMessages(int tmoArg) {
int iret;
QString line;
bool bs=false;
tmo = tmoArg;
if (bufstate <= buf_got_finished) {
bufstate = buf_sent_line;
bs = true;
}
iret = getLine(line);
tmo = 0;
while (iret >= 0) {
if (iret > 0) {
handle(line.latin1());
}
iret = getLine(line);
}
if (bs && bufstate == buf_sent_line) {
bufstate = buf_got_line;
}
return iret;
}
#ifdef TESTSC
#include <stdio.h>
int main(void) {
SicsConnection *sc;
char line[256];
char *lin;
int iret;
sc = SicsNewConnection("lnsl15", 13006);
if (sc == NULL) return -1;
while (1) {
printf("> ");
lin = fgets(line, sizeof line, stdin);
if (lin == NULL) return 1;
iret = SicsCommand(sc, line);
if (iret < 0) return 2;
while (1) {
iret = getLine(line);
if (iret == 0) break;
if (iret < 0) return 3;
if (iret > 0) {
printf("(%s)\n", line.latin1());
}
}
}
}
#endif