//===========================================================================
//  @(#) $Name: cflowd-2-1-b1 $
//  @(#) $Id: CflowdRawFlowLogger.cc,v 1.8 1998/12/22 23:35:57 dwm Exp $
//===========================================================================
//  CAIDA Copyright Notice
//
//  By accessing this software, cflowd++, you are duly informed
//  of and agree to be bound by the conditions described below in this
//  notice:
//
//  This software product, cflowd++, is developed by Daniel W. McRobb, and
//  copyrighted(C) 1998 by the University of California, San Diego
//  (UCSD), with all rights reserved.  UCSD administers the CAIDA grant,
//  NCR-9711092, under which part of this code was developed.
//
//  There is no charge for cflowd++ software. You can redistribute it
//  and/or modify it under the terms of the GNU General Public License,
//  v.  2 dated June 1991 which is incorporated by reference herein.
//  cflowd++ is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF
//  MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that the use
//  of it will not infringe on any third party's intellectual property
//  rights.
//
//  You should have received a copy of the GNU GPL along with cflowd++.
//  Copies can also be obtained from:
//
//    http://www.gnu.org/copyleft/gpl.html
//
//  or by writing to:
//
//    University of California, San Diego
//
//    SDSC/CAIDA
//    9500 Gilman Dr., MS-0505
//    La Jolla, CA 92093 - 0505  USA
//
//  Or contact:
//
//    info@caida.org
//===========================================================================

extern "C" {
#include "aclocal.h"
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
}

#ifdef MSYNC_HAS_FLAGS
  #ifdef HAVE_MS_SYNC
    #define MSYNC_MS_SYNC(_addr_, _len_) msync(_addr_, _len_, MS_SYNC)
  #else
    #define MSYNC_MS_SYNC(_addr_, _len_) msync(_addr_, _len_, 0)
  #endif
#else
  #define MSYNC_MS_SYNC(_addr_, _len_) msync(_addr_, _len_)
#endif

#ifndef MAP_FAILED
  #define MAP_FAILED ((caddr_t)-1)
#endif

#include <string>
#include <strstream.h>

#include "CflowdRawFlow.hh"
#include "CflowdRawFlowLogger.hh"
#include "ArtsPrimitive.hh"

extern ArtsPrimitive g_CfdArtsPrimitive;

static const string rcsid = "@(#) $Name: cflowd-2-1-b1 $ $Id: CflowdRawFlowLogger.cc,v 1.8 1998/12/22 23:35:57 dwm Exp $";

//-------------------------------------------------------------------------
//                CflowdRawFlowLogger::CflowdRawFlowLogger()               
//.........................................................................
//  
//-------------------------------------------------------------------------
CflowdRawFlowLogger::CflowdRawFlowLogger()
{
  this->_logSize = -1;
  this->_numLogs = -1;
  this->_mapAddr = (caddr_t)-1;
  this->_writePtr = (caddr_t)-1;
}

//-------------------------------------------------------------------------
//  CflowdRawFlowLogger::CflowdRawFlowLogger(const string & flowDirectory, 
//                                           const string & flowFilePrefix, 
//                                           int numLogs, int logSize)     
//.........................................................................
//  
//-------------------------------------------------------------------------
CflowdRawFlowLogger::CflowdRawFlowLogger(const string & flowDirectory,
                                         const string & flowFilePrefix,
                                         int numLogs, int logSize)
{
  this->_flowDirectory = flowDirectory;
  this->_flowFilePrefix = flowFilePrefix;
  this->_numLogs  = numLogs;
  this->_logSize  = logSize;
  this->_mapAddr  = (caddr_t)-1;
  this->_writePtr = (caddr_t)-1;
  this->Open();
}

//-------------------------------------------------------------------------
//               CflowdRawFlowLogger::~CflowdRawFlowLogger()               
//.........................................................................
//  
//-------------------------------------------------------------------------
CflowdRawFlowLogger::~CflowdRawFlowLogger()
{
  this->Close();
}

//-------------------------------------------------------------------------
//    const string &
//    CflowdRawFlowLogger::FlowDirectory(const string & flowDirectory)       
//.........................................................................
//  
//-------------------------------------------------------------------------
const string &
CflowdRawFlowLogger::FlowDirectory(const string & flowDirectory)
{
  this->_flowDirectory = flowDirectory;
  return(this->_flowDirectory);
}

//-------------------------------------------------------------------------
//   const string &
//   CflowdRawFlowLogger::FlowFilePrefix(const string & flowFilePrefix)      
//.........................................................................
//  
//-------------------------------------------------------------------------
const string &
CflowdRawFlowLogger::FlowFilePrefix(const string & flowFilePrefix)
{
  this->_flowFilePrefix = flowFilePrefix;
  return(this->_flowFilePrefix);
}

//-------------------------------------------------------------------------
//                  int CflowdRawFlowLogger::Open()       
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdRawFlowLogger::Open()
{
  struct stat  statBuf;
  int          logFd = -1;
  
  if (stat(this->_flowDirectory.c_str(),&statBuf) < 0) {
    syslog(LOG_ERR,"[E] stat(\"%s\",%#x) failed: %m {%s:%d}",
           this->_flowDirectory.c_str(),&statBuf,__FILE__,__LINE__);
    this->_mapAddr = (caddr_t)-1;
    this->_writePtr = (caddr_t)-1;
    return(-1);
  }

  for (int logFileNum = this->_numLogs - 1; logFileNum >= 0; logFileNum--) {
    ostrstream  logFileName;
    logFileName << this->_flowDirectory << "/" << this->_flowFilePrefix
                << "." << logFileNum << ends;
    logFd = open(logFileName.str(),O_RDWR|O_CREAT,0644);
    if (logFd < 0) {
      syslog(LOG_ERR,"[E] open(\"%s\",O_RDWR|O_CREAT,0644) failed: %m {%s:%d}",
             logFileName.str(),__FILE__,__LINE__);
      logFileName.freeze(0);
      this->_mapAddr = (caddr_t)-1;
      this->_writePtr = (caddr_t)-1;
      return(-1);
    }
    if (ftruncate(logFd,this->_logSize) < 0) {
      syslog(LOG_ERR,"[E] ftruncate(%d,%d) failed: %m {%s:%d}",
             logFd,this->_logSize,__FILE__,__LINE__);
      logFileName.freeze(0);
      close(logFd);
      this->_mapAddr = (caddr_t)-1;
      this->_writePtr = (caddr_t)-1;
      return(-1);
    }
    logFileName.freeze(0);
    
    if (logFileNum)
      close(logFd);
  }

  // map the first log file
  caddr_t  mapAddr;

  mapAddr = (caddr_t)mmap(0,this->_logSize,PROT_READ|PROT_WRITE,
                          MAP_SHARED,logFd,0);
  if (mapAddr == (caddr_t)MAP_FAILED) {
    syslog(LOG_ERR,"[E] mmap(0,%d,PROT_READ|PROT_WRITE,MAP_SHARED,%d,0)"
           " failed: %m {%s:%d}",this->_logSize,logFd,__FILE__,__LINE__);
    close(logFd);
    this->_mapAddr = (caddr_t)-1;
    this->_writePtr = (caddr_t)-1;
    return(-1);
  }
  close(logFd);
  this->_mapAddr = mapAddr;
  this->_writePtr = mapAddr;

  return(0);
}

//-------------------------------------------------------------------------
//                     int CflowdRawFlowLogger::Close()                    
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdRawFlowLogger::Close()
{
  if (this->_mapAddr != (caddr_t)-1) {
    memset(this->_writePtr,0,(this->_mapAddr+this->_logSize)-this->_writePtr);
    if (MSYNC_MS_SYNC(this->_mapAddr,this->_logSize) < 0) {
      syslog(LOG_ERR,"[E] MSYNC_MS_SYNC(%#x,%d) failed: %m {%s:%d}",
             this->_mapAddr,this->_logSize,__FILE__,__LINE__);
      return(-1);
    }
    if (munmap(this->_mapAddr,this->_logSize) < 0) {
      syslog(LOG_ERR,"[E] munmap(%#x,%d) failed: %m {%s:%d}",
             this->_mapAddr,this->_logSize,__FILE__,__LINE__);
      return(-1);
    }
    this->_mapAddr = (caddr_t)-1;
    this->_writePtr = (caddr_t)-1;
  }
  return(0);
}

//-------------------------------------------------------------------------
//              int CflowdRawFlowLogger::NumLogs(int numLogs)              
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdRawFlowLogger::NumLogs(int numLogs)
{
  this->_numLogs = numLogs;
  return(this->_numLogs);
}

//-------------------------------------------------------------------------
//                   int CflowdRawFlowLogger::Roll()
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdRawFlowLogger::Roll()
{
  this->Close();

  for (int logFileNum = this->_numLogs - 2; logFileNum >= 0; logFileNum--) {
    ostrstream  from;
    ostrstream  to;

    from << this->_flowDirectory << "/" << this->_flowFilePrefix
         << "." << logFileNum << ends;
    to << this->_flowDirectory << "/" << this->_flowFilePrefix
       << "." << logFileNum + 1 << ends;
    if (rename(from.str(),to.str()) < 0) {
      syslog(LOG_ERR,"[E] rename(\"%s\",\"%s\") failed: %m {%s:%d}",
             from.str(),to.str(),__FILE__,__LINE__);
    }
    from.freeze(0);
    to.freeze(0);
  }
  return(this->Open());
}

//-------------------------------------------------------------------------
//     int CflowdRawFlowLogger::AddFlow(const CflowdRawFlow & rawFlow)     
//.........................................................................
//  
//-------------------------------------------------------------------------
int CflowdRawFlowLogger::AddFlow(const CflowdRawFlow & rawFlow)
{
  int  rc;
  
  if ((this->_writePtr + rawFlow.Length()) >
      (this->_mapAddr + this->_logSize)) {
    this->Roll();
  }

  caddr_t  mapPtr = this->_writePtr;
  rc = rawFlow.Write(mapPtr);
  this->_writePtr = mapPtr;

  return(rc);
}

