#include #include #include #include #include #include #include #include #include #include #include "tlmpnet.h" #include "tlmpnet.m" #include "../tlmplib/tlmplib.h" struct TCPCONNECT_PRIVATE{ _F_TCPCONNECT *c; TCPCONNECT_INFO info; int fd; bool end; SSTRING buf; int time_out; int time_out_usec; bool rawmode; bool never_connected; bool fd_was_open; // Was passed to the constructor void initdef(_F_TCPCONNECT &_c){ never_connected = true; c = &_c; info.host = NULL; info.port = NULL; info.bindaddr = NULL; end = false; fd = -1; fd_was_open = false; time_out = 200; time_out_usec = 0; rawmode = false; } TCPCONNECT_PRIVATE(_F_TCPCONNECT &_c, const char *bindaddr, const char *host, const char *port) { initdef(_c); if (bindaddr != NULL) info.bindaddr = strdup(bindaddr); if (host != NULL) info.host = strdup(host); if (port != NULL) info.port = strdup(port); } TCPCONNECT_PRIVATE(_F_TCPCONNECT &_c, int handle) { initdef(_c); fd = handle; fd_was_open = true; } ~TCPCONNECT_PRIVATE() { free ((char*)info.bindaddr); free ((char*)info.host); free ((char*)info.port); } void setinfo (const char *newhost, const char *newport) { free ((char*)info.host); free ((char*)info.port); info.host = strdup(newhost); info.port = strdup(newport); } }; void _F_TCPCONNECT::fail(TCPCONNECT_INFO &info) { tlmp_error (MSG_U(E_CONNECTFAIL,"Connection failed to server %s port %s\n") ,info.host,info.port); } /* Set the line mode or raw (binary) mode. Return the current mode. */ bool _F_TCPCONNECT::setrawmode (bool mode) { bool ret = priv->rawmode; priv->rawmode = mode; return ret; } /* Set the line mode or raw (binary) mode. Return the current mode. */ PUBLIC bool TCPCONNECT::setrawmode(bool mode) { bool ret = priv->rawmode; priv->rawmode = mode; return ret; } int _F_TCPCONNECT::send(const char *line) { return send (line,strlen(line)); } int _F_TCPCONNECT::send(const void *buf, int len) { int ret = write (priv->fd,buf,len); if (ret == -1 && errno == EPIPE && !priv->end){ end(priv->info); priv->end = true; } return ret; } int _F_TCPCONNECT::sendf(const char *ctl, ...) { va_list list; va_start (list,ctl); char buf[10000]; int n = vsnprintf (buf,sizeof(buf)-1,ctl,list); int ret = send (buf,n); va_end (list); return ret; } void _F_TCPCONNECT::set_timeout (int seconds) { priv->time_out = seconds; priv->time_out_usec = 0; } void _F_TCPCONNECT::set_timeout (int seconds, int usec) { priv->time_out = seconds; priv->time_out_usec = usec; } void _F_TCPCONNECT::settcpnodelay(bool on) { int opt = on ? 1 : 0; setsockopt (priv->fd,SOL_TCP,TCP_NODELAY,&opt,sizeof(opt)); } PUBLIC void TCPCONNECT::settcpnodelay(bool on) { int opt = on ? 1 : 0; setsockopt (priv->fd,SOL_TCP,TCP_NODELAY,&opt,sizeof(opt)); } PUBLIC int TCPCONNECT::send(const char *line) { return send (line,strlen(line)); } PUBLIC int TCPCONNECT::send(const void *buf, int len) { return priv->c->send (buf,len); } PUBLIC int TCPCONNECT::sendf(const char *ctl, ...) { va_list list; va_start (list,ctl); char buf[10000]; int n = vsnprintf (buf,sizeof(buf)-1,ctl,list); int ret = send (buf,n); va_end (list); return ret; } void _F_TCPCONNECT::init(bool &, TCPCONNECT_INFO &) { } void _F_TCPCONNECT::time_out(bool &, TCPCONNECT_INFO &) { } /* Is called when the connection ended (probably by the server) */ void _F_TCPCONNECT::end(TCPCONNECT_INFO &info) { tlmp_error ("tcpconnect: Lost connection %s:%s\n",info.host,info.port); } PUBLIC TCPCONNECT::TCPCONNECT ( _F_TCPCONNECT &c, const char *bindaddr, const char *host, const char *port) { popen_initsignal(); priv = new TCPCONNECT_PRIVATE(c,bindaddr,host,port); c.priv = priv; } PUBLIC TCPCONNECT::TCPCONNECT ( _F_TCPCONNECT &c, const char *host, const char *port) { popen_initsignal(); priv = new TCPCONNECT_PRIVATE(c,NULL,host,port); c.priv = priv; } /* Used for already connected socket */ PUBLIC TCPCONNECT::TCPCONNECT(_F_TCPCONNECT &c, int handle) { popen_initsignal(); priv = new TCPCONNECT_PRIVATE (c,handle); c.priv = priv; } /* Return true if the connection is done */ PUBLIC bool TCPCONNECT::is_ok() { return priv->fd != -1; } PUBLIC VIRTUAL TCPCONNECT::~TCPCONNECT () { close(); delete priv; } PUBLIC int TCPCONNECT::close() { int ret = -1; if (priv->fd != -1 && !priv->fd_was_open){ ret = ::close (priv->fd); priv->fd = -1; priv->c->end (priv->info); } return ret; } /* Reconnect to the same host port. If it is already connected, the connection is closed first. Return -1 if any error. */ PUBLIC int TCPCONNECT::reconnect() { int ret = -1; if (priv->info.host != NULL){ if (is_ok()){ close (); } ret = connect(); } return ret; } /* Establish the connection if not already connected. Return -1 if any error. */ PUBLIC int TCPCONNECT::connect() { int ret = -1; priv->never_connected = false; if (priv->fd != -1){ ret = 0; }else if (priv->info.host != NULL){ priv->fd = cmdsock_connect (priv->info.bindaddr,priv->info.host,priv->info.port ,priv->time_out,1); priv->info.handle = priv->fd; if (priv->fd == -1){ priv->c->fail (priv->info); priv->end = true; }else{ priv->end = false; priv->c->init (priv->end,priv->info); ret = 0; } } return ret; } PRIVATE void TCPCONNECT::first_connect() { if (priv->never_connected){ connect(); } } /* Reconnect to the another host/port. If it is already connected, the connection is closed first. Return -1 if any error. */ PUBLIC int TCPCONNECT::reconnect(const char *newhost, const char *newport) { priv->setinfo (newhost,newport); return reconnect(); } /* Insert the proper handle in the fd_set so a global select may be done. */ PUBLIC int TCPCONNECT::setup_select (fd_set &set, int max_handle) { first_connect(); int fd = priv->fd; if (fd != -1){ max_handle = max_handle > fd ? max_handle : fd; FD_SET (fd,&set); } return max_handle; } PRIVATE void TCPCONNECT::process_lines() { char buf[32*1024+1]; int len = read (priv->fd,buf,sizeof(buf)-1); // fprintf (stderr,"TCPCONNECT process_lines len=%d rawmode=%d\n",len,priv->rawmode); if (len <= 0){ close (); priv->end = true; }else if (priv->rawmode){ priv->info.linelen = len; priv->c->oneline (buf,priv->end,priv->info); }else{ buf[len] = '\0'; SSTRING *s = &priv->buf; s->append (buf); const char *pt = s->get(); while (*pt != '\0'){ const char *start = pt; while (*pt != '\0' && *pt != '\n') pt++; if (*pt == '\n'){ len = (int)(pt-start); char line[len+1]; strncpy (line,start,len); line[len] = '\0'; priv->info.linelen = len; priv->c->oneline (line,priv->end,priv->info); if (priv->end){ close (); break; } pt++; }else{ pt = start; break; } } s->setfrom (pt); } } /* Process the work in the fd_set. */ PUBLIC void TCPCONNECT::process_select ( int select_ret, fd_set &set, long timeout) { if (priv->fd != -1){ _F_TCPCONNECT *c = priv->c; if (select_ret == 0){ c->time_out (priv->end,priv->info); }else if (select_ret > 0 && FD_ISSET(priv->fd,&set)){ process_lines(); } } } PUBLIC int TCPCONNECT::loop(int time_out) { int ret = -1; priv->time_out = time_out; priv->time_out_usec = 0; first_connect(); if (is_ok()){ ret = 0; while (!priv->end){ fd_set set; FD_ZERO (&set); int maxfd = setup_select (set,0); struct timeval timeo; timeo.tv_sec = priv->time_out; timeo.tv_usec = priv->time_out_usec; int ok = select (maxfd+1,&set,NULL,NULL,&timeo); if (ok == -1){ if (errno != EINTR) break; }else{ process_select (ok,set,priv->time_out); } } } return ret; } /* Allow the object to process inputs. Return immediatly: 0 if ok, -1 if any error (end of connection) */ PUBLIC int TCPCONNECT::poll() { int ret = -1; first_connect(); if (is_ok() && !priv->end){ ret = 0; fd_set set; FD_ZERO (&set); int maxfd = setup_select (set,0); struct timeval timeo; timeo.tv_sec = 0; timeo.tv_usec = 0; int ok = select (maxfd+1,&set,NULL,NULL,&timeo); if (ok == -1){ if (errno != EINTR) ret = -1; }else if (ok > 0){ process_select (ok,set,0); } } return ret; } /* Set the time_out to use for the next network operation */ PUBLIC void TCPCONNECT::set_timeout(int seconds) { priv->time_out = seconds; priv->time_out_usec = 0; } struct tcpconnect_private{ const char *host; const char *port; int fd; _F_TCPCONNECT *conn; bool rawmode; tcpconnect_private(){ host = NULL; port = NULL; rawmode = false; } }; void _F_tcpconnect::fail(TCPCONNECT_INFO &info) { tlmp_error (MSG_R(E_CONNECTFAIL),info.host,info.port); } void _F_tcpconnect::settcpnodelay(bool on) { priv->conn->settcpnodelay (on); } /* Set the line mode or raw (binary) mode. Return the current mode. */ bool _F_tcpconnect::setrawmode(bool mode) { return priv->conn->setrawmode (mode); //bool ret = priv->rawmode; //priv->rawmode = mode; //return ret; } void _F_tcpconnect::set_timeout(int seconds) { priv->conn->set_timeout (seconds); } void _F_tcpconnect::set_timeout(int seconds, int usec) { priv->conn->set_timeout (seconds,usec); } int _F_tcpconnect::send(const char *line) { return send (line,strlen(line)); } int _F_tcpconnect::send(const void *buf, int len) { int ret = priv->conn->send (buf,len); return ret; } void _F_tcpconnect::init(bool &, TCPCONNECT_INFO &) { } void _F_tcpconnect::time_out(bool &, TCPCONNECT_INFO &) { } /* Is called when the connection ended (probably by the server) */ void _F_tcpconnect::end(TCPCONNECT_INFO &info) { tlmp_error("tcpconnect: Lost connection %s:%s\n",info.host,info.port); } int _F_tcpconnect::sendf(const char *ctl, ...) { va_list list; va_start (list,ctl); char buf[10000]; int n = vsnprintf (buf,sizeof(buf)-1,ctl,list); int ret = send (buf,n); va_end (list); return ret; } int tcpconnect ( _F_tcpconnect &c, const char *bindaddr, const char *host, // Hostname or unix: for unix domain socket const char *port, // tcp port or unix domain socket path int time_out) { glocal _F_tcpconnect *c = &c; tcpconnect_private priv; priv.host = host; priv.port = port; c.priv = &priv; priv.conn = NULL; (bindaddr,host,port); glocal.c->fail (info); glocal.c->priv->conn = this; glocal.c->init (end,info); glocal.c->time_out (end,info); glocal.c->end (info); glocal.c->oneline (line,end,info); return conn.loop(time_out); } int tcpconnect ( _F_tcpconnect &c, const char *host, // Hostname or unix: for unix domain socket const char *port, // tcp port or unix domain socket path int time_out) { return tcpconnect (c,NULL,host,port,time_out); } /* Process an already connected socket */ int tcpconnect ( _F_tcpconnect &c, int handle, int time_out) { glocal _F_tcpconnect *c = &c; tcpconnect_private priv; c.priv = &priv; priv.conn = NULL; (handle); glocal.c->fail (info); glocal.c->priv->conn = this; glocal.c->init (end,info); glocal.c->time_out (end,info); glocal.c->end (info); glocal.c->oneline (line,end,info); return conn.loop(time_out); }