// @(#)root/proof:$Name:  $:$Id: TSlave.cxx,v 1.22 2003/11/26 17:11:37 rdm Exp $
// Author: Fons Rademakers   14/02/97

/*************************************************************************
 * 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.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TSlave                                                               //
//                                                                      //
// This class describes a PROOF slave server.                           //
// It contains information like the slaves host name, ordinal number,   //
// performance index, socket, etc. Objects of this class can only be    //
// created via TProof member functions.                                 //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TSlave.h"
#include "TProof.h"
#include "TSocket.h"
#include "TSystem.h"
#include "TEnv.h"
#include "TROOT.h"
#include "TAuthenticate.h"
#include "TMessage.h"
#include "THostAuth.h"

ClassImp(TSlave)

//______________________________________________________________________________
 TSlave::TSlave(const char *host, Int_t port, Int_t ord, Int_t perf,
               const char *image, Int_t security, TProof *proof)
{
   // Create a PROOF slave object. Called via the TProof ctor.

   fName     = host;
   fPort     = port;
   fImage    = image;
   fWorkDir  = kPROOF_WorkDir;
   fOrdinal  = ord;
   fPerfIdx  = perf;
   fSecurity = security;
   fProof    = proof;
   fSocket   = 0;
   fInput    = 0;

   // Open connection to remote PROOF slave server.
   fSocket = new TSocket(host, port, 65536);  // make tcpwindosize configurable
   if (fSocket->IsValid()) {

      // Remove socket from global TROOT socket list. Only the TProof object,
      // representing all slave sockets, will be added to this list. This will
      // ensure the correct termination of all proof servers in case the
      // root session terminates.
      gROOT->GetListOfSockets()->Remove(fSocket);

      // Tell remote server to act as master or slave server
      if (proof->IsMaster())
         fSocket->Send("slave");
      else
         fSocket->Send("master");

      // Inquire remote protocol
      fSocket->Send(kROOTD_PROTOCOL);
      Int_t    ProofdProto, what;
      fSocket->Recv(ProofdProto, what);
      if (ProofdProto <= 0) {
         Warning("TSlave",
                 "got negative protocol from proofd (%d) ... may indicate problems",
                  ProofdProto);
         ProofdProto = 7;
         Warning("TSlave", "continuing with ProofdProto: %d)",ProofdProto);
      }

      TAuthenticate *auth;
      auth = new TAuthenticate(fSocket, host,
                               Form("%s:%d", proof->GetUrlProt(),ProofdProto), "");

      // If trasmission of the SRP password is requested make sure
      // that ReUse is switched on to get and send also the Public Key
      // For masters this is already checked in TProofServ
      if (!fProof->IsMaster()) {
         if (gEnv->GetValue("Proofd.SendSRPPwd",0)) {
            TString SRPDets(auth->GetHostAuth()->GetDetails(1));
            Int_t pos = SRPDets.Index("ru:0");
            if (pos > -1) {
               SRPDets.ReplaceAll("ru:0",4,"ru:1",4);
               auth->GetHostAuth()->SetDetails(1,SRPDets);
            } else {
               TSubString ss = SRPDets.SubString("ru:no",TString::kIgnoreCase);
               if (!ss.IsNull()) {
                  SRPDets.ReplaceAll(ss.Data(),5,"ru:1",4);
                  auth->GetHostAuth()->SetDetails(1,SRPDets);
               }
            }
         }
      }

      // Authenticate to proofd...
      if (!proof->IsMaster()) {

         // we are client... need full authentication procedure, either:
         // - via TAuthenticate::SetGlobalUser()/SetGlobalPasswd())
         // - ~/.rootnetrc or ~/.netrc
         // - interactive
         if (!auth->Authenticate()) {
            Error("TSlave", "authentication failed for %s@%s",
                            auth->GetUser(), host);
            delete auth;
            // This is to terminate properly remote proofd in case of failure
            fSocket->Send(Form("%d %s", gSystem->GetPid(), host), kROOTD_CLEANUP);
            SafeDelete(fSocket);
            return;
         }
         proof->fUser     = auth->GetRemoteLogin(auth->GetHostAuth(),
                                                 auth->GetSecurity(),auth->GetDetails());
         proof->fPasswd   = TAuthenticate::GetGlobalPasswd();
         proof->fPwHash   = TAuthenticate::GetGlobalPwHash();
         proof->fSRPPwd   = TAuthenticate::GetGlobalSRPPwd();
         proof->fSecurity = auth->GetSecurity();
         fUser            = proof->fUser;
         fSecurity        = proof->fSecurity;

         PDB(kGlobal,3) {
            auth->GetHostAuth()->PrintEstablished();
            Info("TSlave", "Remote Client: fUser is .... %s", proof->fUser.Data());
         }

      } else {

         // we are a master server... authenticate either:
         // - stored user/passwd (coming from client)
         // - ~/.rootnetrc or ~/.netrc
         // - but NOT interactive (obviously)

         // check for .rootnetrc
         // if not in .rootnetrc and SRP -> fail
         // if not in .rootnetrc and normal -> set global user/passwd
         // Checks for (user,passwd) are done inside TAuthenticate now (4/2003)

         if (!auth->Authenticate()) {
            Error("TSlave", "authentication failed for %s@%s",
                            auth->GetUser(), host);
            delete auth;
            // This is to terminate properly remote proofd in case of failure
            fSocket->Send(Form("%d %s", gSystem->GetPid(), host), kROOTD_CLEANUP);
            SafeDelete(fSocket);
            return;
         }

         proof->fUser     = auth->GetRemoteLogin(auth->GetHostAuth(),
                                                 auth->GetSecurity(),auth->GetDetails());
         proof->fPasswd   = auth->GetPasswd();
         proof->fPwHash   = auth->GetPwHash();
         proof->fSRPPwd   = auth->GetSRPPwd();
         proof->fSecurity = auth->GetSecurity();
         fUser            = proof->fUser;
         fSecurity        = proof->fSecurity;

         PDB(kGlobal,3) {
            auth->GetHostAuth()->PrintEstablished();
            Info("TSlave", "Master Server: fUser is .... %s", fUser.Data());
         }
      }

      // fSecurity is the method successfully tried ...
      fSecurity = auth->GetSecurity();

      char *Sdumm = 0;
      TString Details = auth->GetDetails();
      Int_t RemoteOffSet = auth->GetOffSet(auth,fSecurity,Details,&Sdumm);
      if (Sdumm) delete[] Sdumm;

      delete auth;

      char buf[512];

      fSocket->Recv(buf, sizeof(buf));

      if (strcmp(buf, "Okay")) {
         Printf("%s", buf);
         SafeDelete(fSocket);
      } else {
         // get back startup message of proofserv (we are now talking with
         // the real proofserver and not anymore with the proofd front-end)

         Int_t what;
         fSocket->Recv(buf, sizeof(buf), what);

         if (what == kMESS_NOTOK) {
            SafeDelete(fSocket);
            return;
         }

         // exchange protocol level between client and master and between
         // master and slave
         fSocket->Send(kPROOF_Protocol, kROOTD_PROTOCOL);
         fSocket->Recv(fProtocol, what);
         fProof->fProtocol = fProtocol;   // on master this is the protocol
                                          // of the last slave
         // send user name to remote host
         // for UsrPwd and SRP methods send also passwd, rsa encoded
         TMessage pubkey;
         TString passwd = "";
         Bool_t  pwhash = kFALSE;
         Bool_t  srppwd = kFALSE;
         Bool_t  sndsrp = kFALSE;

         if (!fProof->IsMaster()) {
            if (gEnv->GetValue("Proofd.SendSRPPwd",0))
               if (RemoteOffSet > -1)
                  sndsrp = kTRUE;
         } else {
            if (fProof->fSRPPwd && fProof->fPasswd != "")
               if (RemoteOffSet > -1)
                  sndsrp = kTRUE;
         }

         if (fSecurity == TAuthenticate::kClear ||
            (fSecurity == TAuthenticate::kSRP && sndsrp)) {

            // Send offset to identify remotely the public part of RSA key
            fSocket->Send(RemoteOffSet,kROOTD_RSAKEY);

            passwd = fProof->fPasswd;
            pwhash = fProof->fPwHash;
            srppwd = fProof->fSRPPwd;

            if (RemoteOffSet > -1) {

               // Password should be encoded before sending
               if (TAuthenticate::SecureSend(fSocket, 1, passwd) == -1) {
                   Warning("TSlave",
                      "problems secure-sending pass hash - may result in failures");
               }

            } else if (fSecurity == TAuthenticate::kClear) {
               // Non RSA encoding available: standard passwd inversion
               for (int i = 0; i < passwd.Length(); i++) {
                  char inv = ~passwd(i);
                  passwd.Replace(i, 1, inv);
               }
               TMessage mess;
               mess << passwd;
               fSocket->Send(mess);
            }

         } else {

            // Send notification of no offset to be sent ...
            fSocket->Send(-2,kROOTD_RSAKEY);

         }

         TMessage mess;
         if (!fProof->IsMaster())
            mess << fProof->fUser << pwhash << srppwd << fProof->fConfFile;
         else
            mess << fProof->fUser << pwhash << srppwd << fOrdinal;

         fSocket->Send(mess);

         if (ProofdProto > 6) {
            // Now we send authentication details to access, eg, data servers
            // not in the proof cluster and to be propagated to slaves.
            // This is triggered by the 'proofserv <dserv1> <dserv2> ...'
            // card in .rootauthrc
            if (!fProof->IsMaster())
               SendHostAuth(this, 0);
            else
               SendHostAuth(this, 1);
         }

         // set some socket options
         fSocket->SetOption(kNoDelay, 1);
      }
   } else
      SafeDelete(fSocket);
}

//______________________________________________________________________________
 TSlave::~TSlave()
{
   // Destroy slave.

   Close();
}

//______________________________________________________________________________
 void TSlave::Close(Option_t *)
{
   // Close slave socket.

   SafeDelete(fInput);
   SafeDelete(fSocket);
}

//______________________________________________________________________________
 Int_t TSlave::Compare(const TObject *obj) const
{
   // Used to sort slaves by performance index.

   TSlave *sl = (TSlave *) obj;

   if (fPerfIdx > sl->GetPerfIdx()) return 1;
   if (fPerfIdx < sl->GetPerfIdx()) return -1;
   return 0;
}

//______________________________________________________________________________
 void TSlave::Print(Option_t *) const
{
   // Printf info about slave.

   Printf("*** Slave %d  (%s)", fOrdinal, fSocket ? "valid" : "invalid");
   Printf("    Host name:               %s", GetName());
   Printf("    Port number:             %d", GetPort());
   Printf("    User:                    %s", GetUser());
   Printf("    Authentication method:   %d (%s)", GetSecurity(),
                                             TAuthenticate::GetAuthMethod(GetSecurity()));
   Printf("    Slave protocol version:  %d", GetProtocol());
   Printf("    Image name:              %s", GetImage());
   Printf("    Working directory:       %s", GetWorkDir());
   Printf("    Performance index:       %d", GetPerfIdx());
   Printf("    MB's processed:          %.2f", float(GetBytesRead())/(1024*1024));
   Printf("    MB's sent:               %.2f", fSocket ? float(fSocket->GetBytesRecv())/(1024*1024) : 0.0);
   Printf("    MB's received:           %.2f", fSocket ? float(fSocket->GetBytesSent())/(1024*1024) : 0.0);
   Printf("    Real time used (s):      %.3f", GetRealTime());
   Printf("    CPU time used (s):       %.3f", GetCpuTime());
}

//______________________________________________________________________________
 void TSlave::SetInputHandler(TFileHandler *ih)
{
   // Adopt and register input handler for this slave. Handler will be deleted
   // by the slave.

   fInput = ih;
   fInput->Add();
}

//______________________________________________________________________________
 Int_t TSlave::SendHostAuth(TSlave *sl, Int_t opt)
{
   // Sends the list of the relevant THostAuth objects to the master or
   // to the active slaves.

   int       retval = 0;
   int       bsiz = 0;
   char     *buf  = 0;
   TList    *authInfo = 0;

   // Get pointer to list with authentication info
   authInfo = TAuthenticate::GetAuthInfo();

   if (opt == 0) {
      // We are a client notifying the Master for additional nodes to be
      // considered (typically data servers not in the proof cluster ...).
      // The list is specified in .rootauthrc (or ROOTAUTHRC) under the key
      // 'proofserv'.
      char *net;
      if (gSystem->Getenv("ROOTAUTHRC")) {
         net = (char *)gSystem->Getenv("ROOTAUTHRC");
      } else {
         net = gSystem->ConcatFileName(gSystem->HomeDirectory(), ".rootauthrc");
      }
      PDB(kGlobal,3) Info("SendHostAuth","file: %s",net);

      // Check if file can be read ...
      //      if (gSystem->AccessPathName(net, kReadPermission)) { return 0; }

      if (!gSystem->AccessPathName(net, kReadPermission)) {

         // Open file
         FILE *fd = fopen(net, "r");

         // Scan it ...
         char line[kMAXPATHLEN], host[kMAXPATHLEN], key[kMAXPATHLEN], rest[kMAXPATHLEN];
         char *pnx = 0;
         int  cont = 0;
         while (fgets(line, sizeof(line), fd) != 0) {

           // Skip comment lines
           if (line[0] == '#') continue;
           // Get rid of end of line 'n', if there ...
           if (line[strlen(line)-1] == 'n') line[strlen(line)-1] = '0';
           if (cont == 0) {
              // scan line
              int nw= sscanf(line, "%s %s", key, rest);
              // no useful info provided for this line
              if (nw < 2) continue;
           }
           // This is the list we are looking for ...
           if (!strcmp(key, "proofserv") || cont == 1) {
              PDB(kGlobal,3)
                 Info("SendHostAuth","found proofserv: %s", rest);

              if (cont == 0) {
                 pnx = strstr(line, rest);
              } else if (cont == 1) {
                 pnx  = line;
                 cont = 0;
              }

              while (pnx != 0 && cont == 0) {
                 rest[0] = '0';
                 sscanf(pnx, "%s %s", host, rest);
                 PDB(kGlobal,3)
                    Info("SendHostAuth", "found host: %s %s (cont=%d)",
                          host, rest, cont);

                 // Check if a protocol is requested
                 char *pd1 = 0, *pd2 = 0;
                 char  meth[10] = { "" }, usr[256] = { "" };
                 int   met = -1;
                 pd1 = strchr(host,':');
                 if (pd1 != 0) pd2 = strchr(pd1+1, ':');
                 if (pd2 != 0) {
                    strcpy(meth, pd2+1);
                    if (strlen(meth) > 1) {
                       // Method passed as string: translate it to number
                       met = TAuthenticate::GetAuthMethodIdx(meth);
                       if (met == -1)
                          PDB(kGlobal,2)
                             Info("SendHostAuth",
                                  "unrecognized method (%s): ", meth);
                    } else {
                       met = atoi(meth);
                    }
                    int plen= (int)(pd2-host);
                    host[plen]='0';
                 }
                 if (pd1 != 0) {
                    strcpy(usr, pd1+1);
                    int plen = (int)(pd1-host);
                    host[plen]='0';
                 }
                 PDB(kGlobal,3)
                    Info("SendHostAuth",
                         "host user method: %s %s %d", host, usr, met);

                 // Get methods from file .rootauthrc
                 char **user; Int_t *nmeth,  *security[kMAXSEC]; char **details[kMAXSEC];
                 user = new char*[1]; user[0] = StrDup(usr);
                 Int_t Nuser =
                    TAuthenticate::GetAuthMeth(host, "root", &user, &nmeth, security, details);
                 // Now copy the info to send into buffer
                 int ju = 0;
                 for (ju = 0; ju < Nuser; ju++) {
                    int i = 0;
                    bsiz = strlen(host)+strlen(user[ju])+2+(nmeth[ju]+1)*3;
                    int jm = -1;
                    for (i = 0; i < nmeth[ju]; i++) {
                       bsiz += strlen(details[i][ju])+1;
                       if (security[i][ju] == met) jm = i;
                    }
                    bsiz += 20;
                    if (jm == -1) {
                       // Details for the method chosen were not found in the file
                       // Get defaults ...
                       char *newdet = TAuthenticate::GetDefaultDetails(met, 0, user[ju]);
                       bsiz += strlen(newdet)+1;
                       buf = new char[bsiz];
                       sprintf(buf,"h:%s u:%s n:%d", host, user[ju], nmeth[ju]+1);
                       sprintf(buf,"%s '%d %s' ", buf, met, newdet);
                       for (i = 0; i < nmeth[ju]; i++) {
                          sprintf(buf,"%s '%d %s' ", buf, security[i][ju], details[i][ju]);
                       }
                       delete [] newdet;
                    } else {
                       // Details for the method chosen were found in the file
                       // Put them first ...
                       buf = new char[bsiz];
                       sprintf(buf,"h:%s u:%s n:%d", host, user[ju], nmeth[ju]);
                       // First the one specified, if any
                       for (i = 0; i < nmeth[ju]; i++) {
                          if (security[i][ju] == met)
                             sprintf(buf,"%s '%d %s' ", buf, security[i][ju], details[i][ju]);
                       }
                       for (i = 0; i < nmeth[ju]; i++) {
                          if (security[i][ju] != met)
                             sprintf(buf,"%s '%d %s' ", buf, security[i][ju], details[i][ju]);
                       }
                    }
                    sl->GetSocket()->Send(buf, kPROOF_SENDHOSTAUTH);
                    delete [] buf;
                 }

                 // Got to next, if any
                 if (strlen(rest) > 0) {
                    // Check if there is a continuation line
                    if ((int)rest[0] == 92) cont = 1;
                    pnx = strstr(pnx, rest);
                 } else {
                    pnx = 0;
                 }
              }
           }
        }

        fclose(fd);

      }
      // End of transmission ...
      sl->GetSocket()->Send("END", kPROOF_SENDHOSTAUTH);


   } else if (opt == 1) {
      // We are a Master notifying the Slaves for nodes to be considered
      // This includes the other slaves and additional info received from the Client

      PDB(kGlobal,3)
         Info("SendHostAuth",
              "Number of HostAuth instantiations in memory: %d",
              authInfo->GetSize());

      // Loop over list of auth info
      if (authInfo->GetSize() > 0) {
         TIter next(authInfo);
         THostAuth *fHA;
         while ((fHA = (THostAuth*) next())) {
            PDB(kGlobal,3) fHA->Print();
            // Now copy the info to send into buffer
            int i = 0;
            int nmeth = fHA->NumMethods();
            bsiz = strlen(fHA->GetHost())+strlen(fHA->GetUser())+2+(nmeth+1)*3;
            for (i = 0; i < nmeth; i++) {
               bsiz += strlen(fHA->GetDetails(fHA->GetMethods(i)))+1;
            }
            bsiz += 20;
            buf = new char[bsiz];
            sprintf(buf,"h:%s u:%s n:%d",fHA->GetHost(),fHA->GetUser(),nmeth);
            for (i = 0; i < nmeth; i++) {
               sprintf(buf,"%s '%d %s' ", buf,
                       fHA->GetMethods(i), fHA->GetDetails(fHA->GetMethods(i)));
            }
            sl->GetSocket()->Send(buf, kPROOF_SENDHOSTAUTH);
            delete [] buf;
         }
      }

      // End of transmission ...
      sl->GetSocket()->Send("END", kPROOF_SENDHOSTAUTH);

   }
   return retval;
}


ROOT page - Class index - 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.