diff --git a/src/zm_comms.cpp b/src/zm_comms.cpp new file mode 100644 index 000000000..f5fb040a4 --- /dev/null +++ b/src/zm_comms.cpp @@ -0,0 +1,595 @@ +// +// ZoneMinder Communications Class Implementation, $Date$, $Revision$ +// Copyright (C) 2003 Philip Coombes +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zm.h" +#include "zm_db.h" +#include "zm_comms.h" + +bool CommsBase::Terminate() +{ + if ( IsOpen() ) + { + Close(); + } + return( true ); +} + +bool CommsBase::Reopen() +{ + if ( !Close() ) + return( false ); + if ( !Open() ) + return( false ); + + return( true ); +} + +int CommsBase::ReadV( int iovcnt, /* const void *, int, */ ... ) +{ + va_list arg_ptr; + //struct iovec iov[iovcnt]; + struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); + + va_start( arg_ptr, iovcnt ); + for ( int i = 0; i < iovcnt; i++ ) + { + iov[i].iov_base = va_arg( arg_ptr, void * ); + iov[i].iov_len = va_arg( arg_ptr, int ); + } + va_end( arg_ptr ); + + return( ::readv( rd, iov, iovcnt ) ); +} + +int CommsBase::WriteV( int iovcnt, /* const void *, int, */ ... ) +{ + va_list arg_ptr; + //struct iovec iov[iovcnt]; + struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); + + va_start( arg_ptr, iovcnt ); + for ( int i = 0; i < iovcnt; i++ ) + { + iov[i].iov_base = va_arg( arg_ptr, void * ); + iov[i].iov_len = va_arg( arg_ptr, int ); + } + va_end( arg_ptr ); + + return( ::writev( wd, iov, iovcnt ) ); +} + +bool Pipe::Open() +{ + if ( pipe( fd ) < 0 ) + { + Error(( "pipe(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + + return( true ); +} + +bool Pipe::Close() +{ + if ( fd[0] > -1 ) close( fd[0] ); + fd[0] = -1; + if ( fd[1] > -1 ) close( fd[1] ); + fd[1] = -1; + return( true ); +} + +bool Pipe::SetBlocking( bool blocking ) +{ + int flags; + + /* Now set it for non-blocking I/O */ + if ( (flags = fcntl( fd[1], F_GETFL )) < 0 ) + { + Error(( "fcntl(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + if ( blocking ) + { + flags &= ~O_NONBLOCK; + } + else + { + flags |= O_NONBLOCK; + } + if ( fcntl( fd[1], F_SETFL, flags ) < 0 ) + { + Error(( "fcntl(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + + return( true ); +} + +bool SocketBase::Socket() +{ + if ( (sd = ::socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) + { + Error(( "socket(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + + int val = 1; + + (void)::setsockopt( sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); + (void)::setsockopt( sd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); + + return( true ); +} + +bool SocketBase::Close() +{ + if ( sd > -1 ) close( sd ); + sd = -1; + return( true ); +} + +int SocketBase::BytesToRead() const +{ + int bytes_to_read = 0; + + if ( ioctl( sd, FIONREAD, &bytes_to_read ) < 0 ) + { + Error(( "ioctl(), errno = %d, error = %s", errno, strerror(errno) )); + return( -1 ); + } + return( bytes_to_read ); +} + +bool SocketBase::GetBlocking( bool &blocking ) +{ + int flags; + + if ( (flags = fcntl( sd, F_GETFL )) < 0 ) + { + Error(( "fcntl(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + blocking = (flags & O_NONBLOCK); + return( true ); +} + +bool SocketBase::SetBlocking( bool blocking ) +{ +#if 0 + // ioctl is apparently not recommended + int ioctl_arg = !blocking; + if ( ioctl( sd, FIONBIO, &ioctl_arg ) < 0 ) + { + Error(( "ioctl(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + return( true ); +#endif + + int flags; + + /* Now set it for non-blocking I/O */ + if ( (flags = fcntl( sd, F_GETFL )) < 0 ) + { + Error(( "fcntl(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + if ( blocking ) + { + flags &= ~O_NONBLOCK; + } + else + { + flags |= O_NONBLOCK; + } + if ( fcntl( sd, F_SETFL, flags ) < 0 ) + { + Error(( "fcntl(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + + return( true ); +} + +bool SocketBase::GetSendBufferSize( int &buffersize ) const +{ + socklen_t optlen = sizeof(buffersize); + if ( getsockopt( sd, SOL_SOCKET, SO_SNDBUF, &buffersize, &optlen ) < 0 ) + { + Error(( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( -1 ); + } + return( buffersize ); +} + +bool SocketBase::GetRecvBufferSize( int &buffersize ) const +{ + socklen_t optlen = sizeof(buffersize); + if ( getsockopt( sd, SOL_SOCKET, SO_RCVBUF, &buffersize, &optlen ) < 0 ) + { + Error(( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( -1 ); + } + return( buffersize ); +} + +bool SocketBase::SetSendBufferSize( int buffersize ) +{ + if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) + { + Error(( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + return( true ); +} + +bool SocketBase::SetRecvBufferSize( int buffersize ) +{ + if ( setsockopt( sd, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) + { + Error(( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + return( true ); +} + +bool SocketBase::GetRouting( bool &route ) const +{ + int dontroute; + socklen_t optlen = sizeof(dontroute); + if ( getsockopt( sd, SOL_SOCKET, SO_DONTROUTE, &dontroute, &optlen ) < 0 ) + { + Error(( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + route = !dontroute; + return( true ); +} + +bool SocketBase::SetRouting( bool route ) +{ + int dontroute = !route; + if ( setsockopt( sd, SOL_SOCKET, SO_DONTROUTE, (char *)&dontroute, sizeof(dontroute)) < 0 ) + { + Error(( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + return( true ); +} + +bool SocketBase::GetNoDelay( bool &nodelay ) const +{ + int int_nodelay; + socklen_t optlen = sizeof(int_nodelay); + if ( getsockopt( sd, IPPROTO_TCP, TCP_NODELAY, &int_nodelay, &optlen ) < 0 ) + { + Error(( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + nodelay = int_nodelay; + return( true ); +} + +bool SocketBase::SetNoDelay( bool nodelay ) +{ + int int_nodelay = nodelay; + + if ( setsockopt( sd, IPPROTO_TCP, TCP_NODELAY, (char *)&int_nodelay, sizeof(int_nodelay)) < 0 ) + { + Error(( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + return( true ); +} + +bool SocketClient::SetupRemoteHost( const char *host ) +{ + + struct hostent *p_rem_host=0; + + if ( !(p_rem_host = ::gethostbyname( host ) ) ) + { + Error(( "gethostbyname( %s ), h_errno = %d", host, h_errno )); + return( false ); + } + + memcpy( &rem_host, p_rem_host, sizeof(rem_host) ); + + return( true ); +} + +bool SocketClient::SetupRemoteServ( const char *serv, const char *protocol ) +{ + struct servent *p_rem_serv=0; + + if ( !(p_rem_serv = ::getservbyname( serv, protocol ) ) ) + { + Error(( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) )); + return( false ); + } + + memcpy( &rem_serv, p_rem_serv, sizeof(rem_serv) ); + + return( true ); +} + +SocketClient::SocketClient() +{ + memset( &rem_host, 0, sizeof(rem_host) ); + memset( &rem_serv, 0, sizeof(rem_serv) ); +} + +bool SocketClient::Open() +{ + if ( !Socket() ) + return( false ); + + struct sockaddr_in rem_addr; + + memset( &rem_addr, 0, sizeof(rem_addr) ); + + rem_addr.sin_port = rem_serv.s_port; + rem_addr.sin_family = AF_INET; + rem_addr.sin_addr.s_addr = ((struct in_addr *)(rem_host.h_addr))->s_addr; + + if ( ::connect( sd, (struct sockaddr *)&rem_addr, sizeof(rem_addr) ) == -1 ) + { + Error(( "connect(), errno = %d, error = %s", errno, strerror(errno) )); + Close(); + return( false ); + } + + return( true ); +} + +bool UDPSocket::Socket() +{ + if ( (sd = ::socket( AF_INET, SOCK_DGRAM, 0 ) ) < 0 ) + { + Error(( "socket(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + + return( true ); +} + +bool UDPSocket::Initialise( const char *host, const char *service ) +{ + if ( !SetupRemoteHost( host ) ) + return( false ); + if ( !SetupRemoteServ( service, "udp" ) ) + return( false ); + + if ( !Terminate() ) + return( false ); + + return( true ); +} + +TCPClient::TCPClient() +{ + state = DISCONNECTED; +} + +bool TCPClient::Initialise( const char *host, const char *service ) +{ + state = DISCONNECTED; + + if ( !SetupRemoteHost( host ) ) + return( false ); + if ( !SetupRemoteServ( service, "tcp" ) ) + return( false ); + + if ( !Terminate() ) + return( false ); + + return( true ); +} + +bool TCPClient::Open() +{ + if ( !SocketClient::Open() ) + return( false ); + + state = CONNECTED; + + return( true ); +} + +bool TCPClient::Close() +{ + if ( !SocketClient::Close() ) + return( false ); + + state = DISCONNECTED; + + return( true ); +} + +bool TCPServer::SetupLocalHost() +{ + char host[MAXHOSTNAMELEN]; + + if ( ::gethostname( host, sizeof(host) ) == -1 ) + { + Error(( "gethostname(), errno = %d, error = %s", errno, strerror(errno) )); + return( false ); + } + + struct hostent *p_loc_host=0; + + if ( !(p_loc_host = ::gethostbyname( host ) ) ) + { + Error(( "gethostbyname( %s ), h_errno = %d", host, h_errno )); + return( false ); + } + + memcpy( &loc_host, p_loc_host, sizeof(loc_host) ); + + return( true ); +} + +bool TCPServer::SetupLocalServ( const char *serv ) +{ + struct servent *p_loc_serv=0; + + if ( !(p_loc_serv = ::getservbyname( serv, "tcp" ) ) ) + { + Error(( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) )); + return( false ); + } + + memcpy( &loc_serv, p_loc_serv, sizeof(loc_serv) ); + + return( true ); +} + +TCPServer::TCPServer() +{ + state = DISCONNECTED; + + memset( &loc_host, 0, sizeof(loc_host) ); + memset( &loc_serv, 0, sizeof(loc_serv) ); +} + +TCPServer::TCPServer( const TCPServer &server, int new_sd ) +{ + state = server.state; + + memcpy( &loc_host, &server.loc_host, sizeof(loc_host) ); + memcpy( &loc_serv, &server.loc_serv, sizeof(loc_serv) ); + + sd = new_sd; +} + +bool TCPServer::Initialise( const char *service ) +{ + state = DISCONNECTED; + + if ( !SetupLocalHost() ) + return( false ); + if ( !SetupLocalServ( service ) ) + return( false ); + + if ( !Terminate() ) + return( false ); + + return( true ); +} + +bool TCPServer::Open() +{ + if ( !Socket() ) + return( false ); + + struct sockaddr_in loc_addr; + + memset( &loc_addr, 0, sizeof(loc_addr) ); + + loc_addr.sin_port = loc_serv.s_port; + loc_addr.sin_family = AF_INET; + loc_addr.sin_addr.s_addr = INADDR_ANY; + + if ( ::bind( sd, (struct sockaddr *)&loc_addr, sizeof(loc_addr) ) == -1 ) + { + Error(( "bind(), errno = %d, error = %s", errno, strerror(errno) )); + Close(); + return( false ); + } + + if ( ::listen( sd, SOMAXCONN ) == -1 ) + { + Error(( "listen(), errno = %d, error = %s", errno, strerror(errno) )); + Close(); + return( false ); + } + + state = LISTENING; + + return( true ); +} + +bool TCPServer::Accept() +{ + struct sockaddr_in rem_addr; + socklen_t rem_addr_size = sizeof(rem_addr); + + memset( &rem_addr, 0, sizeof(rem_addr) ); + + int new_sd=-1; + + if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_size )) == -1 ) + { + Error(( "accept(), errno = %d, error = %s", errno, strerror(errno) )); + Close(); + return( false ); + } + + close( sd ); + + sd = new_sd; + + state = CONNECTED; + + return( true ); +} + +bool TCPServer::Accept( TCPServer *&server ) +{ + struct sockaddr_in rem_addr; + socklen_t rem_addr_size = sizeof(rem_addr); + + memset( &rem_addr, 0, sizeof(rem_addr) ); + + int new_sd=-1; + + if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_size )) == -1 ) + { + Error(( "connect(), errno = %d, error = %s", errno, strerror(errno) )); + Close(); + return( false ); + } + + server = new TCPServer( *this, new_sd ); + + return( true ); +} + +bool TCPServer::Close() +{ + if ( !SocketBase::Close() ) + return( false ); + + state = DISCONNECTED; + + return( true ); +} + diff --git a/src/zm_comms.h b/src/zm_comms.h new file mode 100644 index 000000000..e9c93f6c2 --- /dev/null +++ b/src/zm_comms.h @@ -0,0 +1,291 @@ +// +// ZoneMinder Communicatoions Class Interface, $Date$, $Revision$ +// Copyright (C) 2003 Philip Coombes +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// + +#ifndef ZM_COMMS_H +#define ZM_COMMS_H + +#include + +#include "zm.h" +#include "zm_debug.h" + +class CommsBase +{ +protected: + const int &rd; + const int &wd; + +protected: + CommsBase( int &p_rd, int &p_wd ) : rd( p_rd ), wd( p_wd ) + { + } + virtual ~CommsBase() + { + } + +protected: + virtual bool Terminate(); + + virtual bool Open()=0; + virtual bool Close()=0; + + virtual bool IsOpen() const=0; + + virtual bool SetBlocking( bool blocking )=0; + +public: + virtual bool Reopen(); + + virtual int Read( void *msg, int len ) + { + return( ::read( rd, msg, len ) ); + } + virtual int Write( const void *msg, int len ) + { + return( ::write( wd, msg, len ) ); + } + virtual int ReadV( const struct iovec *iov, int iovcnt ) + { + return( ::readv( rd, iov, iovcnt ) ); + } + virtual int WriteV( const struct iovec *iov, int iovcnt ) + { + return( ::writev( wd, iov, iovcnt ) ); + } + virtual int ReadV( int iovcnt, /* const void *msg1, int len1, */ ... ); + virtual int WriteV( int iovcnt, /* const void *msg1, int len1, */ ... ); +}; + +class Pipe : public CommsBase +{ +protected: + int fd[2]; + +public: + Pipe() : CommsBase( fd[0], fd[1] ) + { + fd[0] = -1; + fd[1] = -1; + } + ~Pipe() + { + Terminate(); + } + +public: + bool Open(); + bool Close(); + + bool IsOpen() const + { + return( fd[0] != -1 && fd[1] != -1 ); + } + int GetReadDesc() const + { + return( fd[0] ); + } + int GetWriteDesc() const + { + return( fd[1] ); + } + + bool SetBlocking( bool blocking ); +}; + +class SocketBase : public CommsBase +{ +protected: + int sd; + +protected: + SocketBase() : CommsBase( sd, sd ) + { + sd = -1; + } + ~SocketBase() + { + Terminate(); + } + + virtual bool Socket(); + +public: + virtual bool Open()=0; + virtual bool Close(); + + virtual int Send( const void *msg, int len ) + { + return( ::send( sd, msg, len, 0 ) ); + } + virtual int Recv( void *msg, int len ) + { + return( ::recv( sd, msg, len, 0 ) ); + } + int BytesToRead() const; + + virtual bool IsOpen() const + { + return( sd != -1 ); + } + int GetDesc() const + { + return( sd ); + } + + bool GetBlocking( bool &blocking ); + bool SetBlocking( bool blocking ); + + bool GetSendBufferSize( int & ) const; + bool GetRecvBufferSize( int & ) const; + + bool SetSendBufferSize( int ); + bool SetRecvBufferSize( int ); + + bool GetRouting( bool & ) const; + bool SetRouting( bool ); + + bool GetNoDelay( bool & ) const; + bool SetNoDelay( bool ); + + virtual const char *GetHostName() const=0; + virtual const char *GetServName() const=0; +}; + +class SocketClient : public SocketBase +{ +protected: + struct hostent rem_host; + struct servent rem_serv; + +protected: + bool SetupRemoteHost( const char *host ); + bool SetupRemoteServ( const char *serv, const char *protocol ); + +public: + SocketClient(); + + virtual bool Initialise( const char *host, const char *service )=0; + + virtual bool Open(); + + const char *GetHostName() const + { + return( rem_host.h_name ); + } + const char *GetServName() const + { + return( rem_serv.s_name ); + } +}; + +class UDPSocket : public SocketClient +{ +protected: + virtual bool Socket(); + +public: + virtual bool Initialise( const char *host, const char *service ); +}; + +class TCPClient : public SocketClient +{ +public: + typedef enum { DISCONNECTED, CONNECTED } ConnectionState; + +protected: + ConnectionState state; + +public: + TCPClient(); + + virtual bool Initialise( const char *host, const char *service ); + + virtual bool Open(); + virtual bool Close(); + + ConnectionState GetConnectionState() const + { + return( state ); + } + bool IsDisconnected() const + { + return( state == DISCONNECTED ); + } + bool IsConnected() const + { + return( state == CONNECTED ); + } +}; + +class TCPServer : public SocketBase +{ +public: + typedef enum { DISCONNECTED, LISTENING, CONNECTED } ConnectionState; + +protected: + ConnectionState state; + +protected: + struct hostent loc_host; + struct servent loc_serv; + +protected: + bool SetupLocalHost(); + bool SetupLocalServ( const char *serv ); + +public: + TCPServer( const TCPServer &, int ); + +public: + TCPServer(); + + virtual bool Initialise( const char *service ); + + virtual bool Open(); + bool Accept(); + bool Accept( TCPServer *& ); + virtual bool Close(); + + ConnectionState GetConnectionState() const + { + return( state ); + } + bool IsDisconnected() const + { + return( state == DISCONNECTED ); + } + bool IsListening() const + { + return( state == LISTENING ); + } + bool IsConnected() const + { + return( state == CONNECTED ); + } + const char *GetHostName() const + { + return( loc_host.h_name ); + } + const char *GetServName() const + { + return( loc_serv.s_name ); + } +}; + +#endif // ZM_COMMS_H