// @(#)root/netx:$Name:  $:$Id: TXNetFile.cxx,v 1.9 2005/05/12 12:25:26 rdm Exp $
// Author: Alvise Dorigo, Fabrizio Furano

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TXNetFile                                                            //
//                                                                      //
// TXNetFile is an extension of TNetFile able to deal with new xrootd   //
// server. Its new features are:                                        //
//  - Automatic server kind recognition (xrootd load balancer, xrootd   //
//    data server, old rootd)                                           //
//  - Backward compatibility with old rootd server (acts as an old      //
//    TNetFile)                                                         //
//  - Fault tolerance for read/write operations (read/write timeouts    //
//    and retry)                                                        //
//  - Internal connection timeout (tunable indipendently from the OS    //
//    one) handled by threads                                           //
//  - handling of redirections from server                              //
//  - Single TCP physical channel for multiple TXNetFile's instances    //
//    inside the same application                                       //
//    So, each TXNetFile object client must send messages containing    //
//    its ID (streamid). The server, of course, will respond with       //
//    messages containing the client's ID, in order to make the client  //
//    able to recognize its message by matching its streamid with that  //
//    one contained in the server's response.                           //
//  - Tunable log verbosity level (0 = nothing, 3 = dump read/write     //
//    buffers too!)                                                     //
//  - Many parameters configurable via TEnv facility (see SetParm()     //
//    methods)                                                          //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TError.h"
#include "TEnv.h"
#include "TXNetFile.h"
#include "TXDebug.h"
#include "TXError.h"
#include "TXUrl.h"
#include "TXNetConn.h"


ClassImp(TXNetFile);

void (*evtFunc)();

Bool_t TXNetFile::fgTagAlreadyPrinted = kFALSE;

Short_t gXDebugLevel = 0;

//_____________________________________________________________________________
 TXNetFile::TXNetFile(const char *url, Option_t *option, const char* ftitle,
		     Int_t compress, Int_t netopt) :
              TNetFile(url, ftitle, compress, kFALSE)
{
  // Create a TXNetFile object. A TXNetFile object is the same as a TNetFile
  // (from which the former derives) except that the protocol is extended to
  // support dealing with new xrootd data server or xrootd load balancer
  // server.
  //
  // The "url" argument must be of the form
  //
  //   root://server1:port1[,server2:port2,...,serverN:portN]/pathfile,
  //
  // Note that this means that multiple servers (>= 1) can be specified in
  // the url. The connection will try to connect to the first server:port
  // and if that does not succeed, it will try the second one, and so on
  // until it finds a server that will respond.
  //
  // See the TNetFile documentation for the description of the other arguments.
  //
  // The creation consists of internal variable settings (most important is
  // the client's domain), creation of a TXUrl array containing all specified
  // urls (a single url is serverX:portX/pathfile), trying to connect to the
  // servers calling Connect() method, getting a valid access to the remote
  // server the client is connected to using GetAccessToSrv() method,
  // recognizing the remote server (if an old rootd the TNetFile's Create
  // method will be called)

  // Set debug level
  gXDebugLevel = gEnv->GetValue("XNet.Debug", 0);

  CreateTXNf(url, option, ftitle, compress, netopt);
}

//_____________________________________________________________________________
 TXNetFile::~TXNetFile()
{
   // Destructor
   if (IsOpen()) Close(0);

   SafeDelete(fConnModule);
}

//_____________________________________________________________________________
 void TXNetFile::CreateTXNf(const char *url, Option_t *option, const char* ftitle,
		       Int_t compress, Int_t netopt)
{
  short locallogid;
  Bool_t validDomain = kFALSE;
  fOpenWithRefresh = kFALSE;
  fAlreadyStated = kFALSE;
  fCreateMode = kFALSE;
  fAlreadyDetected = kFALSE;
  fIsROOT = kFALSE;
  fSize = 0;
  if (gEnv->GetValue("XNet.PrintTAG",0) == 1)
     if (!fgTagAlreadyPrinted) {
        Info("CreateTXNf","(C) 2004 SLAC TXNetFile (eXtended TNetFile) %s",
             gROOT->GetVersion());
        fgTagAlreadyPrinted = kTRUE;
     }

  // Setup modified Error Handler which prints time stamps. Note that this
  // check will be done once per TXNetFile creation. It probably should be
  // moved someplace where it gets done only once per job. (And probably
  // could even be a standard option of the default ErrorHandler.)
  if (gEnv->GetValue("XNet.DebugTimestamp",0) == 1) {
     SetErrorHandler(TXNErrorHandler);
  }

  // Using ROOT mechanism to IGNORE SIGPIPE signal
  gSystem->IgnoreSignal(kSigPipe);

  fOpenPars.FileOpened = kFALSE;
  // But we initialize the internal params...
  fOpenPars.option = "";
  if (option)
    fOpenPars.option = option;

  fOpenPars.fTitle = "";
  if (ftitle)
    fOpenPars.fTitle = ftitle;

  fOpenPars.compress = compress;
  fOpenPars.netopt = netopt;

  // Now we try to set up the first connection
  // We cycle through the list of urls given as a single TUrl parameter

  fConnModule = new TXNetConn();
  if (!fConnModule) {
     Error("CreateTXNf","Fatal ERROR *** Object creation with new failed !"
                  " Probable system resources exhausted.");
     gSystem->Abort();
  }
  fConnModule->SetRedirHandler(this);

  // Max number of tries
  Int_t connectMaxTry = gEnv->GetValue("XNet.TryConnectServersList",
                                       DFLT_TRYCONNECTSERVERSLIST);
  // List of regular expressions to match
  TString allowRE = gEnv->GetValue("XNet.ConnectDomainAllowRE",
                                   fConnModule->GetClientHostDomain().Data());
  TString denyRE  = gEnv->GetValue("XNet.ConnectDomainDenyRE",
                                   "<unknown>");

  TXUrl urlArray(url);
  if (!urlArray.IsValid()) {
     Error("CreateTXNf", "The URL(s) provided are incorrect."
                     " Going into zombie state.");
     goto zombie;
  }

  for (Short_t jj=0; jj <=urlArray.Size()-1; jj++) {
     TUrl *thisUrl;
     thisUrl = urlArray.GetNextUrl();
     fUrl = *thisUrl;
     if (fConnModule->CheckHostDomain(fUrl.GetHost(), allowRE, denyRE)) {
	validDomain = kTRUE;
	break;
     }
  }

  if (!validDomain) {
     Error("CreateTXNf", "All the specified servers are disallowed. "
                     "Going into zombie state.");
     goto zombie;
  }

  urlArray.Rewind();
  locallogid = -1;
  for (Int_t connectTry = 0;
      (connectTry < connectMaxTry) && (!fConnModule->IsConnected());
       connectTry++) {

     TUrl *thisUrl;

     // Get an url from the available set
     thisUrl = urlArray.GetARandomUrl();

     if (thisUrl) {
        fUrl = *thisUrl;
        if (fConnModule->CheckHostDomain(fUrl.GetHost(), allowRE, denyRE)) {
           if (DebugLevel() >= kHIDEBUG)
              Info("CreateTXNf", "Trying to connect to %s:%d. Connect try %d.",
                           fUrl.GetHost(), fUrl.GetPort(), connectTry+1);
           locallogid = fConnModule->Connect(fUrl.GetHost(),
                                             fUrl.GetPort(), netopt);
        }
     }

     // We are connected to a host. Let's handshake with it.
     if (fConnModule->IsConnected()) {

        // Now the have the logical Connection ID, that we can use as streamid for
        // communications with the server
        if (DebugLevel() >= kHIDEBUG)
           Info("CreateTXNf", "The logical connection id is %d. This will be the"
                        " streamid for this client",fConnModule->GetLogConnID());
        fConnModule->SetUrl(fUrl);

        if (DebugLevel() >= kHIDEBUG)
	  Info("CreateTXNf", "Working url is [%s]", fUrl.GetUrl());

        // after connection deal with server
        if (!fConnModule->GetAccessToSrv()) {
           if (fConnModule->fOpenError == kXR_NotAuthorized) {
              // Authentication error: does not make much sense to retry
              fConnModule->Disconnect(kTRUE);
              TString msg = fConnModule->fOpenErrorMsg;
              msg.Remove(msg.Last(':'));
              Error("CreateTXNf","Authentication failure: %s",msg.Data());
              goto zombie;
           } else {
              Error("CreateTXNf",
                    "Access to server failed (%d)",fConnModule->fOpenError);
           }
        } else {
           if (DebugLevel() >= kUSERDEBUG)
	      Info("CreateTXNf", "Access to server granted.");
           break;
	}
     }

     // We force a physical disconnection in this special case
     if (DebugLevel() >= kHIDEBUG)
        Info("CreateTXNf", "Disconnecting.");

     fConnModule->Disconnect(kTRUE);

     if (DebugLevel() >= kUSERDEBUG)
        Info("CreateTXNf", "Connection attempt cycle failed. Sleeping %d seconds.",
                     gEnv->GetValue("XNet.ReconnectTimeout",
                                    DFLT_RECONNECTTIMEOUT));

     gSystem->Sleep(1000 * gEnv->GetValue("XNet.ReconnectTimeout",
                                         DFLT_RECONNECTTIMEOUT) );

  } //for connect try

  if (!fConnModule->IsConnected()) {
     Error("CreateTXNf", "Some severe error occurred while opening a connection"
                     " with the servers [%s]. Program exits",
                     urlArray.GetServers().Data());
     goto zombie;
  }

  if (locallogid != fConnModule->GetLogConnID()) {
     Error("CreateTXNf", "Internal error. The logids do not match (%d, %d)",
                     locallogid, fConnModule->GetLogConnID());
     //    abort();
     goto zombie;
  }


  //
  // Variable initialization
  // If the server is a new xrootd ( load balancer or data server)
  //
  if ((fConnModule->GetServerType() != TXNetConn::kSTRootd) &&
      (fConnModule->GetServerType() != TXNetConn::kSTNone)) {
     // Now we are connected to a server that didn't redirect us after the
     // login/auth phase
     // let's continue with the openfile sequence
     // We try to recycle the TNetFile procedure as much as possible, traslating
     // the TNetFile's open modes into the kXR_xxx modes...
     if (DebugLevel() >= kUSERDEBUG)
	Info("CreateTXNf", "Opening the remote file %s", fUrl.GetFile());

     // url is not needed because already stored in fUrl
     // Here we have to init our TFile ancestor
     if (!Open(option, fTitle, compress, netopt, kTRUE)) {
	Error("CreateTXNf", "Error opening the file %s on host %s:%d",
                        fUrl.GetFile(), fUrl.GetHost(), fUrl.GetPort());
          fConnModule->Disconnect(kTRUE);
	  goto zombie;
     } else {
	if (DebugLevel() >= kUSERDEBUG)
	   Info("CreateTXNf", "File opened succesfully.");
     }
  } else {
     // the server is an old rootd
     if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
        TSocket *rootdSocket = fConnModule->GetRootdSocket();
        if (rootdSocket && rootdSocket->GetRemoteProtocol() > 13) {
           // Remote support for reuse of open connection
           TNetFile::Create(rootdSocket, option, netopt);
        } else {
           // Open connection has been closed because could
           // not be reused; TNetFile will open a new connection
           TNetFile::Create(fUrl.GetUrl(), option, netopt);
        }
     }
     if (fConnModule->GetServerType() == TXNetConn::kSTNone) {
        goto zombie;
     }
  }

  return;

zombie:
   // error in file opening occured, make this object a zombie
   MakeZombie();
   gDirectory = gROOT;
}

//_____________________________________________________________________________
 Bool_t TXNetFile::ReadBuffer(char *buffer, Int_t BufferLength)
{
   // Override TNetFile::ReadBuffer to deal with the xrootd server.
   // Returns kTRUE in case of errors.

   if (IsZombie()) {
      Error("ReadBuffer", "ReadBuffer is not possible because object"
            " is in 'zombie' state");
      return kTRUE;
   }

   if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
      if (DebugLevel() >= kHIDEBUG)
         Info("ReadBuffer","Calling TNetFile::ReadBuffer");
      return TNetFile::ReadBuffer(buffer, BufferLength);
   }

   if (!IsOpen()) {
      Error("ReadBuFfer","The remote file %s is not open", fUrl.GetFile());
      return kTRUE;
   }

   Bool_t result = kFALSE;

   if (fCache) {
      Int_t st;
      Long64_t off = fOffset;
      if ((st = fCache->ReadBuffer(fOffset, buffer, BufferLength)) < 0) {
         Error("ReadBuffer", "error reading from cache");
         return kTRUE;
      }
      if (st > 0) {
         // fOffset might have been changed via TCache::ReadBuffer(), reset it
         Seek(off + BufferLength);
         return result;
      }
   }

   // Prepare request
   ClientRequest readFileRequest;
   memset( &readFileRequest, 0, sizeof(readFileRequest) );
   fConnModule->SetSID(readFileRequest.header.streamid);
   readFileRequest.read.requestid = kXR_read;
   memcpy( readFileRequest.read.fhandle, fHandle, sizeof(fHandle) );
   readFileRequest.read.offset = fOffset;
   readFileRequest.read.rlen = BufferLength;
   readFileRequest.read.dlen = 0;

   if (DebugLevel() >= kHIDEBUG)
      Info("ReadBuffer", "Calling TXNetConn::SendGenCommand to read %d"
           " bytes of data at offset %Ld.",
           readFileRequest.read.rlen,
           readFileRequest.read.offset);

   // Original version, without caching
   result = !fConnModule->SendGenCommand(&readFileRequest, 0, 0, buffer,
                                         kFALSE, (char *)"TXNetFile::ReadBuffer");
   if (!result) {
      fOffset += BufferLength;
      fBytesRead += BufferLength;
#ifdef WIN32
      SetFileBytesRead(GetFileBytesRead() + BufferLength);
#else
      fgBytesRead += BufferLength;
#endif
   }
   return result;
}

//_____________________________________________________________________________
 Bool_t TXNetFile::WriteBuffer(const char *buffer, Int_t BufferLength)
{
   // Override TNetFile::WriteBuffer to deal with the xrootd server.
   // Returns kTRUE in case of errors.

   if (IsZombie()) {
      Error("WriteBuffer", "WriteBuffer is not possible because object"
            " is in 'zombie' state");
      return kTRUE;
   }

   fAlreadyStated = kFALSE;

   if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
      if (DebugLevel() >= kHIDEBUG)
         Info("WriteBuffer","Calling TNetFile::WriteBuffer");
      return TNetFile::WriteBuffer(buffer, BufferLength );
   }

   if (!IsOpen()) {
      Error("WriteBuffer","The remote file %s is not open", fUrl.GetFile());
      return kTRUE;
   }

   Bool_t result = kFALSE;

   if (fCache) {
      Int_t st;
      Long64_t off = fOffset;
      if ((st = fCache->WriteBuffer(fOffset, buffer, BufferLength)) < 0) {
         SetBit(kWriteError);
         Error("WriteBuffer", "error writing to cache");
         return kTRUE;
      }
      if (st > 0) {
         // fOffset might have been changed via TCache::WriteBuffer(), reset it
         Seek(off + BufferLength);
         return result;
      }
   }

   // Prepare request
   ClientRequest writeFileRequest;
   memset( &writeFileRequest, 0, sizeof(writeFileRequest) );
   fConnModule->SetSID(writeFileRequest.header.streamid);
   writeFileRequest.write.requestid = kXR_write;
   memcpy( writeFileRequest.write.fhandle, fHandle, sizeof(fHandle) );
   writeFileRequest.write.offset = fOffset;
   writeFileRequest.write.dlen = BufferLength;

   if (DebugLevel() >= kHIDEBUG)
      Info("WriteBuffer", "Calling TXNetConn::SendGenCommand...");

   result = !fConnModule->SendGenCommand(&writeFileRequest, buffer, 0, 0,
                                kFALSE, (char *)"TXNetFile::WriteBuffer");
   if (!result) {
      fOffset += BufferLength;
      fBytesWrite += BufferLength;
#ifdef WIN32
      SetFileBytesWritten(GetFileBytesWritten() + BufferLength);
#else
      fgBytesWrite += BufferLength;
#endif
   }
   return result;
}

//_____________________________________________________________________________
 Bool_t TXNetFile::IsOpen() const
{
  // Return kTRUE if the file is open, kFALSE otherwise

  if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
     if (DebugLevel() >= kHIDEBUG)
        Info("IsOpen","Calling TNetFile::IsOpen");
     return TNetFile::IsOpen();
  }

  return fOpenPars.FileOpened;
}

//_____________________________________________________________________________
 Int_t TXNetFile::ReOpen(const Option_t *Mode)
{
  // Re-open the file (see TNetFile::ReOpen() or TFile::ReOpen()
  // for more details)

  if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
     if (DebugLevel() >= kHIDEBUG)
        Info("ReOpen","Calling TNetFile::ReOpen");
     return TNetFile::ReOpen(Mode);
  }

  fAlreadyStated = kFALSE;
  fSize = 0;

  return TFile::ReOpen(Mode);
}

//_____________________________________________________________________________
 void TXNetFile::Close(const Option_t *opt)
{
  // Close the file (see TNetFile::Close() or TFile::Close()
  // for more details)

  if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
     if (DebugLevel() >= kHIDEBUG)
        Info("Close","Calling TNetFile::Close");
     TNetFile::Close(opt);
     return;
  }

  TFile::Close(opt);

  fSize = 0;
  fAlreadyStated = kFALSE;
  fAlreadyDetected = kFALSE;
  fIsROOT = kFALSE;
}

//_____________________________________________________________________________
 void TXNetFile::Flush()
{
   // Flushes un-written data


   if (IsZombie()) {
      Error("Flush", "Flush is not possible because object is"
                     " in 'zombie' state");
      return;
   }

   if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
      if (DebugLevel() >= kHIDEBUG)
         Info("Flush","Calling TNetFile::Flush");
      TNetFile::Flush();
      return;
   }

   if (!IsOpen()) {
      Error("Flush","The remote file %s is not open", fUrl.GetFile());
      return;
   }

   // Prepare request
   ClientRequest flushFileRequest;
   memset( &flushFileRequest, 0, sizeof(flushFileRequest) );
   fConnModule->SetSID(flushFileRequest.header.streamid);
   flushFileRequest.sync.requestid = kXR_sync;
   memcpy(flushFileRequest.sync.fhandle, fHandle, sizeof(fHandle));
   flushFileRequest.sync.dlen = 0;
   if (DebugLevel() >= kHIDEBUG)
      Info("Flush", "Calling TXNetConn::SendGenCommand...");

   Bool_t cmdres = fConnModule->SendGenCommand(&flushFileRequest, 0, 0, 0,
                                       kFALSE, (char *)"TXNetFile::Flush");

   if (!cmdres)
      Error("Flush","SendGenCommand returned false. Command failed.");
}

//_____________________________________________________________________________
 Bool_t TXNetFile::Open(Option_t *option, const char* ftitle, Int_t compress,
		       Int_t netopt, Bool_t DoInit)
{
  // High level open routine; if the remote server is an old rootd the file
  // has been already opened by the TNetFile's Create() method

  fAlreadyStated = kFALSE;
  fSize = 0;

  if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
    // Do nothing because the file is already open
    // In fact when TNetFile is first instantiated it immediately open the
    // remote file
    return kTRUE;
  }

  // First attempt to open a remote file without the kXR_refresh option ON
  Bool_t lowopenRes = LowOpen(fUrl.GetFileAndOptions(), option, ftitle,
                              compress, netopt, DoInit);
  if (lowopenRes) {
     // Let's remember that we succesfully opened a file without refresh
     fOpenWithRefresh = kFALSE;
     return kTRUE;
  }

  // If the open request failed for the error "file not found" proceed,
  // otherwise return kFALSE
  if (fConnModule->GetOpenError() != kXR_NotFound)
     return kFALSE;

  // If connected to a load balancer that says "File not Found" then we
  // try again one more time with refresh before giving up
  if ((fConnModule->GetServerType() == TXNetConn::kSTBaseXrootd) &&
      (!fOpenWithRefresh)) {
     Info("Open", "Trying to re-open the file with REFRESH option...");

     if (!LowOpen(fUrl.GetFileAndOptions(), option, ftitle, compress,
                  netopt, DoInit, kTRUE)) {
	// Even if after a "resfresh-ed open" the file has not been found
	// goto in zombie state and return; and let's remember that we used
        // the refresh option
	//	if(DebugLevel() >= kUSERDEBUG)
	Error("Open", "Error opening the remote file even after a refresh"
                      " of the load balance. Going into 'zombie' state");
	fOpenWithRefresh = kTRUE;
	return kFALSE;
     } else {
	// Open succeded after the refresh;
	// Remember that we used the refresh option in order to not use it again
	fOpenWithRefresh = kTRUE;
	return kTRUE;
     }
  }

  // If we're here who reported an open error was a data server (rootd or
  // xrootd), then we've to check if we already tried a refresh-ed open
  // request or not...
  if (!fOpenWithRefresh) {

    // if did not use refresh in the last open, and we do not come from a
    // load balancer then goto into zombie state and return
    if (fConnModule->GetLBSUrl() == 0) {
       Error("Open","The remote data server declared 'File Not Found'"
                    " and there's not any load balancer to go back and"
                    " refresh. Going into 'zombie' state...");
       fOpenWithRefresh = kFALSE;
       return kFALSE;
    }

    TString lbsHost = fConnModule->GetLBSUrl()->GetHost();
    Int_t   lbsPort = fConnModule->GetLBSUrl()->GetPort();

    fConnModule->Disconnect(kFALSE);

    Info("Open","The current data server did not find the file. Going back"
                " to the load balancer and reopen the file in REFRESH mode");
    fConnModule->GoToAnotherServer(lbsHost, lbsPort, netopt);

    // now try to open with refresh...
    Bool_t secondTry = LowOpen(fUrl.GetFileAndOptions(), option, ftitle,
                               compress, netopt, DoInit, kTRUE);
    if (!secondTry) {
       Error("Open","File not found even after open with REFRESH mode ON.");
       return kFALSE;
    } else
       return kTRUE;
  }

  // if we're here it means that we already tried to open a file in refresh
  // mode and it failed again... then give up...
  return kFALSE;
}

//_____________________________________________________________________________
 Bool_t TXNetFile::LowOpen(const char* file, Option_t *option,
                          const char* title, Int_t compress,
			  Int_t netopt, Bool_t DoInit, Bool_t refresh_open)
{
  // Low level Open method; deals with xrootd server

  kXR_int16 openOpt; // = 0;

  memset(&openOpt, 0, sizeof(openOpt));

  fOption = option;
  Bool_t forceOpen = kFALSE;
  if (option[0] == '-') {
    fOption   = &option[1];
    forceOpen = kTRUE;
    openOpt |= kXR_force;
  }
  // accept 'f', like 'frecreate' still for backward compatibility
  if (option[0] == 'F' || option[0] == 'f') {
    fOption   = &option[1];
    forceOpen = kTRUE;
    openOpt |= kXR_force;
  }
  Bool_t forceRead = kFALSE;
  if (!strcasecmp(option, "+read")) {
    fOption   = &option[1];
    forceRead = kTRUE;
    openOpt |= kXR_force;
  }
  fOption.ToUpper();
  if (!fOption.CompareTo("NEW")) {
    fOption = "CREATE";
    fWritable = kTRUE;
  }
  if (!fOption.CompareTo("CREATE")) {
    fOption = "CREATE";
    fWritable = kTRUE;
  }
  if (!fOption.CompareTo("UPDATE")) {
    fOption = "UPDATE";
    fWritable = kTRUE;
  }

  Bool_t create   = (!fOption.CompareTo("CREATE"));
  Bool_t recreate = (!fOption.CompareTo("RECREATE"));
  Bool_t update   = (!fOption.CompareTo("UPDATE"));
  Bool_t read     = (!fOption.CompareTo("READ"));

  if (!create && !recreate && !update && !read) {
    read    = kTRUE;
    fOption = "READ";
  }

  Bool_t __recreate = kFALSE;;
  if (recreate) {
    recreate = kFALSE;
    create   = kTRUE;
    fOption  = "CREATE";
    openOpt |= kXR_delete;
    __recreate = kTRUE;
  }
  if (update)
    openOpt |= kXR_open_updt;
  if (read)
    openOpt |= kXR_open_read;

  fCreateMode = create;

  if (create && (!__recreate))
    openOpt |= kXR_new;

  // Send a kXR_open request in order to open the remote file...
  // after formatting the proper data structure...
  ClientRequest openFileRequest;

  struct ServerResponseBody_Open openresp;

  memset(&openFileRequest, 0, sizeof(openFileRequest));
  fConnModule->SetSID(openFileRequest.header.streamid);

  openFileRequest.header.requestid = kXR_open;

  // Now set the options field basing on user's requests
  memset(&openFileRequest.open.options, 0, sizeof(openFileRequest.open.options));
  memcpy(&openFileRequest.open.options, &openOpt, sizeof(openOpt));

  if( refresh_open )
    openFileRequest.open.options |= kXR_refresh;

  // Set the open mode field
  openFileRequest.open.mode =
           kXR_or | kXR_gr | kXR_ur | kXR_uw; // open in rw-r-r mode

  // Set the length of the data (in this case data describes the path and
  // file name)
  openFileRequest.open.dlen = strlen( file );

  // Send request to server and receive response
  bool resp = fConnModule->SendGenCommand(&openFileRequest, (const void *)file,
					 0, &openresp, kFALSE, (char *)"Open");

  if (resp) {
    // Get the file handle to use for future read/write...
    // Note that in case of heavy load the file could have been opened by an
    // internal retry of the kxr_open
    if (!fOpenPars.FileOpened)
      memcpy( fHandle, openresp.fhandle, sizeof(fHandle) );

    fOpenPars.FileOpened = kTRUE;

    // The call seems successful. We copy the parameters for later use
    if (option)
      fOpenPars.option = option;

    if (title)
      fOpenPars.fTitle = title;

    fOpenPars.compress = compress;
    fOpenPars.netopt = netopt;

    if (DoInit)
      Init(create);
  }

  return fOpenPars.FileOpened;
}

//_____________________________________________________________________________
 Int_t TXNetFile::SysStat(Int_t fd, Long_t *id, Long64_t *size, Long_t *flags,
                         Long_t *modtime)
{
   // Override TNetFile::SysStat (see parent's method for more details)

   if (IsZombie()) {
      Error("SysStat", "SysStat is not possible because object is"
                       " in 'zombie' state");
      *size = 0;
      return 0;
   }

   if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
      if (DebugLevel() >= kHIDEBUG)
         Info("SysStat","Calling TNetFile::SysStat");
      return TNetFile::SysStat(fd, id, size, flags, modtime);
   }

   if (!IsOpen()) {
      Error("SysStat","The remote file %s is not open",fUrl.GetFileAndOptions());
      *size = 0;
      return 0;
   }

   // Return file stat information. The interface and return value is
   // identical to TSystem::GetPathInfo().

   // asks the server for stat file informations
   ClientRequest statFileRequest;

   memset(&statFileRequest, 0, sizeof(ClientRequest));

   fConnModule->SetSID(statFileRequest.header.streamid);

   statFileRequest.stat.requestid = kXR_stat;
   memset(statFileRequest.stat.reserved, 0,
          sizeof(statFileRequest.stat.reserved));
   statFileRequest.stat.dlen = strlen(fUrl.GetFileAndOptions());

   if (DebugLevel() >= kHIDEBUG)
      Info("SysStat", "Calling TXNetConn::SendGenCommand...");

   char fStats[2048];

   fConnModule->SendGenCommand(&statFileRequest, fUrl.GetFileAndOptions(),
                               0, fStats , kFALSE, (char *)"SysStat");

   if (DebugLevel() >= kHIDEBUG)
      Info("SysStat", "Returned stats=[%s]",fStats);

   sscanf(fStats, "%ld %lld %ld %ld", id, size, flags, modtime);

   if (*id == -1)
      return 1;

   return 0;
}

//_____________________________________________________________________________
 Int_t TXNetFile::SysClose(Int_t fd)
{
   // Override TNetFile::SysClose (see parent's method for more details)

   if (IsZombie()) {
      Error("SysClose", "SysClose is not possible because object is"
                        " in 'zombie' state");
      return 0;
   }

   if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
      if (DebugLevel() >= kHIDEBUG)
         Info("SysClose","Calling TNetFile::SysClose");
      return TNetFile::SysClose(fd);
   }

   ClientRequest closeFileRequest;

   memset(&closeFileRequest, 0, sizeof(closeFileRequest) );

   fConnModule->SetSID(closeFileRequest.header.streamid);

   closeFileRequest.close.requestid = kXR_close;
   memcpy(closeFileRequest.close.fhandle, fHandle, sizeof(fHandle) );
   closeFileRequest.close.dlen = 0;

   if (DebugLevel() >= kHIDEBUG)
      Info("SysClose", "Calling TXNetConn::SendGenCommand...");

   fConnModule->SendGenCommand(&closeFileRequest, 0,
			       0, 0, kFALSE, (char *)"Close");

   // No file is opened for now
   fOpenPars.FileOpened = kFALSE;

   return 0;
}

//_____________________________________________________________________________
 Int_t TXNetFile::SysOpen(const char* pathname, Int_t flags, UInt_t mode)
{
  // Override TNetFile::SysOpen (see parent's method for more details)

  if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
     if (DebugLevel() >= kHIDEBUG)
        Info("SysOpen", "Calling TNetFile::SysOpen");
     return TNetFile::SysOpen(pathname, flags, mode);
  }

  // url is not needed because already stored in fUrl
  // Here we have to init our TFile ancestor
  if( !Open(fOpenPars.option.Data(), fOpenPars.fTitle.Data(),
            fOpenPars.compress, fOpenPars.netopt, kTRUE) ) {
      Error("SysOpen", "Error opening the file %s on host %s:%d",
                       fUrl.GetFile(),fUrl.GetHost(), fUrl.GetPort());
      return -1;
  }
  return -2;
}

//_____________________________________________________________________________
 void TXNetFile::Streamer(TBuffer &R__b)
{
   // Stream an object of class TXNetFile.
   // Dummy Streamer: rootcint chokes trying to generate one, but ROOT wants
   // one to load the shared library.

   UInt_t R__s, R__c;
   if (R__b.IsReading()) {
      Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }
      R__b.CheckByteCount(R__s, R__c, TXNetFile::IsA());
   } else {
      R__c = R__b.WriteVersion(TXNetFile::IsA(), kTRUE);
      R__b.SetByteCount(R__c, kTRUE);
   }
}

//_____________________________________________________________________________
 Bool_t TXNetFile::OpenFileWhenRedirected(char *newfhandle, Bool_t &wasopen)
{
   // Called by the comm module when it needs to reopen a file
   // after a redir

   wasopen = fOpenPars.FileOpened;

   if (!fOpenPars.FileOpened)
      return kTRUE;

   fOpenPars.FileOpened = kFALSE;

   if (DebugLevel() >= kHIDEBUG)
      Info("OpenFileWhenRedirected", "Trying to reopen the same file." );

   // After a redirection we must not reinit the TFile ancestor...
   if (Open(fOpenPars.option.Data(), fOpenPars.fTitle.Data(),
            fOpenPars.compress, fOpenPars.netopt, kFALSE)) {

      fOpenPars.FileOpened = kTRUE;

      if (DebugLevel() >= kHIDEBUG)
         Info("OpenFileWhenRedirected",
              "Open successful. (handle='%s')", fHandle );

      memcpy(newfhandle, fHandle, sizeof(fHandle));

      return kTRUE;
   } else {
      Error("OpenFileWhenRedirected",
	    "New redir destination server refuses to open the file.");
      MakeZombie();
      return kFALSE;
   }
}

//_____________________________________________________________________________
 Long_t TXNetFile::GetRemoteFile(void **bufFile)
{
   // Retrieve a remote file a store it in memory

   if (IsZombie()) {
      Error("GetRemoteFile", "GetRemoteFile is not possible because"
                             " object is in 'zombie' state");
      return -1;
   }

   if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
      Error("GetRemoteFile",
	    "Using TNetFile to talk to old rootd server, but TNetFile"
            " doesn't support GetFile method");
      return -1;
   }

   if (!IsOpen()) {
      Error("GetRemoteFile","The remote file %s is not open", fUrl.GetFile());
      return -1;
   }

   // First of all we get the file size we want to trasnfer
   Long_t id, flags, modtime;
   Long64_t size;

   this->SysStat(0/* for TNetFile compatibility */, &id, &size, &flags, &modtime);

   ClientRequest readFileRequest;
   memset(&readFileRequest, 0, sizeof(readFileRequest));
   fConnModule->SetSID(readFileRequest.header.streamid);
   readFileRequest.read.requestid = kXR_read;
   memcpy( readFileRequest.read.fhandle, fHandle, sizeof(fHandle) );
   readFileRequest.read.offset = 0;  // we want to read starting from 0 position
                                     // of the file
   readFileRequest.read.rlen = size; // we want to read all the file and put it
                                     // in memory
   readFileRequest.read.dlen = 0;

   // We assume the buffer has been pre-allocated to contain BufferLength
   // bytes by the caller of this function
   if (DebugLevel() >= kHIDEBUG)
      Info("GetRemoteFile",
	   "Calling TXNetConn::SendGenCommand to read %d bytes of data at"
           " offset %Ld.", readFileRequest.read.rlen, readFileRequest.read.offset);

   Bool_t fail = fConnModule->SendGenCommand(&readFileRequest, 0, bufFile,
				     0, kTRUE, (char *)"TXNetFile::GetRemoteFile");

   if (!fail) {
      Error("GetRemoteFile", "SendGenCommand returned error");
      return -1;
   }
   return size;
}

//_____________________________________________________________________________
 Bool_t TXNetFile::ProcessUnsolicitedMsg(TXUnsolicitedMsgSender *,
                                        TXMessage *)
{
   // We are here if an unsolicited response comes from a logical conn
   // The response comes in the form of an TXMessage *, that must NOT be
   // destroyed after processing. It is destroyed by the first sender.
   // Remember that we are in a separate thread, since unsolicited
   // responses are asynchronous by nature.

   Info("ProcessUnsolicitedMsg", "Processing unsolicited response");

   // Local processing ....

   return kTRUE;
}

//_____________________________________________________________________________
 Int_t TXNetFile::LastBytesSent(void)
{
   // Return number of bytes last sent

   if (fConnModule)
      return fConnModule->LastBytesSent();
   else
      return 0;
}

//_____________________________________________________________________________
 Int_t TXNetFile::LastBytesRecv(void)
{
   // Return number of bytes last received

   if (fConnModule)
      return fConnModule->LastBytesRecv();
   else
      return 0;
}

//_____________________________________________________________________________
 Int_t TXNetFile::LastDataBytesSent(void)
{
   // Return number of data bytes last sent

   if (fConnModule)
      return fConnModule->LastDataBytesSent();
   else
      return 0;
}

//_____________________________________________________________________________
 Int_t TXNetFile::LastDataBytesRecv(void)
{
   // Return number of data bytes last received

   if (fConnModule)
      return fConnModule->LastDataBytesRecv();
   else
      return 0;
}

//_____________________________________________________________________________
 Long64_t TXNetFile::Size(void)
{
   // Return file size

   if (fAlreadyStated)
      return fSize;

   Long64_t size;
   Long_t i, f, m;

   this->SysStat((Int_t)0, &i, &size, &f, &m);
   fAlreadyStated = kTRUE;

   memcpy((void *)&fSize, (const void*)&size, sizeof(size));
   return fSize;
}


ROOT page - Class index - Class Hierarchy - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.