zoneminder/src/zm_comms.h

897 lines
21 KiB
C++

//
// ZoneMinder Communicatoions Class Interface, $Date$, $Revision$
// Copyright (C) 2001-2008 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 "zm_exception.h"
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <sys/un.h>
#include <set>
#include <vector>
#if defined(BSD)
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
class CommsException : public Exception
{
public:
CommsException( const std::string &message ) : Exception( message )
{
}
};
class CommsBase
{
protected:
const int &mRd;
const int &mWd;
protected:
CommsBase( int &rd, int &wd ) : mRd( rd ), mWd( wd )
{
}
virtual ~CommsBase()
{
}
public:
virtual bool close()=0;
virtual bool isOpen() const=0;
virtual bool isClosed() const=0;
virtual bool setBlocking( bool blocking )=0;
public:
int getReadDesc() const
{
return( mRd );
}
int getWriteDesc() const
{
return( mWd );
}
int getMaxDesc() const
{
return( mRd>mWd?mRd:mWd );
}
virtual int read( void *msg, int len )
{
ssize_t nBytes = ::read( mRd, msg, len );
if ( nBytes < 0 )
Debug( 1, "Read of %d bytes max on rd %d failed: %s", len, mRd, strerror(errno) );
return( nBytes );
}
virtual int write( const void *msg, int len )
{
ssize_t nBytes = ::write( mWd, msg, len );
if ( nBytes < 0 )
Debug( 1, "Write of %d bytes on wd %d failed: %s", len, mWd, strerror(errno) );
return( nBytes );
}
virtual int readV( const struct iovec *iov, int iovcnt )
{
int nBytes = ::readv( mRd, iov, iovcnt );
if ( nBytes < 0 )
Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) );
return( nBytes );
}
virtual int writeV( const struct iovec *iov, int iovcnt )
{
ssize_t nBytes = ::writev( mWd, iov, iovcnt );
if ( nBytes < 0 )
Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) );
return( nBytes );
}
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 mFd[2];
public:
Pipe() : CommsBase( mFd[0], mFd[1] )
{
mFd[0] = -1;
mFd[1] = -1;
}
~Pipe()
{
close();
}
public:
bool open();
bool close();
bool isOpen() const
{
return( mFd[0] != -1 && mFd[1] != -1 );
}
int getReadDesc() const
{
return( mFd[0] );
}
int getWriteDesc() const
{
return( mFd[1] );
}
bool setBlocking( bool blocking );
};
class SockAddr
{
private:
const struct sockaddr *mAddr;
public:
SockAddr( const struct sockaddr *addr );
virtual ~SockAddr()
{
}
static SockAddr *newSockAddr( const struct sockaddr &addr, socklen_t len );
static SockAddr *newSockAddr( const SockAddr *addr );
int getDomain() const
{
return( mAddr?mAddr->sa_family:AF_UNSPEC );
}
const struct sockaddr *getAddr() const
{
return( mAddr );
}
virtual socklen_t getAddrSize() const=0;
virtual struct sockaddr *getTempAddr() const=0;
};
class SockAddrInet : public SockAddr
{
private:
struct sockaddr_in mAddrIn;
struct sockaddr_in mTempAddrIn;
public:
SockAddrInet();
SockAddrInet( const SockAddrInet &addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( addr.mAddrIn )
{
}
SockAddrInet( const struct sockaddr_in *addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( *addr )
{
}
bool resolve( const char *host, const char *serv, const char *proto );
bool resolve( const char *host, int port, const char *proto );
bool resolve( const char *serv, const char *proto );
bool resolve( int port, const char *proto );
socklen_t getAddrSize() const
{
return( sizeof(mAddrIn) );
}
struct sockaddr *getTempAddr() const
{
return( (sockaddr *)&mTempAddrIn );
}
public:
static socklen_t addrSize()
{
return( sizeof(sockaddr_in) );
}
};
class SockAddrUnix : public SockAddr
{
private:
struct sockaddr_un mAddrUn;
struct sockaddr_un mTempAddrUn;
public:
SockAddrUnix();
SockAddrUnix( const SockAddrUnix &addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( addr.mAddrUn )
{
}
SockAddrUnix( const struct sockaddr_un *addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( *addr )
{
}
bool resolve( const char *path, const char *proto );
socklen_t getAddrSize() const
{
return( sizeof(mAddrUn) );
}
struct sockaddr *getTempAddr() const
{
return( (sockaddr *)&mTempAddrUn );
}
public:
static socklen_t addrSize()
{
return( sizeof(sockaddr_un) );
}
};
class Socket : public CommsBase
{
protected:
typedef enum { CLOSED, DISCONNECTED, LISTENING, CONNECTED } State;
protected:
int mSd;
State mState;
SockAddr *mLocalAddr;
SockAddr *mRemoteAddr;
protected:
Socket() : CommsBase( mSd, mSd ), mSd( -1 ), mState( CLOSED ), mLocalAddr( 0 ), mRemoteAddr( 0 )
{
}
Socket( const Socket &socket, int newSd ) : CommsBase( mSd, mSd ), mSd( newSd ), mState( CONNECTED ), mLocalAddr( 0 ), mRemoteAddr( 0 )
{
if ( socket.mLocalAddr )
mLocalAddr = SockAddr::newSockAddr( mLocalAddr );
if ( socket.mRemoteAddr )
mRemoteAddr = SockAddr::newSockAddr( mRemoteAddr );
}
virtual ~Socket()
{
close();
delete mLocalAddr;
delete mRemoteAddr;
}
public:
bool isOpen() const
{
return( !isClosed() );
}
bool isClosed() const
{
return( mState == CLOSED );
}
bool isDisconnected() const
{
return( mState == DISCONNECTED );
}
bool isConnected() const
{
return( mState == CONNECTED );
}
virtual bool close();
protected:
bool isListening() const
{
return( mState == LISTENING );
}
protected:
virtual bool socket();
virtual bool bind();
protected:
virtual bool connect();
virtual bool listen();
virtual bool accept();
virtual bool accept( int & );
public:
virtual int send( const void *msg, int len ) const
{
ssize_t nBytes = ::send( mSd, msg, len, 0 );
if ( nBytes < 0 )
Debug( 1, "Send of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) );
return( nBytes );
}
virtual int recv( void *msg, int len ) const
{
ssize_t nBytes = ::recv( mSd, msg, len, 0 );
if ( nBytes < 0 )
Debug( 1, "Recv of %d bytes max on sd %d failed: %s", len, mSd, strerror(errno) );
return( nBytes );
}
virtual int send( const std::string &msg ) const
{
ssize_t nBytes = ::send( mSd, msg.data(), msg.size(), 0 );
if ( nBytes < 0 )
Debug( 1, "Send of string '%s' (%zd bytes) on sd %d failed: %s", msg.c_str(), msg.size(), mSd, strerror(errno) );
return( nBytes );
}
virtual int recv( std::string &msg ) const
{
char buffer[msg.capacity()];
int nBytes = 0;
if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 )
{
Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", sizeof(buffer), mSd, strerror(errno) );
return( nBytes );
}
buffer[nBytes] = '\0';
msg = buffer;
return( nBytes );
}
virtual int recv( std::string &msg, size_t maxLen ) const
{
char buffer[maxLen];
int nBytes = 0;
if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 )
{
Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", maxLen, mSd, strerror(errno) );
return( nBytes );
}
buffer[nBytes] = '\0';
msg = buffer;
return( nBytes );
}
virtual int bytesToRead() const;
int getDesc() const
{
return( mSd );
}
//virtual bool isOpen() const
//{
//return( mSd != -1 );
//}
virtual int getDomain() const=0;
virtual int getType() const=0;
virtual const char *getProtocol() const=0;
const SockAddr *getLocalAddr() const
{
return( mLocalAddr );
}
const SockAddr *getRemoteAddr() const
{
return( mRemoteAddr );
}
virtual socklen_t getAddrSize() const=0;
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 );
};
class InetSocket : virtual public Socket
{
public:
int getDomain() const
{
return( AF_INET );
}
virtual socklen_t getAddrSize() const
{
return( SockAddrInet::addrSize() );
}
protected:
bool resolveLocal( const char *host, const char *serv, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( host, serv, proto ) );
}
bool resolveLocal( const char *host, int port, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( host, port, proto ) );
}
bool resolveLocal( const char *serv, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( serv, proto ) );
}
bool resolveLocal( int port, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mLocalAddr = addr;
return( addr->resolve( port, proto ) );
}
bool resolveRemote( const char *host, const char *serv, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mRemoteAddr = addr;
return( addr->resolve( host, serv, proto ) );
}
bool resolveRemote( const char *host, int port, const char *proto )
{
SockAddrInet *addr = new SockAddrInet;
mRemoteAddr = addr;
return( addr->resolve( host, port, proto ) );
}
protected:
bool bind( const SockAddrInet &addr )
{
mLocalAddr = new SockAddrInet( addr );
return( Socket::bind() );
}
bool bind( const char *host, const char *serv )
{
if ( !resolveLocal( host, serv, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool bind( const char *host, int port )
{
if ( !resolveLocal( host, port, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool bind( const char *serv )
{
if ( !resolveLocal( serv, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool bind( int port )
{
if ( !resolveLocal( port, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool connect( const SockAddrInet &addr )
{
mRemoteAddr = new SockAddrInet( addr );
return( Socket::connect() );
}
bool connect( const char *host, const char *serv )
{
if ( !resolveRemote( host, serv, getProtocol() ) )
return( false );
return( Socket::connect() );
}
bool connect( const char *host, int port )
{
if ( !resolveRemote( host, port, getProtocol() ) )
return( false );
return( Socket::connect() );
}
};
class UnixSocket : virtual public Socket
{
public:
int getDomain() const
{
return( AF_UNIX );
}
virtual socklen_t getAddrSize() const
{
return( SockAddrUnix::addrSize() );
}
protected:
bool resolveLocal( const char *serv, const char *proto )
{
SockAddrUnix *addr = new SockAddrUnix;
mLocalAddr = addr;
return( addr->resolve( serv, proto ) );
}
bool resolveRemote( const char *path, const char *proto )
{
SockAddrUnix *addr = new SockAddrUnix;
mRemoteAddr = addr;
return( addr->resolve( path, proto ) );
}
protected:
bool bind( const char *path )
{
if ( !UnixSocket::resolveLocal( path, getProtocol() ) )
return( false );
return( Socket::bind() );
}
bool connect( const char *path )
{
if ( !UnixSocket::resolveRemote( path, getProtocol() ) )
return( false );
return( Socket::connect() );
}
};
class UdpSocket : virtual public Socket
{
public:
int getType() const
{
return( SOCK_DGRAM );
}
const char *getProtocol() const
{
return( "udp" );
}
public:
virtual int sendto( const void *msg, int len, const SockAddr *addr=0 ) const
{
ssize_t nBytes = ::sendto( mSd, msg, len, 0, addr?addr->getAddr():NULL, addr?addr->getAddrSize():0 );
if ( nBytes < 0 )
Debug( 1, "Sendto of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) );
return( nBytes );
}
virtual int recvfrom( void *msg, int len, SockAddr *addr=0 ) const
{
ssize_t nBytes = 0;
if ( addr )
{
struct sockaddr sockAddr;
socklen_t sockLen;
nBytes = ::recvfrom( mSd, msg, len, 0, &sockAddr, &sockLen );
if ( nBytes < 0 )
{
Debug( 1, "Recvfrom of %d bytes max on sd %d (with address) failed: %s", len, mSd, strerror(errno) );
}
else if ( sockLen )
{
addr = SockAddr::newSockAddr( sockAddr, sockLen );
}
}
else
{
nBytes = ::recvfrom( mSd, msg, len, 0, NULL, 0 );
if ( nBytes < 0 )
Debug( 1, "Recvfrom of %d bytes max on sd %d (no address) failed: %s", len, mSd, strerror(errno) );
}
return( nBytes );
}
};
class UdpInetSocket : virtual public UdpSocket, virtual public InetSocket
{
public:
bool bind( const SockAddrInet &addr )
{
return( InetSocket::bind( addr ) );
}
bool bind( const char *host, const char *serv )
{
return( InetSocket::bind( host, serv ) );
}
bool bind( const char *host, int port )
{
return( InetSocket::bind( host, port ) );
}
bool bind( const char *serv )
{
return( InetSocket::bind( serv ) );
}
bool bind( int port )
{
return( InetSocket::bind( port ) );
}
bool connect( const SockAddrInet &addr )
{
return( InetSocket::connect( addr ) );
}
bool connect( const char *host, const char *serv )
{
return( InetSocket::connect( host, serv ) );
}
bool connect( const char *host, int port )
{
return( InetSocket::connect( host, port ) );
}
};
class UdpUnixSocket : virtual public UdpSocket, virtual public UnixSocket
{
public:
bool bind( const char *path )
{
return( UnixSocket::bind( path ) );
}
bool connect( const char *path )
{
return( UnixSocket::connect( path ) );
}
};
class UdpInetClient : public UdpInetSocket
{
protected:
bool bind( const SockAddrInet &addr )
{
return( UdpInetSocket::bind( addr ) );
}
bool bind( const char *host, const char *serv )
{
return( UdpInetSocket::bind( host, serv ) );
}
bool bind( const char *host, int port )
{
return( UdpInetSocket::bind( host, port ) );
}
bool bind( const char *serv )
{
return( UdpInetSocket::bind( serv ) );
}
bool bind( int port )
{
return( UdpInetSocket::bind( port ) );
}
public:
bool connect( const SockAddrInet &addr )
{
return( UdpInetSocket::connect( addr ) );
}
bool connect( const char *host, const char *serv )
{
return( UdpInetSocket::connect( host, serv ) );
}
bool connect( const char *host, int port )
{
return( UdpInetSocket::connect( host, port ) );
}
};
class UdpUnixClient : public UdpUnixSocket
{
public:
bool bind( const char *path )
{
return( UdpUnixSocket::bind( path ) );
}
public:
bool connect( const char *path )
{
return( UdpUnixSocket::connect( path) );
}
};
class UdpInetServer : public UdpInetSocket
{
public:
bool bind( const SockAddrInet &addr )
{
return( UdpInetSocket::bind( addr ) );
}
bool bind( const char *host, const char *serv )
{
return( UdpInetSocket::bind( host, serv ) );
}
bool bind( const char *host, int port )
{
return( UdpInetSocket::bind( host, port ) );
}
bool bind( const char *serv )
{
return( UdpInetSocket::bind( serv ) );
}
bool bind( int port )
{
return( UdpInetSocket::bind( port ) );
}
protected:
bool connect( const char *host, const char *serv )
{
return( UdpInetSocket::connect( host, serv ) );
}
bool connect( const char *host, int port )
{
return( UdpInetSocket::connect( host, port ) );
}
};
class UdpUnixServer : public UdpUnixSocket
{
public:
bool bind( const char *path )
{
return( UdpUnixSocket::bind( path ) );
}
protected:
bool connect( const char *path )
{
return( UdpUnixSocket::connect( path ) );
}
};
class TcpSocket : virtual public Socket
{
public:
TcpSocket()
{
}
TcpSocket( const TcpSocket &socket, int newSd ) : Socket( socket, newSd )
{
}
public:
int getType() const
{
return( SOCK_STREAM );
}
const char *getProtocol() const
{
return( "tcp" );
}
};
class TcpInetSocket : virtual public TcpSocket, virtual public InetSocket
{
public:
TcpInetSocket()
{
}
TcpInetSocket( const TcpInetSocket &socket, int newSd ) : TcpSocket( socket, newSd )
{
}
};
class TcpUnixSocket : virtual public TcpSocket, virtual public UnixSocket
{
public:
TcpUnixSocket()
{
}
TcpUnixSocket( const TcpUnixSocket &socket, int newSd ) : TcpSocket( socket, newSd )
{
}
};
class TcpInetClient : public TcpInetSocket
{
public:
bool connect( const char *host, const char *serv )
{
return( TcpInetSocket::connect( host, serv ) );
}
bool connect( const char *host, int port )
{
return( TcpInetSocket::connect( host, port ) );
}
};
class TcpUnixClient : public TcpUnixSocket
{
public:
bool connect( const char *path )
{
return( TcpUnixSocket::connect( path) );
}
};
class TcpInetServer : public TcpInetSocket
{
public:
bool bind( const char *host, const char *serv )
{
return( TcpInetSocket::bind( host, serv ) );
}
bool bind( const char *host, int port )
{
return( TcpInetSocket::bind( host, port ) );
}
bool bind( const char *serv )
{
return( TcpInetSocket::bind( serv ) );
}
bool bind( int port )
{
return( TcpInetSocket::bind( port ) );
}
public:
bool isListening() const { return( Socket::isListening() ); }
bool listen();
bool accept();
bool accept( TcpInetSocket *&newSocket );
};
class TcpUnixServer : public TcpUnixSocket
{
public:
bool bind( const char *path )
{
return( TcpUnixSocket::bind( path ) );
}
public:
bool isListening() const { return( Socket::isListening() ); }
bool listen();
bool accept();
bool accept( TcpUnixSocket *&newSocket );
};
class Select
{
public:
typedef std::set<CommsBase *> CommsSet;
typedef std::vector<CommsBase *> CommsList;
protected:
CommsSet mReaders;
CommsSet mWriters;
CommsList mReadable;
CommsList mWriteable;
bool mHasTimeout;
struct timeval mTimeout;
int mMaxFd;
public:
Select();
Select( struct timeval timeout );
Select( int timeout );
Select( double timeout );
void setTimeout( int timeout );
void setTimeout( double timeout );
void setTimeout( struct timeval timeout );
void clearTimeout();
void calcMaxFd();
bool addReader( CommsBase *comms );
bool deleteReader( CommsBase *comms );
void clearReaders();
bool addWriter( CommsBase *comms );
bool deleteWriter( CommsBase *comms );
void clearWriters();
int wait();
const CommsList &getReadable() const;
const CommsList &getWriteable() const;
};
#endif // ZM_COMMS_H