<commit_before>// @(#)root/proofd:$Id$
// Author: <NAME>  12/12/2005

/*************************************************************************
 * Copyright (C) 1995-2005, <NAME> and <NAME>.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// XrdProofdProtocol                                                    //
//                                                                      //
// Authors: <NAME>, CERN, 2005                                        //
//                                                                      //
// XrdProtocol implementation to coordinate 'proofserv' applications.   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "XrdProofdPlatform.h"

#ifdef OLDXRDOUC
#  include "XrdOuc/XrdOucError.hh"
#  include "XrdOuc/XrdOucLogger.hh"
#else
#  include "XrdSys/XrdSysError.hh"
#  include "XrdSys/XrdSysLogger.hh"
#endif
#include "XrdSys/XrdSysPriv.hh"
#include "XrdOuc/XrdOucStream.hh"

#include "XrdVersion.hh"
#include "Xrd/XrdBuffer.hh"
#include "XrdNet/XrdNetDNS.hh"

#include "XrdProofdClient.h"
#include "XrdProofdClientMgr.h"
#include "XrdProofdConfig.h"
#include "XrdProofdManager.h"
#include "XrdProofdNetMgr.h"
#include "XrdProofdPriorityMgr.h"
#include "XrdProofdProofServMgr.h"
#include "XrdProofdProtocol.h"
#include "XrdProofdResponse.h"
#include "XrdProofdProofServ.h"
#include "XrdProofSched.h"

// Tracing utils
#include "XrdProofdTrace.h"
XrdOucTrace          *XrdProofdTrace = 0;

// Loggers: we need two to avoid deadlocks
static XrdSysLogger   gMainLogger;

//
// Static area: general protocol managing section
int                   XrdProofdProtocol::fgCount    = 0;
XrdObjectQ<XrdProofdProtocol>
                      XrdProofdProtocol::fgProtStack("ProtStack",
                                                     "xproofd protocol anchor");
XrdSysRecMutex        XrdProofdProtocol::fgBMutex;    // Buffer management mutex
XrdBuffManager       *XrdProofdProtocol::fgBPool    = 0;
int                   XrdProofdProtocol::fgMaxBuffsz= 0;
XrdSysError           XrdProofdProtocol::fgEDest(0, "xpd");
XrdSysLogger         *XrdProofdProtocol::fgLogger   = 0;
//
// Static area: protocol configuration section
bool                  XrdProofdProtocol::fgConfigDone = 0;
//
int                   XrdProofdProtocol::fgReadWait = 0;
// Cluster manager
XrdProofdManager     *XrdProofdProtocol::fgMgr = 0;

// Effective uid
int                   XrdProofdProtocol::fgEUidAtStartup = -1;

// Local definitions
#define MAX_ARGS 128

// Macros used to set conditional options
#ifndef XPDCOND
#define XPDCOND(n,ns) ((n == -1 && ns == -1) || (n > 0 && n >= ns))
#endif
#ifndef XPDSETSTRING
#define XPDSETSTRING(n,ns,c,s) \
 { if (XPDCOND(n,ns)) { \
     SafeFree(c); c = strdup(s.c_str()); ns = n; }}
#endif

#ifndef XPDADOPTSTRING
#define XPDADOPTSTRING(n,ns,c,s) \
  { char *t = 0; \
    XPDSETSTRING(n, ns, t, s); \
    if (t && strlen(t)) { \
       SafeFree(c); c = t; \
  } else \
       SafeFree(t); }
#endif

#ifndef XPDSETINT
#define XPDSETINT(n,ns,i,s) \
 { if (XPDCOND(n,ns)) { \
     i = strtol(s.c_str(),0,10); ns = n; }}
#endif

typedef struct {
   kXR_int32 ptyp;  // must be always 0 !
   kXR_int32 rlen;
   kXR_int32 pval;
   kXR_int32 styp;
} hs_response_t;

typedef struct ResetCtrlcGuard {
   XrdProofdProtocol *xpd;
   int                type;
   ResetCtrlcGuard(XrdProofdProtocol *p, int t) : xpd(p), type(t) { }
   ~ResetCtrlcGuard() { if (xpd && type != kXP_ctrlc) xpd->ResetCtrlC(); }
} ResetCtrlcGuard_t;

//
// Derivation of XrdProofdConfig to read the port from the config file
class XrdProofdProtCfg : public XrdProofdConfig {
public:
   int  fPort; // The port on which we listen
   XrdProofdProtCfg(const char *cfg, XrdSysError *edest = 0);
   int  DoDirective(XrdProofdDirective *, char *, XrdOucStream *, bool);
   void RegisterDirectives();
};

//__________________________________________________________________________
XrdProofdProtCfg::XrdProofdProtCfg(const char *cfg, XrdSysError *edest)
                 : XrdProofdConfig(cfg, edest)
{
   // Constructor

   fPort = -1;
   RegisterDirectives();
}

//__________________________________________________________________________
void XrdProofdProtCfg::RegisterDirectives()
{
   // Register directives for configuration

   Register("port", new XrdProofdDirective("port", this, &DoDirectiveClass));
   Register("xrd.protocol", new XrdProofdDirective("xrd.protocol", this, &DoDirectiveClass));
}

//______________________________________________________________________________
int XrdProofdProtCfg::DoDirective(XrdProofdDirective *d,
                                  char *val, XrdOucStream *cfg, bool)
{
   // Parse directives

   if (!d) return -1;

   XrdOucString port(val);
   if (d->fName == "xrd.protocol") {
      port = cfg->GetWord();
      port.replace("xproofd:", "");
   } else if (d->fName != "port") {
      return -1;
   }
   if (port.length() > 0) {
      fPort = strtol(port.c_str(), 0, 10);
   }
   fPort = (fPort < 0) ? XPD_DEF_PORT : fPort;
   return 0;
}


extern "C" {
//_________________________________________________________________________________
XrdProtocol *XrdgetProtocol(const char *, char *parms, XrdProtocol_Config *pi)
{
   // This protocol is meant to live in a shared library. The interface below is
   // used by the server to obtain a copy of the protocol object that can be used
   // to decide whether or not a link is talking a particular protocol.

   // Return the protocol object to be used if static init succeeds
   if (XrdProofdProtocol::Configure(parms, pi)) {

      return (XrdProtocol *) new XrdProofdProtocol(pi);
   }
   return (XrdProtocol *)0;
}

//_________________________________________________________________________________
int XrdgetProtocolPort(const char * /*pname*/, char * /*parms*/, XrdProtocol_Config *pi)
{
      // This function is called early on to determine the port we need to use. The
      // The default is ostensibly 1093 but can be overidden; which we allow.

      XrdProofdProtCfg pcfg(pi->ConfigFN, pi->eDest);
      // Init some relevant quantities for tracing
      XrdProofdTrace = new XrdOucTrace(pi->eDest);
      pcfg.Config(0);

      // Default XPD_DEF_PORT (1093)
      int port = XPD_DEF_PORT;

      if (pcfg.fPort > 0) {
         port = pcfg.fPort;
      } else {
         port = (pi && pi->Port > 0) ? pi->Port : XPD_DEF_PORT;
      }

      return port;
}}

//__________________________________________________________________________________
XrdProofdProtocol::XrdProofdProtocol(XrdProtocol_Config *pi)
   : XrdProtocol("xproofd protocol handler"), fProtLink(this)
{
   // Protocol constructor
   fLink = 0;
   fArgp = 0;
   fPClient = 0;
   fSecClient = 0;
   fAuthProt = 0;
   fResponses.reserve(10);

   fStdErrFD = (pi && pi->eDest) ? pi->eDest->baseFD() : fileno(stderr);

   // Instantiate a Proofd protocol object
   Reset();
}

//______________________________________________________________________________
XrdProofdResponse *XrdProofdProtocol::Response(kXR_unt16 sid)
{
   // Get response instance corresponding to stream ID 'sid'
   XPDLOC(ALL, "Protocol::Response")

   TRACE(HDBG, "sid: "<<sid<<", size: "<<fResponses.size());

   if (sid > 0)
      if (sid <= fResponses.size())
         return fResponses[sid-1];

   return (XrdProofdResponse *)0;
}

//______________________________________________________________________________
XrdProofdResponse *XrdProofdProtocol::GetNewResponse(kXR_unt16 sid)
{
   // Create new response instance for stream ID 'sid'
   XPDLOC(ALL, "Protocol::GetNewResponse")

   XrdOucString msg;
   XPDFORM(msg, "sid: %d", sid);
   if (sid > 0) {
      if (sid > fResponses.size()) {
         if (sid > fResponses.capacity()) {
            int newsz = (sid < 2 * fResponses.capacity()) ? 2 * fResponses.capacity() : sid+1 ;
            fResponses.reserve(newsz);
            if (TRACING(DBG)) {
               msg += " new capacity: ";
               msg += (int) fResponses.capacity();
            }
        }
         int nnew = sid - fResponses.size();
         while (nnew--)
            fResponses.push_back(new XrdProofdResponse());
         if (TRACING(DBG)) {
            msg += "; new size: ";
            msg += (int) fResponses.size();
         }
      }
   } else {
      TRACE(XERR,"wrong sid: "<<sid);
      return (XrdProofdResponse *)0;
   }

   TRACE(DBG, msg);

   // Done
   return fResponses[sid-1];
}

//______________________________________________________________________________
XrdProtocol *XrdProofdProtocol::Match(XrdLink *lp)
{
   // Check whether the request matches this protocol
   XPDLOC(ALL, "Protocol::Match")

   struct ClientInitHandShake hsdata;
   char  *hsbuff = (char *)&hsdata;

   static hs_response_t hsresp = {0, 0, htonl(XPROOFD_VERSBIN), 0};

   XrdProofdProtocol *xp;
   int dlen;

   // Peek at the first 20 bytes of data
   if ((dlen = lp->Peek(hsbuff,sizeof(hsdata),fgReadWait)) != sizeof(hsdata)) {
      if (dlen <= 0) lp->setEtext("Match: handshake not received");
      if (dlen == 12) {
         // Check if it is a request to open a file via 'rootd'
         hsdata.first = ntohl(hsdata.first);
         if (hsdata.first == 8) {
            if (strlen(fgMgr->RootdExe()) > 0) {
               if (fgMgr->IsRootdAllowed((const char *)lp->Host())) {

                  const char *prog = fgMgr->RootdExe();
                  const char **progArg = fgMgr->RootdArgs();

                  TRACE(ALL, "matched rootd protocol on link: executing "<<prog);

                  // Fork a process to handle this protocol
                  pid_t pid;
                  if ((pid = fgMgr->Sched()->Fork(lp->Name()))) {
                     if (pid < 0) {
                        lp->setEtext("rootd fork failed");
                     } else {
                        lp->setEtext("link transfered");
                     }
                     return (XrdProtocol *)0;
                  }

                  // Restablish standard error for the program we will exec
                  dup2(fStdErrFD, STDERR_FILENO);
                  close(fStdErrFD);

                  // Force stdin/out to point to the socket FD (this will also bypass the
                  // close on exec setting for the socket)
                  dup2(lp->FDnum(), STDIN_FILENO);
                  dup2(lp->FDnum(), STDOUT_FILENO);

                  // Do the exec
                  execv((const char *)prog, (char * const *)progArg);
                  cerr <<"Xrdrootd: Oops! Exec(" <<prog <<") failed; errno=" <<errno <<endl;
                  _exit(17);
               } else {
                  TRACE(ALL, "rootd-file serving not authorized for host '"<<lp->Host()<<"'");
               }
            } else {
               TRACE(ALL, "rootd-file serving not enabled");
            }
         }
      }
      return (XrdProtocol *)0;
   }

   // Verify that this is our protocol
   hsdata.third  = ntohl(hsdata.third);
   if (dlen != sizeof(hsdata) ||  hsdata.first || hsdata.second
       || !(hsdata.third == 1) || hsdata.fourth || hsdata.fifth) return 0;

   // Respond to this request with the handshake response
   if (!lp->Send((char *)&hsresp, sizeof(hsresp))) {
      lp->setEtext("Match: handshake failed");
      return (XrdProtocol *)0;
   }

   // We can now read all 20 bytes and discard them (no need to wait for it)
   int len = sizeof(hsdata);
   if (lp->Recv(hsbuff, len) != len) {
      lp->setEtext("Match: reread failed");
      return (XrdProtocol *)0;
   }

   // Get a protocol object off the stack (if none, allocate a new one)
   if (!(xp = fgProtStack.Pop()))
      xp = new XrdProofdProtocol();

   // Bind the protocol to the link and return the protocol
   xp->fLink = lp;
   strcpy(xp->fSecEntity.prot, "host");
   xp->fSecEntity.host = strdup((char *)lp->Host());

   // Dummy data used by 'proofd'
   kXR_int32 dum[2];
   if (xp->GetData("dummy",(char *)&dum[0],sizeof(dum)) != 0) {
      xp->Recycle(0,0,0);
      return (XrdProtocol *)0;
   }

   // We are done
   return (XrdProtocol *)xp;
}
<commit_msg>Improve notifications when starting rootd to serve a file<commit_after>// @(#)root/proofd:$Id$
// Author: <NAME>  12/12/2005

/*************************************************************************
 * Copyright (C) 1995-2005, <NAME> and <NAME>.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// XrdProofdProtocol                                                    //
//                                                                      //
// Authors: <NAME>, CERN, 2005                                        //
//                                                                      //
// XrdProtocol implementation to coordinate 'proofserv' applications.   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "XrdProofdPlatform.h"

#ifdef OLDXRDOUC
#  include "XrdOuc/XrdOucError.hh"
#  include "XrdOuc/XrdOucLogger.hh"
#else
#  include "XrdSys/XrdSysError.hh"
#  include "XrdSys/XrdSysLogger.hh"
#endif
#include "XrdSys/XrdSysPriv.hh"
#include "XrdOuc/XrdOucStream.hh"

#include "XrdVersion.hh"
#include "Xrd/XrdBuffer.hh"
#include "XrdNet/XrdNetDNS.hh"

#include "XrdProofdClient.h"
#include "XrdProofdClientMgr.h"
#include "XrdProofdConfig.h"
#include "XrdProofdManager.h"
#include "XrdProofdNetMgr.h"
#include "XrdProofdPriorityMgr.h"
#include "XrdProofdProofServMgr.h"
#include "XrdProofdProtocol.h"
#include "XrdProofdResponse.h"
#include "XrdProofdProofServ.h"
#include "XrdProofSched.h"

// Tracing utils
#include "XrdProofdTrace.h"
XrdOucTrace          *XrdProofdTrace = 0;

// Loggers: we need two to avoid deadlocks
static XrdSysLogger   gMainLogger;

//
// Static area: general protocol managing section
int                   XrdProofdProtocol::fgCount    = 0;
XrdObjectQ<XrdProofdProtocol>
                      XrdProofdProtocol::fgProtStack("ProtStack",
                                                     "xproofd protocol anchor");
XrdSysRecMutex        XrdProofdProtocol::fgBMutex;    // Buffer management mutex
XrdBuffManager       *XrdProofdProtocol::fgBPool    = 0;
int                   XrdProofdProtocol::fgMaxBuffsz= 0;
XrdSysError           XrdProofdProtocol::fgEDest(0, "xpd");
XrdSysLogger         *XrdProofdProtocol::fgLogger   = 0;
//
// Static area: protocol configuration section
bool                  XrdProofdProtocol::fgConfigDone = 0;
//
int                   XrdProofdProtocol::fgReadWait = 0;
// Cluster manager
XrdProofdManager     *XrdProofdProtocol::fgMgr = 0;

// Effective uid
int                   XrdProofdProtocol::fgEUidAtStartup = -1;

// Local definitions
#define MAX_ARGS 128

// Macros used to set conditional options
#ifndef XPDCOND
#define XPDCOND(n,ns) ((n == -1 && ns == -1) || (n > 0 && n >= ns))
#endif
#ifndef XPDSETSTRING
#define XPDSETSTRING(n,ns,c,s) \
 { if (XPDCOND(n,ns)) { \
     SafeFree(c); c = strdup(s.c_str()); ns = n; }}
#endif

#ifndef XPDADOPTSTRING
#define XPDADOPTSTRING(n,ns,c,s) \
  { char *t = 0; \
    XPDSETSTRING(n, ns, t, s); \
    if (t && strlen(t)) { \
       SafeFree(c); c = t; \
  } else \
       SafeFree(t); }
#endif

#ifndef XPDSETINT
#define XPDSETINT(n,ns,i,s) \
 { if (XPDCOND(n,ns)) { \
     i = strtol(s.c_str(),0,10); ns = n; }}
#endif

typedef struct {
   kXR_int32 ptyp;  // must be always 0 !
   kXR_int32 rlen;
   kXR_int32 pval;
   kXR_int32 styp;
} hs_response_t;

typedef struct ResetCtrlcGuard {
   XrdProofdProtocol *xpd;
   int                type;
   ResetCtrlcGuard(XrdProofdProtocol *p, int t) : xpd(p), type(t) { }
   ~ResetCtrlcGuard() { if (xpd && type != kXP_ctrlc) xpd->ResetCtrlC(); }
} ResetCtrlcGuard_t;

//
// Derivation of XrdProofdConfig to read the port from the config file
class XrdProofdProtCfg : public XrdProofdConfig {
public:
   int  fPort; // The port on which we listen
   XrdProofdProtCfg(const char *cfg, XrdSysError *edest = 0);
   int  DoDirective(XrdProofdDirective *, char *, XrdOucStream *, bool);
   void RegisterDirectives();
};

//__________________________________________________________________________
XrdProofdProtCfg::XrdProofdProtCfg(const char *cfg, XrdSysError *edest)
                 : XrdProofdConfig(cfg, edest)
{
   // Constructor

   fPort = -1;
   RegisterDirectives();
}

//__________________________________________________________________________
void XrdProofdProtCfg::RegisterDirectives()
{
   // Register directives for configuration

   Register("port", new XrdProofdDirective("port", this, &DoDirectiveClass));
   Register("xrd.protocol", new XrdProofdDirective("xrd.protocol", this, &DoDirectiveClass));
}

//______________________________________________________________________________
int XrdProofdProtCfg::DoDirective(XrdProofdDirective *d,
                                  char *val, XrdOucStream *cfg, bool)
{
   // Parse directives

   if (!d) return -1;

   XrdOucString port(val);
   if (d->fName == "xrd.protocol") {
      port = cfg->GetWord();
      port.replace("xproofd:", "");
   } else if (d->fName != "port") {
      return -1;
   }
   if (port.length() > 0) {
      fPort = strtol(port.c_str(), 0, 10);
   }
   fPort = (fPort < 0) ? XPD_DEF_PORT : fPort;
   return 0;
}


extern "C" {
//_________________________________________________________________________________
XrdProtocol *XrdgetProtocol(const char *, char *parms, XrdProtocol_Config *pi)
{
   // This protocol is meant to live in a shared library. The interface below is
   // used by the server to obtain a copy of the protocol object that can be used
   // to decide whether or not a link is talking a particular protocol.

   // Return the protocol object to be used if static init succeeds
   if (XrdProofdProtocol::Configure(parms, pi)) {

      return (XrdProtocol *) new XrdProofdProtocol(pi);
   }
   return (XrdProtocol *)0;
}

//_________________________________________________________________________________
int XrdgetProtocolPort(const char * /*pname*/, char * /*parms*/, XrdProtocol_Config *pi)
{
      // This function is called early on to determine the port we need to use. The
      // The default is ostensibly 1093 but can be overidden; which we allow.

      XrdProofdProtCfg pcfg(pi->ConfigFN, pi->eDest);
      // Init some relevant quantities for tracing
      XrdProofdTrace = new XrdOucTrace(pi->eDest);
      pcfg.Config(0);

      // Default XPD_DEF_PORT (1093)
      int port = XPD_DEF_PORT;

      if (pcfg.fPort > 0) {
         port = pcfg.fPort;
      } else {
         port = (pi && pi->Port > 0) ? pi->Port : XPD_DEF_PORT;
      }

      return port;
}}

//__________________________________________________________________________________
XrdProofdProtocol::XrdProofdProtocol(XrdProtocol_Config *pi)
   : XrdProtocol("xproofd protocol handler"), fProtLink(this)
{
   // Protocol constructor
   fLink = 0;
   fArgp = 0;
   fPClient = 0;
   fSecClient = 0;
   fAuthProt = 0;
   fResponses.reserve(10);

   fStdErrFD = (pi && pi->eDest) ? pi->eDest->baseFD() : fileno(stderr);

   // Instantiate a Proofd protocol object
   Reset();
}

//______________________________________________________________________________
XrdProofdResponse *XrdProofdProtocol::Response(kXR_unt16 sid)
{
   // Get response instance corresponding to stream ID 'sid'
   XPDLOC(ALL, "Protocol::Response")

   TRACE(HDBG, "sid: "<<sid<<", size: "<<fResponses.size());

   if (sid > 0)
      if (sid <= fResponses.size())
         return fResponses[sid-1];

   return (XrdProofdResponse *)0;
}

//______________________________________________________________________________
XrdProofdResponse *XrdProofdProtocol::GetNewResponse(kXR_unt16 sid)
{
   // Create new response instance for stream ID 'sid'
   XPDLOC(ALL, "Protocol::GetNewResponse")

   XrdOucString msg;
   XPDFORM(msg, "sid: %d", sid);
   if (sid > 0) {
      if (sid > fResponses.size()) {
         if (sid > fResponses.capacity()) {
            int newsz = (sid < 2 * fResponses.capacity()) ? 2 * fResponses.capacity() : sid+1 ;
            fResponses.reserve(newsz);
            if (TRACING(DBG)) {
               msg += " new capacity: ";
               msg += (int) fResponses.capacity();
            }
        }
         int nnew = sid - fResponses.size();
         while (nnew--)
            fResponses.push_back(new XrdProofdResponse());
         if (TRACING(DBG)) {
            msg += "; new size: ";
            msg += (int) fResponses.size();
         }
      }
   } else {
      TRACE(XERR,"wrong sid: "<<sid);
      return (XrdProofdResponse *)0;
   }

   TRACE(DBG, msg);

   // Done
   return fResponses[sid-1];
}

//______________________________________________________________________________
XrdProtocol *XrdProofdProtocol::Match(XrdLink *lp)
{
   // Check whether the request matches this protocol
   XPDLOC(ALL, "Protocol::Match")

   struct ClientInitHandShake hsdata;
   char  *hsbuff = (char *)&hsdata;

   static hs_response_t hsresp = {0, 0, htonl(XPROOFD_VERSBIN), 0};

   XrdProofdProtocol *xp;
   int dlen;
   TRACE(ALL, "enter");

   // Peek at the first 20 bytes of data
   if ((dlen = lp->Peek(hsbuff,sizeof(hsdata),fgReadWait)) != sizeof(hsdata)) {
      if (dlen <= 0) lp->setEtext("Match: handshake not received");
      if (dlen == 12) {
         // Check if it is a request to open a file via 'rootd'
         hsdata.first = ntohl(hsdata.first);
         if (hsdata.first == 8) {
            if (strlen(fgMgr->RootdExe()) > 0) {
               if (fgMgr->IsRootdAllowed((const char *)lp->Host())) {

                  const char *prog = fgMgr->RootdExe();
                  const char **progArg = fgMgr->RootdArgs();

                  TRACE(ALL, "matched rootd protocol on link: executing "<<prog);

                  // Fork a process to handle this protocol
                  pid_t pid;
                  if ((pid = fgMgr->Sched()->Fork(lp->Name()))) {
                     if (pid < 0) {
                        lp->setEtext("rootd fork failed");
                     } else {
                        lp->setEtext("link transfered");
                     }
                     return (XrdProtocol *)0;
                  }

                  // Restablish standard error for the program we will exec
                  dup2(fStdErrFD, STDERR_FILENO);
                  close(fStdErrFD);

                  // Force stdin/out to point to the socket FD (this will also bypass the
                  // close on exec setting for the socket)
                  dup2(lp->FDnum(), STDIN_FILENO);
                  dup2(lp->FDnum(), STDOUT_FILENO);

                  // Do the exec
                  execv((const char *)prog, (char * const *)progArg);
                  TRACE(ALL, "rootd: Oops! Exec(" <<prog <<") failed; errno: " <<errno);
                  _exit(17);
               } else {
                  TRACE(ALL, "rootd-file serving not authorized for host '"<<lp->Host()<<"'");
               }
            } else {
               TRACE(ALL, "rootd-file serving not enabled");
            }
         }
      }
      TRACE(XERR, "peeked incomplete or empty information! (dlen: "<<dlen<<" bytes)");
      return (XrdProtocol *)0;
   }

   // Verify that this is our protocol
   hsdata.third  = ntohl(hsdata.third);
   if (dlen != sizeof(hsdata) ||  hsdata.first || hsdata.second
       || !(hsdata.third == 1) || hsdata.fourth || hsdata.fifth) return 0;

   // Respond to this request with the handshake response
   if (!lp->Send((char *)&hsresp, sizeof(hsresp))) {
      lp->setEtext("Match: handshake failed");
      TRACE(XERR, "handshake failed");
      return (XrdProtocol *)0;
   }

   // We can now read all 20 bytes and discard them (no need to wait for it)
   int len = sizeof(hsdata);
   if (lp->Recv(hsbuff, len) != len) {
      lp->setEtext("Match: reread failed");
      TRACE(XERR, "reread failed");
      return (XrdProtocol *)0;
   }

   // Get a protocol object off the stack (if none, allocate a new one)
   if (!(xp = fgProtStack.Pop()))
      xp = new XrdProofdProtocol();

   // Bind the protocol to the link and return the protocol
   xp->fLink = lp;
   strcpy(xp->fSecEntity.prot, "host");
   xp->fSecEntity.host = strdup((char *)lp->Host());

   // Dummy data used by 'proofd'
   kXR_int32 dum[2];
   if (xp->GetData("dummy",(char *)&dum[0],sizeof(dum)) != 0) {
      xp->Recycle(0,0,0);
      return (XrdProtocol *)0;
   }

   // We are done
   return (XrdProtocol *)xp;
}

{'fraction_non_alphanumeric': 0.14203387172651716, 'fraction_numerical': 0.009722440018817625, 'mean_word_length': 2.2367719832508564, 'pattern_counts': {'":': 0, '<': 71, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 48, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3504534', 'n_tokens_mistral': 8027, 'n_tokens_neox': 7603, 'n_words': 1914}