zoneminder/src/zm_comms.h

651 lines
16 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#ifndef ZM_COMMS_H
#define ZM_COMMS_H
#include "zm_exception.h"
#include "zm_logger.h"
#include <netdb.h>
#include <set>
#include <sys/uio.h>
#include <sys/un.h>
#include <vector>
#if defined(BSD)
#include <sys/socket.h>
#include <netinet/in.h>
#endif
namespace ZM {
class CommsException : public Exception {
public:
explicit CommsException( const std::string &message ) : Exception( message ) { }
};
class CommsBase {
protected:
const int &mRd;
const int &mWd;
protected:
CommsBase(const int &rd, const 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:
explicit 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();
explicit SockAddrInet( const SockAddrInet &addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( addr.mAddrIn ) {
}
explicit 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 ) {
}
explicit 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
{
protected:
int mAddressFamily;
public:
int getDomain() const {
return( mAddressFamily );
}
virtual socklen_t getAddrSize() const {
return( SockAddrInet::addrSize() );
}
protected:
bool connect( const char *host, const char *serv );
bool connect( const char *host, int port );
bool bind( const char *host, const char *serv );
bool bind( const char *host, int port );
bool bind( const char *serv );
bool bind( int port );
};
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=nullptr ) const {
ssize_t nBytes = ::sendto( mSd, msg, len, 0, addr?addr->getAddr():nullptr, 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 {
nBytes = ::recvfrom( mSd, msg, len, 0, nullptr, 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 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 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 {
public:
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 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( 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();
explicit Select( struct timeval timeout );
explicit Select( int timeout );
explicit 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