diff --git a/src/XProtocol/XProtocol.hh b/src/XProtocol/XProtocol.hh index dee4c45a944..74c1ce11e4b 100644 --- a/src/XProtocol/XProtocol.hh +++ b/src/XProtocol/XProtocol.hh @@ -89,6 +89,16 @@ #define kXR_attrProxy 0x00000200 #define kXR_attrSuper 0x00000400 +#define kXR_haveTls 0x80000000 +#define kXR_tlsAll 0x7f000000 +#define kXR_tlsGPFile 0x40000000 +#define kXR_tlsLogin 0x20000000 +#define kXR_tlsModFS 0x10000000 +#define kXR_tlsOpenR 0x08000000 +#define kXR_tlsOpenW 0x04000000 +#define kXR_tlsTPC 0x02000000 +#define kXR_tlsXXX 0x01000000 + #define kXR_maxReqRetry 10 // Kind of error inside a XTNetFile's routine (temporary) @@ -233,7 +243,8 @@ enum XOpenRequestOption { }; enum XProtocolRequestFlags { - kXR_secreqs = 1 // Return security requirements + kXR_secreqs = 1, // Options: Return security requirements + kXR_wantTls = 2 // Options: Change connection to use TLS }; enum XQueryType { diff --git a/src/Xrd/LinkTLS.cc b/src/Xrd/LinkTLS.cc new file mode 100644 index 00000000000..c242c3c0ce3 --- /dev/null +++ b/src/Xrd/LinkTLS.cc @@ -0,0 +1,268 @@ +/******************************************************************************/ +/* S F E r r o r */ +/******************************************************************************/ + +int XrdLinkXeq::SFError(int rc) +{ + Log.Emsg("TLS_Link", rc, "send file to", ID); + return -1; +} + +/******************************************************************************/ +/* Private: T L S _ E r r o r */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Error(const char *act, int rc) +{ + std::string reason = tlsIO.Err2Text(rc); + char msg[512]; + + snprintf(msg, sizeof(msg), "Unable to %s %s;", act, ID); + Log.Emsg("TLS_Link", msg, reason.c_str()); + return -1; +} + +/******************************************************************************/ +/* T L S _ P e e k */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Peek(char *Buff, int Blen, int timeout) +{ + XrdSysMutexHelper theMutex; + int retc, rlen; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Wait until we can actually read something +// + isIdle = 0; + if (timeout) + {retc = Wait4Data(timeout); + if (retc < 1) return retc; + } + +// Do the peek and if sucessful, the number of bytes available. +// + retc = tlsIO.Peek(Buff, Blen, rlen); + if (retc == SSL_ERROR_NONE) return rlen; + +// Dianose the TLS error and return failure +// + return TLS_Error("peek on", retc); +} + +/******************************************************************************/ +/* T L S _ R e c v */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Recv(char *Buff, int Blen) +{ + XrdSysMutexHelper theMutex; + int retc, rlen; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Note that we will read only as much as is queued. Use Recv() with a +// timeout to receive as much data as possible. +// + isIdle = 0; + retc = tlsIO.Read(Buff, Blen, rlen); + if (retc != SSL_ERROR_NONE) return TLS_Error("receive from", retc); + if (rlen > 0) AtomicAdd(BytesIn, rlen); + return rlen; +} + +/******************************************************************************/ + +int XrdLinkXeq::TLS_Recv(char *Buff, int Blen, int timeout) +{ + XrdSysMutexHelper theMutex; + int retc, rlen, totlen = 0, maxnulls = 3; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Wait up to timeout milliseconds for data to arrive +// + isIdle = 0; + while(Blen > 0) + {retc = Wait4Data(timeout); + if (retc < 1) + {if (retc < 0) return -1; + tardyCnt++; + if (totlen) + {if ((++stallCnt & 0xff) == 1) TRACEI(DEBUG,"read timed out"); + AtomicAdd(BytesIn, totlen); + } + return totlen; + } + + // Read as much data as you can. Note that we will force an error + // if we get a zero-length read after poll said it was OK. + // + retc = tlsIO.Read(Buff, Blen, rlen); + if (retc != SSL_ERROR_NONE) + {AtomicAdd(BytesIn, totlen); + return TLS_Error("receive from", retc); + } + if (rlen <= 0 && (maxnulls-- < 1)) return -ENOMSG; + totlen += rlen; Blen -= rlen; Buff += rlen; + } + + AtomicAdd(BytesIn, totlen); + return totlen; +} + +/******************************************************************************/ +/* T L S _ R e c v A l l */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_RecvAll(char *Buff, int Blen, int timeout) +{ + int retc; + +// Check if timeout specified. Notice that the timeout is the max we will +// wait for some data. We will wait forever for all the data. Yeah, it's weird. +// + if (timeout >= 0) + {retc = Wait4Data(timeout); + if (retc != 1) return (retc ? -1 : -ETIMEDOUT); + } + +// Note that we will block until we receive all the bytes. +// + return Recv(Buff, Blen, -1); +} + +/******************************************************************************/ +/* T L S _ S e n d */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Send(const char *Buff, int Blen) +{ + XrdSysMutexHelper lck(wrMutex); + ssize_t bytesleft = Blen; + int retc, byteswritten; + +// Prepare to send +// + isIdle = 0; + AtomicAdd(BytesOut, Blen); + +// Do non-blocking writes if we are setup to do so. +// + if (sendQ) return sendQ->Send(Buff, Blen); + +// Write the data out +// + while(bytesleft) + {retc = tlsIO.write(Buff, bytesleft, byteswrittten); + if (retc != SSL_ERROR_NONE) return TLS_Error("send to", retc); + bytesleft -= byteswritten; Buff += byteswritten; + } + +// All done +// + return Blen; +} + +/******************************************************************************/ + +int XrdLinkXeq::TLS_Send(const struct iovec *iov, int iocnt, int bytes) +{ + XrdSysMutexHelper lck(wrMutex); + int retc; + +// Get a lock and assume we will be successful (statistically we are). Note +// that the calling interface gauranteed bytes are not zero. +// + isIdle = 0; + AtomicAdd(BytesOut, bytes); + +// Do non-blocking writes if we are setup to do so. +// + if (sendQ) return sendQ->Send(iov, iocnt, bytes); + +// Write the data out. +// + for (int i = 0; i < iocnt; i++) + {ssize_t bytesleft = iov[i].iov_len; + char *Buff = iov[i].iov_base; + while(bytesleft) + {retc = tlsIO.write(Buff, bytesleft, byteswrittten); + if (retc != SSL_ERROR_NONE) return TLS_Error("send to", retc); + bytesleft -= byteswritten; Buff += byteswritten; + } + } + +// All done +// + return bytes; +} + +/******************************************************************************/ + +int XrdLinkXeq::TLS_Send(const sfVec *sfP, int sfN) +{ + XrdSysMutexHelper lck(wrMutex); + int bytes, buffsz, fileFD; + off_t offset; + char myBuff[65536]; + +// Convert the sendfile to a regular send. The conversion is not particularly +// fast and caller are advised to avoid using sendfile on TLS connections. +// + isIdle = 0; + for (int i = 0; i < sfN; sfP++, i++) + {if (!(bytes = sfP->sendsz)) continue; + totamt += bytes; + if (sfP->fdnum < 0) + {if (!Write2TLS(sfP->buffer, bytes) return -1; + continue; + } + offset = (off_t)sfP->buffer; + fileFD = sfP->fdnum; + buffsz = (bytes < (int)sizeof(myBuff) ? bytes : sizeof(myBuff)); + do {do {retc = pread(fileFD, myBuff, buffsz, offset);} + while(retc < 0 && errno == EINTR); + if (retc < 0) return SFError(errno); + if (retc != buffsz) return SFError(EOVERFLOW); + if (!TLS_Write(myBuff, buffsz)) return -1; + offset += buffsz; bytes -= buffsz; + } while(bytes > 0); + } + +// We are done +// + AtomicAdd(BytesOut, totamt); + return totamt; +} + +/******************************************************************************/ +/* Private: T L S _ W r i t e */ +/******************************************************************************/ + +bool XrdLinkXeq::TLS_Write(const char *Buff, int Blen) +{ + int retc, byteswritten; + +// Write the data out +// + while(Blen) + {retc = tlsIO.write(Buff, Blen, byteswrittten); + if (retc != SSL_ERROR_NONE) + {TLS_Error("send to", retc); + return false; + } + Blen -= byteswritten; Buff += byteswritten; + } + +// All done +// + return true; +} diff --git a/src/Xrd/XrdConfig.cc b/src/Xrd/XrdConfig.cc index 2a40b11f2a0..a2b7fe0cc0e 100644 --- a/src/Xrd/XrdConfig.cc +++ b/src/Xrd/XrdConfig.cc @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -51,8 +52,10 @@ #include "Xrd/XrdConfig.hh" #include "Xrd/XrdInfo.hh" #include "Xrd/XrdLink.hh" +#include "Xrd/XrdLinkCtl.hh" #include "Xrd/XrdPoll.hh" #include "Xrd/XrdStats.hh" +#include "Xrd/XrdTrace.hh" #include "XrdNet/XrdNetAddr.hh" #include "XrdNet/XrdNetIF.hh" @@ -66,6 +69,7 @@ #include "XrdOuc/XrdOucStream.hh" #include "XrdOuc/XrdOucUtils.hh" +#include "XrdSys/XrdSysFD.hh" #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysTimer.hh" #include "XrdSys/XrdSysUtils.hh" @@ -80,10 +84,23 @@ #endif /******************************************************************************/ -/* S t a t i c O b j e c t s */ +/* G l o b a l O b j e c t s */ /******************************************************************************/ - - const char *XrdConfig::TraceID = "Config"; + +namespace XrdGlobal +{ +extern XrdSysLogger Logger; +extern XrdSysError Log; +extern XrdOucTrace XrdTrace; +extern XrdScheduler Sched; +extern XrdBuffManager BuffPool; +extern XrdTlsContext *tlsCtx; +extern XrdInet *XrdNetTCP; +extern XrdBuffXL xlBuff; +extern int devNull; +}; + +using namespace XrdGlobal; namespace XrdNetSocketCFG { @@ -91,18 +108,21 @@ extern int ka_Idle; extern int ka_Itvl; extern int ka_Icnt; }; - -namespace XrdGlobal -{ -extern XrdBuffXL xlBuff; - - XrdTlsContext *tlsCtx = 0; -} + +/******************************************************************************/ +/* F i l e L o c a l O b j e c t s */ +/******************************************************************************/ namespace { XrdOucEnv theEnv; }; + +/******************************************************************************/ +/* S t a t i c M e m b e r s */ +/******************************************************************************/ + + const char *XrdConfig::TraceID = "Config"; /******************************************************************************/ /* d e f i n e s */ @@ -147,8 +167,7 @@ int wanopt; /* C o n s t r u c t o r */ /******************************************************************************/ -XrdConfig::XrdConfig() : Log(&Logger, "Xrd"), Trace(&Log), Sched(&Log, &Trace), - BuffPool(&Log, &Trace) +XrdConfig::XrdConfig() { // Preset all variables with common defaults @@ -190,7 +209,7 @@ XrdConfig::XrdConfig() : Log(&Logger, "Xrd"), Trace(&Log), Sched(&Log, &Trace), ProtInfo.Sched = &Sched; // Stable -> System Scheduler ProtInfo.ConfigFN= 0; // We will fill this in later ProtInfo.Stats = 0; // We will fill this in later - ProtInfo.Trace = &Trace; // Stable -> Trace Information + ProtInfo.Trace = &XrdTrace; // Stable -> Trace Information ProtInfo.AdmPath = AdminPath; // Stable -> The admin path ProtInfo.AdmMode = AdminMode; // Stable -> The admin path mode ProtInfo.theEnv = &theEnv; // Additional information @@ -302,7 +321,7 @@ int XrdConfig::Configure(int argc, char **argv) case 'c': if (ConfigFN) free(ConfigFN); ConfigFN = strdup(optarg); break; - case 'd': Trace.What |= TRACE_ALL; + case 'd': XrdTrace.What |= TRACE_ALL; ProtInfo.DebugON = 1; XrdOucEnv::Export("XRDDEBUG", "1"); break; @@ -496,6 +515,14 @@ int XrdConfig::Configure(int argc, char **argv) // Log.Say("++++++ ", myInstance, " initialization started."); +// Allocate /dev/null as we need it and can't live without it +// + devNull = XrdSysFD_Open("/dev/null", O_RDONLY); + if (devNull < 0) + {Log.Emsg("Config", errno, "open '/dev/null' which is required!"); + NoGo = 1; + } + // Process the configuration file, if one is present // if (ConfigFN && *ConfigFN) @@ -506,7 +533,7 @@ int XrdConfig::Configure(int argc, char **argv) } if (clPort >= 0) PortTCP = clPort; if (ProtInfo.DebugON) - {Trace.What = TRACE_ALL; + {XrdTrace.What = TRACE_ALL; XrdSysThread::setDebug(&Log); } @@ -977,9 +1004,8 @@ int XrdConfig::Setup(char *dfltp) // Setup the link and socket polling infrastructure // - XrdLink::Init(&Log, &Trace, &Sched); - XrdPoll::Init(&Log, &Trace, &Sched); - if (!XrdLink::Setup(ProtInfo.ConnMax, ProtInfo.idleWait) + XrdPoll::Init(&Log, &XrdTrace, &Sched); + if (!XrdLinkCtl::Setup(ProtInfo.ConnMax, ProtInfo.idleWait) || !XrdPoll::Setup(ProtInfo.ConnMax)) return 1; // Modify the AdminPath to account for any instance name. Note that there is @@ -1004,7 +1030,7 @@ int XrdConfig::Setup(char *dfltp) // We now go through all of the protocols and get each respective port number. // - XrdProtLoad::Init(&Log, &Trace); cp = Firstcp; + XrdProtLoad::Init(&Log, &XrdTrace); cp = Firstcp; while(cp) {ProtInfo.Port = (cp->port < 0 ? PortTCP : cp->port); XrdOucEnv::Export("XRDPORT", ProtInfo.Port); @@ -1022,7 +1048,7 @@ int XrdConfig::Setup(char *dfltp) // Allocate a WAN port number of we need to // - if (PortWAN && (NetWAN = new XrdInet(&Log, &Trace, Police))) + if (PortWAN && (NetWAN = new XrdInet(&Log, &XrdTrace, Police))) {if (Wan_Opts || Wan_Blen) NetWAN->setDefaults(Wan_Opts, Wan_Blen); if (myDomain) NetWAN->setDomain(myDomain); if (NetWAN->BindSD((PortWAN > 0 ? PortWAN : 0), "tcp")) return 1; @@ -1043,7 +1069,7 @@ int XrdConfig::Setup(char *dfltp) else for (i = 0; i < XrdProtLoad::ProtoMax && NetTCP[i]; i++) {if (cp->port == NetTCP[i]->Port()) break;} if (i >= XrdProtLoad::ProtoMax || !NetTCP[i]) - {NetTCP[++NetTCPlep] = new XrdInet(&Log, &Trace, Police); + {NetTCP[++NetTCPlep] = new XrdInet(&Log, &XrdTrace, Police); if (Net_Opts || Net_Blen) NetTCP[NetTCPlep]->setDefaults(Net_Opts, Net_Blen); if (myDomain) NetTCP[NetTCPlep]->setDomain(myDomain); @@ -1059,7 +1085,7 @@ int XrdConfig::Setup(char *dfltp) ProtInfo.WANWSize= Wan_Blen; } else ProtInfo.WANPort = ProtInfo.WANWSize = 0; if (!(cp->port)) arbNet = NetTCPlep; - if (!NetTCPlep) XrdLink::Init(NetTCP[0]); + if (!NetTCPlep) XrdNetTCP = NetTCP[0]; XrdOucEnv::Export("XRDPORT", ProtInfo.Port); } if (!XrdProtLoad::Load(cp->libpath,cp->proname,cp->parms,&ProtInfo)) @@ -1209,7 +1235,7 @@ int XrdConfig::xallow(XrdSysError *eDest, XrdOucStream &Config) {eDest->Emsg("Config", "allow target name not specified"); return 1;} if (!Police) {Police = new XrdNetSecurity(); - if (Trace.What == TRACE_ALL) Police->Trace(&Trace); + if (XrdTrace.What == TRACE_ALL) Police->Trace(&XrdTrace); } if (ishost) Police->AddHost(val); else Police->AddNetGroup(val); @@ -1965,7 +1991,7 @@ int XrdConfig::xtmo(XrdSysError *eDest, XrdOucStream &Config) if (V_read > 0) ProtInfo.readWait = V_read*1000; if (V_hail >= 0) ProtInfo.hailWait = V_hail*1000; if (V_idle >= 0) ProtInfo.idleWait = V_idle; - XrdLink::setKWT(V_read, V_kill); + XrdLinkCtl::setKWT(V_read, V_kill); return 0; } @@ -2021,6 +2047,6 @@ int XrdConfig::xtrace(XrdSysError *eDest, XrdOucStream &Config) } val = Config.GetWord(); } - Trace.What = trval; + XrdTrace.What = trval; return 0; } diff --git a/src/Xrd/XrdConfig.hh b/src/Xrd/XrdConfig.hh index 641bf360f74..537aa958c62 100644 --- a/src/Xrd/XrdConfig.hh +++ b/src/Xrd/XrdConfig.hh @@ -34,7 +34,6 @@ #include "Xrd/XrdProtLoad.hh" #include "Xrd/XrdProtocol.hh" #include "Xrd/XrdScheduler.hh" -#define XRD_TRACE Trace. #include "Xrd/XrdTrace.hh" #include "XrdOuc/XrdOucTrace.hh" @@ -89,12 +88,6 @@ int xtmo(XrdSysError *edest, XrdOucStream &Config); int yport(XrdSysError *edest, const char *ptyp, const char *pval); static const char *TraceID; - -XrdSysLogger Logger; -XrdSysError Log; -XrdOucTrace Trace; -XrdScheduler Sched; -XrdBuffManager BuffPool; XrdNetSecurity *Police; const char *myProg; const char *myName; diff --git a/src/Xrd/XrdGlobals.cc b/src/Xrd/XrdGlobals.cc new file mode 100644 index 00000000000..1c24b654f69 --- /dev/null +++ b/src/Xrd/XrdGlobals.cc @@ -0,0 +1,56 @@ +/******************************************************************************/ +/* */ +/* X r d G l o b a l s . c c */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include "Xrd/XrdBuffer.hh" +#include "Xrd/XrdBuffXL.hh" +#include "Xrd/XrdInet.hh" +#include "Xrd/XrdScheduler.hh" + +#include "XrdOuc/XrdOucTrace.hh" + +#include "XrdSys/XrdSysLogger.hh" +#include "XrdSys/XrdSysError.hh" + +// All the things that the Xrd package requires. +// +class XrdTlsContext; +class XrdBuffXL; + +namespace XrdGlobal +{ +XrdSysLogger Logger; +XrdSysError Log(&Logger, "Xrd"); +XrdOucTrace XrdTrace(&Log); +XrdScheduler Sched(&Log, &XrdTrace); +XrdBuffManager BuffPool(&Log, &XrdTrace); +XrdTlsContext *tlsCtx = 0; +XrdInet *XrdNetTCP = 0; +extern XrdBuffXL xlBuff; +int devNull = -1; +} diff --git a/src/Xrd/XrdInet.cc b/src/Xrd/XrdInet.cc index c381447e1e8..0ef7aa72186 100644 --- a/src/Xrd/XrdInet.cc +++ b/src/Xrd/XrdInet.cc @@ -42,7 +42,7 @@ #include "XrdSys/XrdSysError.hh" #include "Xrd/XrdInet.hh" -#include "Xrd/XrdLink.hh" +#include "Xrd/XrdLinkCtl.hh" #define XRD_TRACE XrdTrace-> #include "Xrd/XrdTrace.hh" @@ -111,7 +111,7 @@ XrdLink *XrdInet::Accept(int opts, int timeout, XrdSysSemaphore *theSem) // Allocate a new network object // - if (!(lp = XrdLink::Alloc(myAddr, lnkopts))) + if (!(lp = XrdLinkCtl::Alloc(myAddr, lnkopts))) {eDest->Emsg("Accept", ENOMEM, "allocate new link for", myAddr.Name(unk)); close(myAddr.SockFD()); } else { @@ -196,7 +196,7 @@ XrdLink *XrdInet::Connect(const char *host, int port, int opts, int tmo) // Return a link object // - if (!(lp = XrdLink::Alloc(myAddr, lnkopts))) + if (!(lp = XrdLinkCtl::Alloc(myAddr, lnkopts))) {eDest->Emsg("Connect", ENOMEM, "allocate new link to", myAddr.Name(unk)); close(myAddr.SockFD()); } else { diff --git a/src/Xrd/XrdLink.cc b/src/Xrd/XrdLink.cc index 7347b57fa96..66ee44e810d 100644 --- a/src/Xrd/XrdLink.cc +++ b/src/Xrd/XrdLink.cc @@ -2,7 +2,7 @@ /* */ /* X r d L i n k . c c */ /* */ -/* (c) 2011 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ @@ -61,394 +61,113 @@ #include "XrdSys/XrdSysPlatform.hh" #include "Xrd/XrdBuffer.hh" + #include "Xrd/XrdLink.hh" -#include "Xrd/XrdInet.hh" +#include "Xrd/XrdLinkCtl.hh" +#include "Xrd/XrdLinkXeq.hh" #include "Xrd/XrdPoll.hh" -#include "Xrd/XrdScheduler.hh" -#include "Xrd/XrdSendQ.hh" #define TRACELINK this -#define XRD_TRACE XrdTrace-> #include "Xrd/XrdTrace.hh" - -/******************************************************************************/ -/* L o c a l C l a s s e s */ -/******************************************************************************/ -/******************************************************************************/ -/* C l a s s x r d _ L i n k S c a n */ -/******************************************************************************/ - -class XrdLinkScan : XrdJob -{ -public: - -void DoIt() {idleScan();} - XrdLinkScan(XrdSysError *eP, XrdOucTrace *tP, XrdScheduler *sP, - int im, int it, const char *lt="idle link scan") - : XrdJob(lt), XrdLog(eP), XrdTrace(tP), XrdSched(sP), - idleCheck(im), idleTicks(it) {} - ~XrdLinkScan() {} +#include "XrdOuc/XrdOucTrace.hh" -private: - -void idleScan(); - -XrdSysError *XrdLog; -XrdOucTrace *XrdTrace; -XrdScheduler *XrdSched; -int idleCheck; -int idleTicks; +#include "XrdSys/XrdSysError.hh" + +/******************************************************************************/ +/* G l o b a l s */ +/******************************************************************************/ -static const char *TraceID; +namespace XrdGlobal +{ +extern XrdSysError Log; +extern XrdOucTrace XrdTrace; }; + +using namespace XrdGlobal; /******************************************************************************/ -/* S t a t i c s */ +/* S t a t i c M e m b e r s */ /******************************************************************************/ - XrdSysError *XrdLink::XrdLog = 0; - XrdOucTrace *XrdLink::XrdTrace = 0; - XrdScheduler *XrdLink::XrdSched = 0; - XrdInet *XrdLink::XrdNetTCP= 0; - #if defined(HAVE_SENDFILE) - int XrdLink::sfOK = 1; + bool XrdLink::sfOK = true; #else - int XrdLink::sfOK = 0; + bool XrdLink::sfOK = false; #endif - XrdLink **XrdLink::LinkTab; - char *XrdLink::LinkBat; - unsigned int XrdLink::LinkAlloc; - int XrdLink::LTLast = -1; - XrdSysMutex XrdLink::LTMutex; - - const char *XrdLink::TraceID = "Link"; - - long long XrdLink::LinkBytesIn = 0; - long long XrdLink::LinkBytesOut = 0; - long long XrdLink::LinkConTime = 0; - long long XrdLink::LinkCountTot = 0; - int XrdLink::LinkCount = 0; - int XrdLink::LinkCountMax = 0; - int XrdLink::LinkTimeOuts = 0; - int XrdLink::LinkStalls = 0; - int XrdLink::LinkSfIntr = 0; - int XrdLink::maxFD = 0; - XrdSysMutex XrdLink::statsMutex; - - const char *XrdLinkScan::TraceID = "LinkScan"; - int XrdLink::devNull = XrdSysFD_Open("/dev/null", O_RDONLY); - short XrdLink::killWait= 3; // Kill then wait - short XrdLink::waitKill= 4; // Wait then kill - -// The following values are defined for LinkBat[]. We assume that FREE is 0 -// -#define XRDLINK_FREE 0x00 -#define XRDLINK_USED 0x01 -#define XRDLINK_IDLE 0x02 +namespace +{ +const char KillMax = 60; +const char KillMsk = 0x7f; +const char KillXwt = 0x80; + +const char *TraceID = "Link"; +} /******************************************************************************/ /* C o n s t r u c t o r */ /******************************************************************************/ -XrdLink::XrdLink() : XrdJob("connection"), IOSemaphore(0, "link i/o") +XrdLink::XrdLink(XrdLinkXeq &lxq) + : XrdJob("connection"), IOSemaphore(0, "link i/o"), linkXQ(lxq) { - Etext = 0; - HostName = 0; - Reset(); + Etext = 0; + HostName = 0; + ResetLink(); } -void XrdLink::Reset() +void XrdLink::ResetLink() { - FD = -1; - if (Etext) {free(Etext); Etext = 0;} - if (HostName) {free(HostName); HostName = 0;} - Uname[sizeof(Uname)-1] = '@'; - Uname[sizeof(Uname)-2] = '?'; - Lname[0] = '?'; - Lname[1] = '\0'; - ID = &Uname[sizeof(Uname)-2]; - Comment = ID; -#if !defined( __linux__ ) && !defined( __solaris__ ) - Next = 0; -#endif - sendQ = 0; - Protocol = 0; - ProtoAlt = 0; - conTime = time(0); - stallCnt = stallCntTot = 0; - tardyCnt = tardyCntTot = 0; - SfIntr = 0; - InUse = 1; - Poller = 0; - PollEnt = 0; - isEnabled= 0; - isIdle = 0; - inQ = 0; - isBridged= 0; - BytesOut = BytesIn = BytesOutTot = BytesInTot = 0; - doPost = 0; - LockReads= 0; - KeepFD = 0; - Instance = 0; - KillcvP = 0; - KillCnt = 0; + KillcvP = 0; + conTime = time(0); + if (Etext) {free(Etext); Etext = 0;} + if (HostName) {free(HostName); HostName = 0;} + Comment = ID; + FD = -1; + Instance = 0; + InUse = 1; + doPost = 0; + isBridged= false; + isTLS = false; + KillCnt = 0; } /******************************************************************************/ -/* A l l o c */ +/* A d d r I n f o */ /******************************************************************************/ - -XrdLink *XrdLink::Alloc(XrdNetAddr &peer, int opts) -{ - static XrdSysMutex instMutex; - static unsigned int myInstance = 1; - XrdLink *lp; - char hName[1024], *unp, buff[16]; - int bl, peerFD = peer.SockFD(); - -// Make sure that the incomming file descriptor can be handled -// - if (peerFD < 0 || peerFD >= maxFD) - {snprintf(hName, sizeof(hName), "%d", peerFD); - XrdLog->Emsg("Link", "attempt to alloc out of range FD -",hName); - return (XrdLink *)0; - } - -// Make sure that the link slot is available -// - LTMutex.Lock(); - if (LinkBat[peerFD]) - {LTMutex.UnLock(); - XrdLog->Emsg("Link", "attempt to reuse active link"); - return (XrdLink *)0; - } - -// Check if we already have a link object in this slot. If not, allocate -// a quantum of link objects and put them in the table. -// - if (!(lp = LinkTab[peerFD])) - {unsigned int i; - XrdLink **blp, *nlp = new XrdLink[LinkAlloc](); - if (!nlp) - {LTMutex.UnLock(); - XrdLog->Emsg("Link", ENOMEM, "create link"); - return (XrdLink *)0; - } - blp = &LinkTab[peerFD/LinkAlloc*LinkAlloc]; - for (i = 0; i < LinkAlloc; i++, blp++) *blp = &nlp[i]; - lp = LinkTab[peerFD]; - } - else lp->Reset(); - LinkBat[peerFD] = XRDLINK_USED; - if (peerFD > LTLast) LTLast = peerFD; - LTMutex.UnLock(); - -// Establish the instance number of this link. This is will prevent us from -// sending asynchronous responses to the wrong client when the file descriptor -// gets reused for connections to the same host. -// - instMutex.Lock(); - lp->Instance = myInstance++; - instMutex.UnLock(); - -// Establish the address and connection name of this link -// - peer.Format(hName, sizeof(hName), XrdNetAddr::fmtAuto, - XrdNetAddr::old6Map4 | XrdNetAddr::noPort); - lp->HostName = strdup(hName); - lp->HNlen = strlen(hName); - XrdNetTCP->Trim(hName); - lp->Addr = peer; - strlcpy(lp->Lname, hName, sizeof(lp->Lname)); - bl = sprintf(buff, "?:%d", peerFD); - unp = lp->Uname + sizeof(Uname) - bl - 1; - memcpy(unp, buff, bl); - lp->ID = unp; - lp->FD = peerFD; - lp->Comment = (const char *)unp; - -// Set options as needed -// - lp->LockReads = (0 != (opts & XRDLINK_RDLOCK)); - lp->KeepFD = (0 != (opts & XRDLINK_NOCLOSE)); -// Update statistics and return the link. We need to actually get the stats -// mutex even when using atomics because we need to use compound operations. -// The atomics will keep reporters from seeing partial results. -// - statsMutex.Lock(); - AtomicInc(LinkCountTot); // LinkCountTot++ - if (LinkCountMax <= AtomicInc(LinkCount)) LinkCountMax = LinkCount; - statsMutex.UnLock(); - return lp; -} +XrdNetAddrInfo *XrdLink::AddrInfo() {return linkXQ.AddrInfo();} + +/******************************************************************************/ +/* a r m B r i d g e */ +/******************************************************************************/ +void XrdLink::armBridge() {isBridged = 1;} + /******************************************************************************/ /* B a c k l o g */ /******************************************************************************/ -int XrdLink::Backlog() -{ - XrdSysMutexHelper lck(wrMutex); - -// Return backlog information -// - return (sendQ ? sendQ->Backlog() : 0); -} +int XrdLink::Backlog() {return linkXQ.Backlog();} /******************************************************************************/ /* C l i e n t */ /******************************************************************************/ -int XrdLink::Client(char *nbuf, int nbsz) -{ - int ulen; - -// Generate full client name -// - if (nbsz <= 0) return 0; - ulen = (Lname - ID); - if ((ulen + HNlen) >= nbsz) ulen = 0; - else {strncpy(nbuf, ID, ulen); - strcpy(nbuf+ulen, HostName); - ulen += HNlen; - } - return ulen; -} +int XrdLink::Client(char *nbuf, int nbsz) {return linkXQ.Client(nbuf, nbsz);} /******************************************************************************/ /* C l o s e */ /******************************************************************************/ -int XrdLink::Close(int defer) -{ XrdSysMutexHelper opHelper(opMutex); - int csec, fd, rc = 0; - -// If a defer close is requested, we can close the descriptor but we must -// keep the slot number to prevent a new client getting the same fd number. -// Linux is peculiar in that any in-progress operations will remain in that -// state even after the FD is closed unless there is some activity either on -// the connection or an event occurs that causes an operation restart. We -// portably solve this problem by issuing a shutdown() on the socket prior -// closing it. On most platforms, this informs readers that the connection is -// gone (though not on old (i.e. <= 2.3) versions of Linux, sigh). Also, if -// nonblocking mode is enabled, we need to do this in a separate thread as -// a shutdown may block for a pretty long time is lot of messages are queued. -// We will ask the SendQ object to schedule the shutdown for us before it -// commits suicide. -// Note that we can hold the opMutex while we also get the wrMutex. -// - if (defer) - {if (!sendQ) Shutdown(false); - else {TRACEI(DEBUG, "Shutdown FD only via SendQ"); - InUse++; - FD = -FD; - wrMutex.Lock(); - sendQ->Terminate(this); - sendQ = 0; - wrMutex.UnLock(); - } - return 0; - } - -// If we got here then this is not a defered close so we just need to check -// if there is a sendq appendage we need to get rid of. -// - if (sendQ) - {wrMutex.Lock(); - sendQ->Terminate(); - sendQ = 0; - wrMutex.UnLock(); - } - -// Multiple protocols may be bound to this link. If it is in use, defer the -// actual close until the use count drops to one. -// - while(InUse > 1) - {opHelper.UnLock(); - TRACEI(DEBUG, "Close defered, use count=" <Recycle(this, csec, Etext); Protocol = 0;} - if (ProtoAlt) {ProtoAlt->Recycle(this, csec, Etext); ProtoAlt = 0;} - if (Etext) {free(Etext); Etext = 0;} - InUse = 0; - -// At this point we can have no lock conflicts, so if someone is waiting for -// us to terminate let them know about it. Note that we will get the condvar -// mutex while we hold the opMutex. This is the required order! We will also -// zero out the pointer to the condvar while holding the opmutex. -// - if (KillcvP) {KillcvP-> Lock(); KillcvP->Signal(); - KillcvP->UnLock(); KillcvP = 0; - } - -// Remove ourselves from the poll table and then from the Link table. We may -// not hold on to the opMutex when we acquire the LTMutex. However, the link -// table needs to be cleaned up prior to actually closing the socket. So, we -// do some fancy footwork to prevent multiple closes of this link. -// - fd = (FD < 0 ? -FD : FD); - if (FD != -1) - {if (Poller) {XrdPoll::Detach(this); Poller = 0;} - FD = -1; - opHelper.UnLock(); - LTMutex.Lock(); - LinkBat[fd] = XRDLINK_FREE; - if (fd == LTLast) while(LTLast && !(LinkBat[LTLast])) LTLast--; - LTMutex.UnLock(); - } else opHelper.UnLock(); - -// Close the file descriptor if it isn't being shared. Do it as the last -// thing because closes and accepts and not interlocked. -// - if (fd >= 2) {if (KeepFD) rc = 0; - else rc = (close(fd) < 0 ? errno : 0); - } - if (rc) XrdLog->Emsg("Link", rc, "close", ID); - return rc; -} +int XrdLink::Close(bool defer) {return linkXQ.Close(defer);} /******************************************************************************/ -/* D o I t */ +/* Protected: D o I t */ /******************************************************************************/ -void XrdLink::DoIt() -{ - int rc; - -// The Process() return code tells us what to do: -// < 0 -> Stop getting requests, -// -EINPROGRESS leave link disabled but otherwise all is well -// -n Error, disable and close the link -// = 0 -> OK, get next request, if allowed, o/w enable the link -// > 0 -> Slow link, stop getting requests and enable the link -// - if (Protocol) - do {rc = Protocol->Process(this);} while (!rc && XrdSched->canStick()); - else {XrdLog->Emsg("Link", "Dispatch on closed link", ID); - return; - } - -// Either re-enable the link and cycle back waiting for a new request, leave -// disabled, or terminate the connection. -// - if (rc >= 0) {if (Poller && !Poller->Enable(this)) Close();} - else if (rc != -EINPROGRESS) Close(); -} +void XrdLink::DoIt() {} // This is overridden by the implementation /******************************************************************************/ /* E n a b l e */ @@ -456,56 +175,37 @@ void XrdLink::DoIt() void XrdLink::Enable() { - if (Poller) Poller->Enable(this); + if (linkXQ.PollInfo.Poller) linkXQ.PollInfo.Poller->Enable(this); } /******************************************************************************/ /* F i n d */ /******************************************************************************/ -// Warning: curr must be set to a value of 0 or less on the initial call and -// not touched therafter unless a null pointer is returned. When an -// actual link object pointer is returned, it's refcount is increased. -// The count is automatically decreased on the next call to Find(). -// XrdLink *XrdLink::Find(int &curr, XrdLinkMatch *who) -{ - XrdLink *lp; - const int MaxSeek = 16; - unsigned int myINS; - int i, seeklim = MaxSeek; + {return XrdLinkCtl::Find(curr, who);} -// Do initialization -// - LTMutex.Lock(); - if (curr >= 0 && LinkTab[curr]) LinkTab[curr]->setRef(-1); - else curr = -1; +/******************************************************************************/ +/* f d 2 l i n k */ +/******************************************************************************/ + +XrdLink *XrdLink::fd2link(int fd) {return XrdLinkCtl::fd2link(fd);} -// Find next matching link. Since this may take some time, we periodically -// release the LTMutex lock which drives up overhead but will still allow -// other critical operations to occur. -// - for (i = curr+1; i <= LTLast; i++) - {if ((lp = LinkTab[i]) && LinkBat[i] && lp->HostName) - if (!who - || who->Match(lp->ID,lp->Lname-lp->ID-1,lp->HostName,lp->HNlen)) - {myINS = lp->Instance; - LTMutex.UnLock(); - lp->setRef(1); - curr = i; - if (myINS == lp->Instance) return lp; - LTMutex.Lock(); - } - if (!seeklim--) {LTMutex.UnLock(); seeklim = MaxSeek; LTMutex.Lock();} - } - -// Done scanning the table -// - LTMutex.UnLock(); - curr = -1; - return 0; -} +/******************************************************************************/ +XrdLink *XrdLink::fd2link(int fd, unsigned int inst) + {return XrdLinkCtl::fd2link(fd, inst);} + +/******************************************************************************/ +/* g e t I O S t a t s */ +/******************************************************************************/ + +int XrdLink::getIOStats(long long &inbytes, long long &outbytes, + int &numstall, int &numtardy) + {return linkXQ.getIOStats(inbytes, outbytes, + numstall, numtardy); + } + /******************************************************************************/ /* g e t N a m e */ /******************************************************************************/ @@ -515,77 +215,46 @@ XrdLink *XrdLink::Find(int &curr, XrdLinkMatch *who) // the name in nbuf. // int XrdLink::getName(int &curr, char *nbuf, int nbsz, XrdLinkMatch *who) -{ - XrdLink *lp; - const int MaxSeek = 16; - int i, ulen = 0, seeklim = MaxSeek; + {return XrdLinkCtl::getName(curr, nbuf, nbsz, who);} -// Find next matching link. Since this may take some time, we periodically -// release the LTMutex lock which drives up overhead but will still allow -// other critical operations to occur. -// - LTMutex.Lock(); - for (i = curr+1; i <= LTLast; i++) - {if ((lp = LinkTab[i]) && LinkBat[i] && lp->HostName) - if (!who - || who->Match(lp->ID,lp->Lname-lp->ID-1,lp->HostName,lp->HNlen)) - {ulen = lp->Client(nbuf, nbsz); - LTMutex.UnLock(); - curr = i; - return ulen; - } - if (!seeklim--) {LTMutex.UnLock(); seeklim = MaxSeek; LTMutex.Lock();} - } - LTMutex.UnLock(); - -// Done scanning the table -// - curr = -1; - return 0; -} +/******************************************************************************/ +/* g e t P o l l I n f o */ +/******************************************************************************/ +XrdPollInfo &XrdLink::getPollInfo() {return linkXQ.PollInfo;} + +/******************************************************************************/ +/* g e t P r o t o c o l */ +/******************************************************************************/ + +XrdProtocol *XrdLink::getProtocol() {return linkXQ.getProtocol();} + +/******************************************************************************/ +/* H o l d */ +/******************************************************************************/ + +void XrdLink::Hold(bool lk) {(lk ? opMutex.Lock() : opMutex.UnLock());} + +/******************************************************************************/ +/* N a m e */ +/******************************************************************************/ + +const char *XrdLink::Name() const {return linkXQ.Name();} + +/******************************************************************************/ +/* N e t A d d r */ +/******************************************************************************/ +const +XrdNetAddr *XrdLink::NetAddr() const {return linkXQ.NetAddr();} + /******************************************************************************/ /* P e e k */ /******************************************************************************/ int XrdLink::Peek(char *Buff, int Blen, int timeout) { - XrdSysMutexHelper theMutex; - struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0}; - ssize_t mlen; - int retc; - -// Lock the read mutex if we need to, the helper will unlock it upon exit -// - if (LockReads) theMutex.Lock(&rdMutex); - -// Wait until we can actually read something -// - isIdle = 0; - do {retc = poll(&polltab, 1, timeout);} while(retc < 0 && errno == EINTR); - if (retc != 1) - {if (retc == 0) return 0; - return XrdLog->Emsg("Link", -errno, "poll", ID); - } - -// Verify it is safe to read now -// - if (!(polltab.revents & (POLLIN|POLLRDNORM))) - {XrdLog->Emsg("Link", XrdPoll::Poll2Text(polltab.revents), - "polling", ID); - return -1; - } - -// Do the peek. -// - do {mlen = recv(FD, Buff, Blen, MSG_PEEK);} - while(mlen < 0 && errno == EINTR); - -// Return the result -// - if (mlen >= 0) return int(mlen); - XrdLog->Emsg("Link", errno, "peek on", ID); - return -1; + if (isTLS) return linkXQ.TLS_Peek(Buff, Blen, timeout); + return linkXQ.Peek (Buff, Blen, timeout); } /******************************************************************************/ @@ -594,115 +263,26 @@ int XrdLink::Peek(char *Buff, int Blen, int timeout) int XrdLink::Recv(char *Buff, int Blen) { - ssize_t rlen; - -// Note that we will read only as much as is queued. Use Recv() with a -// timeout to receive as much data as possible. -// - if (LockReads) rdMutex.Lock(); - isIdle = 0; - do {rlen = read(FD, Buff, Blen);} while(rlen < 0 && errno == EINTR); - if (rlen > 0) AtomicAdd(BytesIn, rlen); - if (LockReads) rdMutex.UnLock(); - - if (rlen >= 0) return int(rlen); - if (FD >= 0) XrdLog->Emsg("Link", errno, "receive from", ID); - return -1; + if (isTLS) return linkXQ.TLS_Recv(Buff, Blen); + return linkXQ.Recv (Buff, Blen); } /******************************************************************************/ int XrdLink::Recv(char *Buff, int Blen, int timeout) { - XrdSysMutexHelper theMutex; - struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0}; - ssize_t rlen, totlen = 0; - int retc; - -// Lock the read mutex if we need to, the helper will unlock it upon exit -// - if (LockReads) theMutex.Lock(&rdMutex); - -// Wait up to timeout milliseconds for data to arrive -// - isIdle = 0; - while(Blen > 0) - {do {retc = poll(&polltab,1,timeout);} while(retc < 0 && errno == EINTR); - if (retc != 1) - {if (retc == 0) - {tardyCnt++; - if (totlen) - {if ((++stallCnt & 0xff) == 1) TRACEI(DEBUG,"read timed out"); - AtomicAdd(BytesIn, totlen); - } - return int(totlen); - } - return (FD >= 0 ? XrdLog->Emsg("Link", -errno, "poll", ID) : -1); - } - - // Verify it is safe to read now - // - if (!(polltab.revents & (POLLIN|POLLRDNORM))) - {XrdLog->Emsg("Link", XrdPoll::Poll2Text(polltab.revents), - "polling", ID); - return -1; - } - - // Read as much data as you can. Note that we will force an error - // if we get a zero-length read after poll said it was OK. - // - do {rlen = recv(FD, Buff, Blen, 0);} while(rlen < 0 && errno == EINTR); - if (rlen <= 0) - {if (!rlen) return -ENOMSG; - return (FD<0 ? -1 : XrdLog->Emsg("Link",-errno,"receive from",ID)); - } - totlen += rlen; Blen -= rlen; Buff += rlen; - } - - AtomicAdd(BytesIn, totlen); - return int(totlen); + if (isTLS) return linkXQ.TLS_Recv(Buff, Blen, timeout); + return linkXQ.Recv (Buff, Blen, timeout); } - /******************************************************************************/ /* R e c v A l l */ /******************************************************************************/ int XrdLink::RecvAll(char *Buff, int Blen, int timeout) { - struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0}; - ssize_t rlen; - int retc; - -// Check if timeout specified. Notice that the timeout is the max we will -// for some data. We will wait forever for all the data. Yeah, it's weird. -// - if (timeout >= 0) - {do {retc = poll(&polltab,1,timeout);} while(retc < 0 && errno == EINTR); - if (retc != 1) - {if (!retc) return -ETIMEDOUT; - XrdLog->Emsg("Link",errno,"poll",ID); - return -1; - } - if (!(polltab.revents & (POLLIN|POLLRDNORM))) - {XrdLog->Emsg("Link",XrdPoll::Poll2Text(polltab.revents),"polling",ID); - return -1; - } - } - -// Note that we will block until we receive all he bytes. -// - if (LockReads) rdMutex.Lock(); - isIdle = 0; - do {rlen = recv(FD,Buff,Blen,MSG_WAITALL);} while(rlen < 0 && errno == EINTR); - if (rlen > 0) AtomicAdd(BytesIn, rlen); - if (LockReads) rdMutex.UnLock(); - - if (int(rlen) == Blen) return Blen; - if (!rlen) {TRACEI(DEBUG, "No RecvAll() data; errno=" < 0) XrdLog->Emsg("RecvAll","Premature end from", ID); - else if (FD >= 0) XrdLog->Emsg("Link",errno,"recieve from",ID); - return -1; + if (isTLS) return linkXQ.TLS_RecvAll(Buff, Blen, timeout); + return linkXQ.RecvAll (Buff, Blen, timeout); } /******************************************************************************/ @@ -711,251 +291,59 @@ int XrdLink::RecvAll(char *Buff, int Blen, int timeout) int XrdLink::Send(const char *Buff, int Blen) { - ssize_t retc = 0, bytesleft = Blen; - -// Get a lock -// - wrMutex.Lock(); - isIdle = 0; - AtomicAdd(BytesOut, Blen); - -// Do non-blocking writes if we are setup to do so. -// - if (sendQ) - {retc = sendQ->Send(Buff, Blen); - wrMutex.UnLock(); - return retc; - } - -// Write the data out -// - while(bytesleft) - {if ((retc = write(FD, Buff, bytesleft)) < 0) - {if (errno == EINTR) continue; - else break; - } - bytesleft -= retc; Buff += retc; - } - -// All done -// - wrMutex.UnLock(); - if (retc >= 0) return Blen; - XrdLog->Emsg("Link", errno, "send to", ID); - return -1; + if (isTLS) return linkXQ.TLS_Send(Buff, Blen); + return linkXQ.Send (Buff, Blen); } /******************************************************************************/ int XrdLink::Send(const struct iovec *iov, int iocnt, int bytes) { - ssize_t bytesleft, n, retc = 0; - const char *Buff; - int i; - -// Add up bytes if they were not given to us +// Allways make sure we have a total byte count // - if (!bytes) for (i = 0; i < iocnt; i++) bytes += iov[i].iov_len; + if (!bytes) for (int i = 0; i < iocnt; i++) bytes += iov[i].iov_len; -// Get a lock and assume we will be successful (statistically we are) +// Execute the send // - wrMutex.Lock(); - isIdle = 0; - AtomicAdd(BytesOut, bytes); - -// Do non-blocking writes if we are setup to do so. -// - if (sendQ) - {retc = sendQ->Send(iov, iocnt, bytes); - wrMutex.UnLock(); - return retc; - } - -// Write the data out. On some version of Unix (e.g., Linux) a writev() may -// end at any time without writing all the bytes when directed to a socket. -// So, we attempt to resume the writev() using a combination of write() and -// a writev() continuation. This approach slowly converts a writev() to a -// series of writes if need be. We must do this inline because we must hold -// the lock until all the bytes are written or an error occurs. -// - bytesleft = static_cast(bytes); - while(bytesleft) - {do {retc = writev(FD, iov, iocnt);} while(retc < 0 && errno == EINTR); - if (retc >= bytesleft || retc < 0) break; - bytesleft -= retc; - while(retc >= (n = static_cast(iov->iov_len))) - {retc -= n; iov++; iocnt--;} - Buff = (const char *)iov->iov_base + retc; n -= retc; iov++; iocnt--; - while(n) {if ((retc = write(FD, Buff, n)) < 0) - {if (errno == EINTR) continue; - else break; - } - n -= retc; Buff += retc; - } - if (retc < 0 || iocnt < 1) break; - } - -// All done -// - wrMutex.UnLock(); - if (retc >= 0) return bytes; - XrdLog->Emsg("Link", errno, "send to", ID); - return -1; + if (isTLS) return linkXQ.TLS_Send(iov, iocnt, bytes); + return linkXQ.Send (iov, iocnt, bytes); } /******************************************************************************/ + int XrdLink::Send(const sfVec *sfP, int sfN) { -#if !defined(HAVE_SENDFILE) || defined(__APPLE__) - return -1; -#else // Make sure we have valid vector count // if (sfN < 1 || sfN > XrdOucSFVec::sfMax) - {XrdLog->Emsg("Link", EINVAL, "send file to", ID); + {Log.Emsg("Link", E2BIG, "send file to", ID); return -1; } -#ifdef __solaris__ - sendfilevec_t vecSF[XrdOucSFVec::sfMax], *vecSFP = vecSF; - size_t xframt, totamt, bytes = 0; - ssize_t retc; - int i = 0; - -// Construct the sendfilev() vector -// - for (i = 0; i < sfN; sfP++, i++) - {if (sfP->fdnum < 0) - {vecSF[i].sfv_fd = SFV_FD_SELF; - vecSF[i].sfv_off = (off_t)sfP->buffer; - } else { - vecSF[i].sfv_fd = sfP->fdnum; - vecSF[i].sfv_off = sfP->offset; - } - vecSF[i].sfv_flag = 0; - vecSF[i].sfv_len = sfP->sendsz; - bytes += sfP->sendsz; - } - totamt = bytes; - -// Lock the link, issue sendfilev(), and unlock the link. The documentation -// is very spotty and inconsistent. We can only retry this operation under -// very limited conditions. -// - wrMutex.Lock(); - isIdle = 0; -do{retc = sendfilev(FD, vecSFP, sfN, &xframt); - -// Check if all went well and return if so (usual case) -// - if (xframt == bytes) - {AtomicAdd(BytesOut, bytes); - wrMutex.UnLock(); - return totamt; - } - -// The only one we will recover from is EINTR. We cannot legally get EAGAIN. -// - if (retc < 0 && errno != EINTR) break; - -// Try to resume the transfer -// - if (xframt > 0) - {AtomicAdd(BytesOut, xframt); bytes -= xframt; SfIntr++; - while(xframt > 0 && sfN) - {if ((ssize_t)xframt < (ssize_t)vecSFP->sfv_len) - {vecSFP->sfv_off += xframt; vecSFP->sfv_len -= xframt; break;} - xframt -= vecSFP->sfv_len; vecSFP++; sfN--; - } - } - } while(sfN > 0); - -// See if we can recover without destroying the connection -// - retc = (retc < 0 ? errno : ECANCELED); - wrMutex.UnLock(); - XrdLog->Emsg("Link", retc, "send file to", ID); - return -1; - -#elif defined(__linux__) - - static const int setON = 1, setOFF = 0; - ssize_t retc = 0, bytesleft; - off_t myOffset; - int i, xfrbytes = 0, uncork = 1, xIntr = 0; - -// lock the link -// - wrMutex.Lock(); - isIdle = 0; - -// In linux we need to cork the socket. On permanent errors we do not uncork -// the socket because it will be closed in short order. -// - if (setsockopt(FD, SOL_TCP, TCP_CORK, &setON, sizeof(setON)) < 0) - {XrdLog->Emsg("Link", errno, "cork socket for", ID); - uncork = 0; sfOK = 0; - } - -// Send the header first +// Do the send // - for (i = 0; i < sfN; sfP++, i++) - {if (sfP->fdnum < 0) retc = sendData(sfP->buffer, sfP->sendsz); - else {myOffset = sfP->offset; bytesleft = sfP->sendsz; - while(bytesleft - && (retc=sendfile(FD,sfP->fdnum,&myOffset,bytesleft)) > 0) - {bytesleft -= retc; xIntr++;} - } - if (retc < 0 && errno == EINTR) continue; - if (retc <= 0) break; - xfrbytes += sfP->sendsz; - } - -// Diagnose any sendfile errors -// - if (retc <= 0) - {if (retc == 0) errno = ECANCELED; - wrMutex.UnLock(); - XrdLog->Emsg("Link", errno, "send file to", ID); - return -1; - } - -// Now uncork the socket -// - if (uncork && setsockopt(FD, SOL_TCP, TCP_CORK, &setOFF, sizeof(setOFF)) < 0) - XrdLog->Emsg("Link", errno, "uncork socket for", ID); - -// All done -// - if (xIntr > sfN) SfIntr += (xIntr - sfN); - AtomicAdd(BytesOut, xfrbytes); - wrMutex.UnLock(); - return xfrbytes; -#endif -#endif + if (isTLS) return linkXQ.TLS_Send(sfP, sfN); + return linkXQ.Send (sfP, sfN); } /******************************************************************************/ -/* private s e n d D a t a */ +/* S e r i a l i z e */ /******************************************************************************/ -int XrdLink::sendData(const char *Buff, int Blen) +void XrdLink::Serialize() { - ssize_t retc = 0, bytesleft = Blen; - -// Write the data out -// - while(bytesleft) - {if ((retc = write(FD, Buff, bytesleft)) < 0) - {if (errno == EINTR) continue; - else break; - } - bytesleft -= retc; Buff += retc; - } -// All done +// This is meant to make sure that no protocol objects are refering to this +// link so that we can safely run in psuedo single thread mode for critical +// functions. // - return retc; + opMutex.Lock(); + if (InUse <= 1) opMutex.UnLock(); + else {doPost++; + opMutex.UnLock(); + TRACEI(DEBUG, "Waiting for link serialization; use=" < (int)sizeof(Uname)) ulen = sizeof(Uname); - *bp = '@'; bp--; - while(ulen--) {*bp = *sp; bp--; sp--;} - ID = bp+1; - Comment = (const char *)ID; -} + {linkXQ.setID(userid, procid);} /******************************************************************************/ /* s e t N B */ /******************************************************************************/ -bool XrdLink::setNB() -{ -// We don't support non-blocking output except for Linux at the moment -// -#if !defined(__linux__) - return false; -#else -// Trace this request -// - TRACEI(DEBUG,"enabling non-blocking output"); - -// If we don't already have a sendQ object get one. This is a one-time call -// so to optimize checking if this object exists we also get the opMutex.' -// - opMutex.Lock(); - if (!sendQ) - {wrMutex.Lock(); - sendQ = new XrdSendQ(*this, wrMutex); - wrMutex.UnLock(); - } - opMutex.UnLock(); - return true; -#endif -} +bool XrdLink::setNB() {return linkXQ.setNB();} /******************************************************************************/ -/* S e t u p */ +/* s e t L o c a t i o n */ /******************************************************************************/ - -int XrdLink::Setup(int maxfds, int idlewait) -{ - int numalloc, iticks, ichk; - -// Compute the number of link objects we should allocate at a time. Generally, -// we like to allocate 8k of them at a time but always as a power of two. -// - maxFD = maxfds; - numalloc = 8192 / sizeof(XrdLink); - LinkAlloc = 1; - while((numalloc = numalloc/2)) LinkAlloc = LinkAlloc*2; - TRACE(DEBUG, "Allocating " <Emsg("Link", ENOMEM, "create LinkTab"); return 0;} - memset((void *)LinkTab, 0, maxfds*sizeof(XrdLink *)); - -// Create the slot status table -// - if (!(LinkBat = (char *)malloc(maxfds*sizeof(char)+LinkAlloc))) - {XrdLog->Emsg("Link", ENOMEM, "create LinkBat"); return 0;} - memset((void *)LinkBat, XRDLINK_FREE, maxfds*sizeof(char)); - -// Create an idle connection scan job -// - if (idlewait) - {if (!(ichk = idlewait/3)) {iticks = 1; ichk = idlewait;} - else iticks = 3; - XrdLinkScan *ls = new XrdLinkScan(XrdLog,XrdTrace,XrdSched,ichk,iticks); - XrdSched->Schedule((XrdJob *)ls, ichk+time(0)); - } - -// Initialize the send queue -// - XrdSendQ::Init(XrdLog, XrdSched); - -// All done -// - return 1; -} -/******************************************************************************/ -/* S e r i a l i z e */ -/******************************************************************************/ - -void XrdLink::Serialize() -{ +void XrdLink::setLocation(XrdNetAddrInfo::LocInfo &loc) + {linkXQ.setLocation(loc);} -// This is meant to make sure that no protocol objects are refering to this -// link so that we can safely run in psuedo single thread mode for critical -// functions. -// - opMutex.Lock(); - if (InUse <= 1) opMutex.UnLock(); - else {doPost++; - opMutex.UnLock(); - TRACEI(DEBUG, "Waiting for link serialization; use=" < 0) waitKill = static_cast(wkSec); - if (kwSec > 0) killWait = static_cast(kwSec); -} - /******************************************************************************/ /* s e t P r o t o c o l */ /******************************************************************************/ -XrdProtocol *XrdLink::setProtocol(XrdProtocol *pp) +XrdProtocol *XrdLink::setProtocol(XrdProtocol *pp, bool runit, bool push) { -// Set new protocol. +// Ask the mplementation to set or push the protocol // - opMutex.Lock(); - XrdProtocol *op = Protocol; - Protocol = pp; - opMutex.UnLock(); + XrdProtocol *op = linkXQ.setProtocol(pp, push); + +// Run the protocol if so requested +// + if (runit) DoIt(); return op; } - + /******************************************************************************/ /* s e t R e f */ /******************************************************************************/ @@ -1125,7 +408,7 @@ void XrdLink::setRef(int use) if (!InUse) {InUse = 1; opMutex.UnLock(); - XrdLog->Emsg("Link", "Zero use count for", ID); + Log.Emsg("Link", "Zero use count for", ID); } else if (InUse == 1 && doPost) {while(doPost) @@ -1138,222 +421,112 @@ void XrdLink::setRef(int use) else if (InUse < 0) {InUse = 1; opMutex.UnLock(); - XrdLog->Emsg("Link", "Negative use count for", ID); + Log.Emsg("Link", "Negative use count for", ID); } else opMutex.UnLock(); } /******************************************************************************/ -/* S h u t d o w n */ +/* s e t T L S */ /******************************************************************************/ -void XrdLink::Shutdown(bool getLock) +bool XrdLink::setTLS(bool enable) { - int temp, theFD; - -// Trace the entry +// If we are already in a compatible mode, we are done // - TRACEI(DEBUG, (getLock ? "Async" : "Sync") <<" link shutdown in progress"); + if (isTLS == enable) return true; -// Get the lock if we need too (external entry via another thread) -// - if (getLock) opMutex.Lock(); - -// If there is something to do, do it now -// - temp = Instance; Instance = 0; - if (!KeepFD) - {theFD = (FD < 0 ? -FD : FD); - shutdown(theFD, SHUT_RDWR); - if (dup2(devNull, theFD) < 0) - {Instance = temp; - XrdLog->Emsg("Link", errno, "shutdown FD for", ID); - } - } - -// All done -// - if (getLock) opMutex.UnLock(); + return linkXQ.setTLS(enable); } - + /******************************************************************************/ -/* S t a t s */ +/* S h u t d o w n */ /******************************************************************************/ -int XrdLink::Stats(char *buff, int blen, int do_sync) -{ - static const char statfmt[] = "%d" - "%d%lld%lld%lld" - "%lld%d%d" - "%d"; - int i, myLTLast; - -// Check if actual length wanted -// - if (!buff) return sizeof(statfmt)+17*6; +void XrdLink::Shutdown(bool getLock) {linkXQ.Shutdown(getLock);} -// We must synchronize the statistical counters -// - if (do_sync) - {LTMutex.Lock(); myLTLast = LTLast; LTMutex.UnLock(); - for (i = 0; i <= myLTLast; i++) - if (LinkBat[i] == XRDLINK_USED && LinkTab[i]) - LinkTab[i]->syncStats(); - } +/******************************************************************************/ +/* S t a t s */ +/******************************************************************************/ -// Obtain lock on the stats area and format it -// - AtomicBeg(statsMutex); - i = snprintf(buff, blen, statfmt, AtomicGet(LinkCount), - AtomicGet(LinkCountMax), - AtomicGet(LinkCountTot), - AtomicGet(LinkBytesIn), - AtomicGet(LinkBytesOut), - AtomicGet(LinkConTime), - AtomicGet(LinkTimeOuts), - AtomicGet(LinkStalls), - AtomicGet(LinkSfIntr)); - AtomicEnd(statsMutex); - return i; -} +int XrdLink::Stats(char *buff, int blen, bool do_sync) + {return XrdLinkXeq::Stats(buff, blen, do_sync);} /******************************************************************************/ /* s y n c S t a t s */ /******************************************************************************/ -void XrdLink::syncStats(int *ctime) -{ - long long tmpLL; - int tmpI4; - -// If this is dynamic, get the opMutex lock -// - if (!ctime) opMutex.Lock(); - -// Either the caller has the opMutex or this is called out of close. In either -// case, we need to get the read and write mutexes; each followed by the stats -// mutex. This order is important because we should not hold the stats mutex -// for very long and the r/w mutexes may take a long time to acquire. If we -// must maintain the link count we need to actually acquire the stats mutex as -// we will be doing compound operations. Atomics are still used to keep other -// threads from seeing partial results. -// - AtomicBeg(rdMutex); - - if (ctime) - {*ctime = time(0) - conTime; - AtomicAdd(LinkConTime, *ctime); - statsMutex.Lock(); - if (LinkCount > 0) AtomicDec(LinkCount); - statsMutex.UnLock(); - } - - AtomicBeg(statsMutex); - - tmpLL = AtomicFAZ(BytesIn); - AtomicAdd(LinkBytesIn, tmpLL); AtomicAdd(BytesInTot, tmpLL); - tmpI4 = AtomicFAZ(tardyCnt); - AtomicAdd(LinkTimeOuts, tmpI4); AtomicAdd(tardyCntTot, tmpI4); - tmpI4 = AtomicFAZ(stallCnt); - AtomicAdd(LinkStalls, tmpI4); AtomicAdd(stallCntTot, tmpI4); - AtomicEnd(statsMutex); AtomicEnd(rdMutex); - - AtomicBeg(wrMutex); AtomicBeg(statsMutex); - tmpLL = AtomicFAZ(BytesOut); - AtomicAdd(LinkBytesOut, tmpLL); AtomicAdd(BytesOutTot, tmpLL); - tmpI4 = AtomicFAZ(SfIntr); - AtomicAdd(LinkSfIntr, tmpI4); - AtomicEnd(statsMutex); AtomicEnd(wrMutex); - -// Make sure the protocol updates it's statistics as well -// - if (Protocol) Protocol->Stats(0, 0, 1); - -// Clear our local counters -// - if (!ctime) opMutex.UnLock(); -} +void XrdLink::syncStats(int *ctime) {linkXQ.syncStats(ctime);} /******************************************************************************/ /* T e r m i n a t e */ /******************************************************************************/ -int XrdLink::Terminate(const XrdLink *owner, int fdnum, unsigned int inst) +int XrdLink::Terminate(const char *owner, int fdnum, unsigned int inst) { - XrdSysCondVar killDone(0); - XrdLink *lp; - char buff[1024], *cp; - int wTime, killTries; - -// Find the correspodning link -// - if (!(lp = fd2link(fdnum, inst))) return -ESRCH; - -// If this is self termination, then indicate that to the caller -// - if (lp == owner) return 0; -// Serialize the target link -// - lp->Serialize(); - lp->opMutex.Lock(); +// Find the correspodning link and check for self-termination. Otherwise, if +// the target link is owned by the owner then ask the link to terminate itself. +// + if (!owner) + {XrdLink *lp; + char *cp; + if (!(lp = fd2link(fdnum, inst))) return -ESRCH; + if (lp == this) return 0; + lp->Hold(true); + if (!(cp = index(ID, ':')) || strncmp(lp->ID, ID, cp-ID) + || strcmp(HostName, lp->Host())) + {lp->Hold(false); + return -EACCES; + } + int rc = lp->Terminate(ID, fdnum, inst); + lp->Hold(false); + return rc; + } +// At this pint, we are excuting in the context of the target link. // If this link is now dead, simply ignore the request. Typically, this // indicates a race condition that the server won. // - if ( lp->FD != fdnum || lp->Instance != inst - || !(lp->Poller) || !(lp->Protocol)) - {lp->opMutex.UnLock(); - return -EPIPE; - } - -// Verify that the owner of this link is making the request -// - if (owner - && (!(cp = index(owner->ID, ':')) - || strncmp(lp->ID, owner->ID, cp-(owner->ID)) - || strcmp(owner->Lname, lp->Lname))) - {lp->opMutex.UnLock(); - return -EACCES; - } + if ( FD != fdnum || Instance != inst + || !linkXQ.PollInfo.Poller || !linkXQ.getProtocol()) return -EPIPE; // Check if we have too many tries here // - killTries = lp->KillCnt & KillMsk; - if (killTries > KillMax) - {lp->opMutex.UnLock(); - return -ETIME; - } + int wTime, killTries; + killTries = KillCnt & KillMsk; + if (killTries > KillMax) return -ETIME; // Wait time increases as we have more unsuccessful kills. Update numbers. // wTime = killTries++; - lp->KillCnt = killTries | KillXwt; + KillCnt = killTries | KillXwt; -// Make sure we can disable this link. Of not, then force the caller to wait +// Make sure we can disable this link. If not, then force the caller to wait // a tad more than the read timeout interval. // - if (!(lp->isEnabled) || lp->InUse > 1 || lp->KillcvP) - {wTime = wTime*2+waitKill; - lp->opMutex.UnLock(); + if (!linkXQ.PollInfo.isEnabled || InUse > 1 || KillcvP) + {wTime = wTime*2+XrdLinkCtl::waitKill; return (wTime > 60 ? 60: wTime); } // Set the pointer to our condvar. We are holding the opMutex to prevent a race. // - lp->KillcvP = &killDone; + XrdSysCondVar killDone(0); + KillcvP = &killDone; killDone.Lock(); // We can now disable the link and schedule a close // - snprintf(buff, sizeof(buff), "ended by %s", ID); + char buff[1024]; + snprintf(buff, sizeof(buff), "ended by %s", owner); buff[sizeof(buff)-1] = '\0'; - lp->Poller->Disable(lp, buff); - lp->opMutex.UnLock(); + linkXQ.PollInfo.Poller->Disable(this, buff); + opMutex.UnLock(); // Now wait for the link to shutdown. This avoids lock problems. // - if (killDone.Wait(int(killWait))) wTime += killWait; + if (killDone.Wait(int(XrdLinkCtl::killWait))) wTime += XrdLinkCtl::killWait; else wTime = -EPIPE; killDone.UnLock(); @@ -1362,7 +535,7 @@ int XrdLink::Terminate(const XrdLink *owner, int fdnum, unsigned int inst) // an arbitrary mutex with a condvar. But since this code is rarely executed // the ugliness is sort of tolerable. // - lp->opMutex.Lock(); lp->KillcvP = 0; lp->opMutex.UnLock(); + opMutex.Lock(); KillcvP = 0; opMutex.UnLock(); // Do some tracing // @@ -1371,49 +544,28 @@ int XrdLink::Terminate(const XrdLink *owner, int fdnum, unsigned int inst) } /******************************************************************************/ -/* i d l e S c a n */ +/* W a i t 4 D a t a */ /******************************************************************************/ -#undef TRACELINK -#define TRACELINK lp - -void XrdLinkScan::idleScan() +int XrdLink::Wait4Data(int timeout) { - XrdLink *lp; - int i, ltlast, lnum = 0, tmo = 0, tmod = 0; - -// Get the current link high watermark -// - XrdLink::LTMutex.Lock(); - ltlast = XrdLink::LTLast; - XrdLink::LTMutex.UnLock(); + struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0}; + int retc; -// Scan across all links looking for idle links. Links are never deallocated -// so we don't need any special kind of lock for these +// Issue poll and do preliminary check // - for (i = 0; i <= ltlast; i++) - {if (XrdLink::LinkBat[i] != XRDLINK_USED - || !(lp = XrdLink::LinkTab[i])) continue; - lnum++; - lp->opMutex.Lock(); - if (lp->isIdle) tmo++; - lp->isIdle++; - if ((int(lp->isIdle)) < idleTicks) {lp->opMutex.UnLock(); continue;} - lp->isIdle = 0; - if (!(lp->Poller) || !(lp->isEnabled)) - XrdLog->Emsg("LinkScan","Link",lp->ID,"is disabled and idle."); - else if (lp->InUse == 1) - {lp->Poller->Disable(lp, "idle timeout"); - tmod++; - } - lp->opMutex.UnLock(); - } - -// Trace what we did -// - TRACE(CONN, lnum <<" links; " <Schedule((XrdJob *)this, idleCheck+time(0)); + if (!(polltab.revents & (POLLIN|POLLRDNORM))) + {Log.Emsg("Link", XrdPoll::Poll2Text(polltab.revents), "polling", ID); + return -1; + } + return 1; } diff --git a/src/Xrd/XrdLink.hh b/src/Xrd/XrdLink.hh index 08c3e4af612..0d9ad1de1bd 100644 --- a/src/Xrd/XrdLink.hh +++ b/src/Xrd/XrdLink.hh @@ -4,7 +4,7 @@ /* */ /* X r d L i n k . h h */ /* */ -/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Department of Energy */ /* */ @@ -30,44 +30,25 @@ /******************************************************************************/ #include -#include -#include #include "XrdNet/XrdNetAddr.hh" #include "XrdOuc/XrdOucSFVec.hh" #include "XrdSys/XrdSysPthread.hh" #include "Xrd/XrdJob.hh" -#include "Xrd/XrdLinkMatch.hh" -#include "Xrd/XrdProtocol.hh" -/******************************************************************************/ -/* X r d L i n k O p t i o n s */ -/******************************************************************************/ - -#define XRDLINK_RDLOCK 0x0001 -#define XRDLINK_NOCLOSE 0x0002 - /******************************************************************************/ /* C l a s s D e f i n i t i o n */ /******************************************************************************/ -class XrdInet; -class XrdNetAddr; -class XrdPoll; -class XrdOucTrace; -class XrdScheduler; -class XrdSendQ; -class XrdSysError; - -class XrdLink : XrdJob +class XrdLinkMatch; +class XrdLinkXeq; +class XrdPollInfo; +class XrdProtocol; + +class XrdLink : public XrdJob { public: -friend class XrdLinkScan; -friend class XrdPoll; -friend class XrdPollPoll; -friend class XrdPollDev; -friend class XrdPollE; //----------------------------------------------------------------------------- //! Obtain the address information for this link. @@ -75,66 +56,152 @@ friend class XrdPollE; //! @return Pointer to the XrdAddrInfo object. The pointer is valid while the //! end-point is connected. //----------------------------------------------------------------------------- -inline -XrdNetAddrInfo *AddrInfo() {return (XrdNetAddrInfo *)&Addr;} + +XrdNetAddrInfo *AddrInfo(); //----------------------------------------------------------------------------- -//! Allocate a new link object. +//! Obtain the number of queued async requests. //! -//! @param peer The connection information for the endpoint. -//! @param opts Processing options: -//! XRDLINK_NOCLOSE - do not close the FD upon recycling. -//! XRDLINK_RDLOCK - obtain a lock prior to reading data. +//! @return The number of async requests queued. +//----------------------------------------------------------------------------- + +int Backlog(); + +//----------------------------------------------------------------------------- +//! Get a copy of the client's name as known by the link. //! -//! @return !0 The pointer to the new object. -//! =0 A new link object could not be allocated. +//! @param buff Pointer to buffer to hold the name. +//! @param blen Length of the buffer. +//! +//! @return !0 The length of the name in gthe buffer. +//! =0 The name could not be returned. +//----------------------------------------------------------------------------- + +int Client(char *buff, int blen); + +//----------------------------------------------------------------------------- +//! Close the link. +//! +//! @param defer If true, the link is made unaccessible but the link +//! object not the file descriptor is released. +//! +//! @return !0 An error occurred, the return value is the errno. +//! =0 Action successfully completed. //----------------------------------------------------------------------------- -static XrdLink *Alloc(XrdNetAddr &peer, int opts=0); +int Close(bool defer=false); + -int Backlog(); +//----------------------------------------------------------------------------- +//! Enable the link to field interrupts. +//----------------------------------------------------------------------------- -void Bind() {} // Obsolete -void Bind(pthread_t tid) { (void)tid; } // Obsolete +void Enable(); -int Client(char *buff, int blen); +//----------------------------------------------------------------------------- +//! Get the associated file descriptor. +//! +//! @return The file descriptor number. +//----------------------------------------------------------------------------- -int Close(int defer=0); +inline int FDnum() {int fd = FD; return (fd < 0 ? -fd : fd);} -void DoIt(); +//----------------------------------------------------------------------------- +//! Translate a file descriptor number to the corresponding link object. +//! +//! @param fd The file descriptor number. +//! +//! @return !0 Pointer to the link object. +//! =0 The file descriptor is not associated with a link. +//----------------------------------------------------------------------------- -void Enable(); +static XrdLink *fd2link(int fd); -int FDnum() {int fd = FD; return (fd < 0 ? -fd : fd);} +//----------------------------------------------------------------------------- +//! Translate a file descriptor number and an instance to a link object. +//! +//! @param fd The file descriptor number. +//! @param inst The file descriptor number instance number. +//! +//! @return !0 Pointer to the link object. +//! =0 The file descriptor instance is not associated with a link. +//----------------------------------------------------------------------------- -static XrdLink *fd2link(int fd) - {if (fd < 0) fd = -fd; - return (fd <= LTLast && LinkBat[fd] ? LinkTab[fd] : 0); - } +static XrdLink *fd2link(int fd, unsigned int inst); -static XrdLink *fd2link(int fd, unsigned int inst) - {if (fd < 0) fd = -fd; - if (fd <= LTLast && LinkBat[fd] && LinkTab[fd] - && LinkTab[fd]->Instance == inst) return LinkTab[fd]; - return (XrdLink *)0; - } +//----------------------------------------------------------------------------- +//! Find the next link matching certain attributes. +//! +//! @param curr Is an internal tracking value that allows repeated calls. +//! It must be set to a value of 0 or less on the initial call +//! and not touched therafter unless a null pointer is returned. +//! @param who If the object use to check if teh link matches the wanted +//! criterea (typically, client name and host name). If the +//! ppointer is nil, the next link is always returned. +//! +//! @return !0 Pointer to the link object that matches the criterea. The +//! link's reference counter is increased to prevent it from +//! being reused. A subsequent call will reduce the number. +//! =0 No more links exist with the specified criterea. +//----------------------------------------------------------------------------- static XrdLink *Find(int &curr, XrdLinkMatch *who=0); - int getIOStats(long long &inbytes, long long &outbytes, - int &numstall, int &numtardy) - { inbytes = BytesIn + BytesInTot; - outbytes = BytesOut+BytesOutTot; - numstall = stallCnt + stallCntTot; - numtardy = tardyCnt + tardyCntTot; - return InUse; - } +//----------------------------------------------------------------------------- +//! Get I/O statistics. +//! +//! @param inbytes The number of bytes read. +//! @param outbytes The number of bytes written. +//! @param numstall The number of times the link was rescheduled due to +//! unavailability. +//! @param numtardy The number of times the link was delayed due to +//! unavailability. +//! +//! @return The link's reference count. The parameters will hold the +//! indicated statistic. +//----------------------------------------------------------------------------- -static int getName(int &curr, char *bname, int blen, XrdLinkMatch *who=0); + int getIOStats(long long &inbytes, long long &outbytes, + int &numstall, int &numtardy); -XrdProtocol *getProtocol() {return Protocol;} // opmutex must be locked +//----------------------------------------------------------------------------- +//! Find the next client name matching certain attributes. +//! +//! @param cur Is an internal tracking value that allows repeated calls. +//! It must be set to a value of 0 or less on the initial call +//! and not touched therafter unless zero is returned. +//! @param bname Pointer to a buffer where the name is to be returned. +//! @param blen The length of the buffer. +//! @param who If the object use to check if the link matches the wanted +//! criterea (typically, client name and host name). If the +//! pointer is nil, a match always occurs. +//! +//! @return !0 The length of teh name placed in the buffer. +//! =0 No more links exist with the specified criterea. +//----------------------------------------------------------------------------- + +static int getName(int &curr, char *bname, int blen, XrdLinkMatch *who=0); + +//----------------------------------------------------------------------------- +//! Obtain current protocol object pointer. +//----------------------------------------------------------------------------- -void Hold(int lk) {(lk ? opMutex.Lock() : opMutex.UnLock());} +XrdProtocol *getProtocol(); + +//----------------------------------------------------------------------------- +//! Obtain polling information object (used by poller only) +//----------------------------------------------------------------------------- + +XrdPollInfo &getPollInfo(); + +//----------------------------------------------------------------------------- +//! Lock or unlock the mutex used for control operations. +//! +//! @param lk When true, a lock is obtained. Otherwise it is released. +//! The caller is responsible for consistency. +//----------------------------------------------------------------------------- + +void Hold(bool lk); //----------------------------------------------------------------------------- //! Get the fully qualified name of the endpoint. @@ -143,14 +210,13 @@ void Hold(int lk) {(lk ? opMutex.Lock() : opMutex.UnLock());} //! while the endpoint is connected. //----------------------------------------------------------------------------- -const char *Host() {return (const char *)HostName;} - -char *ID; // This is referenced a lot +const char *Host() const {return (const char *)HostName;} -static void Init(XrdSysError *eP, XrdOucTrace *tP, XrdScheduler *sP) - {XrdLog = eP; XrdTrace = tP; XrdSched = sP;} +//----------------------------------------------------------------------------- +//! Pointer to the client's link identity. +//----------------------------------------------------------------------------- -static void Init(XrdInet *iP) {XrdNetTCP = iP;} +char *ID; // This is referenced a lot (should have been const). //----------------------------------------------------------------------------- //! Obtain the link's instance number. @@ -158,7 +224,7 @@ static void Init(XrdInet *iP) {XrdNetTCP = iP;} //! @return The link's instance number. //----------------------------------------------------------------------------- inline -unsigned int Inst() {return Instance;} +unsigned int Inst() const {return Instance;} //----------------------------------------------------------------------------- //! Indicate whether or not the link has an outstanding error. @@ -167,7 +233,7 @@ unsigned int Inst() {return Instance;} //! the link has no outstanding error. //----------------------------------------------------------------------------- inline -bool isFlawed() {return Etext != 0;} +bool isFlawed() const {return Etext != 0;} //----------------------------------------------------------------------------- //! Indicate whether or not this link is of a particular instance. @@ -175,12 +241,12 @@ bool isFlawed() {return Etext != 0;} //! //! @param inst the expected instance number. //! -//! @return True the link matches the instance number. -//! the link differs the instance number. +//! @return true the link matches the instance number. +//! false the link differs the instance number. //----------------------------------------------------------------------------- inline -bool isInstance(unsigned int inst) - {return FD >= 0 && Instance == inst;} +bool isInstance(unsigned int inst) const + {return FD >= 0 && Instance == inst;} //----------------------------------------------------------------------------- //! Obtain the domain trimmed name of the end-point. The returned value should @@ -188,8 +254,8 @@ bool isInstance(unsigned int inst) //! //! @return Pointer to the name that remains valid during the link's lifetime. //----------------------------------------------------------------------------- -inline -const char *Name() {return (const char *)Lname;} + +const char *Name() const; //----------------------------------------------------------------------------- //! Obtain the network address object for this link. The returned value is @@ -197,141 +263,295 @@ const char *Name() {return (const char *)Lname;} //! //! @return Pointer to the object and remains valid during the link's lifetime. //----------------------------------------------------------------------------- -inline const -XrdNetAddr *NetAddr() {return &Addr;} +const +XrdNetAddr *NetAddr() const; -int Peek(char *buff, int blen, int timeout=-1); +//----------------------------------------------------------------------------- +//! Issue a socket peek() and return result (do not use for TLS connections). +//! +//! @param buff pointer to buffer to hold data. +//! @param blen length of buffer. +//! @param timeout milliseconds to wait for data. A negative value waits +//! forever. +//! +//! @return >=0 buffer holds data equal to the returned value. +//! < 0 an error or timeout occurred. +//----------------------------------------------------------------------------- + +int Peek(char *buff, int blen, int timeout=-1); + +//----------------------------------------------------------------------------- +//! Read data from a link. Note that this call blocks until some data is +//! available. Use Recv() with a timeout to avoid blocking. +//! +//! @param buff pointer to buffer to hold data. +//! @param blen length of buffer (implies the maximum bytes wanted). +//! +//! @return >=0 buffer holds data equal to the returned value. +//! < 0 an error occurred. +//----------------------------------------------------------------------------- -int Recv(char *buff, int blen); -int Recv(char *buff, int blen, int timeout); +int Recv(char *buff, int blen); -int RecvAll(char *buff, int blen, int timeout=-1); +//----------------------------------------------------------------------------- +//! Read data from a link. Note that this call either reads all the data wanted +//! or no data if the passed timeout occurs before any data is present. +//! +//! @param buff pointer to buffer to hold data. +//! @param blen length of buffer (implies the actual bytes wanted). +//! @param timeout milliseconds to wait for data. A negative value waits +//! forever. +//! +//! @return >=0 buffer holds data equal to the returned value. +//! < 0 an error occurred. Note that a special error -ENOMSG +//! is returned if poll() indicated data was present but +//! no bytes were actually read. +//----------------------------------------------------------------------------- -int Send(const char *buff, int blen); -int Send(const struct iovec *iov, int iocnt, int bytes=0); +int Recv(char *buff, int blen, int timeout); -static int sfOK; // True if Send(sfVec) enabled +//----------------------------------------------------------------------------- +//! Read data from a link. Note that this call reads as much data as it can +//! or until the passed timeout has occurred. +//! +//! @param buff pointer to buffer to hold data. +//! @param blen length of buffer (implies the maximum bytes wanted). +//! @param timeout milliseconds to wait for data. A negative value waits +//! forever. +//! +//! @return >=0 buffer holds data equal to the returned value. +//! < 0 an error occurred or when -ETIMEDOUT is returned, no data +//! arrived within the timeout period. +//----------------------------------------------------------------------------- -typedef XrdOucSFVec sfVec; +int RecvAll(char *buff, int blen, int timeout=-1); -int Send(const sfVec *sdP, int sdn); // Iff sfOK > 0 +//----------------------------------------------------------------------------- +//! Send data on a link. This calls may block unless the socket was marked +//! nonblocking. If a block would occur, the data is copied for later sending. +//! +//! @param buff pointer to buffer to send. +//! @param blen length of buffer. +//! +//! @return >=0 number of bytes sent. +//! < 0 an error or occurred. +//----------------------------------------------------------------------------- -void Serialize(); // ASYNC Mode +int Send(const char *buff, int blen); -int setEtext(const char *text); +//----------------------------------------------------------------------------- +//! Send data on a link. This calls may block unless the socket was marked +//! nonblocking. If a block would occur, the data is copied for later sending. +//! +//! @param iov pointer to the message vector. +//! @param iocnt number of iov elements in the vector. +//! @param bytes the sum of the sizes in the vector. +//! +//! @return >=0 number of bytes sent. +//! < 0 an error occurred. +//----------------------------------------------------------------------------- -void setID(const char *userid, int procid); +int Send(const struct iovec *iov, int iocnt, int bytes=0); -static void setKWT(int wkSec, int kwSec); +//----------------------------------------------------------------------------- +//! Send data on a link using sendfile(). This call always blocks until all +//! data is sent. It should only be called if sfOK is true (see below). +//! +//! @param sdP pointer to the sendfile vector. +//! @param sdn number of elements in the vector. +//! +//! @return >=0 number of bytes sent. +//! < 0 an error occurred. +//----------------------------------------------------------------------------- -void setLocation(XrdNetAddrInfo::LocInfo &loc) {Addr.SetLocation(loc);} +static bool sfOK; // True if Send(sfVec) enabled -bool setNB(); +typedef XrdOucSFVec sfVec; -XrdProtocol *setProtocol(XrdProtocol *pp); +int Send(const sfVec *sdP, int sdn); // Iff sfOK is true -void setRef(int cnt); // ASYNC Mode +//----------------------------------------------------------------------------- +//! Wait for all outstanding requests to be completed on the link. +//----------------------------------------------------------------------------- -static int Setup(int maxfd, int idlewait); +void Serialize(); - void Shutdown(bool getLock); +//----------------------------------------------------------------------------- +//! Set an error indication on he link. +//! +//! @param text a message describing the error. +//! +//! @return =0 message set, the link is considered in error. +//! -1 the message pointer was nil. +//----------------------------------------------------------------------------- -static int Stats(char *buff, int blen, int do_sync=0); +int setEtext(const char *text); - void syncStats(int *ctime=0); +//----------------------------------------------------------------------------- +//! Set the client's link identity. +//! +//! @param userid pointer to the client's username. +//! @param procid the client's process id (i.e. pid). +//----------------------------------------------------------------------------- - int Terminate(const XrdLink *owner, int fdnum, unsigned int inst); +void setID(const char *userid, int procid); -time_t timeCon() {return conTime;} +//----------------------------------------------------------------------------- +//! Set the client's location. +//! +//! @param loc reference to the location information. +//----------------------------------------------------------------------------- -int UseCnt() {return InUse;} +void setLocation(XrdNetAddrInfo::LocInfo &loc); -void armBridge() {isBridged = 1;} -int hasBridge() {return isBridged;} +//----------------------------------------------------------------------------- +//! Set the link to be non-blocking. +//! +//! @return true mode has been set. +//! @return false mode is not supported for this operating system. +//----------------------------------------------------------------------------- - XrdLink(); - ~XrdLink() {} // Is never deleted! +bool setNB(); -private: +//----------------------------------------------------------------------------- +//! Set the link's protocol. +//! +//! @param pp pointer to the protocol object. +//! @param runit if true, starts running the protocol. +//! @param push if true, pushes current protocol to be the alternate one. +//! +//! @return pointer to the previous protocol (may be nil). +//----------------------------------------------------------------------------- -void Reset(); -int sendData(const char *Buff, int Blen); +XrdProtocol *setProtocol(XrdProtocol *pp, bool runit=false, bool push=false); -static XrdSysError *XrdLog; -static XrdOucTrace *XrdTrace; -static XrdScheduler *XrdSched; -static XrdInet *XrdNetTCP; +//----------------------------------------------------------------------------- +//! Set the link's parallel usage count. +//! +//! @param cnt should be 1 to increased the count and -1 to decrease it. +//----------------------------------------------------------------------------- -static XrdSysMutex LTMutex; // For the LinkTab only LTMutex->IOMutex allowed -static XrdLink **LinkTab; -static char *LinkBat; -static unsigned int LinkAlloc; -static int LTLast; -static const char *TraceID; -static int devNull; -static short killWait; -static short waitKill; +void setRef(int cnt); -// Statistical area (global and local) -// -static long long LinkBytesIn; -static long long LinkBytesOut; -static long long LinkConTime; -static long long LinkCountTot; -static int LinkCount; -static int LinkCountMax; -static int LinkTimeOuts; -static int LinkStalls; -static int LinkSfIntr; -static int maxFD; - long long BytesIn; - long long BytesInTot; - long long BytesOut; - long long BytesOutTot; - int stallCnt; - int stallCntTot; - int tardyCnt; - int tardyCntTot; - int SfIntr; -static XrdSysMutex statsMutex; - -// Identification section +//----------------------------------------------------------------------------- +//! Enable or disable TLS on the link. // -XrdNetAddr Addr; -char Uname[24]; // Uname and Lname must be adjacent! -char Lname[232]; -char *HostName; -int HNlen; -#if defined( __linux__ ) || defined( __solaris__ ) -pthread_t TID; // Hack to keep abi compatability -#else -XrdLink *Next; // Only used by PollPoll.icc -#endif -XrdSysMutex opMutex; -XrdSysMutex rdMutex; -XrdSysMutex wrMutex; -XrdSysSemaphore IOSemaphore; -XrdSysCondVar *KillcvP; // Protected by opMutex! -XrdSendQ *sendQ; // Protected by wrMutex && opMutex -XrdProtocol *Protocol; -XrdProtocol *ProtoAlt; -XrdPoll *Poller; -struct pollfd *PollEnt; -char *Etext; -int FD; -unsigned int Instance; -time_t conTime; -int InUse; -int doPost; -char LockReads; -char KeepFD; -char isEnabled; -char isIdle; -char inQ; // Only used by PollPoll.icc -char isBridged; -char KillCnt; // Protected by opMutex! -static const char KillMax = 60; -static const char KillMsk = 0x7f; -static const char KillXwt = 0x80; +//! @param enable if true, TLS is enabled if not already enabled. Otherwise, +//! TLS is disabled and the TLS logical connection torn down. +//! statistics may be contradictory as they are collected async. +//! +//! @return True if successful, false otherwise. +//----------------------------------------------------------------------------- + +bool setTLS(bool enable); + +//----------------------------------------------------------------------------- +//! Shutdown the link but otherwise keep it intact. +//! +//! @param getlock if true, the operation is performed under a lock. +//----------------------------------------------------------------------------- + +void Shutdown(bool getLock); + +//----------------------------------------------------------------------------- +//! Obtain link statistics. +//! +//! @param buff pointer to the buffer for the xml statistics. +//! @param blen length of the buffer. +//! @param do_sync if true, the statistics self-consistent. Otherwise, the +//! statistics may be contradictory as they are collected async. +//! +//! @return number of bytes placed in the buffer excluding the null byte. +//----------------------------------------------------------------------------- + +static int Stats(char *buff, int blen, bool do_sync=0); + +//----------------------------------------------------------------------------- +//! Add all local statistics to the global counters. +//! +//! @param ctime if not nil, return the total connect time in seconds. +//----------------------------------------------------------------------------- + +void syncStats(int *ctime=0); + +//----------------------------------------------------------------------------- +//! Terminate a connection. +//! +//! @param owner pointer to the link ID representing a client who made +//! the connection to be terminated. If nil then this is a +//! request for the link to terminate another link, if possible. +//! @param fdnum the file descriptor number of the link to be terminated. +//! @param inst the link's instance number. +//! +//! @return >0 caller should wait this number of seconds and try again. +//! @return =0 link terminated. +//! @return <0 link could not be terminated: +//! -EACCES the links was not created by the passed owner. +//! -EPIPE link already being terminated. +//! -ESRCH fdnum does not refer to a link. +//! -ETIME unsuccessful, too many tries. +//----------------------------------------------------------------------------- + +int Terminate(const char *owner, int fdnum, unsigned int inst); + +//----------------------------------------------------------------------------- +//! Return the time the link was made active (i.e. time of connection). +//----------------------------------------------------------------------------- + +time_t timeCon() const {return conTime;} + +//----------------------------------------------------------------------------- +//! Return link's reference count. +//----------------------------------------------------------------------------- + +inline int UseCnt() const {return InUse;} + +//----------------------------------------------------------------------------- +//! Mark this link as an in-memory communications bridge (internal use only). +//----------------------------------------------------------------------------- + +void armBridge(); + +//----------------------------------------------------------------------------- +//! Determine if this link is a bridge. +//! +//! @return true this link is a bridge. +//! @return false this link is a plain old link. +//----------------------------------------------------------------------------- + +inline bool hasBridge() const {return isBridged;} + +//----------------------------------------------------------------------------- +//! Constructor +//! +//! @param lxq Reference to the implementation. +//----------------------------------------------------------------------------- + + XrdLink(XrdLinkXeq &lxq); + +protected: + ~XrdLink() {} // Is never deleted! + +void DoIt(); // This is an override of XrdJob::DoIt. +void ResetLink(); +int Wait4Data(int timeout); + +void *rsvd2[3]; // Reserved for future use +XrdSysCondVar *KillcvP; // Protected by opMutex! + +XrdSysSemaphore IOSemaphore; // Serialization semaphore +time_t conTime; // Unix time connected +char *Etext; // -> error text, if nil then no error. +char *HostName; // -> peer's host nameh +XrdLinkXeq &linkXQ; // The implementation +int FD; // File descriptor (may be negative) +unsigned int Instance; // Instance number of this object +XrdSysRecMutex opMutex; // Serialization mutex +int InUse; // Number of threads using this object +int doPost; // Number of threads waiting for serialization +bool isBridged; // If true, this link is an in-memory bridge +bool isTLS; // If true, this link uses TLS for all I/O +char KillCnt; // Number of times a kill has been attempted +char rsvd1[5]; }; #endif diff --git a/src/Xrd/XrdLinkCtl.cc b/src/Xrd/XrdLinkCtl.cc new file mode 100644 index 00000000000..6940947f6c2 --- /dev/null +++ b/src/Xrd/XrdLinkCtl.cc @@ -0,0 +1,402 @@ +/******************************************************************************/ +/* */ +/* X r d L i n k C t l . c c */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include +#include +#include + +#include "Xrd/XrdInet.hh" +#include "Xrd/XrdLinkCtl.hh" +#include "Xrd/XrdLinkMatch.hh" +#include "Xrd/XrdPoll.hh" +#include "Xrd/XrdScheduler.hh" + +#define TRACELINK this +#include "Xrd/XrdTrace.hh" + +#include "XrdOuc/XrdOucTrace.hh" + +#include "XrdSys/XrdSysAtomics.hh" +#include "XrdSys/XrdSysLogger.hh" +#include "XrdSys/XrdSysPthread.hh" + +/******************************************************************************/ +/* G l o b a l O b j e c t s */ +/******************************************************************************/ + +namespace XrdGlobal +{ +extern XrdSysError Log; +extern XrdOucTrace XrdTrace; +extern XrdScheduler Sched; +extern XrdInet *XrdNetTCP; +}; + +using namespace XrdGlobal; + +/******************************************************************************/ +/* S t a t i c s */ +/******************************************************************************/ + + XrdLinkCtl **XrdLinkCtl::LinkTab = 0; + char *XrdLinkCtl::LinkBat = 0; + unsigned int XrdLinkCtl::LinkAlloc= 0; + int XrdLinkCtl::LTLast = -1; + int XrdLinkCtl::maxFD = 0; + XrdSysMutex XrdLinkCtl::LTMutex; + short XrdLinkCtl::killWait = 3; // Kill then wait; + short XrdLinkCtl::waitKill = 4; // Wait then kill + + const char *XrdLinkCtl::TraceID = "LinkCtl"; + +namespace +{ + XrdSysMutex instMutex; + unsigned int myInstance = 1; + int idleCheck; + int idleTicks; + +static const int XRDLINK_USED = 0x01; +static const int XRDLINK_FREE = 0x00; + +class LinkScan : public XrdJob +{ +public: + +void DoIt() {XrdLinkCtl::idleScan(); + Sched.Schedule((XrdJob *)this, idleCheck+time(0)); + } + LinkScan() : XrdJob("Idle link scan") {} + ~LinkScan() {} +}; +} + +/******************************************************************************/ +/* A l l o c */ +/******************************************************************************/ + +XrdLink *XrdLinkCtl::Alloc(XrdNetAddr &peer, int opts) +{ + XrdLinkCtl *lp; + char hName[1024], *unp, buff[16]; + int bl, peerFD = peer.SockFD(); + +// Make sure that the incomming file descriptor can be handled +// + if (peerFD < 0 || peerFD >= maxFD) + {snprintf(hName, sizeof(hName), "%d", peerFD); + Log.Emsg("Link", "attempt to alloc out of range FD -",hName); + return (XrdLink *)0; + } + +// Make sure that the link slot is available +// + LTMutex.Lock(); + if (LinkBat[peerFD]) + {LTMutex.UnLock(); + Log.Emsg("Link", "attempt to reuse active link"); + return (XrdLink *)0; + } + +// Check if we already have a link object in this slot. If not, allocate +// a quantum of link objects and put them in the table. +// + if (!(lp = LinkTab[peerFD])) + {unsigned int i; + XrdLinkCtl **blp, *nlp = new XrdLinkCtl[LinkAlloc](); + if (!nlp) + {LTMutex.UnLock(); + Log.Emsg("Link", ENOMEM, "create link"); + return (XrdLink *)0; + } + blp = &LinkTab[peerFD/LinkAlloc*LinkAlloc]; + for (i = 0; i < LinkAlloc; i++, blp++) *blp = &nlp[i]; + lp = LinkTab[peerFD]; + } + else lp->Reset(); + LinkBat[peerFD] = XRDLINK_USED; + if (peerFD > LTLast) LTLast = peerFD; + LTMutex.UnLock(); + +// Establish the instance number of this link. This is will prevent us from +// sending asynchronous responses to the wrong client when the file descriptor +// gets reused for connections to the same host. +// + instMutex.Lock(); + lp->Instance = myInstance++; + instMutex.UnLock(); + +// Establish the address and connection name of this link +// + peer.Format(hName, sizeof(hName), XrdNetAddr::fmtAuto, + XrdNetAddr::old6Map4 | XrdNetAddr::noPort); + lp->HostName = strdup(hName); + lp->HNlen = strlen(hName); + XrdNetTCP->Trim(hName); + lp->Addr = peer; + strlcpy(lp->Lname, hName, sizeof(lp->Lname)); + bl = sprintf(buff, "?:%d", peerFD); + unp = lp->Uname + sizeof(Uname) - bl - 1; // Solaris compatability + memcpy(unp, buff, bl); + lp->ID = unp; + lp->FD = lp->PollInfo.pollFD = peerFD; + lp->Comment = (const char *)unp; + +// Set options as needed +// + lp->LockReads = (0 != (opts & XRDLINK_RDLOCK)); + lp->KeepFD = (0 != (opts & XRDLINK_NOCLOSE)); + +// Update statistics and return the link. We need to actually get the stats +// mutex even when using atomics because we need to use compound operations. +// The atomics will keep reporters from seeing partial results. +// + statsMutex.Lock(); + AtomicInc(LinkCountTot); // LinkCountTot++ + if (LinkCountMax <= AtomicInc(LinkCount)) LinkCountMax = LinkCount; + statsMutex.UnLock(); + return lp; +} + +/******************************************************************************/ +/* F i n d */ +/******************************************************************************/ + +// Warning: curr must be set to a value of 0 or less on the initial call and +// not touched therafter unless a null pointer is returned. When an +// actual link object pointer is returned, it's refcount is increased. +// The count is automatically decreased on the next call to Find(). +// +XrdLink *XrdLinkCtl::Find(int &curr, XrdLinkMatch *who) +{ + XrdLinkCtl *lp; + const int MaxSeek = 16; + unsigned int myINS; + int i, seeklim = MaxSeek; + +// Do initialization +// + LTMutex.Lock(); + if (curr >= 0 && LinkTab[curr]) LinkTab[curr]->setRef(-1); + else curr = -1; + +// Find next matching link. Since this may take some time, we periodically +// release the LTMutex lock which drives up overhead but will still allow +// other critical operations to occur. +// + for (i = curr+1; i <= LTLast; i++) + {if ((lp = LinkTab[i]) && LinkBat[i] && lp->HostName) + if (!who + || who->Match(lp->ID,lp->Lname-lp->ID-1,lp->HostName,lp->HNlen)) + {myINS = lp->Instance; + LTMutex.UnLock(); + lp->setRef(1); + curr = i; + if (myINS == lp->Instance) return lp; + LTMutex.Lock(); + } + if (!seeklim--) {LTMutex.UnLock(); seeklim = MaxSeek; LTMutex.Lock();} + } + +// Done scanning the table +// + LTMutex.UnLock(); + curr = -1; + return 0; +} + +/******************************************************************************/ +/* g e t N a m e */ +/******************************************************************************/ + +// Warning: curr must be set to a value of 0 or less on the initial call and +// not touched therafter unless null is returned. Returns the length +// the name in nbuf. +// +int XrdLinkCtl::getName(int &curr, char *nbuf, int nbsz, XrdLinkMatch *who) +{ + XrdLinkCtl *lp; + const int MaxSeek = 16; + int i, ulen = 0, seeklim = MaxSeek; + +// Find next matching link. Since this may take some time, we periodically +// release the LTMutex lock which drives up overhead but will still allow +// other critical operations to occur. +// + LTMutex.Lock(); + for (i = curr+1; i <= LTLast; i++) + {if ((lp = LinkTab[i]) && LinkBat[i] && lp->HostName) + if (!who + || who->Match(lp->ID,lp->Lname-lp->ID-1,lp->HostName,lp->HNlen)) + {ulen = lp->Client(nbuf, nbsz); + LTMutex.UnLock(); + curr = i; + return ulen; + } + if (!seeklim--) {LTMutex.UnLock(); seeklim = MaxSeek; LTMutex.Lock();} + } + LTMutex.UnLock(); + +// Done scanning the table +// + curr = -1; + return 0; +} + +/******************************************************************************/ +/* i d l e S c a n */ +/******************************************************************************/ + +#undef TRACELINK +#define TRACELINK lp + +void XrdLinkCtl::idleScan() +{ + XrdLinkCtl *lp; + int i, ltlast, lnum = 0, tmo = 0, tmod = 0; + +// Get the current link high watermark +// + LTMutex.Lock(); + ltlast = LTLast; + LTMutex.UnLock(); + +// Scan across all links looking for idle links. Links are never deallocated +// so we don't need any special kind of lock for these +// + for (i = 0; i <= ltlast; i++) + {if (LinkBat[i] != XRDLINK_USED + || !(lp = LinkTab[i])) continue; + lnum++; + lp->opMutex.Lock(); + if (lp->isIdle) tmo++; + lp->isIdle++; + if ((int(lp->isIdle)) < idleTicks) {lp->opMutex.UnLock(); continue;} + lp->isIdle = 0; + if (!(lp->PollInfo.Poller) || !(lp->PollInfo.isEnabled)) + Log.Emsg("LinkScan","Link",lp->ID,"is disabled and idle."); + else if (lp->InUse == 1) + {lp->PollInfo.Poller->Disable(lp, "idle timeout"); + tmod++; + } + lp->opMutex.UnLock(); + } + +// Trace what we did +// + TRACE(CONN, lnum <<" links; " < 0) waitKill = static_cast(wkSec); + if (kwSec > 0) killWait = static_cast(kwSec); +} + +/******************************************************************************/ +/* S e t u p */ +/******************************************************************************/ + +int XrdLinkCtl::Setup(int maxfds, int idlewait) +{ + int numalloc; + +// Compute the number of link objects we should allocate at a time. Generally, +// we like to allocate 8k of them at a time but always as a power of two. +// + maxFD = maxfds; + numalloc = 8192 / sizeof(XrdLink); + LinkAlloc = 1; + while((numalloc = numalloc/2)) LinkAlloc = LinkAlloc*2; + TRACE(DEBUG, "Allocating " <syncStats();} +} + +/******************************************************************************/ +/* U n h o o k */ +/******************************************************************************/ + +void XrdLinkCtl::Unhook(int fd) +{ + +// Indicate link no longer actvely neing used +// + LTMutex.Lock(); + LinkBat[fd] = XRDLINK_FREE; + if (fd == LTLast) while(LTLast && !(LinkBat[LTLast])) LTLast--; + LTMutex.UnLock(); +} diff --git a/src/Xrd/XrdLinkCtl.hh b/src/Xrd/XrdLinkCtl.hh new file mode 100644 index 00000000000..d15cf6d3043 --- /dev/null +++ b/src/Xrd/XrdLinkCtl.hh @@ -0,0 +1,194 @@ +#ifndef __XRD_LINKCTL_H__ +#define __XRD_LINKCTL_H__ +/******************************************************************************/ +/* */ +/* X r d L i n k C t l . h h */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include "Xrd/XrdLinkXeq.hh" + +#include "XrdSys/XrdSysPthread.hh" + +/******************************************************************************/ +/* C l a s s D e f i n i t i o n */ +/******************************************************************************/ + +class XrdLinkMatch; + +class XrdLinkCtl : protected XrdLinkXeq +{ +public: + +//----------------------------------------------------------------------------- +//! Allocate a new link object. +//! +//! @param peer The connection information for the endpoint. +//! @param opts Processing options: +//! XRDLINK_NOCLOSE - do not close the FD upon recycling. +//! XRDLINK_RDLOCK - obtain a lock prior to reading data. +//! +//! @return !0 The pointer to the new object. +//! =0 A new link object could not be allocated. +//----------------------------------------------------------------------------- + +#define XRDLINK_RDLOCK 0x0001 +#define XRDLINK_NOCLOSE 0x0002 + +static XrdLink *Alloc(XrdNetAddr &peer, int opts=0); + +//----------------------------------------------------------------------------- +//! Translate a file descriptor number to the corresponding link object. +//! +//! @param fd The file descriptor number. +//! +//! @return !0 Pointer to the link object. +//! =0 The file descriptor is not associated with a link. +//----------------------------------------------------------------------------- + +static XrdLink *fd2link(int fd) + {if (fd < 0) fd = -fd; + return (fd <= LTLast && LinkBat[fd] ? LinkTab[fd] : 0); + } + +//----------------------------------------------------------------------------- +//! Translate a file descriptor number and an instance to a link object. +//! +//! @param fd The file descriptor number. +//! @param inst The file descriptor number instance number. +//! +//! @return !0 Pointer to the link object. +//! =0 The file descriptor instance is not associated with a link. +//----------------------------------------------------------------------------- + +static XrdLink *fd2link(int fd, unsigned int inst) + {if (fd < 0) fd = -fd; + if (fd <= LTLast && LinkBat[fd] && LinkTab[fd] + && LinkTab[fd]->Instance == inst) return LinkTab[fd]; + return (XrdLink *)0; + } + +//----------------------------------------------------------------------------- +//! Find the next link matching certain attributes. +//! +//! @param cur Is an internal tracking value that allows repeated calls. +//! It must be set to a value of 0 or less on the initial call +//! and not touched therafter unless a null pointer is returned. +//! @param who If the object use to check if teh link matches the wanted +//! criterea (typically, client name and host name). If the +//! ppointer is nil, the next link is always returned. +//! +//! @return !0 Pointer to the link object that matches the criterea. The +//! link's reference counter is increased to prevent it from +//! being reused. A subsequent call will reduce the number. +//! =0 No more links exist with the specified criterea. +//----------------------------------------------------------------------------- + +static XrdLink *Find(int &curr, XrdLinkMatch *who=0); + +//----------------------------------------------------------------------------- +//! Find the next client name matching certain attributes. +//! +//! @param cur Is an internal tracking value that allows repeated calls. +//! It must be set to a value of 0 or less on the initial call +//! and not touched therafter unless zero is returned. +//! @param bname Pointer to a buffer where the name is to be returned. +//! @param blen The length of the buffer. +//! @param who If the object use to check if the link matches the wanted +//! criterea (typically, client name and host name). If the +//! pointer is nil, a match always occurs. +//! +//! @return !0 The length of the name placed in the buffer. +//! =0 No more links exist with the specified criterea. +//----------------------------------------------------------------------------- + +static int getName(int &curr, char *bname, int blen, XrdLinkMatch *who=0); + +//----------------------------------------------------------------------------- +//! Look for idle links and close hem down. +//----------------------------------------------------------------------------- + +static void idleScan(); + +//----------------------------------------------------------------------------- +//! Set kill constants. +//! +//! @param wksec Seconds to wait for kill to happed, +//! @param kwsec The minimum number of seconds to wait after killing a +//! connection for it to end. +//----------------------------------------------------------------------------- + +static void setKWT(int wkSec, int kwSec); + +//----------------------------------------------------------------------------- +//! Setup link processing. +//! +//! @param maaxfds The maximum number of connections to handle. +//! @param idlewt The time interval to check for idle connections. +//! +//! @return !0 Successful. +//! =0 Setup failed. +//----------------------------------------------------------------------------- + +static int Setup(int maxfds, int idlewt); + +//----------------------------------------------------------------------------- +//! Synchronize statustics for ll links. +//----------------------------------------------------------------------------- + +static void SyncAll(); + +//----------------------------------------------------------------------------- +//! Unhook a link from the active table of links. +//----------------------------------------------------------------------------- + +static void Unhook(int fd); + +//----------------------------------------------------------------------------- +//! Link destruction control constants +//----------------------------------------------------------------------------- + +static short killWait; // Kill then wait; +static short waitKill; // Wait then kill + +//----------------------------------------------------------------------------- +//! Constructor +//----------------------------------------------------------------------------- + + XrdLinkCtl() {} + +private: + ~XrdLinkCtl() {} // Is never deleted! + +static XrdSysMutex LTMutex; // For the LinkTab only LTMutex->IOMutex allowed +static XrdLinkCtl **LinkTab; +static char *LinkBat; +static unsigned int LinkAlloc; +static int LTLast; +static int maxFD; +static const char *TraceID; +}; +#endif diff --git a/src/Xrd/XrdLinkXeq.cc b/src/Xrd/XrdLinkXeq.cc new file mode 100644 index 00000000000..15b6da2e2cd --- /dev/null +++ b/src/Xrd/XrdLinkXeq.cc @@ -0,0 +1,1199 @@ +/******************************************************************************/ +/* */ +/* X r d L i n k X e q . c c */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#if !defined(TCP_CORK) +#undef HAVE_SENDFILE +#endif +#endif + +#ifdef HAVE_SENDFILE + +#ifndef __APPLE__ +#include +#endif + +#endif + +#include "XrdSys/XrdSysAtomics.hh" +#include "XrdSys/XrdSysError.hh" +#include "XrdSys/XrdSysFD.hh" +#include "XrdSys/XrdSysPlatform.hh" + +#include "Xrd/XrdBuffer.hh" +#include "Xrd/XrdLink.hh" +#include "Xrd/XrdLinkCtl.hh" +#include "Xrd/XrdLinkXeq.hh" +#include "Xrd/XrdPoll.hh" +#include "Xrd/XrdScheduler.hh" +#include "Xrd/XrdSendQ.hh" + +#define TRACELINK this +#include "Xrd/XrdTrace.hh" + +/******************************************************************************/ +/* G l o b a l s */ +/******************************************************************************/ + +namespace XrdGlobal +{ +extern XrdSysError Log; +extern XrdOucTrace XrdTrace; +extern XrdScheduler Sched; +extern XrdTlsContext *tlsCtx; +extern int devNull; +}; + +using namespace XrdGlobal; + +/******************************************************************************/ +/* S t a t i c s */ +/******************************************************************************/ + + const char *XrdLinkXeq::TraceID = "LinkXeq"; + + long long XrdLinkXeq::LinkBytesIn = 0; + long long XrdLinkXeq::LinkBytesOut = 0; + long long XrdLinkXeq::LinkConTime = 0; + long long XrdLinkXeq::LinkCountTot = 0; + int XrdLinkXeq::LinkCount = 0; + int XrdLinkXeq::LinkCountMax = 0; + int XrdLinkXeq::LinkTimeOuts = 0; + int XrdLinkXeq::LinkStalls = 0; + int XrdLinkXeq::LinkSfIntr = 0; + XrdSysMutex XrdLinkXeq::statsMutex; + +/******************************************************************************/ +/* C o n s t r u c t o r */ +/******************************************************************************/ + +XrdLinkXeq::XrdLinkXeq() : XrdLink(*this), PollInfo((XrdLink *)this) +{ + XrdLinkXeq::Reset(); +} + +void XrdLinkXeq::Reset() +{ + Uname[sizeof(Uname)-1] = '@'; + Uname[sizeof(Uname)-2] = '?'; + Lname[0] = '?'; + Lname[1] = '\0'; + ID = &Uname[sizeof(Uname)-2]; + sendQ = 0; + stallCnt = stallCntTot = 0; + tardyCnt = tardyCntTot = 0; + SfIntr = 0; + isIdle = 0; + BytesOut = BytesIn = BytesOutTot = BytesInTot = 0; + LockReads= false; + KeepFD = false; + Protocol = 0; + ProtoAlt = 0; + + PollInfo.Zorch(); + ResetLink(); +} + +/******************************************************************************/ +/* B a c k l o g */ +/******************************************************************************/ + +int XrdLinkXeq::Backlog() +{ + XrdSysMutexHelper lck(wrMutex); + +// Return backlog information +// + return (sendQ ? sendQ->Backlog() : 0); +} + +/******************************************************************************/ +/* C l i e n t */ +/******************************************************************************/ + +int XrdLinkXeq::Client(char *nbuf, int nbsz) +{ + int ulen; + +// Generate full client name +// + if (nbsz <= 0) return 0; + ulen = (Lname - ID); + if ((ulen + HNlen) >= nbsz) ulen = 0; + else {strncpy(nbuf, ID, ulen); + strcpy(nbuf+ulen, HostName); + ulen += HNlen; + } + return ulen; +} + +/******************************************************************************/ +/* C l o s e */ +/******************************************************************************/ + +int XrdLinkXeq::Close(bool defer) +{ XrdSysMutexHelper opHelper(opMutex); + int csec, fd, rc = 0; + +// Cleanup TLS if it is active +// + if (isTLS) tlsIO.Shutdown(!defer); + +// If a defer close is requested, we can close the descriptor but we must +// keep the slot number to prevent a new client getting the same fd number. +// Linux is peculiar in that any in-progress operations will remain in that +// state even after the FD is closed unless there is some activity either on +// the connection or an event occurs that causes an operation restart. We +// portably solve this problem by issuing a shutdown() on the socket prior +// closing it. On most platforms, this informs readers that the connection is +// gone (though not on old (i.e. <= 2.3) versions of Linux, sigh). Also, if +// nonblocking mode is enabled, we need to do this in a separate thread as +// a shutdown may block for a pretty long time is lot of messages are queued. +// We will ask the SendQ object to schedule the shutdown for us before it +// commits suicide. +// Note that we can hold the opMutex while we also get the wrMutex. +// + if (defer) + {if (!sendQ) Shutdown(false); + else {TRACEI(DEBUG, "Shutdown FD only via SendQ"); + InUse++; + FD = -FD; + wrMutex.Lock(); + sendQ->Terminate(this); + sendQ = 0; + wrMutex.UnLock(); + } + return 0; + } + +// If we got here then this is not a defered close so we just need to check +// if there is a sendq appendage we need to get rid of. +// + if (sendQ) + {wrMutex.Lock(); + sendQ->Terminate(); + sendQ = 0; + wrMutex.UnLock(); + } + +// Multiple protocols may be bound to this link. If it is in use, defer the +// actual close until the use count drops to one. +// + while(InUse > 1) + {opHelper.UnLock(); + TRACEI(DEBUG, "Close defered, use count=" <Recycle(this, csec, Etext); Protocol = 0;} + if (ProtoAlt) {ProtoAlt->Recycle(this, csec, Etext); ProtoAlt = 0;} + if (Etext) {free(Etext); Etext = 0;} + InUse = 0; + +// At this point we can have no lock conflicts, so if someone is waiting for +// us to terminate let them know about it. Note that we will get the condvar +// mutex while we hold the opMutex. This is the required order! We will also +// zero out the pointer to the condvar while holding the opmutex. +// + if (KillcvP) {KillcvP-> Lock(); KillcvP->Signal(); + KillcvP->UnLock(); KillcvP = 0; + } + +// Remove ourselves from the poll table and then from the Link table. We may +// not hold on to the opMutex when we acquire the LTMutex. However, the link +// table needs to be cleaned up prior to actually closing the socket. So, we +// do some fancy footwork to prevent multiple closes of this link. +// + fd = (FD < 0 ? -FD : FD); + if (FD != -1) + {if (PollInfo.Poller) {XrdPoll::Detach(this); PollInfo.Poller = 0;} + FD = -1; + opHelper.UnLock(); + XrdLinkCtl::Unhook(fd); + } else opHelper.UnLock(); + +// Close the file descriptor if it isn't being shared. Do it as the last +// thing because closes and accepts and not interlocked. +// + if (fd >= 2) {if (KeepFD) rc = 0; + else rc = (close(fd) < 0 ? errno : 0); + } + if (rc) Log.Emsg("Link", rc, "close", ID); + return rc; +} + +/******************************************************************************/ +/* D o I t */ +/******************************************************************************/ + +void XrdLinkXeq::DoIt() +{ + int rc; + +// The Process() return code tells us what to do: +// < 0 -> Stop getting requests, +// -EINPROGRESS leave link disabled but otherwise all is well +// -n Error, disable and close the link +// = 0 -> OK, get next request, if allowed, o/w enable the link +// > 0 -> Slow link, stop getting requests and enable the link +// + if (Protocol) + do {rc = Protocol->Process(this);} while (!rc && Sched.canStick()); + else {Log.Emsg("Link", "Dispatch on closed link", ID); + return; + } + +// Either re-enable the link and cycle back waiting for a new request, leave +// disabled, or terminate the connection. +// + if (rc >= 0) + {if (PollInfo.Poller && !PollInfo.Poller->Enable(this)) Close();} + else if (rc != -EINPROGRESS) Close(); +} + +/******************************************************************************/ +/* P e e k */ +/******************************************************************************/ + +int XrdLinkXeq::Peek(char *Buff, int Blen, int timeout) +{ + XrdSysMutexHelper theMutex; + struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0}; + ssize_t mlen; + int retc; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Wait until we can actually read something +// + isIdle = 0; + do {retc = poll(&polltab, 1, timeout);} while(retc < 0 && errno == EINTR); + if (retc != 1) + {if (retc == 0) return 0; + return Log.Emsg("Link", -errno, "poll", ID); + } + +// Verify it is safe to read now +// + if (!(polltab.revents & (POLLIN|POLLRDNORM))) + {Log.Emsg("Link", XrdPoll::Poll2Text(polltab.revents), "polling", ID); + return -1; + } + +// Do the peek. +// + do {mlen = recv(FD, Buff, Blen, MSG_PEEK);} + while(mlen < 0 && errno == EINTR); + +// Return the result +// + if (mlen >= 0) return int(mlen); + Log.Emsg("Link", errno, "peek on", ID); + return -1; +} + +/******************************************************************************/ +/* R e c v */ +/******************************************************************************/ + +int XrdLinkXeq::Recv(char *Buff, int Blen) +{ + ssize_t rlen; + +// Note that we will read only as much as is queued. Use Recv() with a +// timeout to receive as much data as possible. +// + if (LockReads) rdMutex.Lock(); + isIdle = 0; + do {rlen = read(FD, Buff, Blen);} while(rlen < 0 && errno == EINTR); + if (rlen > 0) AtomicAdd(BytesIn, rlen); + if (LockReads) rdMutex.UnLock(); + + if (rlen >= 0) return int(rlen); + if (FD >= 0) Log.Emsg("Link", errno, "receive from", ID); + return -1; +} + +/******************************************************************************/ + +int XrdLinkXeq::Recv(char *Buff, int Blen, int timeout) +{ + XrdSysMutexHelper theMutex; + struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0}; + ssize_t rlen, totlen = 0; + int retc; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Wait up to timeout milliseconds for data to arrive +// + isIdle = 0; + while(Blen > 0) + {do {retc = poll(&polltab,1,timeout);} while(retc < 0 && errno == EINTR); + if (retc != 1) + {if (retc == 0) + {tardyCnt++; + if (totlen) + {if ((++stallCnt & 0xff) == 1) TRACEI(DEBUG,"read timed out"); + AtomicAdd(BytesIn, totlen); + } + return int(totlen); + } + return (FD >= 0 ? Log.Emsg("Link", -errno, "poll", ID) : -1); + } + + // Verify it is safe to read now + // + if (!(polltab.revents & (POLLIN|POLLRDNORM))) + {Log.Emsg("Link", XrdPoll::Poll2Text(polltab.revents), + "polling", ID); + return -1; + } + + // Read as much data as you can. Note that we will force an error + // if we get a zero-length read after poll said it was OK. + // + do {rlen = recv(FD, Buff, Blen, 0);} while(rlen < 0 && errno == EINTR); + if (rlen <= 0) + {if (!rlen) return -ENOMSG; + return (FD<0 ? -1 : Log.Emsg("Link",-errno,"receive from",ID)); + } + totlen += rlen; Blen -= rlen; Buff += rlen; + } + + AtomicAdd(BytesIn, totlen); + return int(totlen); +} + + +/******************************************************************************/ +/* R e c v A l l */ +/******************************************************************************/ + +int XrdLinkXeq::RecvAll(char *Buff, int Blen, int timeout) +{ + struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0}; + ssize_t rlen; + int retc; + +// Check if timeout specified. Notice that the timeout is the max we will +// for some data. We will wait forever for all the data. Yeah, it's weird. +// + if (timeout >= 0) + {do {retc = poll(&polltab,1,timeout);} while(retc < 0 && errno == EINTR); + if (retc != 1) + {if (!retc) return -ETIMEDOUT; + Log.Emsg("Link",errno,"poll",ID); + return -1; + } + if (!(polltab.revents & (POLLIN|POLLRDNORM))) + {Log.Emsg("Link",XrdPoll::Poll2Text(polltab.revents),"polling",ID); + return -1; + } + } + +// Note that we will block until we receive all he bytes. +// + if (LockReads) rdMutex.Lock(); + isIdle = 0; + do {rlen = recv(FD,Buff,Blen,MSG_WAITALL);} while(rlen < 0 && errno == EINTR); + if (rlen > 0) AtomicAdd(BytesIn, rlen); + if (LockReads) rdMutex.UnLock(); + + if (int(rlen) == Blen) return Blen; + if (!rlen) {TRACEI(DEBUG, "No RecvAll() data; errno=" < 0) Log.Emsg("RecvAll","Premature end from", ID); + else if (FD >= 0) Log.Emsg("Link",errno,"recieve from",ID); + return -1; +} + +/******************************************************************************/ +/* S e n d */ +/******************************************************************************/ + +int XrdLinkXeq::Send(const char *Buff, int Blen) +{ + ssize_t retc = 0, bytesleft = Blen; + +// Get a lock +// + wrMutex.Lock(); + isIdle = 0; + AtomicAdd(BytesOut, Blen); + +// Do non-blocking writes if we are setup to do so. +// + if (sendQ) + {retc = sendQ->Send(Buff, Blen); + wrMutex.UnLock(); + return retc; + } + +// Write the data out +// + while(bytesleft) + {if ((retc = write(FD, Buff, bytesleft)) < 0) + {if (errno == EINTR) continue; + else break; + } + bytesleft -= retc; Buff += retc; + } + +// All done +// + wrMutex.UnLock(); + if (retc >= 0) return Blen; + Log.Emsg("Link", errno, "send to", ID); + return -1; +} + +/******************************************************************************/ + +int XrdLinkXeq::Send(const struct iovec *iov, int iocnt, int bytes) +{ + ssize_t bytesleft, n, retc = 0; + const char *Buff; + +// Get a lock and assume we will be successful (statistically we are) +// + wrMutex.Lock(); + isIdle = 0; + AtomicAdd(BytesOut, bytes); + +// Do non-blocking writes if we are setup to do so. +// + if (sendQ) + {retc = sendQ->Send(iov, iocnt, bytes); + wrMutex.UnLock(); + return retc; + } + +// Write the data out. On some version of Unix (e.g., Linux) a writev() may +// end at any time without writing all the bytes when directed to a socket. +// So, we attempt to resume the writev() using a combination of write() and +// a writev() continuation. This approach slowly converts a writev() to a +// series of writes if need be. We must do this inline because we must hold +// the lock until all the bytes are written or an error occurs. +// + bytesleft = static_cast(bytes); + while(bytesleft) + {do {retc = writev(FD, iov, iocnt);} while(retc < 0 && errno == EINTR); + if (retc >= bytesleft || retc < 0) break; + bytesleft -= retc; + while(retc >= (n = static_cast(iov->iov_len))) + {retc -= n; iov++; iocnt--;} + Buff = (const char *)iov->iov_base + retc; n -= retc; iov++; iocnt--; + while(n) {if ((retc = write(FD, Buff, n)) < 0) + {if (errno == EINTR) continue; + else break; + } + n -= retc; Buff += retc; + } + if (retc < 0 || iocnt < 1) break; + } + +// All done +// + wrMutex.UnLock(); + if (retc >= 0) return bytes; + Log.Emsg("Link", errno, "send to", ID); + return -1; +} + +/******************************************************************************/ +int XrdLinkXeq::Send(const sfVec *sfP, int sfN) +{ +#if !defined(HAVE_SENDFILE) || defined(__APPLE__) + return -1; + +#else + +#ifdef __solaris__ + sendfilevec_t vecSF[XrdOucSFVec::sfMax], *vecSFP = vecSF; + size_t xframt, totamt, bytes = 0; + ssize_t retc; + int i = 0; + +// Construct the sendfilev() vector +// + for (i = 0; i < sfN; sfP++, i++) + {if (sfP->fdnum < 0) + {vecSF[i].sfv_fd = SFV_FD_SELF; + vecSF[i].sfv_off = (off_t)sfP->buffer; + } else { + vecSF[i].sfv_fd = sfP->fdnum; + vecSF[i].sfv_off = sfP->offset; + } + vecSF[i].sfv_flag = 0; + vecSF[i].sfv_len = sfP->sendsz; + bytes += sfP->sendsz; + } + totamt = bytes; + +// Lock the link, issue sendfilev(), and unlock the link. The documentation +// is very spotty and inconsistent. We can only retry this operation under +// very limited conditions. +// + wrMutex.Lock(); + isIdle = 0; +do{retc = sendfilev(FD, vecSFP, sfN, &xframt); + +// Check if all went well and return if so (usual case) +// + if (xframt == bytes) + {AtomicAdd(BytesOut, bytes); + wrMutex.UnLock(); + return totamt; + } + +// The only one we will recover from is EINTR. We cannot legally get EAGAIN. +// + if (retc < 0 && errno != EINTR) break; + +// Try to resume the transfer +// + if (xframt > 0) + {AtomicAdd(BytesOut, xframt); bytes -= xframt; SfIntr++; + while(xframt > 0 && sfN) + {if ((ssize_t)xframt < (ssize_t)vecSFP->sfv_len) + {vecSFP->sfv_off += xframt; vecSFP->sfv_len -= xframt; break;} + xframt -= vecSFP->sfv_len; vecSFP++; sfN--; + } + } + } while(sfN > 0); + +// See if we can recover without destroying the connection +// + retc = (retc < 0 ? errno : ECANCELED); + wrMutex.UnLock(); + Log.Emsg("Link", retc, "send file to", ID); + return -1; + +#elif defined(__linux__) + + static const int setON = 1, setOFF = 0; + ssize_t retc = 0, bytesleft; + off_t myOffset; + int i, xfrbytes = 0, uncork = 1, xIntr = 0; + +// lock the link +// + wrMutex.Lock(); + isIdle = 0; + +// In linux we need to cork the socket. On permanent errors we do not uncork +// the socket because it will be closed in short order. +// + if (setsockopt(FD, SOL_TCP, TCP_CORK, &setON, sizeof(setON)) < 0) + {Log.Emsg("Link", errno, "cork socket for", ID); + uncork = 0; sfOK = 0; + } + +// Send the header first +// + for (i = 0; i < sfN; sfP++, i++) + {if (sfP->fdnum < 0) retc = sendData(sfP->buffer, sfP->sendsz); + else {myOffset = sfP->offset; bytesleft = sfP->sendsz; + while(bytesleft + && (retc=sendfile(FD,sfP->fdnum,&myOffset,bytesleft)) > 0) + {bytesleft -= retc; xIntr++;} + } + if (retc < 0 && errno == EINTR) continue; + if (retc <= 0) break; + xfrbytes += sfP->sendsz; + } + +// Diagnose any sendfile errors +// + if (retc <= 0) + {if (retc == 0) errno = ECANCELED; + wrMutex.UnLock(); + Log.Emsg("Link", errno, "send file to", ID); + return -1; + } + +// Now uncork the socket +// + if (uncork && setsockopt(FD, SOL_TCP, TCP_CORK, &setOFF, sizeof(setOFF)) < 0) + Log.Emsg("Link", errno, "uncork socket for", ID); + +// All done +// + if (xIntr > sfN) SfIntr += (xIntr - sfN); + AtomicAdd(BytesOut, xfrbytes); + wrMutex.UnLock(); + return xfrbytes; +#endif +#endif +} + +/******************************************************************************/ +/* private s e n d D a t a */ +/******************************************************************************/ + +int XrdLinkXeq::sendData(const char *Buff, int Blen) +{ + ssize_t retc = 0, bytesleft = Blen; + +// Write the data out +// + while(bytesleft) + {if ((retc = write(FD, Buff, bytesleft)) < 0) + {if (errno == EINTR) continue; + else break; + } + bytesleft -= retc; Buff += retc; + } + +// All done +// + return retc; +} + +/******************************************************************************/ +/* s e t I D */ +/******************************************************************************/ + +void XrdLinkXeq::setID(const char *userid, int procid) +{ + char buff[sizeof(Uname)], *bp, *sp; + int ulen; + + snprintf(buff, sizeof(buff), "%s.%d:%d", userid, procid, FD); + ulen = strlen(buff); + sp = buff + ulen - 1; + bp = &Uname[sizeof(Uname)-1]; + if (ulen > (int)sizeof(Uname)) ulen = sizeof(Uname); + *bp = '@'; bp--; + while(ulen--) {*bp = *sp; bp--; sp--;} + ID = bp+1; + Comment = (const char *)ID; +} + +/******************************************************************************/ +/* s e t N B */ +/******************************************************************************/ + +bool XrdLinkXeq::setNB() +{ +// We don't support non-blocking output except for Linux at the moment +// +#if !defined(__linux__) + return false; +#else +// Trace this request +// + TRACEI(DEBUG,"enabling non-blocking output"); + +// If we don't already have a sendQ object get one. This is a one-time call +// so to optimize checking if this object exists we also get the opMutex.' +// + opMutex.Lock(); + if (!sendQ) + {wrMutex.Lock(); + sendQ = new XrdSendQ(*this, wrMutex); + wrMutex.UnLock(); + } + opMutex.UnLock(); + return true; +#endif +} + +/******************************************************************************/ +/* s e t P r o t o c o l */ +/******************************************************************************/ + +XrdProtocol *XrdLinkXeq::setProtocol(XrdProtocol *pp, bool push) +{ + +// Set new protocol. +// + opMutex.Lock(); + XrdProtocol *op = Protocol; + if (push) ProtoAlt = Protocol; + Protocol = pp; + opMutex.UnLock(); + return op; +} + +/******************************************************************************/ +/* s e t T L S */ +/******************************************************************************/ + +bool XrdLinkXeq::setTLS(bool enable) +{ + static const XrdTlsConnection::RW_Mode rwMode=XrdTlsConnection::TLS_RNB_WBL; + static const XrdTlsConnection::HS_Mode hsMode=XrdTlsConnection::TLS_HS_BLOCK; + const char *eNote; + +// If we are already in a compatible mode, we are done +// + if (isTLS == enable) return true; + +// Either enable TLS or shut it down. +// + if (enable) eNote = tlsIO.Init(*tlsCtx, FD, rwMode, hsMode, false); + else {tlsIO.Shutdown(); + eNote = 0; + } + +// Check for errors +// + if (eNote) + {char buff[1024]; + snprintf(buff, sizeof(buff), "Unable to %s tls for %s;", + (enable ? "enable" : "disable"), ID); + Log.Emsg("LinkXeq", buff, eNote); + return false; + } + +// All set. Record our TLS state. +// + isTLS = enable; + Addr.SetTLS(enable); + return true; +} + +/******************************************************************************/ +/* S F E r r o r */ +/******************************************************************************/ + +int XrdLinkXeq::SFError(int rc) +{ + Log.Emsg("TLS_Link", rc, "send file to", ID); + return -1; +} + +/******************************************************************************/ +/* S h u t d o w n */ +/******************************************************************************/ + +void XrdLinkXeq::Shutdown(bool getLock) +{ + int temp, theFD; + +// Trace the entry +// + TRACEI(DEBUG, (getLock ? "Async" : "Sync") <<" link shutdown in progress"); + +// Get the lock if we need too (external entry via another thread) +// + if (getLock) opMutex.Lock(); + +// If there is something to do, do it now +// + temp = Instance; Instance = 0; + if (!KeepFD) + {theFD = (FD < 0 ? -FD : FD); + shutdown(theFD, SHUT_RDWR); + if (dup2(devNull, theFD) < 0) + {Instance = temp; + Log.Emsg("Link", errno, "shutdown FD for", ID); + } + } + +// All done +// + if (getLock) opMutex.UnLock(); +} + +/******************************************************************************/ +/* S t a t s */ +/******************************************************************************/ + +int XrdLinkXeq::Stats(char *buff, int blen, bool do_sync) +{ + static const char statfmt[] = "%d" + "%d%lld%lld%lld" + "%lld%d%d" + "%d"; + int i; + +// Check if actual length wanted +// + if (!buff) return sizeof(statfmt)+17*6; + +// We must synchronize the statistical counters +// + if (do_sync) XrdLinkCtl::SyncAll(); + +// Obtain lock on the stats area and format it +// + AtomicBeg(statsMutex); + i = snprintf(buff, blen, statfmt, AtomicGet(LinkCount), + AtomicGet(LinkCountMax), + AtomicGet(LinkCountTot), + AtomicGet(LinkBytesIn), + AtomicGet(LinkBytesOut), + AtomicGet(LinkConTime), + AtomicGet(LinkTimeOuts), + AtomicGet(LinkStalls), + AtomicGet(LinkSfIntr)); + AtomicEnd(statsMutex); + return i; +} + +/******************************************************************************/ +/* s y n c S t a t s */ +/******************************************************************************/ + +void XrdLinkXeq::syncStats(int *ctime) +{ + long long tmpLL; + int tmpI4; + +// If this is dynamic, get the opMutex lock +// + if (!ctime) opMutex.Lock(); + +// Either the caller has the opMutex or this is called out of close. In either +// case, we need to get the read and write mutexes; each followed by the stats +// mutex. This order is important because we should not hold the stats mutex +// for very long and the r/w mutexes may take a long time to acquire. If we +// must maintain the link count we need to actually acquire the stats mutex as +// we will be doing compound operations. Atomics are still used to keep other +// threads from seeing partial results. +// + AtomicBeg(rdMutex); + + if (ctime) + {*ctime = time(0) - conTime; + AtomicAdd(LinkConTime, *ctime); + statsMutex.Lock(); + if (LinkCount > 0) AtomicDec(LinkCount); + statsMutex.UnLock(); + } + + AtomicBeg(statsMutex); + + tmpLL = AtomicFAZ(BytesIn); + AtomicAdd(LinkBytesIn, tmpLL); AtomicAdd(BytesInTot, tmpLL); + tmpI4 = AtomicFAZ(tardyCnt); + AtomicAdd(LinkTimeOuts, tmpI4); AtomicAdd(tardyCntTot, tmpI4); + tmpI4 = AtomicFAZ(stallCnt); + AtomicAdd(LinkStalls, tmpI4); AtomicAdd(stallCntTot, tmpI4); + AtomicEnd(statsMutex); AtomicEnd(rdMutex); + + AtomicBeg(wrMutex); AtomicBeg(statsMutex); + tmpLL = AtomicFAZ(BytesOut); + AtomicAdd(LinkBytesOut, tmpLL); AtomicAdd(BytesOutTot, tmpLL); + tmpI4 = AtomicFAZ(SfIntr); + AtomicAdd(LinkSfIntr, tmpI4); + AtomicEnd(statsMutex); AtomicEnd(wrMutex); + +// Make sure the protocol updates it's statistics as well +// + if (Protocol) Protocol->Stats(0, 0, 1); + +// All done +// + if (!ctime) opMutex.UnLock(); +} + +/******************************************************************************/ +/* Private: T L S _ E r r o r */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Error(const char *act, int rc) +{ + std::string reason = tlsIO.Err2Text(rc); + char msg[512]; + + snprintf(msg, sizeof(msg), "Unable to %s %s;", act, ID); + Log.Emsg("TLS_Link", msg, reason.c_str()); + return -1; +} + +/******************************************************************************/ +/* T L S _ P e e k */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Peek(char *Buff, int Blen, int timeout) +{ + XrdSysMutexHelper theMutex; + int retc, rlen; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Wait until we can actually read something +// + isIdle = 0; + if (timeout) + {retc = Wait4Data(timeout); + if (retc < 1) return retc; + } + +// Do the peek and if sucessful, the number of bytes available. +// + retc = tlsIO.Peek(Buff, Blen, rlen); + if (retc == SSL_ERROR_NONE) return rlen; + +// Dianose the TLS error and return failure +// + return TLS_Error("peek on", retc); +} + +/******************************************************************************/ +/* T L S _ R e c v */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Recv(char *Buff, int Blen) +{ + XrdSysMutexHelper theMutex; + int retc, rlen; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Note that we will read only as much as is queued. Use Recv() with a +// timeout to receive as much data as possible. +// + isIdle = 0; + retc = tlsIO.Read(Buff, Blen, rlen); + if (retc != SSL_ERROR_NONE) return TLS_Error("receive from", retc); + if (rlen > 0) AtomicAdd(BytesIn, rlen); + return rlen; +} + +/******************************************************************************/ + +int XrdLinkXeq::TLS_Recv(char *Buff, int Blen, int timeout) +{ + XrdSysMutexHelper theMutex; + int retc, rlen, totlen = 0, maxnulls = 3; + +// Lock the read mutex if we need to, the helper will unlock it upon exit +// + if (LockReads) theMutex.Lock(&rdMutex); + +// Wait up to timeout milliseconds for data to arrive +// + isIdle = 0; + while(Blen > 0) + {retc = Wait4Data(timeout); + if (retc < 1) + {if (retc < 0) return -1; + tardyCnt++; + if (totlen) + {if ((++stallCnt & 0xff) == 1) TRACEI(DEBUG,"read timed out"); + AtomicAdd(BytesIn, totlen); + } + return totlen; + } + + // Read as much data as you can. Note that we will force an error + // if we get a zero-length read after poll said it was OK. + // + retc = tlsIO.Read(Buff, Blen, rlen); + if (retc != SSL_ERROR_NONE) + {AtomicAdd(BytesIn, totlen); + return TLS_Error("receive from", retc); + } +//??? if (rlen <= 0 && (maxnulls-- < 1)) return -ENOMSG; + if (rlen <= 0 && maxnulls) return 0; + totlen += rlen; Blen -= rlen; Buff += rlen; + } + + AtomicAdd(BytesIn, totlen); + return totlen; +} + +/******************************************************************************/ +/* T L S _ R e c v A l l */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_RecvAll(char *Buff, int Blen, int timeout) +{ + int retc; + +// Check if timeout specified. Notice that the timeout is the max we will +// wait for some data. We will wait forever for all the data. Yeah, it's weird. +// + if (timeout >= 0) + {retc = Wait4Data(timeout); + if (retc != 1) return (retc ? -1 : -ETIMEDOUT); + } + +// Note that we will block until we receive all the bytes. +// + return Recv(Buff, Blen, -1); +} + +/******************************************************************************/ +/* T L S _ S e n d */ +/******************************************************************************/ + +int XrdLinkXeq::TLS_Send(const char *Buff, int Blen) +{ + XrdSysMutexHelper lck(wrMutex); + ssize_t bytesleft = Blen; + int retc, byteswritten; + +// Prepare to send +// + isIdle = 0; + AtomicAdd(BytesOut, Blen); + +// Do non-blocking writes if we are setup to do so. +// + if (sendQ) return sendQ->Send(Buff, Blen); + +// Write the data out +// + while(bytesleft) + {retc = tlsIO.Write(Buff, bytesleft, byteswritten); + if (retc != SSL_ERROR_NONE) return TLS_Error("send to", retc); + bytesleft -= byteswritten; Buff += byteswritten; + } + +// All done +// + return Blen; +} + +/******************************************************************************/ + +int XrdLinkXeq::TLS_Send(const struct iovec *iov, int iocnt, int bytes) +{ + XrdSysMutexHelper lck(wrMutex); + int byteswritten, retc; + +// Get a lock and assume we will be successful (statistically we are). Note +// that the calling interface gauranteed bytes are not zero. +// + isIdle = 0; + AtomicAdd(BytesOut, bytes); + +// Do non-blocking writes if we are setup to do so. +// + if (sendQ) return sendQ->Send(iov, iocnt, bytes); + +// Write the data out. +// + for (int i = 0; i < iocnt; i++) + {ssize_t bytesleft = iov[i].iov_len; + char *Buff = (char *)iov[i].iov_base; + while(bytesleft) + {retc = tlsIO.Write(Buff, bytesleft, byteswritten); + if (retc != SSL_ERROR_NONE) return TLS_Error("send to", retc); + bytesleft -= byteswritten; Buff += byteswritten; + } + } + +// All done +// + return bytes; +} + +/******************************************************************************/ + +int XrdLinkXeq::TLS_Send(const sfVec *sfP, int sfN) +{ + XrdSysMutexHelper lck(wrMutex); + int bytes, buffsz, fileFD, retc; + off_t offset; + ssize_t totamt = 0; + char myBuff[65536]; + +// Convert the sendfile to a regular send. The conversion is not particularly +// fast and caller are advised to avoid using sendfile on TLS connections. +// + isIdle = 0; + for (int i = 0; i < sfN; sfP++, i++) + {if (!(bytes = sfP->sendsz)) continue; + totamt += bytes; + if (sfP->fdnum < 0) + {if (!TLS_Write(sfP->buffer, bytes)) return -1; + continue; + } + offset = (off_t)sfP->buffer; + fileFD = sfP->fdnum; + buffsz = (bytes < (int)sizeof(myBuff) ? bytes : sizeof(myBuff)); + do {do {retc = pread(fileFD, myBuff, buffsz, offset);} + while(retc < 0 && errno == EINTR); + if (retc < 0) return SFError(errno); + if (retc != buffsz) return SFError(EOVERFLOW); + if (!TLS_Write(myBuff, buffsz)) return -1; + offset += buffsz; bytes -= buffsz; + } while(bytes > 0); + } + +// We are done +// + AtomicAdd(BytesOut, totamt); + return totamt; +} + +/******************************************************************************/ +/* Private: T L S _ W r i t e */ +/******************************************************************************/ + +bool XrdLinkXeq::TLS_Write(const char *Buff, int Blen) +{ + int retc, byteswritten; + +// Write the data out +// + while(Blen) + {retc = tlsIO.Write(Buff, Blen, byteswritten); + if (retc != SSL_ERROR_NONE) + {TLS_Error("send to", retc); + return false; + } + Blen -= byteswritten; Buff += byteswritten; + } + +// All done +// + return true; +} diff --git a/src/Xrd/XrdLinkXeq.hh b/src/Xrd/XrdLinkXeq.hh new file mode 100644 index 00000000000..5f87d717db7 --- /dev/null +++ b/src/Xrd/XrdLinkXeq.hh @@ -0,0 +1,187 @@ +#ifndef __XRD_LINKXEQ_H__ +#define __XRD_LINKXEQ_H__ +/******************************************************************************/ +/* */ +/* X r d L i n k X e q . h h */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +#include +#include +#include + +#include "Xrd/XrdLink.hh" +#include "Xrd/XrdPollInfo.hh" +#include "Xrd/XrdProtocol.hh" + +#include "XrdNet/XrdNetAddr.hh" + +#include "XrdTls/XrdTlsConnection.hh" + +/******************************************************************************/ +/* C l a s s D e f i n i t i o n */ +/******************************************************************************/ + +class XrdSendQ; + +class XrdLinkXeq : protected XrdLink +{ +public: + +inline +XrdNetAddrInfo *AddrInfo() {return (XrdNetAddrInfo *)&Addr;} + +int Backlog(); + +int Client(char *buff, int blen); + +int Close(bool defer=false); + +void DoIt(); // Override + + + int getIOStats(long long &inbytes, long long &outbytes, + int &numstall, int &numtardy) + { inbytes = BytesIn + BytesInTot; + outbytes = BytesOut+BytesOutTot; + numstall = stallCnt + stallCntTot; + numtardy = tardyCnt + tardyCntTot; + return InUse; + } + +static int getName(int &curr, char *bname, int blen, XrdLinkMatch *who=0); + +inline +XrdProtocol *getProtocol() {return Protocol;} + +inline +const char *Name() const {return (const char *)Lname;} + +inline const +XrdNetAddr *NetAddr() const {return &Addr;} + +int Peek(char *buff, int blen, int timeout=-1); + +int Recv(char *buff, int blen); +int Recv(char *buff, int blen, int timeout); + +int RecvAll(char *buff, int blen, int timeout=-1); + +int Send(const char *buff, int blen); +int Send(const struct iovec *iov, int iocnt, int bytes=0); + +int Send(const sfVec *sdP, int sdn); // Iff sfOK > 0 + +void setID(const char *userid, int procid); + +void setLocation(XrdNetAddrInfo::LocInfo &loc) {Addr.SetLocation(loc);} + +bool setNB(); + +XrdProtocol *setProtocol(XrdProtocol *pp, bool push); + +bool setTLS(bool enable); + + void Shutdown(bool getLock); + +static int Stats(char *buff, int blen, bool do_sync=false); + + void syncStats(int *ctime=0); + +int TLS_Peek(char *Buff, int Blen, int timeout); + +int TLS_Recv(char *Buff, int Blen); + +int TLS_Recv(char *Buff, int Blen, int timeout); + +int TLS_RecvAll(char *Buff, int Blen, int timeout); + +int TLS_Send(const char *Buff, int Blen); + +int TLS_Send(const struct iovec *iov, int iocnt, int bytes); + +int TLS_Send(const sfVec *sfP, int sfN); + + XrdLinkXeq(); + ~XrdLinkXeq() {} // Is never deleted! + +XrdPollInfo PollInfo; + +protected: + +void Reset(); +int sendData(const char *Buff, int Blen); +int SFError(int rc); +int TLS_Error(const char *act, int rc); +bool TLS_Write(const char *Buff, int Blen); + +static const char *TraceID; + +// Statistical area (global and local) +// +static long long LinkBytesIn; +static long long LinkBytesOut; +static long long LinkConTime; +static long long LinkCountTot; +static int LinkCount; +static int LinkCountMax; +static int LinkTimeOuts; +static int LinkStalls; +static int LinkSfIntr; + long long BytesIn; + long long BytesInTot; + long long BytesOut; + long long BytesOutTot; + int stallCnt; + int stallCntTot; + int tardyCnt; + int tardyCntTot; + int SfIntr; +static XrdSysMutex statsMutex; + +// Protocol section +// +XrdProtocol *Protocol; // -> Protocol tied to the link +XrdProtocol *ProtoAlt; // -> Alternate/stacked protocol + +// TLS section +// +XrdTlsConnection tlsIO; + +// Identification section +// +XrdNetAddr Addr; +XrdSysMutex rdMutex; +XrdSysMutex wrMutex; +XrdSendQ *sendQ; // Protected by wrMutex && opMutex +int HNlen; +bool LockReads; +bool KeepFD; +char isIdle; +char Uname[24]; // Uname and Lname must be adjacent! +char Lname[256]; +}; +#endif diff --git a/src/Xrd/XrdMain.cc b/src/Xrd/XrdMain.cc index 8b0c3520be7..738584376e0 100644 --- a/src/Xrd/XrdMain.cc +++ b/src/Xrd/XrdMain.cc @@ -95,9 +95,7 @@ static XrdConfig Config; void DoIt() {XrdLink *newlink; if ((newlink = theNet->Accept(0, -1, theSem))) - {newlink->setProtocol(theProt); - newlink->DoIt(); - } + newlink->setProtocol(theProt, true); } XrdMain() : XrdJob("main accept"), theSem(0), theProt(0), diff --git a/src/Xrd/XrdPoll.cc b/src/Xrd/XrdPoll.cc index 7479c8408ae..1bebab3df15 100644 --- a/src/Xrd/XrdPoll.cc +++ b/src/Xrd/XrdPoll.cc @@ -46,10 +46,13 @@ #include "Xrd/XrdPollDev.hh" #elif defined( __linux__ ) #include "Xrd/XrdPollE.hh" +//#include "Xrd/XrdPollPoll.hh" #else #include "Xrd/XrdPollPoll.hh" #endif +#include "Xrd/XrdPollInfo.hh" + /******************************************************************************/ /* L o c a l C l a s s e s */ /******************************************************************************/ @@ -158,11 +161,12 @@ int XrdPoll::Attach(XrdLink *lp) // Complete the link setup // - lp->Poller = pp; + lp->getPollInfo().Poller = pp; pp->numAttached++; doingAttach.UnLock(); - TRACEI(POLL, "FD " <FD <<" attached to poller " <PID <<"; num=" <numAttached); - return 1; + TRACEI(POLL, "FD " <FDnum() <<" attached to poller " <PID + <<"; num=" <numAttached); + return 1; } /******************************************************************************/ @@ -175,7 +179,7 @@ void XrdPoll::Detach(XrdLink *lp) // If link is not attached, simply return // - if (!(pp = lp->Poller)) return; + if (!(pp = lp->getPollInfo().Poller)) return; // Exclude this link from the associated poll set // @@ -202,21 +206,18 @@ int XrdPoll::Finish(XrdLink *lp, const char *etxt) // If this link is already scheduled for termination, ignore this call. // - if (lp->Protocol == &LinkEnd) - {TRACEI(POLL, "Link " <FD <<" already terminating; " + if (lp->getProtocol() == &LinkEnd) + {TRACEI(POLL, "Link " <FDnum() <<" already terminating; " <<(etxt ? etxt : "") <<" request ignored."); return 0; } // Set the protocol pointer to be link termination // - lp->ProtoAlt = lp->Protocol; - lp->Protocol = static_cast(&LinkEnd); - if (etxt) - {if (lp->Etext) free(lp->Etext); - lp->Etext = strdup(etxt); - } else etxt = "reason unknown"; - TRACEI(POLL, "Link " <FD <<" terminating: " <setProtocol(&LinkEnd, false, true); + if (!etxt) etxt = "reason unknown"; + lp->setEtext(etxt); + TRACEI(POLL, "Link " <FDnum() <<" terminating: " <getPollInfo(); PipeData cmdbuff[2]; int myerrno = 0; // Simply return if the link is already disabled // - if (!lp->isEnabled) return; + if (!pInfo.isEnabled) return; // Trace this event // - TRACEI(POLL, "Poller " <FD); + TRACEI(POLL, "Poller " <FD; + cmdbuff[0].Parms.Arg.fd = pInfo.pollFD; cmdbuff[1].req = PipeData::Post; cmdbuff[1].Parms.theSem = &mySem; myerrno = sendCmd((char *)&cmdbuff, sizeof(cmdbuff)); @@ -122,17 +124,18 @@ void XrdPollDev::Disable(XrdLink *lp, const char *etxt) int XrdPollDev::Enable(XrdLink *lp) { + XrdPollInfo &pInfo = lp->getPollInfo(); PipeData cmdbuff; int nogo; // Simply return if the link is already enabled // - if (lp->isEnabled) return 1; + if (pInfo.isEnabled) return 1; // Send an enable request to the poller thread handling this link // cmdbuff.req = PipeData::EnFD; - cmdbuff.Parms.Arg.fd = lp->FD; + cmdbuff.Parms.Arg.fd = pInfo.pollFD; nogo = sendCmd((char *)&cmdbuff, sizeof(cmdbuff)); // Verify that all went well @@ -150,7 +153,7 @@ void XrdPollDev::Exclude(XrdLink *lp) // Make sure this link is not enabled // - if (lp->isEnabled) + if (lp->getPollInfo().isEnabled) {XrdLog->Emsg("Poll", "Detach of enabled link", lp->ID); Disable(lp); } @@ -219,15 +222,17 @@ void XrdPollDev::Start(XrdSysSemaphore *syncsem, int &retcode) for (i = 0; i < numpolled; i++) {if (PollTab[i].fd == ReqFD) {xReq = 1; continue;} if (lp = XrdLink::fd2link(PollTab[i].fd)) - if (!(lp->isEnabled)) - XrdLog->Emsg("Poll", "Disabled event occured for", lp->ID); - else {lp->isEnabled = 0; - if (!(PollTab[i].revents & pollOK)) - Finish(lp, Poll2Text(PollTab[i].revents)); - lp->NextJob = jfirst; jfirst = (XrdJob *)lp; - if (!jlast) jlast=(XrdJob *)lp; - num2sched++; - } else LogEvent(&PollTab[i]); + {XrdPollInfo &pInfo = lp->getPollInfo(); + if (!(pInfo.isEnabled)) + XrdLog->Emsg("Poll", "Disabled event occured for", lp->ID); + else {pInfo.isEnabled = false; + if (!(PollTab[i].revents & pollOK)) + Finish(lp, Poll2Text(PollTab[i].revents)); + lp->NextJob = jfirst; jfirst = (XrdJob *)lp; + if (!jlast) jlast=(XrdJob *)lp; + num2sched++; + } + } else LogEvent(&PollTab[i]); PollTab[i].events = POLLREMOVE; PollTab[i].revents = 0; } @@ -261,8 +266,9 @@ void XrdPollDev::doRequests(int maxreq) struct pollfd ptab = {0, 0, 0}; XrdLink *lp; const char *act; - char buff[16], edval; + char buff[16]; int num2do; + bool edval; // To keep ourselves from being swamped, base request read-aheads on the number // of pending poll events. @@ -284,12 +290,12 @@ void XrdPollDev::doRequests(int maxreq) } else if (ReqBuff.req == PipeData::EnFD) {ptab.events = POLLIN | POLLRDNORM; - act = " enable fd "; edval = 0x01; numEnabled++; + act = " enable fd "; edval = true; numEnabled++; } else if (ReqBuff.req == PipeData::DiFD) {ptab.events = POLLREMOVE; num2do++; - act = " disable fd "; edval = 0x00; + act = " disable fd "; edval = false; } else {XrdLog->Emsg("Poll", "Received an invalid poll pipe request"); continue; @@ -298,7 +304,7 @@ void XrdPollDev::doRequests(int maxreq) TRACE(POLL, "Poller " <Emsg("Poll", errno, act); - if (lp) lp->isEnabled = edval; + if (lp) lp->getPollInfo().isEnabled = edval; } } diff --git a/src/Xrd/XrdPollE.icc b/src/Xrd/XrdPollE.icc index 04b17df8a74..dba94ec3c89 100644 --- a/src/Xrd/XrdPollE.icc +++ b/src/Xrd/XrdPollE.icc @@ -97,7 +97,7 @@ void XrdPollE::Disable(XrdLink *lp, const char *etxt) // Simply return if the link is already disabled // - if (!lp->isEnabled) return; + if (!lp->getPollInfo().isEnabled) return; // If Linux 2.6.9 we use EPOLLONESHOT to automatically disable a polled fd. // So, the Disable() method need not do anything. Prior kernels did not have @@ -115,8 +115,8 @@ void XrdPollE::Disable(XrdLink *lp, const char *etxt) // Trace this event // - lp->isEnabled = 0; - TRACEI(POLL, "Poller " <FD); + lp->getPollInfo().isEnabled = 0; + TRACEI(POLL, "Poller " <FDnum()); // Check if this link needs to be rescheduled. If so, the caller better have // the link opMutex lock held for this to work! @@ -131,18 +131,19 @@ void XrdPollE::Disable(XrdLink *lp, const char *etxt) int XrdPollE::Enable(XrdLink *lp) { struct epoll_event myEvents = {ePollEvents, {(void *)lp}}; + XrdPollInfo &pInfo = lp->getPollInfo(); // Simply return if the link is already enabled // - if (lp->isEnabled) return 1; + if (pInfo.isEnabled) return 1; // Enable this fd. Unlike solaris, epoll_ctl() does not block when the pollfd // is being waited upon by another thread. // - lp->isEnabled = 1; + pInfo.isEnabled = true; if (epoll_ctl(PollDfd, EPOLL_CTL_MOD, lp->FDnum(), &myEvents)) {XrdLog->Emsg("Poll", errno, "enable link", lp->ID); - lp->isEnabled = 0; + pInfo.isEnabled = false; return 0; } @@ -162,7 +163,7 @@ void XrdPollE::Exclude(XrdLink *lp) // Make sure this link is not enabled // - if (lp->isEnabled) + if (lp->getPollInfo().isEnabled) {XrdLog->Emsg("Poll", "Detach of enabled link", lp->ID); Disable(lp); } @@ -245,8 +246,9 @@ void XrdPollE::Start(XrdSysSemaphore *syncsem, int &retcode) jfirst = jlast = 0; num2sched = 0; for (i = 0; i < numpolled; i++) {if ((lp = (XrdLink *)PollTab[i].data.ptr)) - if (!(lp->isEnabled)) remFD(lp, PollTab[i].events); - else {lp->isEnabled = 0; + {XrdPollInfo &pInfo = lp->getPollInfo(); + if (!(pInfo.isEnabled)) remFD(lp, PollTab[i].events); + else {pInfo.isEnabled = 0; if (!(PollTab[i].events & pollOK)) Finish(lp, x2Text(PollTab[i].events, eBuff)); lp->NextJob = jfirst; jfirst = (XrdJob *)lp; @@ -257,7 +259,8 @@ void XrdPollE::Start(XrdSysSemaphore *syncsem, int &retcode) if (epoll_ctl(PollDfd,EPOLL_CTL_MOD,lp->FDnum(),&PollTab[i])) XrdLog->Emsg("Poll", errno, "disable link", lp->ID); #endif - } else XrdLog->Emsg("Poll", "null link event!!!!"); + } + } else XrdLog->Emsg("Poll", "null link event!!!!"); } // Schedule the polled links diff --git a/src/Xrd/XrdPollInfo.hh b/src/Xrd/XrdPollInfo.hh new file mode 100644 index 00000000000..6820e4badcf --- /dev/null +++ b/src/Xrd/XrdPollInfo.hh @@ -0,0 +1,58 @@ +#ifndef __XRD_POLLINFO_H__ +#define __XRD_POLLINFO_H__ +/******************************************************************************/ +/* */ +/* X r d P o l l I n f o . h h */ +/* */ +/* (c) 2018 by the Board of Trustees of the Leland Stanford, Jr., University */ +/* Produced by Andrew Hanushevsky for Stanford University under contract */ +/* DE-AC02-76-SFO0515 with the Department of Energy */ +/* */ +/* This file is part of the XRootD software suite. */ +/* */ +/* XRootD is free software: you can redistribute it and/or modify it under */ +/* the terms of the GNU Lesser General Public License as published by the */ +/* Free Software Foundation, either version 3 of the License, or (at your */ +/* option) any later version. */ +/* */ +/* XRootD is distributed in the hope that it will be useful, but WITHOUT */ +/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */ +/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */ +/* License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public License */ +/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */ +/* COPYING (GPL license). If not, see . */ +/* */ +/* The copyright holder's institutional names and contributor's names may not */ +/* be used to endorse or promote products derived from this software without */ +/* specific prior written permission of the institution or contributor. */ +/******************************************************************************/ + +class XrdLink; +class XrdPoll; +struct pollfd; + +class XrdPollInfo +{ +public: + +XrdPollInfo *Next; // Chain of links waiting for a PollPoll event +XrdLink *Link; // Link associated with this object +struct pollfd *PollEnt; // Used only by PollPoll +XrdPoll *Poller; // -> Poller object associated with this object +int pollFD; // Associated file descriptor number +bool inQ; // True -> in a PollPoll event queue +bool isEnabled; // True -> interrupts are enabled +char rsv[2]; // Reserved for future flags + +void Zorch() {Next = 0; PollEnt = 0; + Poller = 0; pollFD = -1; + isEnabled = false; inQ = false; + rsv[0] = 0; rsv[1] = 0; + } + + XrdPollInfo(XrdLink *lnk) : Link(lnk) {Zorch();} + ~XrdPollInfo() {} +}; +#endif diff --git a/src/Xrd/XrdPollPoll.hh b/src/Xrd/XrdPollPoll.hh index e76c0c39512..306d4408a34 100644 --- a/src/Xrd/XrdPollPoll.hh +++ b/src/Xrd/XrdPollPoll.hh @@ -34,6 +34,7 @@ #include "Xrd/XrdPoll.hh" class XrdLink; +class XrdPollInfo; class XrdPollPoll : XrdPoll { @@ -58,15 +59,15 @@ protected: private: void doRequests(int maxreq); -void dqLink(XrdLink *lp); +void dqLink(XrdLink *lp, XrdPollInfo *pInfo); void LogEvent(int req, int pollfd, int cmdfd); void Recover(int numleft); void Restart(int ecode); -struct pollfd *PollTab; //<--- - int PollTNum; // PollMutex protects these elements - XrdLink *PollQ; //<--- - XrdSysMutex PollMutex; - int maxent; +struct pollfd *PollTab; //<--- + int PollTNum; // PollMutex protects these elements + XrdPollInfo *PollQ; //<--- + XrdSysMutex PollMutex; + int maxent; }; #endif diff --git a/src/Xrd/XrdPollPoll.icc b/src/Xrd/XrdPollPoll.icc index d5ab165c07e..aca2c6fe0f3 100644 --- a/src/Xrd/XrdPollPoll.icc +++ b/src/Xrd/XrdPollPoll.icc @@ -36,6 +36,7 @@ #include "XrdSys/XrdSysError.hh" #include "Xrd/XrdLink.hh" +#include "Xrd/XrdPollInfo.hh" #include "Xrd/XrdPollPoll.hh" #include "Xrd/XrdScheduler.hh" @@ -94,6 +95,7 @@ XrdPollPoll::~XrdPollPoll() int XrdPollPoll::Include(XrdLink *lp) { struct pollfd *pfd; + XrdPollInfo &pInfo = lp->getPollInfo(); int ptnum; // Lock down the poll data structure @@ -116,13 +118,13 @@ int XrdPollPoll::Include(XrdLink *lp) // Initialize the polltable entry // pfd = &(PollTab[ptnum]); - pfd->fd = -lp->FD; + pfd->fd = -pInfo.pollFD; pfd->events = POLLIN | POLLRDNORM; pfd->revents = 0; // Record relevant information in the link // - lp->PollEnt = pfd; + pInfo.PollEnt = pfd; if (ptnum == PollTNum) PollTNum++; // All done @@ -138,28 +140,29 @@ int XrdPollPoll::Include(XrdLink *lp) void XrdPollPoll::Disable(XrdLink *lp, const char *etxt) { XrdSysSemaphore mySem(0); + XrdPollInfo &pInfo = lp->getPollInfo(); PipeData cmdbuff[2]; int myerrno = 0; // Check if this link is in the pollQ. If so, remove it. // - if (lp->inQ) dqLink(lp); + if (pInfo.inQ) dqLink(lp, &pInfo); // Simply return if the link is already disabled // - if (!lp->isEnabled) return; + if (!pInfo.isEnabled) return; // Trace this event // - TRACEI(POLL, "Poller " <FD); + TRACEI(POLL, "Poller " <FD; - cmdbuff[0].Parms.Arg.ent = lp->PollEnt - PollTab; + cmdbuff[0].Parms.Arg.fd = pInfo.pollFD; + cmdbuff[0].Parms.Arg.ent = pInfo.PollEnt - PollTab; cmdbuff[1].req = PipeData::Post; cmdbuff[1].Parms.theSem = &mySem; PollPipe.Lock(); @@ -180,27 +183,28 @@ void XrdPollPoll::Disable(XrdLink *lp, const char *etxt) int XrdPollPoll::Enable(XrdLink *lp) { + XrdPollInfo &pInfo = lp->getPollInfo(); PipeData cmdbuff; int myerrno = 0; // Simply return if the link is already enabled // - if (lp->isEnabled) return 1; + if (pInfo.isEnabled) return 1; // Add this link element to the queue // PollMutex.Lock(); - lp->Next = PollQ; - PollQ = lp; - lp->inQ = 1; + pInfo.Next = PollQ; + PollQ = &pInfo; + pInfo.inQ = true; PollMutex.UnLock(); // Send an enable request to the poller thread handling this link // - TRACEI(POLL, "sending poller " <FD); + TRACEI(POLL, "sending poller " <FD; - cmdbuff.Parms.Arg.ent = lp->PollEnt - PollTab; + cmdbuff.Parms.Arg.fd = pInfo.pollFD; + cmdbuff.Parms.Arg.ent = pInfo.PollEnt - PollTab; PollPipe.Lock(); if (write(CmdFD, &cmdbuff, sizeof(cmdbuff)) < 0) myerrno = errno; PollPipe.UnLock(); @@ -221,24 +225,25 @@ int XrdPollPoll::Enable(XrdLink *lp) void XrdPollPoll::Exclude(XrdLink *lp) { + XrdPollInfo &pInfo = lp->getPollInfo(); XrdSysSemaphore mySem(0); PipeData cmdbuff[2]; int myerrno = 0; // Make sure this link is not enabled // - if (lp->isEnabled) + if (pInfo.isEnabled) {XrdLog->Emsg("Poll", "Detach of enabled link", lp->ID); Disable(lp); } - else if (lp->inQ) dqLink(lp); + else if (pInfo.inQ) dqLink(lp, &pInfo); // Send a deatch request to the poller thread handling this link // - TRACEI(POLL, "sending poller " <FD); + TRACEI(POLL, "sending poller " <FD; - cmdbuff[0].Parms.Arg.ent = lp->PollEnt - PollTab; + cmdbuff[0].Parms.Arg.fd = pInfo.pollFD; + cmdbuff[0].Parms.Arg.ent = pInfo.PollEnt - PollTab; cmdbuff[1].req = PipeData::Post; cmdbuff[1].Parms.theSem = &mySem; PollPipe.Lock(); @@ -259,7 +264,8 @@ void XrdPollPoll::Start(XrdSysSemaphore *syncsem, int &retcode) { int numpolled, num2sched; XrdJob *jfirst, *jlast; - XrdLink *plp, *lp, *nlp; + XrdPollInfo *plp, *nlp, *pInfo; + XrdLink *lp; short pollevents; const short pollOK = POLLIN | POLLRDNORM; @@ -300,24 +306,25 @@ void XrdPollPoll::Start(XrdSysSemaphore *syncsem, int &retcode) // PollMutex.Lock(); plp = 0; nlp = PollQ; jfirst = jlast = 0; num2sched = 0; - while ((lp = nlp) && numpolled > 0) - {if ((pollevents = lp->PollEnt->revents)) - {lp->PollEnt->fd = -lp->PollEnt->fd; - if (plp) nlp = plp->Next = lp->Next; - else nlp = PollQ = lp->Next; - numpolled--; lp->inQ = 0; + while ((pInfo = nlp) && numpolled > 0) + {if ((pollevents = pInfo->PollEnt->revents)) + {pInfo->PollEnt->fd = -pInfo->PollEnt->fd; + if (plp) nlp = plp->Next = pInfo->Next; + else nlp = PollQ = pInfo->Next; + numpolled--; pInfo->inQ = false; if (!(pollevents & pollOK)) - Finish(lp, Poll2Text(pollevents)); - if (!(lp->isEnabled)) + Finish(pInfo->Link, Poll2Text(pollevents)); + lp = pInfo->Link; + if (!(pInfo->isEnabled)) XrdLog->Emsg("Poll", "Disabled event occured for", lp->ID); - else {lp->isEnabled = 0; + else {pInfo->isEnabled = false; lp->NextJob = jfirst; jfirst = (XrdJob *)lp; if (!jlast) jlast=(XrdJob *)lp; num2sched++; continue; } } - plp = lp; nlp = lp->Next; + plp = pInfo; nlp = pInfo->Next; } if (numpolled) Recover(numpolled); PollMutex.UnLock(); @@ -383,19 +390,19 @@ void XrdPollPoll::doRequests(int maxreq) if (ReqBuff.req == PipeData::EnFD) {PollTab[pti].events = POLLIN | POLLRDNORM; PollTab[pti].fd = ptfd; - lp->isEnabled = 1; numEnabled++; + lp->getPollInfo().isEnabled = true; numEnabled++; act = " enabled fd "; } else if (ReqBuff.req == PipeData::DiFD) {PollTab[pti].fd = -ptfd; act = " disabled fd "; - lp->isEnabled = 0; + lp->getPollInfo().isEnabled = false; } else if (ReqBuff.req == PipeData::RmFD) {PollTab[pti].fd = -1; doDetach(pti); act = " detached fd "; - lp->isEnabled = 0; + lp->getPollInfo().isEnabled = false; } else {XrdLog->Emsg("Poll", "Received an invalid poll pipe request"); continue; @@ -409,16 +416,16 @@ void XrdPollPoll::doRequests(int maxreq) /* d q L i n k */ /******************************************************************************/ -void XrdPollPoll::dqLink(XrdLink *lp) +void XrdPollPoll::dqLink(XrdLink *lp, XrdPollInfo *pInfo) { - XrdLink *plp, *nlp; + XrdPollInfo *plp, *nlp; // Find matching link in the queue // PollMutex.Lock(); - lp->inQ = 0; + pInfo->inQ = false; plp = 0; nlp = PollQ; - while (nlp && (lp != nlp)) {plp=nlp; nlp = nlp->Next;} + while (nlp && (pInfo != nlp)) {plp=nlp; nlp = nlp->Next;} // If we found the link, remove it. Otherwise complain // @@ -477,7 +484,8 @@ void XrdPollPoll::Recover(int numleft) for (i = 1; i < PollTNum; i++) if (PollTab[i].revents) {if (!(lp = XrdLink::fd2link(PollTab[i].fd))) PollTab[i].fd = -1; - else {lp->isEnabled = 0; PollTab[i].fd = -PollTab[i].fd; + else {lp->getPollInfo().isEnabled = false; + PollTab[i].fd = -PollTab[i].fd; XrdLog->Emsg("Poll","Improper poll event for", lp->ID); } } @@ -489,7 +497,7 @@ void XrdPollPoll::Recover(int numleft) void XrdPollPoll::Restart(int ecode) { - XrdLink *lp; + XrdPollInfo *pInfo; // Issue error message // @@ -499,11 +507,11 @@ void XrdPollPoll::Restart(int ecode) // For any outstanding link here, close the link and detach it // PollMutex.Lock(); - while((lp = PollQ)) - {PollQ = lp->Next; - lp->PollEnt->fd = -1; - Finish(lp, "Unexpected polling error"); - XrdSched->Schedule((XrdJob *)lp); + while((pInfo = PollQ)) + {PollQ = pInfo->Next; + pInfo->PollEnt->fd = -1; + Finish(pInfo->Link, "Unexpected polling error"); + XrdSched->Schedule((XrdJob *)pInfo->Link); } PollMutex.UnLock(); } diff --git a/src/Xrd/XrdSendQ.cc b/src/Xrd/XrdSendQ.cc index 9cb1c366d55..36def214c2b 100644 --- a/src/Xrd/XrdSendQ.cc +++ b/src/Xrd/XrdSendQ.cc @@ -63,12 +63,23 @@ virtual ~LinkShutdown() {} XrdLink *myLink; }; + +/******************************************************************************/ +/* G l o b a l O b j e c t s */ +/******************************************************************************/ + +namespace XrdGlobal +{ +extern XrdSysError Log; +extern XrdScheduler Sched; +}; + +using namespace XrdGlobal; + /******************************************************************************/ /* S t a t i c O b j e c t s */ /******************************************************************************/ -XrdScheduler *XrdSendQ::Sched = 0; -XrdSysError *XrdSendQ::Say = 0; unsigned int XrdSendQ::qWarn = 3; unsigned int XrdSendQ::qMax = 0xffffffff; bool XrdSendQ::qPerm = false; @@ -143,8 +154,8 @@ bool XrdSendQ::QMsg(XrdSendQ::mBuff *theMsg) {char qBuff[80]; snprintf(qBuff, sizeof(qBuff), "%u) reached; %hu message(s) discarded!", qMax, discards); - Say->Emsg("SendQ", mLink.Host(), - "appears to be slow; queue limit (", qBuff); + Log.Emsg("SendQ", mLink.Host(), + "appears to be slow; queue limit (", qBuff); } return false; } @@ -160,7 +171,7 @@ bool XrdSendQ::QMsg(XrdSendQ::mBuff *theMsg) // If there is no active thread handling this queue, schedule one // if (!active) - {Sched->Schedule((XrdJob *)this); + {Sched.Schedule((XrdJob *)this); active = true; } @@ -170,7 +181,7 @@ bool XrdSendQ::QMsg(XrdSendQ::mBuff *theMsg) {char qBuff[32]; qWmsg += qWarn; snprintf(qBuff, sizeof(qBuff), "%ud messages queued!", inQ); - Say->Emsg("SendQ", mLink.Host(), "appears to be slow;", qBuff); + Log.Emsg("SendQ", mLink.Host(), "appears to be slow;", qBuff); } else { if (inQ < qWarn && qWmsg != qWarn) qWmsg = qWarn; } @@ -328,7 +339,7 @@ int XrdSendQ::SendNB(const char *Buff, int Blen) // if (retc <= 0) {if (!retc || errno == EAGAIN || retc == EWOULDBLOCK) return bytesleft; - Say->Emsg("SendQ", errno, "send to", mLink.ID); + Log.Emsg("SendQ", errno, "send to", mLink.ID); return -1; } return bytesleft; @@ -363,7 +374,7 @@ int XrdSendQ::SendNB(const struct iovec *iov, int iocnt, int bytes, int &iovX) if (retc <= 0) {if (!retc || errno == EAGAIN || retc == EWOULDBLOCK) return msgL; - Say->Emsg("SendQ", errno, "send to", mLink.ID); + Log.Emsg("SendQ", errno, "send to", mLink.ID); return -1; } msgL -= retc; @@ -386,7 +397,7 @@ void XrdSendQ::Terminate(XrdLink *lP) { // First step is to see if we need to schedule a shutdown prior to quiting // - if (lP) Sched->Schedule((XrdJob *)new LinkShutdown(lP)); + if (lP) Sched.Schedule((XrdJob *)new LinkShutdown(lP)); // If there is an active thread then we need to let the thread handle the // termination of this object. Otherwise, we can do it now. diff --git a/src/Xrd/XrdSendQ.hh b/src/Xrd/XrdSendQ.hh index 42400c5ebaa..6ddc0dab06d 100644 --- a/src/Xrd/XrdSendQ.hh +++ b/src/Xrd/XrdSendQ.hh @@ -47,8 +47,6 @@ unsigned int Backlog() {return inQ;} virtual void DoIt(); -static void Init(XrdSysError *eP, XrdScheduler *sP) {Say = eP; Sched = sP;} - int Send(const char *buff, int blen); int Send(const struct iovec *iov, int iovcnt, int iotot); @@ -81,8 +79,6 @@ bool QMsg(mBuff *theMsg); void RelMsgs(mBuff *mP); void Scuttle(); -static XrdScheduler *Sched; -static XrdSysError *Say; static unsigned int qWarn; static unsigned int qMax; static bool qPerm; diff --git a/src/XrdCrypto/XrdCryptosslFactory.cc b/src/XrdCrypto/XrdCryptosslFactory.cc index cc2337389a8..b8fc1fe020f 100644 --- a/src/XrdCrypto/XrdCryptosslFactory.cc +++ b/src/XrdCrypto/XrdCryptosslFactory.cc @@ -44,6 +44,9 @@ #include "XrdSys/XrdSysLogger.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSut/XrdSutRndm.hh" + +#include "XrdTls/XrdTlsContext.hh" + #include "XrdCrypto/XrdCryptosslTrace.hh" #include "XrdVersion.hh" @@ -56,76 +59,25 @@ static XrdSysLogger Logger; static XrdSysError eDest(0,"cryptossl_"); -// Mutexes for OpenSSL -XrdSysMutex *XrdCryptosslFactory::CryptoMutexPool[SSLFACTORY_MAX_CRYPTO_MUTEX]; - -/******************************************************************************/ -/* T h r e a d - S a f e n e s s F u n c t i o n s */ -/******************************************************************************/ -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#ifdef __solaris__ -extern "C" { -#endif -static unsigned long sslfactory_id_callback(void) { - return (unsigned long)XrdSysThread::ID(); -} - -static void sslfactory_lock(int mode, int n, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) { - if (XrdCryptosslFactory::CryptoMutexPool[n]) { - XrdCryptosslFactory::CryptoMutexPool[n]->Lock(); - } - } else { - if (XrdCryptosslFactory::CryptoMutexPool[n]) { - XrdCryptosslFactory::CryptoMutexPool[n]->UnLock(); - } - } -} -#ifdef __solaris__ -} -#endif -#endif - - //______________________________________________________________________________ XrdCryptosslFactory::XrdCryptosslFactory() : XrdCryptoFactory("ssl",XrdCryptosslFactoryID) { // Constructor: init the needed components of the OpenSSL library - EPNAME("sslFactory::XrdCryptosslFactory"); // Init SSL ... - SSL_library_init(); - // ... and its error strings - SSL_load_error_strings(); - // Load Ciphers - OpenSSL_add_all_ciphers(); - // Load Msg Digests - OpenSSL_add_all_digests(); - - if (SSLFACTORY_MAX_CRYPTO_MUTEX < CRYPTO_num_locks() ) { - SetTrace(0); - TRACE(ALL, "WARNING: do not have enough crypto mutexes as required by crypto_ssl"); - TRACE(ALL, " (suggestion: recompile increasing SSLFACTORY_MAX_CRYPTO_MUTEX to "<< CRYPTO_num_locks()<<")"); - } else { - // This code taken from XrdSecProtocolssl thread-safety - for (int i = 0; i < SSLFACTORY_MAX_CRYPTO_MUTEX; i++) { - XrdCryptosslFactory::CryptoMutexPool[i] = new XrdSysMutex(); - } - } + // + static const char *eText = XrdTlsContext::InitSSL(); -#if defined(OPENSSL_THREADS) - // Thread support enabled: set callback functions - CRYPTO_set_locking_callback(sslfactory_lock); - CRYPTO_set_id_callback(sslfactory_id_callback); -#else - SetTrace(0); - TRACE(ALL, "WARNING: OpenSSL lacks thread support: possible thread-safeness problems!"); - TRACE(ALL, " (suggestion: recompile enabling thread support)"); -#endif + // Make sure all went well (we need to possibly abort here) + // + if (eText) + {std::cerr <<"XrdCryptosslFactory: " < #include #include #include #include #include +#include +#include #define XRHTTP_TK_GRACETIME 600 @@ -93,7 +97,6 @@ XrdOucGMap *XrdHttpProtocol::servGMap = 0; // Grid mapping service int XrdHttpProtocol::sslverifydepth = 9; SSL_CTX *XrdHttpProtocol::sslctx = 0; BIO *XrdHttpProtocol::sslbio_err = 0; -XrdCryptoFactory *XrdHttpProtocol::myCryptoFactory = 0; XrdHttpSecXtractor *XrdHttpProtocol::secxtractor = 0; struct XrdHttpProtocol::XrdHttpExtHandlerInfo XrdHttpProtocol::exthandler[MAX_XRDHTTPEXTHANDLERS]; int XrdHttpProtocol::exthandlercnt = 0; @@ -1629,32 +1632,16 @@ extern "C" int verify_callback(int ok, X509_STORE_CTX * store) { return ok; } - - - - /// Initialization of the ssl security int XrdHttpProtocol::InitSecurity() { - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_ciphers(); - OpenSSL_add_all_algorithms(); - OpenSSL_add_all_digests(); - -#ifdef HAVE_XRDCRYPTO -#ifndef WIN32 - // Borrow the initialization of XrdCryptossl, in order to share the - // OpenSSL threading bits - if (!(myCryptoFactory = XrdCryptoFactory::GetCryptoFactory("ssl"))) { - cerr << "Cannot instantiate crypto factory ssl" << endl; - exit(1); - } - -#endif -#endif + static const char *eText = XrdTlsContext::InitSSL(); + if (eText) + {cerr << "XrdHttp: " < #include "XrdSys/XrdSysError.hh" -#include "Xrd/XrdLink.hh" #include "Xrd/XrdPollPoll.hh" #include "Xrd/XrdScheduler.hh" diff --git a/src/XrdTls/XrdTlsConnection.cc b/src/XrdTls/XrdTlsConnection.cc index 65dbedf5ebe..83a98696e2e 100644 --- a/src/XrdTls/XrdTlsConnection.cc +++ b/src/XrdTls/XrdTlsConnection.cc @@ -35,7 +35,7 @@ XrdTlsConnection::XrdTlsConnection( XrdTlsContext &ctx, int sfd, XrdTlsConnection::RW_Mode rwm, XrdTlsConnection::HS_Mode hsm, bool isClient ) - : hsDone( false ) + : ssl(0), sFD(-1), hsDone(false ) { // Simply initialize this object and throw an exception if it fails @@ -66,6 +66,18 @@ int XrdTlsConnection::Connect() return error; } +/******************************************************************************/ +/* E r r 2 T e x t */ +/******************************************************************************/ + +std::string XrdTlsConnection::Err2Text(int errc) +{ + char eBuff[1024]; + + ERR_error_string_n(errc, eBuff, sizeof(eBuff)); + return std::string(eBuff); +} + /******************************************************************************/ /* G e t E r r o r s */ /******************************************************************************/ @@ -163,9 +175,68 @@ const char *XrdTlsConnection::Init( XrdTlsContext &ctx, int sfd, sFD = sfd; if (wbio == 0) wbio = rbio; SSL_set_bio( ssl, rbio, wbio ); - return 0; + +// Do an acccept or connect depending on who this is +// + int rc = (isClient ? Connect() : Accept()); + return (rc == SSL_ERROR_NONE ? 0 : "TLS handshake failed"); } +/******************************************************************************/ +/* P e e k */ +/******************************************************************************/ + + int XrdTlsConnection::Peek( char *buffer, size_t size, int &bytesPeek ) + { + int error; + + //------------------------------------------------------------------------ + // If necessary, SSL_read() will negotiate a TLS/SSL session, so we don't + // have to explicitly call SSL_connect or SSL_do_handshake. + //------------------------------------------------------------------------ + do{int rc = SSL_peek( ssl, buffer, size ); + + // Note that according to SSL whenever rc > 0 then SSL_ERROR_NONE can be + // returned to the caller. So, we short-circuit all the error handling. + // + if( rc > 0 ) + {bytesPeek = rc; + return SSL_ERROR_NONE; + } + + // We have a potential error. Get the SSL error code and whether or + // not the handshake actually is finished (semi-accurate) + // + hsDone = bool( SSL_is_init_finished( ssl ) ); + error = SSL_get_error( ssl, rc ); + + // The connection creator may wish that we wait for the handshake to + // complete. This is a tricky issue for non-blocking bio's as a read + // may force us to wait until writes are possible. All of this is rare! + // + if ((!hsMode || hsDone || (error != SSL_ERROR_WANT_READ && + error != SSL_ERROR_WANT_WRITE)) + || (hsMode == xyBlock && error == SSL_ERROR_WANT_READ)) return error; + + } while(Wait4OK(error == SSL_ERROR_WANT_READ)); + + return SSL_ERROR_SYSCALL; + } + +/******************************************************************************/ +/* P e n d i n g */ +/******************************************************************************/ + +int XrdTlsConnection::Pending(bool any) +{ + if (!any) return SSL_pending(ssl); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + return SSL_pending(ssl) != 0; +#else + return SSL_has_pending(ssl); +#endif +} + /******************************************************************************/ /* P r i n t E r r s */ /******************************************************************************/ @@ -182,7 +253,15 @@ void XrdTlsConnection::PrintErrs(const char *pfx, XrdSysError *eDest) int XrdTlsConnection::Read( char *buffer, size_t size, int &bytesRead ) { int error; - +/* + if (!(cAttr & rBlocking)) + {size = SSL_pending(ssl); + if (size < 1) + {bytesRead = 0; + return SSL_ERROR_NONE; + } + } +*/ //------------------------------------------------------------------------ // If necessary, SSL_read() will negotiate a TLS/SSL session, so we don't // have to explicitly call SSL_connect or SSL_do_handshake. @@ -251,7 +330,8 @@ void XrdTlsConnection::Shutdown(bool force) /* W r i t e */ /******************************************************************************/ - int XrdTlsConnection::Write( char *buffer, size_t size, int &bytesWritten ) + int XrdTlsConnection::Write( const char *buffer, size_t size, + int &bytesWritten ) { int error; diff --git a/src/XrdTls/XrdTlsConnection.hh b/src/XrdTls/XrdTlsConnection.hh index f117f68d148..2fb92ca6bce 100644 --- a/src/XrdTls/XrdTlsConnection.hh +++ b/src/XrdTls/XrdTlsConnection.hh @@ -103,6 +103,16 @@ enum HS_Mode XrdTlsContext *Context() {return tlsctx;} +//------------------------------------------------------------------------ +//! Convert rror code to explanatory text. +//! +//! @param errc The error code returned by this object. +//! +//! @return A string containg the error text. +//------------------------------------------------------------------------ + + std::string Err2Text(int errc); + //------------------------------------------------------------------------ //! Retrieve all errors encountered so far. //! @@ -139,6 +149,28 @@ enum HS_Mode const char *Init( XrdTlsContext &ctx, int sfd, RW_Mode rwm, HS_Mode hsm, bool isClient ); + +//------------------------------------------------------------------------ +//! Peek at the TLS connection data +//! (If necessary, will establish a TLS/SSL session.) +//------------------------------------------------------------------------ + + int Peek( char *buffer, size_t size, int &bytesPeek ); + +//------------------------------------------------------------------------ +//! Check if data is pending or readable. +//! +//! @param any True to return in any data is in the queue. False to +//! return the number of processed bytes. +//! @return any = true: 1 is returned if there is data in the queue +//! (processed or not). 0 is returned o/w. +//! any = false: the number of processed bytes that are +//! available. These are not necesarily data +//! bytes. A subsequent read may still return 0. +//------------------------------------------------------------------------ + + int Pending(bool any=true); + //------------------------------------------------------------------------ //! Print all errors encountered so far. //! @@ -170,7 +202,7 @@ enum HS_Mode //! (If necessary, will establish a TLS/SSL session.) //------------------------------------------------------------------------ - int Write( char *buffer, size_t size, int &bytesWritten ); + int Write( const char *buffer, size_t size, int &bytesWritten ); //------------------------------------------------------------------------ //! @return : true if the TLS/SSL session is not established yet, @@ -221,7 +253,7 @@ bool Wait4OK(bool wantRead); char cAttr; static const int isServer = 0x01; static const int rBlocking = 0x02; - static const int wBlocking = 0x03; + static const int wBlocking = 0x04; char hsMode; static const int noBlock = 0; diff --git a/src/XrdTls/XrdTlsContext.cc b/src/XrdTls/XrdTlsContext.cc index d8113ba5000..aaf500c5378 100644 --- a/src/XrdTls/XrdTlsContext.cc +++ b/src/XrdTls/XrdTlsContext.cc @@ -91,6 +91,7 @@ void InitTLS() // happen when a server and a client instance happen to be both defined. // if (initDone) return; + initDone = true; // SSL library initialisation // @@ -241,6 +242,25 @@ std::string XrdTlsContext::GetErrs(const char *pfx) return eBlob; } +/******************************************************************************/ +/* I n i t S S L */ +/******************************************************************************/ + +const char *XrdTlsContext::InitSSL() +{ + +// Disallow use if this object unless SSL provides thread-safety! +// +#ifndef OPENSSL_THREADS + return "Installed OpenSSL lacks the required thread support!"; +#endif + +// Initialize the library (one time call) +// + InitTLS(); + return 0; +} + /******************************************************************************/ /* P r i n t E r r s */ /******************************************************************************/ @@ -256,6 +276,4 @@ void XrdTlsContext::PrintErrs(const char *pfx, XrdSysError *eDest) do {eMsg(eDest, pfx, ERR_reason_error_string(eCode)); } while((eCode = ERR_get_error())); } - - eText = 0; } diff --git a/src/XrdTls/XrdTlsContext.hh b/src/XrdTls/XrdTlsContext.hh index 54313f50d7f..4da35ca55b5 100644 --- a/src/XrdTls/XrdTlsContext.hh +++ b/src/XrdTls/XrdTlsContext.hh @@ -49,6 +49,15 @@ public: std::string GetErrs(const char *pfx=0); +//------------------------------------------------------------------------ +//! Simply initialize the SSL library. +//! +//! @return =0 Library initialized. +//! !0 Library not initialized, return string indicates why. +//------------------------------------------------------------------------ +static +const char *InitSSL(); + //------------------------------------------------------------------------ //! Print all errors encountered so far. //! @@ -71,9 +80,11 @@ std::string GetErrs(const char *pfx=0); //! @param prot The protocols that the context should support. Choose //! one of the enums defined below. Note that doSSL includes //! TLS but deprecates SSL protocols mainly for https support. +//! When prot is doNONE then only the SSL library is +//! initialized but otherwise, no context is established. //------------------------------------------------------------------------ - enum Protocol {doSSL = 0, doTLS}; + enum Protocol {doNONE = 0, doSSL, doTLS}; XrdTlsContext(const char *cert=0, const char *key=0, Protocol prot=doTLS); diff --git a/src/XrdUtils.cmake b/src/XrdUtils.cmake index 25722207d92..33b7f9d6f86 100644 --- a/src/XrdUtils.cmake +++ b/src/XrdUtils.cmake @@ -166,12 +166,16 @@ add_library( Xrd/XrdInfo.cc Xrd/XrdInfo.hh Xrd/XrdJob.hh Xrd/XrdLink.cc Xrd/XrdLink.hh + Xrd/XrdLinkCtl.cc Xrd/XrdLinkCtl.hh + Xrd/XrdLinkXeq.cc Xrd/XrdLinkXeq.hh Xrd/XrdLinkMatch.cc Xrd/XrdLinkMatch.hh + Xrd/XrdGlobals.cc Xrd/XrdPoll.cc Xrd/XrdPoll.hh Xrd/XrdPollDev.hh Xrd/XrdPollDev.icc Xrd/XrdPollE.hh Xrd/XrdPollE.icc + Xrd/XrdPollInfo.hh Xrd/XrdPollPoll.hh Xrd/XrdPollPoll.icc Xrd/XrdProtocol.cc Xrd/XrdProtocol.hh diff --git a/src/XrdXml/tinyxml.cpp b/src/XrdXml/tinyxml.cpp index 9c161dfcb93..c3a24ed1fb4 100644 --- a/src/XrdXml/tinyxml.cpp +++ b/src/XrdXml/tinyxml.cpp @@ -667,7 +667,7 @@ int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const } -int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* valp ) const { const TiXmlAttribute* node = attributeSet.Find( name ); if ( !node ) @@ -675,7 +675,7 @@ int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) co int ival = 0; int result = node->QueryIntValue( &ival ); - *value = (unsigned)ival; + *valp = (unsigned)ival; return result; } diff --git a/src/XrdXrootd/XrdXrootdConfig.cc b/src/XrdXrootd/XrdXrootdConfig.cc index e9562305786..7e1b24b8432 100644 --- a/src/XrdXrootd/XrdXrootdConfig.cc +++ b/src/XrdXrootd/XrdXrootdConfig.cc @@ -163,6 +163,7 @@ int XrdXrootdProtocol::Configure(char *parms, XrdProtocol_Config *pi) Window = pi->WSize; WANPort = pi->WANPort; WANWindow = pi->WANWSize; + tlsCtx = pi->tlsCtx; // Record globally accessible values // @@ -470,6 +471,10 @@ int XrdXrootdProtocol::Configure(char *parms, XrdProtocol_Config *pi) } } +// Finally note whether or not we have TLS enabled +// + if (tlsCtx) myRole |= kXR_haveTls; + // Return success // free(adminp); @@ -585,6 +590,10 @@ int XrdXrootdProtocol::ConfigSecurity(XrdOucEnv &xEnv, const char *cfn) myGNLen = n; } +// TLS support is independent of security per se. Record context, if any. +// + xEnv.PutPtr("XrdTLSContext*", (void *)tlsCtx); + // Check if we need to loadanything // if (!SecLib) @@ -1641,16 +1650,15 @@ int XrdXrootdProtocol::xreq(XrdOucStream &Config) char *val; static struct requireopts {const char *opname; int opval;} reqopts[] = { - {"all", TRACE_ALL}, - {"getfile", TRACE_EMSG}, - {"login", TRACE_EMSG}, - {"modfs", TRACE_DEBUG}, - {"openr", TRACE_FS}, - {"openw", TRACE_LOGIN}, - {"putfile", TRACE_LOGIN}, - {"tpc", TRACE_MEM} + {"all", kXR_tlsAll}, + {"gpfile", kXR_tlsGPFile}, + {"login", kXR_tlsLogin}, + {"modfs", kXR_tlsModFS}, + {"openr", kXR_tlsOpenR}, + {"openw", kXR_tlsOpenW}, + {"tpc", kXR_tlsTPC} }; - int i, trval = 0, numopts = sizeof(reqopts)/sizeof(struct requireopts); + int i, numopts = sizeof(reqopts)/sizeof(struct requireopts); bool neg; if (!(val = Config.GetWord())) @@ -1663,12 +1671,12 @@ int XrdXrootdProtocol::xreq(XrdOucStream &Config) {eDest.Emsg("config", "require option not specified"); return 1;} while (val) - {if (!strcmp(val, "off")) trval = 0; + {if (!strcmp(val, "off")) myRole &= ~kXR_tlsAll; else {if ((neg = (val[0] == '-' && val[1]))) val++; for (i = 0; i < numopts; i++) {if (!strcmp(val, reqopts[i].opname)) - {if (neg) trval &= ~reqopts[i].opval; - else trval |= reqopts[i].opval; + {if (neg) myRole &= ~reqopts[i].opval; + else myRole |= reqopts[i].opval; break; } } @@ -1680,7 +1688,12 @@ int XrdXrootdProtocol::xreq(XrdOucStream &Config) val = Config.GetWord(); } -// XrdXrootdTrace->What = trval; +// If there are any TLS requirement then TLS must have been configured. +// + if (myRole & kXR_tlsAll && !tlsCtx) + {eDest.Emsg("config", "Unable to honor requirement; TLS not configured!"); + return 1; + } return 0; } diff --git a/src/XrdXrootd/XrdXrootdProtocol.cc b/src/XrdXrootd/XrdXrootdProtocol.cc index 4a84caf5d19..a1659fa5b14 100644 --- a/src/XrdXrootd/XrdXrootdProtocol.cc +++ b/src/XrdXrootd/XrdXrootdProtocol.cc @@ -69,6 +69,7 @@ char *XrdXrootdProtocol::digParm = 0; XrdXrootdFileLock *XrdXrootdProtocol::Locker; XrdSecService *XrdXrootdProtocol::CIA = 0; XrdSecProtector *XrdXrootdProtocol::DHS = 0; +XrdTlsContext *XrdXrootdProtocol::tlsCtx = 0; char *XrdXrootdProtocol::SecLib = 0; char *XrdXrootdProtocol::pidPath = strdup("/tmp"); XrdScheduler *XrdXrootdProtocol::Sched; diff --git a/src/XrdXrootd/XrdXrootdProtocol.hh b/src/XrdXrootd/XrdXrootdProtocol.hh index 01994d837ae..23200de871e 100644 --- a/src/XrdXrootd/XrdXrootdProtocol.hh +++ b/src/XrdXrootd/XrdXrootdProtocol.hh @@ -80,6 +80,7 @@ class XrdSfsFileSystem; class XrdSecProtocol; class XrdBuffer; class XrdLink; +class XrdTlsContext; class XrdXrootdAioReq; class XrdXrootdFile; class XrdXrootdFileLock; @@ -254,6 +255,7 @@ static XrdSfsFileSystem *osFS; // The filesystem static XrdSfsFileSystem *digFS; // The filesystem (digFS) static XrdSecService *CIA; // Authentication Server static XrdSecProtector *DHS; // Protection Server +static XrdTlsContext *tlsCtx; // Protection Server TLS available static XrdXrootdFileLock *Locker; // File lock handler static XrdScheduler *Sched; // System scheduler static XrdBuffManager *BPool; // Buffer manager diff --git a/src/XrdXrootd/XrdXrootdXeq.cc b/src/XrdXrootd/XrdXrootdXeq.cc index e388eb0588b..19bc83ec269 100644 --- a/src/XrdXrootd/XrdXrootdXeq.cc +++ b/src/XrdXrootd/XrdXrootdXeq.cc @@ -751,7 +751,7 @@ int XrdXrootdProtocol::do_Endsess() // Terminate the indicated session, if possible. This could also be a self-termination. // if ((sessID.FD == 0 && sessID.Inst == 0) - || !(rc = Link->Terminate(Link, sessID.FD, sessID.Inst))) return -1; + || !(rc = Link->Terminate(0, sessID.FD, sessID.Inst))) return -1; // Trace this request // @@ -1748,7 +1748,8 @@ int XrdXrootdProtocol::do_Protocol(ServerResponseBody_Protocol *rsp) ServerResponseBody_Protocol theResp; ServerResponseBody_Protocol *respP = (rsp ? rsp : &theResp); - int RespLen = kXR_ShortProtRespLen; + int rc, RespLen = kXR_ShortProtRespLen; + bool doTLS = false; // Keep Statistics // @@ -1764,6 +1765,8 @@ int XrdXrootdProtocol::do_Protocol(ServerResponseBody_Protocol *rsp) if (DHS && cvn >= kXR_PROTSIGNVERSION && Request.protocol.flags & kXR_secreqs) RespLen += DHS->ProtResp(respP->secreq, *(Link->AddrInfo()), cvn); + doTLS = (Request.protocol.flags & kXR_wantTls) != 0 + && (myRole & kXR_haveTls) != 0 && rsp == 0; respP->flags = theRle; } else { respP->flags = theRlf; @@ -1771,8 +1774,20 @@ int XrdXrootdProtocol::do_Protocol(ServerResponseBody_Protocol *rsp) // Return info // - respP->pval = verNum; - return (rsp ? RespLen : Response.Send((void *)&theResp,RespLen)); + respP->pval = verNum; + if (rsp) return RespLen; + rc = Response.Send((void *)&theResp,RespLen); + +// If the clientwants to start using TLS, enable it now. If we fail then we +// have no choice but to terminate the connection. +// + if (rc == 0 && doTLS) + {if (!Link->setTLS(true)) + {eDest.Emsg("Xeq", "Unable to enable tls for", Link->ID); + rc = -1; + } + } + return rc; } /******************************************************************************/ @@ -3448,20 +3463,20 @@ int XrdXrootdProtocol::fsOvrld(char opC, const char *Path, char *Cgi) // if (OD_Bypass && clientPV & XrdOucEI::uUrlOK && (pOff = XrdOucUtils::isFWD(Path, &port, dest, sizeof(dest)))) - { rdrResp[1].iov_base = (void *)&negOne; + { rdrResp[1].iov_base = (char *)&negOne; rdrResp[1].iov_len = sizeof(negOne); - rdrResp[2].iov_base = (void *)prot; + rdrResp[2].iov_base = (char *)prot; rdrResp[2].iov_len = 7; // root:// - rdrResp[3].iov_base = (void *)dest; + rdrResp[3].iov_base = (char *)dest; rdrResp[3].iov_len = strlen(dest); // host:port - rdrResp[4].iov_base = (void *)&slash; + rdrResp[4].iov_base = (char *)&slash; rdrResp[4].iov_len = (*Path == '/' ? 1 : 0); // / or nil for objid - rdrResp[5].iov_base = (void *)(Path+pOff); + rdrResp[5].iov_base = (char *)(Path+pOff); rdrResp[5].iov_len = strlen(Path+pOff); // path if (Cgi && *Cgi) - {rdrResp[6].iov_base = (void *)? + {rdrResp[6].iov_base = (char *)? rdrResp[6].iov_len = sizeof(quest); // ? - rdrResp[7].iov_base = (void *)Cgi; + rdrResp[7].iov_base = (char *)Cgi; rdrResp[7].iov_len = strlen(Cgi); // cgi iovNum = 8; } else iovNum = 6;