#include #include #include #include #include #include #include #include #include #include "sicsconn.h" #include "instr_hosts.h" #include #include #include #include #include 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 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