// @(#)root/netx:$Name:  $:$Id: TXUrl.cxx,v 1.5 2004/12/16 19:23:18 rdm Exp $
// Author: Alvise Dorigo, Fabrizio Furano

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

#include "TXUrl.h"
#include "TXDebug.h"
#include "TString.h"
#include "TError.h"
#include "TSystem.h"
#include "TInetAddress.h"

#include <ctype.h>               // needed by isdigit()
#ifndef R__WIN32
#include <netdb.h>
#endif

using namespace std;


ClassImp(TXUrl);

//_____________________________________________________________________________
 TXUrl::TXUrl(TString Urls) : fIsValid(kTRUE)
{
   // A container for multiple urls.
   // It creates an array of multiple urls parsing the argument Urls and
   //  resolving the DNS aliases
   //
   // Urls MUST be in the form:
   //
   //    root://[username1@]server1:port1[,[username2@]server2:port2, ... ,
   //           [usernameN@]serverN:portN]/pathfile
   //
   // Using the method GetNextUrl() the user can obtain the next TUrl object pointer in the array
   // (the array is cyclic).
   // Using the method GetARandomUrl() the user can obtain a random TUrl from the array

   UrlArray urlArray;
   TString listOfMachines;
   TString protoToMatch(PROTO);

   protoToMatch += "://";

   // Init of the random number generator with the internal clock as a seed
   fRndgen.SetSeed(gSystem->GetPid());

   //
   // We assume the protol is "root://", because this
   // must be the protocol for TXNetFile
   //


   if ( !Urls.BeginsWith(protoToMatch) ) {
      Error("TXUrl", "This is not a %s protocol.", protoToMatch.Data() );
      fIsValid = kFALSE;
   } else
      // remove leading 'root://' from the string
      // Now sUrls will contain [user1@]machine1[:port1],...,
      //                        [userN]machineN[:portN]/pathfile
      Urls.Remove( 0, protoToMatch.Length() );

   //
   // Save the list of comma separated servers:ports, assuming they are
   // separated by a '/' char from the pathfile
   //
   listOfMachines = Urls;

   Short_t slashPos = (Short_t)Urls.First('/');
   if( slashPos != kNPOS) {
      listOfMachines.Remove(slashPos);
   }

   // remove trailing "," that would introduce a null host
   while (listOfMachines.EndsWith(","))
      listOfMachines.Remove(listOfMachines.Length()-1);

   // remove leading "," that would introduce a null host
   while (listOfMachines.BeginsWith(","))
      listOfMachines.Remove(0,1);

   if(DebugLevel() >= kUSERDEBUG)
      Info("TXUrl", "List of servers to connect to is [%s]",
                    listOfMachines.Data());

   //
   // Set fPathName
   //
   fPathName = "";

   if( slashPos != kNPOS) {
      fPathName = Urls;
      fPathName.Remove(0, slashPos);
   }

   // If at this point we have a strange pathfile, then it's bad
   if ( fPathName.CompareTo("/") == 0 ) {
      Error("TXUrl", "Malformed pathfile %s", fPathName.Data());
      fIsValid = kFALSE;
   }

   if(DebugLevel() >= kHIDEBUG)
      Info("TXUrl", "Remote file to open is [%s]", fPathName.Data());

   if (fIsValid) {
      ConvertDNSAliases(fUrlArray, listOfMachines, fPathName);

      if (fUrlArray.size() <= 0)
	 fIsValid = kFALSE;

      if(DebugLevel() >= kUSERDEBUG)
	 ShowUrls();
   }

}

//_____________________________________________________________________________
 TXUrl::~TXUrl()
{
   fTmpUrlArray.clear();

   for( UShort_t i=0; i < fUrlArray.size(); i++)
      SafeDelete( fUrlArray[i] );

   fUrlArray.clear();
}

//_____________________________________________________________________________
 TUrl *TXUrl::GetNextUrl()
{
   // Returns the next TUrl object pointer in the array.
   // After the last object is returned, the array is rewind-ed.
   // Now implemented as a pick from the tmpUrlArray queue

   TUrl *retval;

   if ( !fTmpUrlArray.size() ) Rewind();

   retval = fTmpUrlArray.back();

   fTmpUrlArray.pop_back();

   return retval;
}

//_____________________________________________________________________________
 void TXUrl::Rewind()
{
   // Rebuilds tmpUrlArray, i..e the urls that have to be picked
   fTmpUrlArray.clear();

   for( UShort_t i=0; i <= fUrlArray.size()-1; i++)
      fTmpUrlArray.push_back( fUrlArray[i] );
}

//_____________________________________________________________________________
 TUrl *TXUrl::GetARandomUrl()
{
   TUrl *retval;
   UInt_t rnd;

   for (int i=0; i < 10; i++)
      rnd = fRndgen.Integer(fTmpUrlArray.size());

   // Returns a random url from the ones that have to be picked
   // When all the urls have been picked, we restart from the full url set

   if ( !fTmpUrlArray.size() ) Rewind();

   UrlArray::iterator it = fTmpUrlArray.begin() + rnd;

   retval = *it;
   fTmpUrlArray.erase(it);

   return retval;
}

//_____________________________________________________________________________
 void TXUrl::ShowUrls()
{
   // Prints the list of urls

   Info("ShowUrls", "The converted URLs count is %d.", fUrlArray.size());

   for(UInt_t i=0; i < fUrlArray.size(); i++)
      Info("ShowUrls", "URL n.%d: %s .", i+1, fUrlArray[i]->GetUrl());

}

//_____________________________________________________________________________
 void TXUrl::CheckPort(TString &machine)
{
   // Checks the validity of port in the given host[:port]
   // Eventually completes the port if specified in the services file
   Short_t commaPos = machine.First(':');

   if(commaPos == kNPOS) {
      // Port not specified

      if(DebugLevel() >= kHIDEBUG)
	 Warning("checkPort",
		 "TCP port not specified for host %s. Trying to get it from /etc/services...", machine.Data());

      Int_t prt = gSystem->GetServiceByName("rootd");

      if(prt <= 0) {
	 if(DebugLevel() >= kHIDEBUG)
	    Warning("checkPort", "Service %s not specified in /etc/services; using default IANA tcp port 1094", PROTO);
	 machine += ":1094";
      } else {
         if (DebugLevel() >= kHIDEBUG)
     	    Info("checkPort", "Found tcp port %d in /etc/service", prt);

	 machine += ":";
	 machine += prt;
      }

   } else {
      // The port seems there

      TString tmp(machine);
      tmp.Remove(0, commaPos+1);

      TString sPort = tmp;

      if(sPort.CompareTo("") == 0)
	 Error("checkPort","The specified tcp port is 0 for %s", machine.Data());
      else {
	 for(Short_t i=0; i<=sPort.Length()-1; i++)
	    if(isdigit(sPort[i]) == 0) {
	       Error("checkPort","The specified tcp port is not numeric for %s", machine.Data());
	    }
      }
   }
}

//_____________________________________________________________________________
 Bool_t TXUrl::ConvertSingleDNSAlias(UrlArray& urls, TString hostname,
                                                    TString fname)
{
   // Converts a single host[:port] into an array of TUrls.
   // The new Turls are appended to the given UrlArray.

   Bool_t specifiedPort;
   Int_t port = 0;

   TString tmpaddr;

   specifiedPort = ( hostname.First(':') != kNPOS );

   hostname.Append("/fakefile");
   TUrl tmp(hostname.Data());

   if(specifiedPort) {
      port = tmp.GetPort();
      if(DebugLevel() >= kHIDEBUG)
	 Info("ConvertSingleDNSAlias","Resolving %s:%d.", tmp.GetHost(), port);
   } else
      if(DebugLevel() >= kHIDEBUG)
	 Info("ConvertSingleDNSAlias","Resolving %s.", tmp.GetHost());

   TInetAddress iaddr = gSystem->GetHostByName(tmp.GetHost());

   if(!iaddr.IsValid()) {
#ifndef R__WIN32
      Error("ConvertSingleDNSAlias","GetHostByName error for host %s (%s)",
	    tmp.GetHost(), hstrerror(h_errno));
#else
      Error("ConvertSingleDNSAlias","GetHostByName error for host %s",
	    tmp.GetHost());
#endif
      return kFALSE;
   }

   // Let's get the list of the addresses for the host
   TInetAddress::AddressList_t::const_iterator j =
                                    iaddr.GetAddresses().begin();
   for ( ; j != iaddr.GetAddresses().end(); j++) {

      const char *addr = TInetAddress::GetHostAddress(*j);
      TInetAddress c = gSystem->GetHostByName(addr);

      // Set the user for the child urls
      tmpaddr = "";
      if ( strlen(tmp.GetUser()) ) {
	 tmpaddr = tmp.GetUser();
	 tmpaddr += "@";
      }

      if (!strcmp(c.GetHostName(), "UnNamedHost"))
	 tmpaddr += addr;
      else
	 tmpaddr += c.GetHostName();

      if(DebugLevel() >= kHIDEBUG)
	 Info("ConvertSingleDNSAlias","Found host %s", tmpaddr.Data() );

      if(specifiedPort) {
	 char Port[7];
	 memset((void *)Port, 0, 7);
	 sprintf((char *)Port, "%d", port);
	 tmpaddr.Append(':');
	 tmpaddr.Append(Port);
      }

      if (fname.Length())
	 tmpaddr.Append(fname);

      urls.push_back( new TUrl(tmpaddr.Data()) );
   }

   return (urls.size() > 0);
}

//_____________________________________________________________________________
 void TXUrl::ConvertDNSAliases(UrlArray& urls, TString list, TString fname)
{
   // Given a list of comma-separated host[:port]
   // every entry is resolved via DNS into its aliases
   // The parameter is overwritten with the processed data
   Short_t colonPos;

   list += ",";

   while(list.Length() > 0) {
      colonPos = list.First(',');
      if (colonPos != kNPOS) {
	 TString tmp(list);

	 tmp.Remove(colonPos);
	 list.Remove(0,colonPos+1);

	 CheckPort(tmp);
	 ConvertSingleDNSAlias(urls, tmp, fname);
      }

   }



}


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.